feat(android): 创建 Android WebView 壳应用
- 添加 MainActivity 和 WebView 配置 - 添加 AssetReader JS 接口用于读取本地文件 - 支持全屏、横屏模式 - 添加错误页面和测试页面 - 添加 Gradle 构建配置和一键构建脚本
This commit is contained in:
parent
b7f16e1444
commit
327f03c562
71
android/README.md
Normal file
71
android/README.md
Normal file
@ -0,0 +1,71 @@
|
||||
# Android WebView 壳
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
android/
|
||||
├── app/src/main/
|
||||
│ ├── java/com/iptv/app/
|
||||
│ │ └── MainActivity.java # WebView 主活动
|
||||
│ ├── res/
|
||||
│ │ ├── layout/activity_main.xml
|
||||
│ │ ├── values/
|
||||
│ │ │ ├── strings.xml
|
||||
│ │ │ ├── styles.xml
|
||||
│ │ │ └── colors.xml
|
||||
│ │ └── mipmap-*/ # 图标资源
|
||||
│ ├── assets/
|
||||
│ │ └── www/ # 打包的 Web 资源 (从 ui/dist-web 复制)
|
||||
│ └── AndroidManifest.xml
|
||||
├── build.gradle # 项目级构建配置
|
||||
├── settings.gradle
|
||||
└── gradle.properties
|
||||
```
|
||||
|
||||
## 构建步骤
|
||||
|
||||
### 1. 构建 Web UI
|
||||
|
||||
```bash
|
||||
cd ../ui
|
||||
npm install
|
||||
npm run build:web
|
||||
```
|
||||
|
||||
### 2. 复制资源到 Android
|
||||
|
||||
```bash
|
||||
# 将构建好的 web 资源复制到 Android assets
|
||||
cp -r ../ui/dist-web/* app/src/main/assets/www/
|
||||
```
|
||||
|
||||
### 3. 构建 APK
|
||||
|
||||
```bash
|
||||
./gradlew assembleDebug
|
||||
```
|
||||
|
||||
APK 输出位置: `app/build/outputs/apk/debug/app-debug.apk`
|
||||
|
||||
## 开发模式
|
||||
|
||||
如需连接开发服务器测试,修改 `MainActivity.java`:
|
||||
|
||||
```java
|
||||
private static final String LOAD_MODE = "remote";
|
||||
private static final String REMOTE_URL = "http://你的IP:5173";
|
||||
```
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ WebView 加载 Web UI
|
||||
- ✅ 全屏无标题栏
|
||||
- ✅ 下拉刷新
|
||||
- ✅ 返回键支持页面后退
|
||||
- ✅ 视频全屏自动横屏
|
||||
- ✅ 暗色主题
|
||||
|
||||
## 权限
|
||||
|
||||
- `INTERNET` - 网络访问
|
||||
- `ACCESS_NETWORK_STATE` - 网络状态检测
|
||||
33
android/app/build.gradle
Normal file
33
android/app/build.gradle
Normal file
@ -0,0 +1,33 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.iptv.app'
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.iptv.app"
|
||||
minSdk 21
|
||||
targetSdk 34
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
}
|
||||
1
android/app/proguard-rules.pro
vendored
Normal file
1
android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1 @@
|
||||
# ProGuard rules
|
||||
27
android/app/src/main/AndroidManifest.xml
Normal file
27
android/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.iptv.app">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:screenOrientation="landscape">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
74
android/app/src/main/assets/error.html
Normal file
74
android/app/src/main/assets/error.html
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>加载错误</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #0a0a0a;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 12px;
|
||||
color: #ff6b6b;
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.tips {
|
||||
margin-top: 30px;
|
||||
padding: 16px;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 8px;
|
||||
max-width: 400px;
|
||||
}
|
||||
.tips h2 {
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
.tips ul {
|
||||
text-align: left;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.tips li {
|
||||
font-size: 13px;
|
||||
color: #aaa;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="icon">⚠️</div>
|
||||
<h1>页面加载失败</h1>
|
||||
<p>无法加载应用资源,可能是以下原因:</p>
|
||||
|
||||
<div class="tips">
|
||||
<h2>可能的解决方案:</h2>
|
||||
<ul>
|
||||
<li>检查网络连接是否正常</li>
|
||||
<li>清除应用数据后重试</li>
|
||||
<li>重新安装应用</li>
|
||||
<li>如果是开发模式,请检查远程服务器地址</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
49
android/app/src/main/java/com/iptv/app/AssetReader.java
Normal file
49
android/app/src/main/java/com/iptv/app/AssetReader.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.iptv.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class AssetReader {
|
||||
private Context context;
|
||||
|
||||
public AssetReader(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String readFile(String path) {
|
||||
try {
|
||||
// path 如: "www/api/result.txt"
|
||||
InputStream is = context.getAssets().open(path);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
reader.close();
|
||||
return sb.toString();
|
||||
} catch (IOException e) {
|
||||
return "ERROR: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String readChannelData() {
|
||||
return readFile("www/api/result.txt");
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean fileExists(String path) {
|
||||
try {
|
||||
context.getAssets().open(path).close();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
231
android/app/src/main/java/com/iptv/app/MainActivity.java
Normal file
231
android/app/src/main/java/com/iptv/app/MainActivity.java
Normal file
@ -0,0 +1,231 @@
|
||||
package com.iptv.app;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "IPTV";
|
||||
private WebView webView;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
|
||||
// 加载模式:"local" 使用本地打包的web资源,"remote" 使用远程服务器,"test" 使用测试页面
|
||||
private static final String LOAD_MODE = "local";
|
||||
|
||||
// 远程服务器地址(开发测试用)
|
||||
private static final String REMOTE_URL = "http://192.168.1.100:5173";
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Log.d(TAG, "onCreate started");
|
||||
|
||||
// 必须在 setContentView 之前调用
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
webView = findViewById(R.id.webview);
|
||||
swipeRefresh = findViewById(R.id.swipe_refresh);
|
||||
|
||||
if (webView == null) {
|
||||
Log.e(TAG, "WebView is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 全屏设置(在 setContentView 之后)
|
||||
setFullscreen();
|
||||
|
||||
// 添加 JavaScript 接口用于读取本地文件
|
||||
webView.addJavascriptInterface(new AssetReader(this), "AndroidAsset");
|
||||
|
||||
// WebView 设置
|
||||
WebSettings settings = webView.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setDomStorageEnabled(true);
|
||||
settings.setDatabaseEnabled(true);
|
||||
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
|
||||
settings.setMediaPlaybackRequiresUserGesture(false);
|
||||
settings.setAllowFileAccess(true);
|
||||
settings.setAllowContentAccess(true);
|
||||
settings.setAllowFileAccessFromFileURLs(true);
|
||||
settings.setAllowUniversalAccessFromFileURLs(true);
|
||||
|
||||
// 支持缩放
|
||||
settings.setSupportZoom(true);
|
||||
settings.setBuiltInZoomControls(true);
|
||||
settings.setDisplayZoomControls(false);
|
||||
|
||||
// 启用硬件加速
|
||||
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
|
||||
// WebViewClient 处理页面跳转
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
Log.d(TAG, "shouldOverrideUrlLoading: " + request.getUrl());
|
||||
return false; // 在 WebView 内打开链接
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, android.graphics.Bitmap favicon) {
|
||||
Log.d(TAG, "onPageStarted: " + url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
Log.d(TAG, "onPageFinished: " + url);
|
||||
if (swipeRefresh != null) {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||
Log.e(TAG, "onReceivedError: " + errorCode + " - " + description + " - " + failingUrl);
|
||||
}
|
||||
});
|
||||
|
||||
// WebChromeClient 处理全屏视频和 JS 控制台
|
||||
webView.setWebChromeClient(new WebChromeClient() {
|
||||
private View customView;
|
||||
private CustomViewCallback customViewCallback;
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) {
|
||||
Log.d(TAG, "WebView Console: " + consoleMessage.message() + " at " + consoleMessage.sourceId() + ":" + consoleMessage.lineNumber());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(WebView view, int newProgress) {
|
||||
Log.d(TAG, "Loading progress: " + newProgress + "%");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowCustomView(View view, CustomViewCallback callback) {
|
||||
customView = view;
|
||||
customViewCallback = callback;
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHideCustomView() {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
if (customViewCallback != null) {
|
||||
customViewCallback.onCustomViewHidden();
|
||||
}
|
||||
customView = null;
|
||||
}
|
||||
});
|
||||
|
||||
// 下拉刷新
|
||||
if (swipeRefresh != null) {
|
||||
swipeRefresh.setOnRefreshListener(() -> webView.reload());
|
||||
swipeRefresh.setColorSchemeResources(android.R.color.holo_blue_bright);
|
||||
}
|
||||
|
||||
// 延迟加载页面,确保 WebView 完全初始化
|
||||
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||
loadContent();
|
||||
}, 100);
|
||||
|
||||
Log.d(TAG, "onCreate finished");
|
||||
}
|
||||
|
||||
private void loadContent() {
|
||||
Log.d(TAG, "loadContent, mode: " + LOAD_MODE);
|
||||
String url;
|
||||
if ("test".equals(LOAD_MODE)) {
|
||||
// 加载测试页面(红色背景)
|
||||
url = "file:///android_asset/test.html";
|
||||
} else if ("local".equals(LOAD_MODE)) {
|
||||
// 加载本地打包的web资源
|
||||
url = "file:///android_asset/www/index.html";
|
||||
} else {
|
||||
// 加载远程服务器(开发测试)
|
||||
url = REMOTE_URL;
|
||||
}
|
||||
Log.d(TAG, "Loading URL: " + url);
|
||||
webView.loadUrl(url);
|
||||
}
|
||||
|
||||
private void setFullscreen() {
|
||||
// 设置状态栏和导航栏颜色
|
||||
getWindow().setStatusBarColor(Color.BLACK);
|
||||
getWindow().setNavigationBarColor(Color.BLACK);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
getWindow().setDecorFitsSystemWindows(false);
|
||||
WindowInsetsController controller = getWindow().getInsetsController();
|
||||
if (controller != null) {
|
||||
controller.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
|
||||
controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||
}
|
||||
} else {
|
||||
getWindow().setFlags(
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
);
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (webView != null && webView.canGoBack()) {
|
||||
webView.goBack();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (webView != null) {
|
||||
webView.onResume();
|
||||
}
|
||||
setFullscreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (webView != null) {
|
||||
webView.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (webView != null) {
|
||||
webView.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
android/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
10
android/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M38,32 L38,76 L46,76 L46,58 L62,76 L74,76 L74,32 L62,32 L46,50 L46,32 Z"/>
|
||||
</vector>
|
||||
15
android/app/src/main/res/layout/activity_main.xml
Normal file
15
android/app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/swipe_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#0a0a0a">
|
||||
|
||||
<WebView
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#0a0a0a" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
5
android/app/src/main/res/values/colors.xml
Normal file
5
android/app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#0a0a0a</color>
|
||||
</resources>
|
||||
3
android/app/src/main/res/values/strings.xml
Normal file
3
android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">IPTV</string>
|
||||
</resources>
|
||||
9
android/app/src/main/res/values/styles.xml
Normal file
9
android/app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<resources>
|
||||
<style name="AppTheme" parent="Theme.Material3.Dark.NoActionBar">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowBackground">@android:color/black</item>
|
||||
</style>
|
||||
</resources>
|
||||
4
android/build.gradle
Normal file
4
android/build.gradle
Normal file
@ -0,0 +1,4 @@
|
||||
// Top-level build file
|
||||
plugins {
|
||||
id 'com.android.application' version '8.1.0' apply false
|
||||
}
|
||||
31
android/build.sh
Executable file
31
android/build.sh
Executable file
@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Android WebView 壳打包脚本
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== IPTV Android 构建 ==="
|
||||
|
||||
# 检查 UI 构建产物
|
||||
if [ ! -d "../ui/dist-web" ]; then
|
||||
echo "错误: 未找到 ../ui/dist-web 目录"
|
||||
echo "请先构建 Web UI: cd ../ui && npm run build"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 复制 Web 资源到 Android assets
|
||||
echo "复制 Web 资源..."
|
||||
mkdir -p app/src/main/assets/www
|
||||
cp -r ../ui/dist-web/* app/src/main/assets/www/
|
||||
|
||||
# 统计文件
|
||||
echo "已复制文件数量:"
|
||||
find app/src/main/assets/www -type f | wc -l
|
||||
|
||||
# 构建 Debug APK
|
||||
echo "构建 Debug APK..."
|
||||
./gradlew assembleDebug
|
||||
|
||||
echo ""
|
||||
echo "=== 构建完成 ==="
|
||||
echo "APK 位置: app/build/outputs/apk/debug/app-debug.apk"
|
||||
4
android/gradle.properties
Normal file
4
android/gradle.properties
Normal file
@ -0,0 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
android.useAndroidX=true
|
||||
kotlin.code.style=official
|
||||
android.nonTransitiveRClass=true
|
||||
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
17
android/gradlew
vendored
Executable file
17
android/gradlew
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Gradle Wrapper Startup Script
|
||||
|
||||
# Find the script's directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
WRAPPER_JAR="$SCRIPT_DIR/gradle/wrapper/gradle-wrapper.jar"
|
||||
|
||||
# Find Java
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
JAVA_CMD="$JAVA_HOME/bin/java"
|
||||
else
|
||||
JAVA_CMD="java"
|
||||
fi
|
||||
|
||||
# Run Gradle wrapper
|
||||
exec "$JAVA_CMD" -cp "$WRAPPER_JAR" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
16
android/settings.gradle
Normal file
16
android/settings.gradle
Normal file
@ -0,0 +1,16 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
rootProject.name = "IPTV App"
|
||||
include ':app'
|
||||
Loading…
x
Reference in New Issue
Block a user