初始化

This commit is contained in:
谢宇宁
2026-09-15 15:44:13 +07:00
commit a23ba685e9
1065 changed files with 101122 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'dev.flutter.flutter-gradle-plugin'
}
apply plugin: 'top.niunaijun.blackobfuscator'
apply plugin: 'walle'
def bundledReleaseKeystore = file('../keystore/nut.jks')
def legacyBundledReleaseKeystore = file('../keystore/nut.jks.old')
def resolvedReleaseKeystore = bundledReleaseKeystore.exists() ? bundledReleaseKeystore : legacyBundledReleaseKeystore
android {
namespace "com.xvproject"
compileSdk 36
ndkVersion "28.2.13676358"
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
coreLibraryDesugaringEnabled true
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
applicationId "com.noporn.newchatone_twelve_five"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.12.6"
}
signingConfigs {
release {
storeFile resolvedReleaseKeystore
storePassword 'jiahua123456'
storeType 'jks'
keyAlias 'nut'
keyPassword 'jiahua123456'
v1SigningEnabled false
v2SigningEnabled true
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
shrinkResources true
signingConfig signingConfigs.release
}
debug {
minifyEnabled false
shrinkResources false
versionNameSuffix "test"
}
}
packagingOptions {
jniLibs {
excludes += ['**/armeabi-v7a/**', '**/x86/**', '**/x86_64/**']
}
}
applicationVariants.configureEach { variant ->
println "variant.name= ${variant.name}"
variant.outputs.configureEach {
//只处理生产版本
if (buildType.name == 'release') {
println "buildType= ${variant.buildType.getName()}"
def outputFileDir = "${project.projectDir.absolutePath}/release/"
def createTime = new Date().format("MM-dd-HH-mm", TimeZone.getTimeZone("GMT+08:00"))
println "outputFileDir= ${outputFileDir}"
def abi = filters.find { it.filterType.toString() == "ABI" }?.identifier
def abiSuffix = (variant.outputs.size() > 1 && abi) ? "_${abi}" : ""
def path = "hgdj" + '_release' + "_" + variant.versionName + abiSuffix + "_" + createTime + ".apk"
println("path= " + path)
outputFileName = path
}
}
}
}
flutter {
source '../..'
}
configurations.configureEach {
resolutionStrategy {
force 'androidx.core:core-ktx:1.6.0'
}
}
configurations.all {
resolutionStrategy {
force 'com.tencent:mmkv:1.2.14'
}
}
// 加入混淆配置
BlackObfuscator {
// 是否启用
enabled true
// 混淆深度
depth 3
// 需要混淆的包或者类(匹配前面一段)
obfClass = ["lib"]
// blackClass中的包或者类不会进行混淆(匹配前面一段)
// blackClass = ["top.niunaijun.black"]
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
// android 读取渠道号码
implementation 'com.mcxiaoke.packer-ng:helper:2.0.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.lifecycle:lifecycle-common:2.6.2'
def camerax_version = "1.0.0-beta11"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
// 协程核心库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
// 协程Android支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
// 协程Java8支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.4"
implementation 'com.qiniu:happy-dns:2.0.1'
implementation 'com.github.Petterpx.walle:library:1.0.5'
}
+3
View File
@@ -0,0 +1,3 @@
运行 ./resp_genkey.sh xxxxx
xxxxx 为alias/密码和密钥的名称;
如果提示输入密钥,直接输入xxxxx即可
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
if [ $# -ne 1 ]; then
printf -- "Please input alias.\n\n"
exit
fi
echo "输入参数密钥$1\n"
keystoreName="$1.jks"
# echo "test code 2"
printf "\n开始生成key:[$keystoreName]...\n\n"
`keytool -genkey -alias $1 -keyalg RSA -validity 10000 -keystore $keystoreName`
printf "\n[$keystoreName]正在进行pkcs12重新加密...\n\n"
`keytool -importkeystore -srckeystore $keystoreName -destkeystore $keystoreName -deststoretype pkcs12`
printf "\n删除老的key:[$keystoreName.old]...okay\n\n"
`rm -rf $keystoreName.old`
cat /dev/null > ../../key.properties
echo "storePassword=$1" >> ../../key.properties
echo "keyPassword=$1" >> ../../key.properties
echo "keyAlias=$1" >> ../../key.properties
echo "storeFile=./key/$keystoreName" >> ../../key.properties
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/expect
#功能:自动填充生成androidkey功能
#注意:expect 是用c语言写的 所以用argc和argv接收参数
#注意:expect 只接受shell而不是bash 请./运行
# expect 常用命令
# spawn 交互程序开始后面跟命令或者指定程序
# expect 获取匹配信息匹配成功则执行expect后面的程序动作
# send exp_send 用于发送指定的字符串信息
# exp_continue 在expect中多次匹配就需要用到
# send_user 用来打印输出 相当于shell中的echo
# exit 退出expect脚本
# eof expect执行结束 退出
# set 定义变量
# puts 输出变量
# set timeout 设置超时时间
if {$argc < 1} {
puts "Please input alias.\n\n"
exit 1
}
set password [lindex $argv 0]
# send_user $password
puts stderr $password
# spawn [exec bash ./genkey.sh $password]
spawn bash ./genkey.sh $password
# expect "输入密钥库口令:"
# send "$password\r"
# expect "再次输入新口令:"
# send "$password\r"
# expect "是什么?"
# send "$password\r"
# expect "是什么?"
# send "$password\r"
# expect "是什么?"
# send "$password\r"
# expect "是什么?"
# send "$password\r"
# expect "是什么?"
# send "$password\r"
# expect "是什么?"
# send "$password\r"
# expect "是否正确?"
# send "y\r"
# expect "按回车):"
# send "\r"
# expect "输入源密钥库口令:"
# send "$password\r"
# # expect "输入目标密钥库口令:"
# # send "$password\r"
# # expect "再次输入新口令:"
# # send "$password\r"
expect {
"输入密钥库口令:"
{ send "$password\r"; exp_continue }
"再次输入新口令:"
{send "$password\r"; exp_continue}
"是什么?"
{send "$password\r"; exp_continue}
"是否正确?"
{send "y\r"; exp_continue}
"按回车):"
{send "\r"; exp_continue}
"输入源密钥库口令:"
{send "$password\r";exp_continue}
"输入目标密钥库口令:"
{send "$password\r";exp_continue}
"再次输入新口令:"
{send "$password\r";exp_continue}
}
# interact
+21
View File
@@ -0,0 +1,21 @@
-dontwarn javax.imageio.spi.ImageInputStreamSpi
-dontwarn javax.imageio.spi.ImageOutputStreamSpi
-dontwarn javax.imageio.spi.ImageReaderSpi
-dontwarn javax.imageio.spi.ImageWriterSpi
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
# ffmpeg_kit_flutter_new_https_gpl:原生库 JNI_OnLoad 通过 RegisterNatives 绑定 AbiDetect.getNativeCpuAbi 等方法,
# release R8 混淆改名会导致绑定失败 JNI_OnLoad 返回 0 启动 SIGSEGV 闪退,必须 keep
-keep class com.antonkarpenko.ffmpegkit.** { *; }
# 通用兜底:任何含 native 方法的类,保留其 native 方法名(防止其它 JNI 库重蹈覆辙)
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
}
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
+318
View File
@@ -0,0 +1,318 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.xvproject">
<!-- release 包只用 main manifestINTERNET 必须在这里声明;
否则 release 无网络权限,启动选线 host lookup errno=7,弹"线路检测失败" -->
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="黄果短剧"
android:name=".JPApplication"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true"
android:largeHeap="true"
android:hardwareAccelerated="true"
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity-alias
android:name=".lld"
android:targetActivity=".MainActivity"
android:label="黄果短剧"
android:enabled="false"
android:icon="@mipmap/ic_launcher"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".aiyinyue"
android:targetActivity=".MainActivity"
android:label="爱音乐"
android:enabled="false"
android:icon="@mipmap/icon_1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".aiyuedu"
android:targetActivity=".MainActivity"
android:label="爱阅读"
android:enabled="false"
android:icon="@mipmap/icon_2"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".alipay"
android:targetActivity=".MainActivity"
android:label="支付宝"
android:enabled="false"
android:icon="@mipmap/icon_3"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".bilibili"
android:targetActivity=".MainActivity"
android:label="哔哩哔哩"
android:enabled="false"
android:icon="@mipmap/icon_4"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".binggandazhuozhan"
android:targetActivity=".MainActivity"
android:label="金铲铲之战"
android:enabled="false"
android:icon="@mipmap/icon_5"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".meisijiejian"
android:targetActivity=".MainActivity"
android:label="美食借鉴"
android:enabled="false"
android:icon="@mipmap/icon_6"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".myworld"
android:targetActivity=".MainActivity"
android:label="我的世界"
android:enabled="false"
android:icon="@mipmap/icon_7"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".shenxuanzhizhan"
android:targetActivity=".MainActivity"
android:label="神选之战"
android:enabled="false"
android:icon="@mipmap/icon_8"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".shuixinwuxian"
android:targetActivity=".MainActivity"
android:label="随心无线"
android:enabled="false"
android:icon="@mipmap/icon_9"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".texuanxiaoshuo"
android:targetActivity=".MainActivity"
android:label="特轩小说"
android:enabled="false"
android:icon="@mipmap/icon_10"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".tiantianmajiang"
android:targetActivity=".MainActivity"
android:label="天天麻将"
android:enabled="false"
android:icon="@mipmap/icon_11"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".tuyou"
android:targetActivity=".MainActivity"
android:label="途游"
android:enabled="false"
android:icon="@mipmap/icon_12"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".weichat"
android:targetActivity=".MainActivity"
android:label="微信"
android:enabled="false"
android:icon="@mipmap/icon_13"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".wendao"
android:targetActivity=".MainActivity"
android:label="问道"
android:enabled="false"
android:icon="@mipmap/icon_14"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".yinyutusujiao"
android:targetActivity=".MainActivity"
android:label="英语图书角"
android:enabled="false"
android:icon="@mipmap/icon_15"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".icon_16"
android:targetActivity=".MainActivity"
android:label="91PO黄果短剧RN"
android:enabled="false"
android:icon="@mipmap/icon_16"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".icon_17"
android:targetActivity=".MainActivity"
android:label="黄果短剧"
android:enabled="false"
android:icon="@mipmap/icon_17"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".icon_18"
android:targetActivity=".MainActivity"
android:label="黄果短剧"
android:enabled="false"
android:icon="@mipmap/icon_18"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/totalfilepath"
tools:replace="android:resource" />
</provider>
<provider
android:name="com.pichillilorenzo.flutter_inappwebview_android.InAppWebViewFileProvider"
android:authorities="${applicationId}.flutter_inappwebview_android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
@@ -0,0 +1,104 @@
package io.flutter.plugins;
import android.media.MediaMetadataRetriever;
import android.text.TextUtils;
import java.io.FileInputStream;
/// 本地视频文件信息:时长 / 分辨率 / 比例 / 高度 / 比特率
/// 注:MediaMetadataRetriever 持有 native 资源,每次用完必须 release,否则多次调用后取不到任何信息
public class JPFileUtils {
/**
* 获取视频时长(秒)
*/
public static int getVideoDuration(String filePath) {
String ms = extract(filePath, MediaMetadataRetriever.METADATA_KEY_DURATION);
if (TextUtils.isEmpty(ms) || !TextUtils.isDigitsOnly(ms)) return 0;
return Integer.parseInt(ms) / 1000;
}
/**
* 获取视频分辨率,格式 "宽*高",取不到返回空串
*/
public static String getVideoResolution(String filePath) {
int[] size = videoSize(filePath);
return size[0] > 0 && size[1] > 0 ? size[0] + "*" + size[1] : "";
}
/**
* 获取视频宽高比
*/
public static String getVideoRatio(String filePath) {
int[] size = videoSize(filePath);
if (size[0] <= 0 || size[1] <= 0) return "0";
return String.valueOf((float) size[0] / size[1]);
}
/**
* 获取视频高度
*/
public static String getVideoHeight(String filePath) {
int[] size = videoSize(filePath);
if (size[0] <= 0 || size[1] <= 0) return "0";
return String.valueOf(size[1]);
}
/**
* 获取比特率
*/
public static String getVideoBitrate(String filePath) {
String bitrate = extract(filePath, MediaMetadataRetriever.METADATA_KEY_BITRATE);
return TextUtils.isEmpty(bitrate) ? "0" : bitrate;
}
/**
* 取单个 metadata;用 FD 而非路径设源,避开部分机型对中文/特殊字符路径的兼容问题
*/
private static String extract(String filePath, int key) {
if (TextUtils.isEmpty(filePath)) return null;
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
try (FileInputStream in = new FileInputStream(filePath)) {
mmr.setDataSource(in.getFD());
return mmr.extractMetadata(key);
} catch (Exception e) {
return null;
} finally {
release(mmr);
}
}
/**
* 视频宽高,取不到返回 {0,0}。宽高一次打开取完,避免为了宽和高开两次文件
*/
private static int[] videoSize(String filePath) {
int[] empty = {0, 0};
if (TextUtils.isEmpty(filePath)) return empty;
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
try (FileInputStream in = new FileInputStream(filePath)) {
mmr.setDataSource(in.getFD());
int width = parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
int height = parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
return new int[]{width, height};
} catch (Exception e) {
return empty;
} finally {
release(mmr);
}
}
private static int parseInt(String value) {
try {
return Integer.parseInt(value);
} catch (Exception e) {
return 0;
}
}
private static void release(MediaMetadataRetriever mmr) {
try {
mmr.release();
} catch (Exception ignored) {
}
}
}
@@ -0,0 +1,96 @@
package io.flutter.plugins;
import android.app.Activity;
import android.content.Context;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Build;
import com.meituan.android.walle.WalleChannelReader;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
/// 本地插件:设备号 / 视频文件信息 / 渠道号 / H265 硬解能力
public class JPNativePlugin implements MethodChannel.MethodCallHandler {
private static final String CHANNEL = "com.yinse/device";
private final Activity activity;
private JPNativePlugin(Activity activity) {
this.activity = activity;
}
/// 注册
public static void registerWith(FlutterActivity flutterActivity, FlutterEngine engine) {
MethodChannel channel = new MethodChannel(engine.getDartExecutor().getBinaryMessenger(), CHANNEL);
channel.setMethodCallHandler(new JPNativePlugin(flutterActivity));
}
/// 调用
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "getVideoDuration":
result.success(JPFileUtils.getVideoDuration(call.argument("filePath")));
break;
case "getVideoResolution":
result.success(JPFileUtils.getVideoResolution(call.argument("filePath")));
break;
case "getVideoRatio":
result.success(JPFileUtils.getVideoRatio(call.argument("filePath")));
break;
case "getVideoHeight":
result.success(JPFileUtils.getVideoHeight(call.argument("filePath")));
break;
case "getVideoBitrate":
result.success(JPFileUtils.getVideoBitrate(call.argument("filePath")));
break;
case "getChannel": // 获取android 渠道的东西
result.success(getChannel(activity));
break;
case "isH265HardwareSupported":
result.success(isHevcHardwareSupported());
break;
default:
// 不回 result 的话 Dart 那边的 Future 永远不完成
result.notImplemented();
}
}
/// 获取渠道号
public static String getChannel(Context context) {
return WalleChannelReader.getChannel(context);
}
/// 本机是否**硬解** H.265(HEVC)。只认硬解:软解(google/c2.android)当不支持,回落 264。
/// 查询编解码器能力,任何异常都按不支持处理。
private static boolean isHevcHardwareSupported() {
try {
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo info : list.getCodecInfos()) {
if (info.isEncoder() || !supportsHevc(info)) continue;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (info.isHardwareAccelerated()) return true; // API29+ 直接问硬解
} else {
String name = info.getName().toLowerCase(); // 旧版本靠名字排除软解
if (!name.startsWith("omx.google.") && !name.startsWith("c2.android.")) return true;
}
}
} catch (Exception e) {
return false;
}
return false;
}
private static boolean supportsHevc(MediaCodecInfo info) {
for (String type : info.getSupportedTypes()) {
if (type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) return true;
}
return false;
}
}
@@ -0,0 +1,5 @@
package com.xvproject
import io.flutter.app.FlutterApplication
class JPApplication : FlutterApplication()
@@ -0,0 +1,77 @@
package com.xvproject
import android.annotation.SuppressLint
import android.app.ActivityManager
import android.content.ComponentName
import android.content.Intent
import android.content.pm.PackageManager
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.JPNativePlugin
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
JPNativePlugin.registerWith(this, flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
METHOD_CHANGE_ICON -> {
val index = call.argument<Int>(ARG_ICON_INDEX) ?: -1
ICON_ALIASES.getOrNull(index)?.let { switchIcon("$ALIAS_PACKAGE.$it") }
result.success(true)
}
// 不回 result 的话 Dart 那边的 Future 永远不完成
else -> result.notImplemented()
}
}
}
/// 切换启动图标:禁用当前入口 + 启用目标 alias,再杀掉桌面进程让图标立刻刷新
private fun switchIcon(alias: String) {
val pm = packageManager
pm.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
)
pm.setComponentEnabledSetting(
ComponentName(this, alias),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP
)
restartLauncher(pm)
}
/// 杀掉桌面进程,强制它重读图标
@SuppressLint("ServiceCast")
private fun restartLauncher(pm: PackageManager) {
val am = getSystemService(ACTIVITY_SERVICE) as ActivityManager
val intent = Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.addCategory(Intent.CATEGORY_DEFAULT)
for (info in pm.queryIntentActivities(intent, 0)) {
am.killBackgroundProcesses(info.activityInfo?.packageName ?: continue)
}
}
companion object {
//这个跟flutter的通道名称保持一致
private const val CHANNEL = "com.yinse/request"
private const val METHOD_CHANGE_ICON = "changeIcon"
private const val ARG_ICON_INDEX = "iconIndex"
/// 图标 alias 所在包名:AndroidManifest 里 activity-alias 写的是相对名(manifest package=com.xvproject)
/// 与 applicationId 不同,不能用 packageName 拼
private const val ALIAS_PACKAGE = "com.xvproject"
/// 下标即 Flutter 传来的 iconIndex,顺序必须与 manifest 里 activity-alias 的声明顺序一致
private val ICON_ALIASES = listOf(
"lld", "aiyinyue", "aiyuedu", "alipay", "bilibili", "binggandazhuozhan",
"meisijiejian", "myworld", "shenxuanzhizhan", "shuixinwuxian", "texuanxiaoshuo",
"tiantianmajiang", "tuyou", "weichat", "wendao", "yinyutusujiao",
"icon_16", "icon_17", "icon_18", //清明 / 端午 / 劳动
)
}
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:opacity="opaque">
<item android:gravity="center">
<bitmap
android:gravity="center_horizontal"
android:mipMap="true"
android:src="@mipmap/ic_splash_bg" />
</item>
</layer-list>
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_bg</item>
<item name="android:colorPrimary">#12131C</item>
<item name="colorPrimaryDark">#12131C</item>
<item name="colorAccent">#12131C</item>
<item name="android:windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>
<!-- Flutter UI 初始化时及运行时的窗口背景;缺失会导致浅色模式下窗口透明 -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
@@ -0,0 +1,23 @@
<paths>
<external-path
name="external-path"
path="." />
<external-cache-path
name="external-cache-path"
path="." />
<external-files-path
name="external-files-path"
path="." />
<files-path
name="files_path"
path="." />
<cache-path
name="cache-path"
path="." />
<root-path
name="name"
path="." />
<external-path
name="external_storage_root"
path="." />
</paths>
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>