feat: Flutter APK 编译成功 + Gradle 配置修复 + APK 下载部署 + 待优化清单
- 通过 qemu-user-static 实现 ARM64 主机编译 Android APK (51MB) - 修复 Gradle: Aliyun 镜像 + PREFER_SETTINGS + JVM 内存 1536M - 部署 APK 到 configs/downloads/, Web 下载接口已验证 (HTTP 200) - 新增 Flutter TODO.md: 10项待优化 (P0/P1/P2 分级) - 新增 pm_soul.md, 更新 routes.rs APK 下载路由 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -79,6 +79,14 @@ struct PlaylistSnapshot {
|
||||
current_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AppInfoResponse {
|
||||
version: String,
|
||||
apk_available: bool,
|
||||
apk_size: Option<u64>,
|
||||
download_url: &'static str,
|
||||
}
|
||||
|
||||
pub(crate) fn build_routes(
|
||||
tx: mpsc::Sender<Envelope>,
|
||||
state: Arc<HttpState>,
|
||||
@@ -103,6 +111,7 @@ pub(crate) fn build_routes(
|
||||
let media_api = video_list_route(Arc::clone(&state))
|
||||
.or(video_upload_route(Arc::clone(&state)))
|
||||
.or(video_delete_route(Arc::clone(&state)))
|
||||
.or(app_info_route(Arc::clone(&state)))
|
||||
.or(wifi_status_route(tx.clone(), Arc::clone(&state)))
|
||||
.or(wifi_scan_route(tx.clone(), Arc::clone(&state)))
|
||||
.or(wifi_connect_route(tx.clone(), Arc::clone(&state)))
|
||||
@@ -496,6 +505,31 @@ fn video_list_route(
|
||||
})
|
||||
}
|
||||
|
||||
fn app_info_route(
|
||||
state: Arc<HttpState>,
|
||||
) -> impl Filter<Extract = impl Reply, Error = warp::Rejection> + Clone {
|
||||
warp::path!("api" / "app" / "info")
|
||||
.and(warp::get())
|
||||
.and(with_state(state))
|
||||
.and_then(|state: Arc<HttpState>| async move {
|
||||
let apk_path = downloads_dir(state.config().as_ref()).join("showen-app.apk");
|
||||
let apk_size = std::fs::metadata(&apk_path)
|
||||
.ok()
|
||||
.filter(|meta| meta.is_file())
|
||||
.map(|meta| meta.len());
|
||||
|
||||
Ok::<_, Infallible>(json_response(
|
||||
StatusCode::OK,
|
||||
&AppInfoResponse {
|
||||
version: "0.1.0".to_string(),
|
||||
apk_available: apk_size.is_some(),
|
||||
apk_size,
|
||||
download_url: "/download/showen-app.apk",
|
||||
},
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn video_upload_route(
|
||||
state: Arc<HttpState>,
|
||||
) -> impl Filter<Extract = impl Reply, Error = warp::Rejection> + Clone {
|
||||
@@ -1900,6 +1934,8 @@ const WEB_UI_HTML: &str = r##"<!doctype html>
|
||||
.download-qr img{display:block;width:180px;height:180px;border-radius:12px;background:#fff}
|
||||
.download-actions{display:flex;gap:10px;flex-wrap:wrap}
|
||||
.download-actions>*{flex:1}
|
||||
.download-meta{font-size:13px;color:var(--text);margin-bottom:14px;padding:10px 12px;border-radius:12px;background:rgba(255,255,255,.03);border:1px solid var(--border)}
|
||||
.download-meta.warn{color:var(--amber);border-color:rgba(245,158,11,.3);background:rgba(245,158,11,.08)}
|
||||
.download-note{font-size:12px;color:var(--muted);margin-top:12px}
|
||||
|
||||
.tabs{display:flex;gap:4px;background:var(--surface);border-radius:var(--radius);padding:4px;margin-bottom:16px;overflow-x:auto}
|
||||
@@ -1955,7 +1991,8 @@ const WEB_UI_HTML: &str = r##"<!doctype html>
|
||||
<div id="download-modal" class="modal-backdrop" onclick="closeDownloadModal(event)">
|
||||
<div class="download-modal">
|
||||
<h2>下载 Showen App</h2>
|
||||
<p>扫描二维码或点击下载,支持蓝牙配网和远程控制</p>
|
||||
<p id="download-modal-desc">扫描二维码或点击下载,支持蓝牙配网和远程控制</p>
|
||||
<div id="download-meta" class="download-meta">正在获取 App 信息...</div>
|
||||
<div class="download-qr"><img id="download-qr-image" alt="Showen App 下载二维码"></div>
|
||||
<div class="download-actions">
|
||||
<a id="download-apk-link" class="btn header-link" href="/download/showen-app.apk" download>下载 APK</a>
|
||||
@@ -2136,8 +2173,43 @@ var cachedConfig=null,ws=null,wsReady=false;
|
||||
var fmCurrentDir='videos',fmCurrentPath='';
|
||||
function $(id){return document.getElementById(id)}
|
||||
function toast(msg,err){var el=$('toast');el.textContent=msg;el.className='toast '+(err?'err':'ok');el.style.display='block';clearTimeout(el._t);el._t=setTimeout(function(){el.style.display='none'},3000)}
|
||||
function appDownloadUrl(){return location.origin+'/download/showen-app.apk'}
|
||||
function openDownloadModal(){var modal=$('download-modal');var url=appDownloadUrl();$('download-apk-link').href=url;$('download-qr-image').src='https://api.qrserver.com/v1/create-qr-code/?size=180x180&data='+encodeURIComponent(url);modal.classList.add('open')}
|
||||
function formatBytes(size){if(size===undefined||size===null||isNaN(size))return '--';if(size<1024)return size+' B';if(size<1048576)return(size/1024).toFixed(1)+' KB';if(size<1073741824)return(size/1048576).toFixed(1)+' MB';return(size/1073741824).toFixed(1)+' GB'}
|
||||
function updateDownloadModal(info){
|
||||
var meta=$('download-meta'),link=$('download-apk-link'),qr=$('download-qr-image'),desc=$('download-modal-desc');
|
||||
if(!info||!info.apk_available){
|
||||
meta.textContent='APK 尚未发布,请稍后再试';
|
||||
meta.className='download-meta warn';
|
||||
link.style.display='none';
|
||||
qr.style.display='none';
|
||||
qr.removeAttribute('src');
|
||||
desc.textContent='当前暂未提供可下载的 APK 安装包';
|
||||
return;
|
||||
}
|
||||
var url=location.origin+(info.download_url||'/download/showen-app.apk');
|
||||
meta.textContent='版本 '+(info.version||'0.1.0')+' · '+formatBytes(info.apk_size);
|
||||
meta.className='download-meta';
|
||||
link.style.display='inline-flex';
|
||||
link.href=info.download_url||'/download/showen-app.apk';
|
||||
qr.style.display='block';
|
||||
qr.src='https://api.qrserver.com/v1/create-qr-code/?size=180x180&data='+encodeURIComponent(url);
|
||||
desc.textContent='扫描二维码或点击下载,支持蓝牙配网和远程控制';
|
||||
}
|
||||
function openDownloadModal(){
|
||||
var modal=$('download-modal');
|
||||
$('download-meta').textContent='正在获取 App 信息...';
|
||||
$('download-meta').className='download-meta';
|
||||
$('download-apk-link').style.display='none';
|
||||
$('download-qr-image').style.display='none';
|
||||
$('download-qr-image').removeAttribute('src');
|
||||
$('download-modal-desc').textContent='扫描二维码或点击下载,支持蓝牙配网和远程控制';
|
||||
modal.classList.add('open');
|
||||
fetch('/api/app/info').then(function(r){if(!r.ok)throw new Error('加载 App 信息失败');return r.json()}).then(updateDownloadModal).catch(function(){
|
||||
$('download-meta').textContent='APK 信息加载失败,请稍后再试';
|
||||
$('download-meta').className='download-meta warn';
|
||||
$('download-apk-link').style.display='none';
|
||||
$('download-qr-image').style.display='none';
|
||||
})
|
||||
}
|
||||
function closeDownloadModal(ev){if(ev&&ev.target!==$('download-modal'))return;$('download-modal').classList.remove('open')}
|
||||
document.addEventListener('keydown',function(ev){if(ev.key==='Escape')closeDownloadModal()})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user