* refa: jni

* refa: jni
This commit is contained in:
My-Responsitories
2026-05-30 12:10:35 +00:00
committed by GitHub
parent 6a75061dbb
commit d4d9fc3405
17 changed files with 1002 additions and 305 deletions

View File

@@ -0,0 +1,191 @@
package com.example.piliplus;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.provider.Settings;
import android.view.WindowManager;
import androidx.annotation.Keep;
import com.github.dart_lang.jni_flutter.JniFlutterPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@Keep
public final class AndroidHelper {
public static volatile boolean isFoldable = false;
private AndroidHelper() {
}
private static Context getContext() {
return JniFlutterPlugin.getApplicationContext();
}
public static void back() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
}
public static void biliSendCommAntifraud(
int action, long oid, int type, long rpId, long root, long parent, long ctime, @NotNull String commentText,
String pictures, @NotNull String sourceId, long uid, @NotNull String cookie
) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(
"icu.freedomIntrovert.biliSendCommAntifraud",
"icu.freedomIntrovert.biliSendCommAntifraud.ByXposedLaunchedActivity"
));
intent.putExtra("action", action);
intent.putExtra("oid", oid);
intent.putExtra("type", type);
intent.putExtra("rpid", rpId);
intent.putExtra("root", root);
intent.putExtra("parent", parent);
intent.putExtra("ctime", ctime);
intent.putExtra("comment_text", commentText);
if (pictures != null) {
intent.putExtra("pictures", pictures);
}
intent.putExtra("source_id", sourceId);
intent.putExtra("uid", uid);
ArrayList<String> cookiesList = new ArrayList<>(1);
cookiesList.add(cookie);
intent.putStringArrayListExtra("cookies", cookiesList);
getContext().startActivity(intent);
}
public static void openLinkVerifySettings() {
Context context = getContext();
Uri uri = Uri.parse("package:" + context.getPackageName());
try {
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
intent = new Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, uri);
} else {
intent = new Intent(Intent.ACTION_MAIN, uri);
intent.setClassName(
"com.android.settings",
"com.android.settings.applications.InstalledAppOpenByDefaultActivity"
);
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (Throwable ignored) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
public static boolean openMusic(@NotNull String title, String artist, String album) {
Intent intent = new Intent(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(SearchManager.QUERY, title);
intent.putExtra(MediaStore.EXTRA_MEDIA_TITLE, title);
if (artist != null) {
intent.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
}
if (album != null) {
intent.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, album);
}
intent.addCategory(Intent.CATEGORY_DEFAULT);
Context context = getContext();
PackageManager pm = context.getPackageManager();
try {
if (pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
context.startActivity(intent);
return true;
}
} catch (Throwable ignored) {
}
try {
intent.setAction(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH);
if (pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
context.startActivity(intent);
return true;
}
} catch (Throwable ignored) {
}
return false;
}
public static void setPipAutoEnterEnabled(boolean autoEnable, long engineId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PictureInPictureParams params = new PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnable).build();
android.app.Activity activity = JniFlutterPlugin.getActivity(engineId);
if (activity != null) {
activity.setPictureInPictureParams(params);
}
}
}
public static int[] maxScreenSize() {
Context context = getContext();
WindowManager wm = context.getSystemService(WindowManager.class);
try {
float density = context.getResources().getDisplayMetrics().density;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Rect maxBounds = wm.getMaximumWindowMetrics().getBounds();
return new int[]{Math.round(maxBounds.width() / density), Math.round(maxBounds.height() / density)};
} else {
Point realSize = new Point();
wm.getDefaultDisplay().getRealSize(realSize);
return new int[]{Math.round(realSize.x / density), Math.round(realSize.y / density)};
}
} catch (Exception e) {
return null;
}
}
public static void createShortcut(@NotNull String id, @NotNull String uri, @NotNull String label, @NotNull String icon) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Context context = getContext();
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
if (shortcutManager != null && shortcutManager.isRequestPinShortcutSupported()) {
Bitmap bitmap = BitmapFactory.decodeFile(icon);
ShortcutInfo shortcut = new ShortcutInfo.Builder(context, id)
.setShortLabel(label)
.setIcon(Icon.createWithAdaptiveBitmap(bitmap))
.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(uri)))
.build();
// TODO: WorkerThread
Intent pinIntent = shortcutManager.createShortcutResultIntent(shortcut);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, pinIntent, PendingIntent.FLAG_IMMUTABLE
);
shortcutManager.requestPinShortcut(shortcut, pendingIntent.getIntentSender());
}
}
}
@Keep
public static final class ToDart {
public static volatile Runnable onUserLeaveHint;
public static volatile Runnable onConfigurationChanged;
private ToDart() {
}
}
}

View File

@@ -1,32 +1,19 @@
package com.example.piliplus
import android.app.PendingIntent
import android.app.PictureInPictureParams
import android.app.SearchManager
import android.content.ComponentName
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.content.res.Configuration
import android.graphics.BitmapFactory
import android.graphics.Point
import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.provider.Settings
import android.view.WindowManager.LayoutParams
import androidx.core.net.toUri
import com.ryanheise.audioservice.AudioServiceActivity
import io.flutter.SystemChrome
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.SystemChrome
import kotlin.math.roundToInt
class MainActivity : AudioServiceActivity() {
// TODO: remove
private lateinit var methodChannel: MethodChannel
private var isFoldable = false
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
@@ -34,169 +21,15 @@ class MainActivity : AudioServiceActivity() {
methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "PiliPlus")
methodChannel.setMethodCallHandler { call, result ->
when (call.method) {
"back" -> back()
"biliSendCommAntifraud" -> {
try {
val action = call.argument<Int>("action") ?: 0
val oid = call.argument<Number>("oid") ?: 0L
val type = call.argument<Int>("type") ?: 0
val rpid = call.argument<Number>("rpid") ?: 0L
val root = call.argument<Number>("root") ?: 0L
val parent = call.argument<Number>("parent") ?: 0L
val ctime = call.argument<Number>("ctime") ?: 0L
val commentText = call.argument<String>("comment_text") ?: ""
val pictures = call.argument<String?>("pictures")
val sourceId = call.argument<String>("source_id") ?: ""
val uid = call.argument<Number>("uid") ?: 0L
val cookies = call.argument<List<String>>("cookies") ?: emptyList()
val intent = Intent().apply {
component = ComponentName(
"icu.freedomIntrovert.biliSendCommAntifraud",
"icu.freedomIntrovert.biliSendCommAntifraud.ByXposedLaunchedActivity"
)
putExtra("action", action)
putExtra("oid", oid.toLong())
putExtra("type", type)
putExtra("rpid", rpid.toLong())
putExtra("root", root.toLong())
putExtra("parent", parent.toLong())
putExtra("ctime", ctime.toLong())
putExtra("comment_text", commentText)
if (pictures != null)
putExtra("pictures", pictures)
putExtra("source_id", sourceId)
putExtra("uid", uid.toLong())
putStringArrayListExtra("cookies", ArrayList(cookies))
}
startActivity(intent)
} catch (_: Exception) {
}
}
"linkVerifySettings" -> {
val uri = ("package:" + context.packageName).toUri()
try {
val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, uri)
} else {
Intent("android.intent.action.MAIN", uri).setClassName(
"com.android.settings",
"com.android.settings.applications.InstalledAppOpenByDefaultActivity"
)
}
context.startActivity(intent)
} catch (_: Throwable) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri)
context.startActivity(intent)
}
}
"music" -> {
val title = call.argument<String>("title")
val intent = Intent(MediaStore.INTENT_ACTION_MEDIA_SEARCH).apply {
putExtra(SearchManager.QUERY, title)
putExtra(MediaStore.EXTRA_MEDIA_TITLE, title)
call.argument<String?>("artist")
?.let { putExtra(MediaStore.EXTRA_MEDIA_ARTIST, it) }
call.argument<String?>("album")
?.let { putExtra(MediaStore.EXTRA_MEDIA_ALBUM, it) }
addCategory(Intent.CATEGORY_DEFAULT)
}
try {
if (packageManager.resolveActivity(
intent,
PackageManager.MATCH_DEFAULT_ONLY
) != null
) {
startActivity(intent)
result.success(true)
return@setMethodCallHandler
}
} catch (_: Throwable) {
}
try {
intent.action = MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
if (packageManager.resolveActivity(
intent,
PackageManager.MATCH_DEFAULT_ONLY
) != null
) {
startActivity(intent)
result.success(true)
return@setMethodCallHandler
}
} catch (_: Throwable) {
}
result.success(false)
}
"setPipAutoEnterEnabled" -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val params = PictureInPictureParams.Builder()
.setAutoEnterEnabled(call.argument<Boolean>("autoEnable") ?: false)
.build()
setPictureInPictureParams(params)
}
}
"createShortcut" -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
val shortcutManager =
context.getSystemService(ShortcutManager::class.java)
if (shortcutManager.isRequestPinShortcutSupported) {
val id = call.argument<String>("id")!!
val uri = call.argument<String>("uri")!!
val label = call.argument<String>("label")!!
val icon = call.argument<String>("icon")!!
val bitmap = BitmapFactory.decodeFile(icon)
val shortcut =
ShortcutInfo.Builder(context, id)
.setShortLabel(label)
.setIcon(Icon.createWithAdaptiveBitmap(bitmap))
.setIntent(Intent(Intent.ACTION_VIEW, uri.toUri()))
.build()
val pinIntent =
shortcutManager.createShortcutResultIntent(shortcut)
val pendingIntent = PendingIntent.getBroadcast(
context, 0, pinIntent, PendingIntent.FLAG_IMMUTABLE
)
shortcutManager.requestPinShortcut(
shortcut,
pendingIntent.intentSender
)
}
} catch (_: Exception) {
}
}
}
"maxScreenSize" -> {
maxScreenSize()?.let {
result.success(it)
}
}
"isFoldable" -> {
result.success(isFoldable)
}
"SystemChrome.setEnabledSystemUIMode" -> {
SystemChrome.onMethodCall(
this,
"SystemChrome.setEnabledSystemUIMode",
call.argument("arguments")
this, "SystemChrome.setEnabledSystemUIMode", call.argument("arguments")
)
}
"SystemChrome.setEnabledSystemUIOverlays" -> {
SystemChrome.onMethodCall(
this,
"SystemChrome.setEnabledSystemUIOverlays",
call.argument("arguments")
this, "SystemChrome.setEnabledSystemUIOverlays", call.argument("arguments")
)
}
@@ -207,57 +40,20 @@ class MainActivity : AudioServiceActivity() {
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (isFoldable) {
maxScreenSize()?.let {
MethodChannel(
flutterEngine!!.dartExecutor.binaryMessenger,
"ScreenChannel"
).invokeMethod("onConfigChanged", it)
}
if (AndroidHelper.isFoldable) {
AndroidHelper.ToDart.onConfigurationChanged?.run()
}
}
private fun maxScreenSize(): Map<String, Int>? {
try {
val density = resources.displayMetrics.density
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val maxBounds = windowManager.maximumWindowMetrics.bounds
return mapOf(
"maxWidth" to (maxBounds.width() / density).roundToInt(),
"maxHeight" to (maxBounds.height() / density).roundToInt(),
)
} else {
val realSizePoint = Point()
windowManager.defaultDisplay.getRealSize(realSizePoint)
return mapOf(
"maxWidth" to (realSizePoint.x / density).roundToInt(),
"maxHeight" to (realSizePoint.y / density).roundToInt(),
)
}
} catch (_: Exception) {
return null
}
}
private fun back() {
val intent = Intent(Intent.ACTION_MAIN).apply {
addCategory(Intent.CATEGORY_HOME)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode =
LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
window.attributes.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
isFoldable =
packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_HINGE_ANGLE)
AndroidHelper.isFoldable = packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_HINGE_ANGLE)
} catch (_: Exception) {
}
}
@@ -270,17 +66,15 @@ class MainActivity : AudioServiceActivity() {
override fun onUserLeaveHint() {
super.onUserLeaveHint()
methodChannel.invokeMethod("onUserLeaveHint", null)
AndroidHelper.ToDart.onUserLeaveHint?.run()
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration?
isInPictureInPictureMode: Boolean, newConfig: Configuration?
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
MethodChannel(
flutterEngine!!.dartExecutor.binaryMessenger,
"floating"
).invokeMethod("onPipChanged", isInPictureInPictureMode)
MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, "floating").invokeMethod(
"onPipChanged", isInPictureInPictureMode
)
}
}

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

11
jnigen.yaml Normal file
View File

@@ -0,0 +1,11 @@
output:
dart:
path: lib/utils/android/bindings.g.dart
structure: single_file
class_path:
- android/app/src/main/java
classes:
- 'com.example.piliplus.AndroidHelper'
- 'java.lang.Runnable'

View File

@@ -115,8 +115,9 @@ void main() async {
CacheManager.autoClearCache();
if (PlatformUtils.isMobile) {
if (Platform.isAndroid) MaxScreenSize.init();
await Future.wait([
if (Platform.isAndroid) ...[_initSdkInt(), MaxScreenSize.init()],
if (Platform.isAndroid) _initSdkInt(),
if (Pref.horizontalScreen) ?fullMode() else ?portraitUpMode(),
setupServiceLocator(),
]);

View File

@@ -12,6 +12,7 @@ import 'package:PiliPlus/pages/mine/controller.dart';
import 'package:PiliPlus/services/logger.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/android/android_helper.dart';
import 'package:PiliPlus/utils/cache_manager.dart';
import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/device_utils.dart';
@@ -177,7 +178,7 @@ Commit Hash: ${BuildConfig.commitHash}''',
),
if (Platform.isAndroid)
ListTile(
onTap: () => Utils.channel.invokeMethod('linkVerifySettings'),
onTap: PiliAndroidHelper.openLinkVerifySettings,
leading: const Icon(MdiIcons.linkBoxOutline),
title: const Text('打开受支持的链接'),
trailing: Icon(

View File

@@ -13,6 +13,7 @@ import 'package:PiliPlus/pages/home/view.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/plugin/pl_player/controller.dart';
import 'package:PiliPlus/plugin/pl_player/models/play_status.dart';
import 'package:PiliPlus/utils/android/android_helper.dart';
import 'package:PiliPlus/utils/app_scheme.dart';
import 'package:PiliPlus/utils/extension/context_ext.dart';
import 'package:PiliPlus/utils/extension/size_ext.dart';
@@ -21,7 +22,6 @@ import 'package:PiliPlus/utils/mobile_observer.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
@@ -249,9 +249,10 @@ class _MainAppState extends PopScopeState<MainApp>
await trayManager.setContextMenu(trayMenu);
}
@pragma('vm:prefer-inline')
static void _onBack() {
if (Platform.isAndroid) {
Utils.channel.invokeMethod('back');
PiliAndroidHelper.back();
}
}

View File

@@ -32,6 +32,7 @@ import 'package:PiliPlus/pages/member_pgc/view.dart';
import 'package:PiliPlus/pages/member_shop/view.dart';
import 'package:PiliPlus/pages/member_video_web/archive/view.dart';
import 'package:PiliPlus/pages/member_video_web/season_series/view.dart';
import 'package:PiliPlus/utils/android/android_helper.dart';
import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/extension/context_ext.dart';
import 'package:PiliPlus/utils/extension/string_ext.dart';
@@ -706,14 +707,11 @@ class _MemberPageState extends State<MemberPage> {
'${_userController.userAvatar!}@200w_200h.webp'.http2https,
));
SmartDialog.dismiss();
await Utils.channel.invokeMethod(
'createShortcut',
<String, String>{
'id': _userController.mid.toString(),
'uri': 'bilibili://space/${_userController.mid}',
'label': _userController.username!,
'icon': file.path,
},
PiliAndroidHelper.createShortcut(
_userController.mid.toString(),
'bilibili://space/${_userController.mid}',
_userController.username!,
file.path,
);
} catch (e) {
SmartDialog.showToast(e.toString());

View File

@@ -17,6 +17,7 @@ import 'package:PiliPlus/pages/common/dyn/common_dyn_page.dart';
import 'package:PiliPlus/pages/music/controller.dart';
import 'package:PiliPlus/pages/music/video/view.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/android/android_helper.dart';
import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/extension/get_ext.dart';
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
@@ -691,16 +692,13 @@ class _MusicDetailPageState extends CommonDynPageState<MusicDetailPage> {
);
}
Future<void> _searchMusic(MusicDetail item) async {
final res =
Platform.isAndroid &&
(await Utils.channel.invokeMethod<bool>('music', {
'title': item.musicTitle,
'artist': item.originArtist ?? item.originArtistList,
'album': item.album,
}) ??
false);
if (!res) {
void _searchMusic(MusicDetail item) {
if (!Platform.isAndroid ||
!PiliAndroidHelper.openMusic(
item.musicTitle!,
item.originArtist ?? item.originArtistList,
item.album,
)) {
Utils.copyText(item.musicTitle!);
}
}

View File

@@ -30,6 +30,8 @@ import 'package:PiliPlus/plugin/pl_player/models/video_fit_type.dart';
import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart';
import 'package:PiliPlus/services/service_locator.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/android/android_helper.dart';
import 'package:PiliPlus/utils/android/bindings.g.dart';
import 'package:PiliPlus/utils/asset_utils.dart';
import 'package:PiliPlus/utils/device_utils.dart';
import 'package:PiliPlus/utils/extension/box_ext.dart';
@@ -302,9 +304,7 @@ class PlPlayerController with BlockConfigMixin {
void _disableAutoEnterPip() {
if (_shouldSetPip) {
Utils.channel.invokeMethod('setPipAutoEnterEnabled', {
'autoEnable': false,
});
PiliAndroidHelper.setPipAutoEnterEnabled(false);
}
}
@@ -580,19 +580,21 @@ class PlPlayerController with BlockConfigMixin {
if (Platform.isAndroid && autoPiP) {
if (DeviceUtils.sdkInt < 36) {
Utils.channel.setMethodCallHandler((call) async {
if (call.method == 'onUserLeaveHint') {
if (playerStatus.isPlaying && _isCurrVideoPage) {
enterPip();
}
}
});
AndroidHelper$ToDart.onUserLeaveHint = Runnable.implement(
$Runnable(run: _onUserLeaveHint),
);
} else {
_shouldSetPip = true;
}
}
}
void _onUserLeaveHint() {
if (playerStatus.isPlaying && _isCurrVideoPage) {
enterPip();
}
}
// 获取实例 传参
static PlPlayerController getInstance({bool isLive = false}) {
// 如果实例尚未创建,则创建一个新实例
@@ -1631,7 +1633,8 @@ class PlPlayerController with BlockConfigMixin {
if (showSeekPreview) {
_clearPreview();
}
Utils.channel.setMethodCallHandler(null);
AndroidHelper$ToDart.onUserLeaveHint?.release();
AndroidHelper$ToDart.onUserLeaveHint = null;
_timer?.cancel();
// _position.close();
// _playerEventSubs?.cancel();

View File

@@ -0,0 +1,108 @@
import 'dart:convert';
import 'dart:ui';
import 'package:PiliPlus/utils/android/bindings.g.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:jni/jni.dart';
abstract final class PiliAndroidHelper {
@pragma('vm:prefer-inline')
static void back() => AndroidHelper.back();
static void biliSendCommAntifraud(
int action,
int oid,
int type,
int rpId,
int root,
int parent,
int ctime,
String commentText,
List pictures,
String sourceId,
int uid,
String cookie,
) {
final jCommentText = commentText.toJString();
final jSourceId = sourceId.toJString();
final jCookie = cookie.toJString();
final jPictures = pictures.isEmpty
? null
: jsonEncode(pictures).toJString();
try {
AndroidHelper.biliSendCommAntifraud(
action,
oid,
type,
rpId,
root,
parent,
ctime,
jCommentText,
jPictures,
jSourceId,
uid,
jCookie,
);
} catch (e) {
Utils.reportError(e);
} finally {
jCommentText.release();
jSourceId.release();
jCookie.release();
jPictures?.release();
}
}
@pragma('vm:prefer-inline')
static void openLinkVerifySettings() =>
AndroidHelper.openLinkVerifySettings();
static bool openMusic(String title, String? artist, String? album) {
final jTitle = title.toJString();
final jArtist = artist?.toJString();
final jAlbum = album?.toJString();
try {
return AndroidHelper.openMusic(jTitle, jArtist, jAlbum);
} finally {
jTitle.release();
jArtist?.release();
jAlbum?.release();
}
}
@pragma('vm:prefer-inline')
static void setPipAutoEnterEnabled(bool autoEnable) =>
AndroidHelper.setPipAutoEnterEnabled(
autoEnable,
PlatformDispatcher.instance.engineId!,
);
static (int, int)? maxScreenSize() {
final jIArr = AndroidHelper.maxScreenSize();
if (jIArr != null) {
try {
return (jIArr[0], jIArr[1]);
} finally {
jIArr.release();
}
}
return null;
}
static void createShortcut(String id, String uri, String label, String path) {
final jId = id.toJString();
final jUri = uri.toJString();
final jLabel = label.toJString();
final jPath = path.toJString();
try {
AndroidHelper.createShortcut(jId, jUri, jLabel, jPath);
} finally {
jId.release();
jUri.release();
jLabel.release();
jPath.release();
}
}
}

View File

@@ -0,0 +1,586 @@
// AUTO GENERATED BY JNIGEN 0.17.0. DO NOT EDIT!
// ignore_for_file: annotate_overrides
// ignore_for_file: argument_type_not_assignable
// ignore_for_file: camel_case_extensions
// ignore_for_file: camel_case_types
// ignore_for_file: constant_identifier_names
// ignore_for_file: comment_references
// ignore_for_file: doc_directive_unknown
// ignore_for_file: file_names
// ignore_for_file: inference_failure_on_untyped_parameter
// ignore_for_file: invalid_internal_annotation
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: library_prefixes
// ignore_for_file: lines_longer_than_80_chars
// ignore_for_file: no_leading_underscores_for_library_prefixes
// ignore_for_file: no_leading_underscores_for_local_identifiers
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: only_throw_errors
// ignore_for_file: overridden_fields
// ignore_for_file: prefer_double_quotes
// ignore_for_file: unintended_html_in_doc_comment
// ignore_for_file: unnecessary_cast
// ignore_for_file: unnecessary_non_null_assertion
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: unused_element
// ignore_for_file: unused_field
// ignore_for_file: unused_import
// ignore_for_file: unused_local_variable
// ignore_for_file: unused_shown_name
// ignore_for_file: use_super_parameters
import 'dart:core' as core$_;
import 'dart:core' show Object, String;
import 'package:jni/_internal.dart' as jni$_;
import 'package:jni/jni.dart' as jni$_;
const _$jniVersionCheck = jni$_.JniVersionCheck(1, 0);
/// from: `com.example.piliplus.AndroidHelper$ToDart`
extension type AndroidHelper$ToDart._(jni$_.JObject _$this)
implements jni$_.JObject {
static final _class = jni$_.JClass.forName(
r'com/example/piliplus/AndroidHelper$ToDart',
);
/// The type which includes information such as the signature of this class.
static const jni$_.JType<AndroidHelper$ToDart> type =
$AndroidHelper$ToDart$Type$();
static final _id_onUserLeaveHint = _class.staticFieldId(
r'onUserLeaveHint',
r'Ljava/lang/Runnable;',
);
/// from: `static public bridge java.lang.Runnable onUserLeaveHint`
/// The returned object must be released after use, by calling the [release] method.
static Runnable? get onUserLeaveHint =>
_id_onUserLeaveHint.getNullable(_class, Runnable.type) as Runnable?;
/// from: `static public bridge java.lang.Runnable onUserLeaveHint`
/// The returned object must be released after use, by calling the [release] method.
static set onUserLeaveHint(Runnable? value) =>
_id_onUserLeaveHint.set(_class, Runnable.type, value);
static final _id_onConfigurationChanged = _class.staticFieldId(
r'onConfigurationChanged',
r'Ljava/lang/Runnable;',
);
/// from: `static public bridge java.lang.Runnable onConfigurationChanged`
/// The returned object must be released after use, by calling the [release] method.
static Runnable? get onConfigurationChanged =>
_id_onConfigurationChanged.getNullable(_class, Runnable.type)
as Runnable?;
/// from: `static public bridge java.lang.Runnable onConfigurationChanged`
/// The returned object must be released after use, by calling the [release] method.
static set onConfigurationChanged(Runnable? value) =>
_id_onConfigurationChanged.set(_class, Runnable.type, value);
}
final class $AndroidHelper$ToDart$Type$
extends jni$_.JType<AndroidHelper$ToDart> {
@jni$_.internal
const $AndroidHelper$ToDart$Type$();
@jni$_.internal
@core$_.override
String get signature => r'Lcom/example/piliplus/AndroidHelper$ToDart;';
}
/// from: `com.example.piliplus.AndroidHelper`
extension type AndroidHelper._(jni$_.JObject _$this) implements jni$_.JObject {
static final _class = jni$_.JClass.forName(
r'com/example/piliplus/AndroidHelper',
);
/// The type which includes information such as the signature of this class.
static const jni$_.JType<AndroidHelper> type = $AndroidHelper$Type$();
static final _id_isFoldable = _class.staticFieldId(
r'isFoldable',
r'Z',
);
/// from: `static public bridge boolean isFoldable`
static core$_.bool get isFoldable =>
_id_isFoldable.getNullable(_class, jni$_.jboolean.type) as core$_.bool;
/// from: `static public bridge boolean isFoldable`
static set isFoldable(core$_.bool value) =>
_id_isFoldable.set(_class, jni$_.jboolean.type, value);
static final _id_back = _class.staticMethodId(
r'back',
r'()V',
);
static final _back =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>
>('globalEnv_CallStaticVoidMethod')
.asFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>();
/// from: `static public void back()`
static void back() {
final _$$classRef = _class.reference;
_back(_$$classRef.pointer, _id_back.pointer).check();
}
static final _id_biliSendCommAntifraud = _class.staticMethodId(
r'biliSendCommAntifraud',
r'(IJIJJJJLjava/lang/String;Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;)V',
);
static final _biliSendCommAntifraud =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
jni$_.VarArgs<
(
jni$_.Int32,
jni$_.Int64,
jni$_.Int32,
jni$_.Int64,
jni$_.Int64,
jni$_.Int64,
jni$_.Int64,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Int64,
jni$_.Pointer<jni$_.Void>,
)
>,
)
>
>('globalEnv_CallStaticVoidMethod')
.asFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
core$_.int,
core$_.int,
core$_.int,
core$_.int,
core$_.int,
core$_.int,
core$_.int,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
core$_.int,
jni$_.Pointer<jni$_.Void>,
)
>();
/// from: `static public void biliSendCommAntifraud(int i, long j, int i1, long j1, long j2, long j3, long j4, java.lang.String string, java.lang.String string1, java.lang.String string2, long j5, java.lang.String string3)`
static void biliSendCommAntifraud(
core$_.int i,
core$_.int j,
core$_.int i1,
core$_.int j1,
core$_.int j2,
core$_.int j3,
core$_.int j4,
jni$_.JString string,
jni$_.JString? string1,
jni$_.JString string2,
core$_.int j5,
jni$_.JString string3,
) {
final _$$classRef = _class.reference;
final _$string = string.reference;
final _$string1 = string1?.reference ?? jni$_.jNullReference;
final _$string2 = string2.reference;
final _$string3 = string3.reference;
_biliSendCommAntifraud(
_$$classRef.pointer,
_id_biliSendCommAntifraud.pointer,
i,
j,
i1,
j1,
j2,
j3,
j4,
_$string.pointer,
_$string1.pointer,
_$string2.pointer,
j5,
_$string3.pointer,
).check();
}
static final _id_openLinkVerifySettings = _class.staticMethodId(
r'openLinkVerifySettings',
r'()V',
);
static final _openLinkVerifySettings =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>
>('globalEnv_CallStaticVoidMethod')
.asFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>();
/// from: `static public void openLinkVerifySettings()`
static void openLinkVerifySettings() {
final _$$classRef = _class.reference;
_openLinkVerifySettings(
_$$classRef.pointer,
_id_openLinkVerifySettings.pointer,
).check();
}
static final _id_openMusic = _class.staticMethodId(
r'openMusic',
r'(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z',
);
static final _openMusic =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JniResult Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
jni$_.VarArgs<
(
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
)
>,
)
>
>('globalEnv_CallStaticBooleanMethod')
.asFunction<
jni$_.JniResult Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
)
>();
/// from: `static public boolean openMusic(java.lang.String string, java.lang.String string1, java.lang.String string2)`
static core$_.bool openMusic(
jni$_.JString string,
jni$_.JString? string1,
jni$_.JString? string2,
) {
final _$$classRef = _class.reference;
final _$string = string.reference;
final _$string1 = string1?.reference ?? jni$_.jNullReference;
final _$string2 = string2?.reference ?? jni$_.jNullReference;
return _openMusic(
_$$classRef.pointer,
_id_openMusic.pointer,
_$string.pointer,
_$string1.pointer,
_$string2.pointer,
).boolean;
}
static final _id_setPipAutoEnterEnabled = _class.staticMethodId(
r'setPipAutoEnterEnabled',
r'(ZJ)V',
);
static final _setPipAutoEnterEnabled =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
jni$_.VarArgs<(jni$_.Int32, jni$_.Int64)>,
)
>
>('globalEnv_CallStaticVoidMethod')
.asFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
core$_.int,
core$_.int,
)
>();
/// from: `static public void setPipAutoEnterEnabled(boolean z, long j)`
static void setPipAutoEnterEnabled(
core$_.bool z,
core$_.int j,
) {
final _$$classRef = _class.reference;
_setPipAutoEnterEnabled(
_$$classRef.pointer,
_id_setPipAutoEnterEnabled.pointer,
z ? 1 : 0,
j,
).check();
}
static final _id_maxScreenSize = _class.staticMethodId(
r'maxScreenSize',
r'()[I',
);
static final _maxScreenSize =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JniResult Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>
>('globalEnv_CallStaticObjectMethod')
.asFunction<
jni$_.JniResult Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>();
/// from: `static public int[] maxScreenSize()`
/// The returned object must be released after use, by calling the [release] method.
static jni$_.JIntArray? maxScreenSize() {
final _$$classRef = _class.reference;
return _maxScreenSize(
_$$classRef.pointer,
_id_maxScreenSize.pointer,
).object<jni$_.JIntArray?>();
}
static final _id_createShortcut = _class.staticMethodId(
r'createShortcut',
r'(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V',
);
static final _createShortcut =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
jni$_.VarArgs<
(
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
)
>,
)
>
>('globalEnv_CallStaticVoidMethod')
.asFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
jni$_.Pointer<jni$_.Void>,
)
>();
/// from: `static public void createShortcut(java.lang.String string, java.lang.String string1, java.lang.String string2, java.lang.String string3)`
static void createShortcut(
jni$_.JString string,
jni$_.JString string1,
jni$_.JString string2,
jni$_.JString string3,
) {
final _$$classRef = _class.reference;
final _$string = string.reference;
final _$string1 = string1.reference;
final _$string2 = string2.reference;
final _$string3 = string3.reference;
_createShortcut(
_$$classRef.pointer,
_id_createShortcut.pointer,
_$string.pointer,
_$string1.pointer,
_$string2.pointer,
_$string3.pointer,
).check();
}
}
final class $AndroidHelper$Type$ extends jni$_.JType<AndroidHelper> {
@jni$_.internal
const $AndroidHelper$Type$();
@jni$_.internal
@core$_.override
String get signature => r'Lcom/example/piliplus/AndroidHelper;';
}
/// from: `java.lang.Runnable`
extension type Runnable._(jni$_.JObject _$this) implements jni$_.JObject {
static final _class = jni$_.JClass.forName(r'java/lang/Runnable');
/// The type which includes information such as the signature of this class.
static const jni$_.JType<Runnable> type = $Runnable$Type$();
/// Maps a specific port to the implemented interface.
static final core$_.Map<core$_.int, $Runnable> _$impls = {};
static jni$_.JObjectPtr _$invoke(
core$_.int port,
jni$_.JObjectPtr descriptor,
jni$_.JObjectPtr args,
) {
return _$invokeMethod(
port,
jni$_.MethodInvocation.fromAddresses(
0,
descriptor.address,
args.address,
),
);
}
static final jni$_.Pointer<
jni$_.NativeFunction<
jni$_.JObjectPtr Function(jni$_.Int64, jni$_.JObjectPtr, jni$_.JObjectPtr)
>
>
_$invokePointer = jni$_.Pointer.fromFunction(_$invoke);
static jni$_.Pointer<jni$_.Void> _$invokeMethod(
core$_.int $p,
jni$_.MethodInvocation $i,
) {
try {
final $d = $i.methodDescriptor.toDartString(releaseOriginal: true);
final $a = $i.args;
if ($d == r'run()V') {
_$impls[$p]!.run();
return jni$_.nullptr;
}
} catch (e) {
return jni$_.ProtectedJniExtensions.newDartException(e);
}
return jni$_.nullptr;
}
static void implementIn(
jni$_.JImplementer implementer,
$Runnable $impl,
) {
late final jni$_.RawReceivePort $p;
$p = jni$_.RawReceivePort(($m) {
if ($m == null) {
_$impls.remove($p.sendPort.nativePort);
$p.close();
return;
}
final $i = jni$_.MethodInvocation.fromMessage($m);
final $r = _$invokeMethod($p.sendPort.nativePort, $i);
jni$_.ProtectedJniExtensions.returnResult($i.result, $r);
});
implementer.add(
r'java.lang.Runnable',
$p,
_$invokePointer,
[
if ($impl.run$async) r'run()V',
],
);
final $a = $p.sendPort.nativePort;
_$impls[$a] = $impl;
}
factory Runnable.implement(
$Runnable $impl,
) {
final $i = jni$_.JImplementer();
implementIn($i, $impl);
return $i.implement<Runnable>();
}
}
extension Runnable$$Methods on Runnable {
static final _id_run = Runnable._class.instanceMethodId(
r'run',
r'()V',
);
static final _run =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>
>('globalEnv_CallVoidMethod')
.asFunction<
jni$_.JThrowablePtr Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>();
/// from: `public abstract void run()`
void run() {
final _$$selfRef = reference;
_run(_$$selfRef.pointer, _id_run.pointer).check();
}
}
abstract base mixin class $Runnable {
factory $Runnable({
required void Function() run,
core$_.bool run$async,
}) = _$Runnable;
void run();
core$_.bool get run$async => false;
}
final class _$Runnable with $Runnable {
_$Runnable({
required void Function() run,
this.run$async = false,
}) : _run = run;
final void Function() _run;
final core$_.bool run$async;
void run() {
return _run();
}
}
final class $Runnable$Type$ extends jni$_.JType<Runnable> {
@jni$_.internal
const $Runnable$Type$();
@jni$_.internal
@core$_.override
String get signature => r'Ljava/lang/Runnable;';
}

View File

@@ -1,36 +1,26 @@
import 'dart:io' show Platform;
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/services.dart' show MethodChannel;
import 'package:PiliPlus/utils/android/android_helper.dart';
import 'package:PiliPlus/utils/android/bindings.g.dart';
abstract final class MaxScreenSize {
static int? _maxWidth;
static int? _maxHeight;
static Future<void> init() {
return Future.wait([_initFoldable(), _initScreenSize()]);
}
static Future<void> _initFoldable() async {
final isFoldable = await Utils.channel.invokeMethod('isFoldable');
if (isFoldable == true) {
const MethodChannel('ScreenChannel').setMethodCallHandler((call) {
if (call.method == 'onConfigChanged') {
_handleRes(call.arguments);
}
return Future.syncValue(null);
});
static void init() {
_initScreenSize();
if (AndroidHelper.isFoldable) {
AndroidHelper$ToDart.onUserLeaveHint = Runnable.implement(
$Runnable(run: _initScreenSize),
);
}
}
static Future<void> _initScreenSize() {
return Utils.channel.invokeMethod('maxScreenSize').then(_handleRes);
}
static void _handleRes(dynamic res) {
if (res is Map) {
_maxWidth = res['maxWidth'];
_maxHeight = res['maxHeight'];
static void _initScreenSize() {
final size = PiliAndroidHelper.maxScreenSize();
if (size != null) {
_maxWidth = size.$1;
_maxHeight = size.$2;
}
}

View File

@@ -1,4 +1,3 @@
import 'dart:convert' show jsonEncode;
import 'dart:io' show Platform;
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
@@ -8,6 +7,7 @@ import 'package:PiliPlus/http/reply.dart';
import 'package:PiliPlus/models/common/reply/reply_sort_type.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/android/android_helper.dart';
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
import 'package:PiliPlus/utils/extension/theme_ext.dart';
import 'package:PiliPlus/utils/id_utils.dart';
@@ -55,11 +55,11 @@ abstract final class ReplyUtils {
required int type,
required int id,
required String message,
dynamic root,
dynamic parent,
dynamic ctime,
List? pictures,
dynamic mid,
required int root,
required int parent,
required int ctime,
required List pictures,
required int mid,
bool isManual = false,
required bool biliSendCommAntifraud,
required sourceId,
@@ -67,27 +67,24 @@ abstract final class ReplyUtils {
// biliSendCommAntifraud
if (Platform.isAndroid && biliSendCommAntifraud) {
try {
final String cookieString = Accounts.main.cookieJar
final cookieString = Accounts.main.cookieJar
.toJson()
.entries
.map((i) => '${i.key}=${i.value}')
.join(';');
Utils.channel.invokeMethod(
'biliSendCommAntifraud',
{
'action': 0,
'oid': oid,
'type': type,
'rpid': id,
'root': root,
'parent': parent,
'ctime': ctime,
'comment_text': message,
if (pictures?.isNotEmpty == true) 'pictures': jsonEncode(pictures),
'source_id': '$sourceId',
'uid': mid,
'cookies': [cookieString],
},
PiliAndroidHelper.biliSendCommAntifraud(
0,
oid,
type,
id,
root,
parent,
ctime,
message,
pictures,
sourceId,
mid,
cookieString,
);
} catch (e) {
if (kDebugMode) debugPrint('biliSendCommAntifraud: $e');

View File

@@ -1,17 +1,13 @@
import 'dart:convert' show JsonEncoder, base64;
import 'dart:math' show Random;
import 'package:PiliPlus/common/constants.dart';
import 'package:catcher_2/catcher_2.dart';
import 'package:flutter/services.dart'
show Clipboard, ClipboardData, MethodChannel;
import 'package:flutter/services.dart' show Clipboard, ClipboardData;
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
abstract final class Utils {
static final random = Random();
static const channel = MethodChannel(Constants.appName);
static const jsonEncoder = JsonEncoder.withIndent(' ');
static final numericRegex = RegExp(r'^[\d\.]+$');

View File

@@ -268,6 +268,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.4"
cli_config:
dependency: transitive
description:
name: cli_config
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
url: "https://pub.dev"
source: hosted
version: "0.2.0"
cli_util:
dependency: transitive
description:
@@ -994,7 +1002,7 @@ packages:
source: hosted
version: "0.6.1"
jni:
dependency: transitive
dependency: "direct main"
description:
name: jni
sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f
@@ -1009,6 +1017,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
jnigen:
dependency: "direct dev"
description:
path: "pkgs/jnigen"
ref: HEAD
resolved-ref: "14f4ce655c0cebb203777b8200303a9bc1f0b04e"
url: "https://github.com/dart-lang/native.git"
source: git
version: "0.17.0-wip"
js:
dependency: transitive
description:

View File

@@ -116,6 +116,7 @@ dependencies:
image_cropper: ^12.0.0
image_picker: ^1.1.2
intl: ^0.20.2
jni: ^1.0.0
json_annotation: ^4.11.0
live_photo_maker: ^0.0.6
logger: ^2.5.0
@@ -219,6 +220,10 @@ dev_dependencies:
flutter_launcher_icons: ^0.14.4
flutter_lints: ^6.0.0
flutter_native_splash: ^2.4.6
jnigen:
git:
url: https://github.com/dart-lang/native.git
path: pkgs/jnigen
flutter_launcher_icons:
android: true