diff --git a/android/app/src/main/java/com/example/piliplus/AndroidHelper.java b/android/app/src/main/java/com/example/piliplus/AndroidHelper.java new file mode 100644 index 000000000..73c2948ba --- /dev/null +++ b/android/app/src/main/java/com/example/piliplus/AndroidHelper.java @@ -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 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() { + } + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt b/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt index b2878bbdf..3f03a3044 100644 --- a/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt @@ -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("action") ?: 0 - val oid = call.argument("oid") ?: 0L - val type = call.argument("type") ?: 0 - val rpid = call.argument("rpid") ?: 0L - val root = call.argument("root") ?: 0L - val parent = call.argument("parent") ?: 0L - val ctime = call.argument("ctime") ?: 0L - val commentText = call.argument("comment_text") ?: "" - val pictures = call.argument("pictures") - val sourceId = call.argument("source_id") ?: "" - val uid = call.argument("uid") ?: 0L - val cookies = call.argument>("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("title") - val intent = Intent(MediaStore.INTENT_ACTION_MEDIA_SEARCH).apply { - putExtra(SearchManager.QUERY, title) - putExtra(MediaStore.EXTRA_MEDIA_TITLE, title) - call.argument("artist") - ?.let { putExtra(MediaStore.EXTRA_MEDIA_ARTIST, it) } - call.argument("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("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("id")!! - val uri = call.argument("uri")!! - val label = call.argument("label")!! - val icon = call.argument("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? { - 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 + ) } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index db0a6f9fe..497e99979 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/jnigen.yaml b/jnigen.yaml new file mode 100644 index 000000000..73090ab1c --- /dev/null +++ b/jnigen.yaml @@ -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' diff --git a/lib/main.dart b/lib/main.dart index c198ce8be..db9d381fe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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(), ]); diff --git a/lib/pages/about/view.dart b/lib/pages/about/view.dart index 9e59f25ca..0935babba 100644 --- a/lib/pages/about/view.dart +++ b/lib/pages/about/view.dart @@ -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( diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 4d7e61d26..921d6afa3 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -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 await trayManager.setContextMenu(trayMenu); } + @pragma('vm:prefer-inline') static void _onBack() { if (Platform.isAndroid) { - Utils.channel.invokeMethod('back'); + PiliAndroidHelper.back(); } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 63ae22380..629bcbe6e 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -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 { '${_userController.userAvatar!}@200w_200h.webp'.http2https, )); SmartDialog.dismiss(); - await Utils.channel.invokeMethod( - 'createShortcut', - { - '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()); diff --git a/lib/pages/music/view.dart b/lib/pages/music/view.dart index d41674f04..0635cc675 100644 --- a/lib/pages/music/view.dart +++ b/lib/pages/music/view.dart @@ -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 { ); } - Future _searchMusic(MusicDetail item) async { - final res = - Platform.isAndroid && - (await Utils.channel.invokeMethod('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!); } } diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 3d4790c44..da40d5563 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -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(); diff --git a/lib/utils/android/android_helper.dart b/lib/utils/android/android_helper.dart new file mode 100644 index 000000000..0cbac42b4 --- /dev/null +++ b/lib/utils/android/android_helper.dart @@ -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(); + } + } +} diff --git a/lib/utils/android/bindings.g.dart b/lib/utils/android/bindings.g.dart new file mode 100644 index 000000000..5e0a77592 --- /dev/null +++ b/lib/utils/android/bindings.g.dart @@ -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 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 { + @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 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$_.JMethodIDPtr, + ) + > + >('globalEnv_CallStaticVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + 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$_.JMethodIDPtr, + jni$_.VarArgs< + ( + jni$_.Int32, + jni$_.Int64, + jni$_.Int32, + jni$_.Int64, + jni$_.Int64, + jni$_.Int64, + jni$_.Int64, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Int64, + jni$_.Pointer, + ) + >, + ) + > + >('globalEnv_CallStaticVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + core$_.int, + core$_.int, + core$_.int, + core$_.int, + core$_.int, + core$_.int, + core$_.int, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + core$_.int, + jni$_.Pointer, + ) + >(); + + /// 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$_.JMethodIDPtr, + ) + > + >('globalEnv_CallStaticVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + 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$_.JMethodIDPtr, + jni$_.VarArgs< + ( + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + ) + >, + ) + > + >('globalEnv_CallStaticBooleanMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + ) + >(); + + /// 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$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Int32, jni$_.Int64)>, + ) + > + >('globalEnv_CallStaticVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + 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$_.JMethodIDPtr, + ) + > + >('globalEnv_CallStaticObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + 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(); + } + + 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$_.JMethodIDPtr, + jni$_.VarArgs< + ( + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + ) + >, + ) + > + >('globalEnv_CallStaticVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + ) + >(); + + /// 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 { + @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 type = $Runnable$Type$(); + + /// Maps a specific port to the implemented interface. + static final core$_.Map _$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 _$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(); + } +} + +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$_.JMethodIDPtr, + ) + > + >('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + 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 { + @jni$_.internal + const $Runnable$Type$(); + + @jni$_.internal + @core$_.override + String get signature => r'Ljava/lang/Runnable;'; +} diff --git a/lib/utils/max_screen_size.dart b/lib/utils/max_screen_size.dart index 9fd4cdf5b..bc90951b9 100644 --- a/lib/utils/max_screen_size.dart +++ b/lib/utils/max_screen_size.dart @@ -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 init() { - return Future.wait([_initFoldable(), _initScreenSize()]); - } - - static Future _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 _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; } } diff --git a/lib/utils/reply_utils.dart b/lib/utils/reply_utils.dart index 18772a205..25962ac56 100644 --- a/lib/utils/reply_utils.dart +++ b/lib/utils/reply_utils.dart @@ -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'); diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 1b7258109..c6fcf584e 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -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\.]+$'); diff --git a/pubspec.lock b/pubspec.lock index 8fb13feb2..bfac5c7e5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: diff --git a/pubspec.yaml b/pubspec.yaml index 7981c816e..f4260ca58 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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