* refa: jni

* refa: jni
This commit is contained in:
My-Responsitories
2026-05-30 12:10:35 +00:00
committed by dom
parent f3f871c893
commit 1a7678ee0f
17 changed files with 530 additions and 177 deletions

View File

@@ -0,0 +1,84 @@
package com.example.piliplus;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import androidx.annotation.Keep;
import com.github.dart_lang.jni_flutter.JniFlutterPlugin;
import org.jetbrains.annotations.NotNull;
@Keep
public final class AndroidHelper {
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 int sdkInt() {
return Build.VERSION.SDK_INT;
}
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 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());
}
}
}
}

View File

@@ -1,22 +1,15 @@
package com.example.piliplus package com.example.piliplus
import android.app.PendingIntent
import android.app.SearchManager
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.BitmapFactory
import android.graphics.drawable.Icon
import android.os.Build import android.os.Build
import android.provider.MediaStore import android.os.Bundle
import android.provider.Settings import android.view.WindowManager.LayoutParams
import androidx.core.net.toUri
import com.ryanheise.audioservice.AudioServiceActivity import com.ryanheise.audioservice.AudioServiceActivity
import io.flutter.SystemChrome
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import io.flutter.SystemChrome
class MainActivity : AudioServiceActivity() { class MainActivity : AudioServiceActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) { override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
@@ -25,115 +18,15 @@ class MainActivity : AudioServiceActivity() {
val methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "PiliPlus") val methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "PiliPlus")
methodChannel.setMethodCallHandler { call, result -> methodChannel.setMethodCallHandler { call, result ->
when (call.method) { when (call.method) {
"back" -> back()
"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)
}
"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) {
}
}
}
"sdkInt" -> {
result.success(Build.VERSION.SDK_INT)
}
"SystemChrome.setEnabledSystemUIMode" -> { "SystemChrome.setEnabledSystemUIMode" -> {
SystemChrome.onMethodCall( SystemChrome.onMethodCall(
this, this, "SystemChrome.setEnabledSystemUIMode", call.argument("arguments")
"SystemChrome.setEnabledSystemUIMode",
call.argument("arguments")
) )
} }
"SystemChrome.setEnabledSystemUIOverlays" -> { "SystemChrome.setEnabledSystemUIOverlays" -> {
SystemChrome.onMethodCall( SystemChrome.onMethodCall(
this, this, "SystemChrome.setEnabledSystemUIOverlays", call.argument("arguments")
"SystemChrome.setEnabledSystemUIOverlays",
call.argument("arguments")
) )
} }
@@ -142,12 +35,12 @@ class MainActivity : AudioServiceActivity() {
} }
} }
private fun back() { override fun onCreate(savedInstanceState: Bundle?) {
val intent = Intent(Intent.ACTION_MAIN).apply { super.onCreate(savedInstanceState)
addCategory(Intent.CATEGORY_HOME) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
flags = Intent.FLAG_ACTIVITY_NEW_TASK window.attributes.layoutInDisplayCutoutMode =
LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
} }
startActivity(intent)
} }
override fun onDestroy() { override fun onDestroy() {
@@ -156,13 +49,11 @@ class MainActivity : AudioServiceActivity() {
} }
override fun onPictureInPictureModeChanged( override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean, isInPictureInPictureMode: Boolean, newConfig: Configuration?
newConfig: Configuration?
) { ) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
MethodChannel( MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, "floating").invokeMethod(
flutterEngine!!.dartExecutor.binaryMessenger, "onPipChanged", isInPictureInPictureMode
"floating" )
).invokeMethod("onPipChanged", isInPictureInPictureMode)
} }
} }

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@@ -18,7 +18,6 @@ import 'package:PiliPlus/services/service_locator.dart';
import 'package:PiliPlus/utils/cache_manager.dart'; import 'package:PiliPlus/utils/cache_manager.dart';
import 'package:PiliPlus/utils/calc_window_position.dart'; import 'package:PiliPlus/utils/calc_window_position.dart';
import 'package:PiliPlus/utils/date_utils.dart'; import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/device_utils.dart';
import 'package:PiliPlus/utils/extension/theme_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart';
import 'package:PiliPlus/utils/json_file_handler.dart'; import 'package:PiliPlus/utils/json_file_handler.dart';
import 'package:PiliPlus/utils/path_utils.dart'; import 'package:PiliPlus/utils/path_utils.dart';
@@ -84,10 +83,6 @@ Future<void> _initAppPath() async {
appSupportDirPath = (await getApplicationSupportDirectory()).path; appSupportDirPath = (await getApplicationSupportDirectory()).path;
} }
Future<void> _initSdkInt() async {
DeviceUtils.sdkInt = await Utils.channel.invokeMethod('sdkInt');
}
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized(); MediaKit.ensureInitialized();
@@ -109,7 +104,6 @@ void main() async {
if (PlatformUtils.isMobile) { if (PlatformUtils.isMobile) {
await Future.wait([ await Future.wait([
if (Platform.isAndroid) _initSdkInt(),
if (Pref.horizontalScreen) ?fullMode() else ?portraitUpMode(), if (Pref.horizontalScreen) ?fullMode() else ?portraitUpMode(),
setupServiceLocator(), setupServiceLocator(),
]); ]);

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,3 @@
import 'dart:io' show Platform;
import 'dart:math' as math; import 'dart:math' as math;
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
@@ -428,15 +427,10 @@ class _MusicDetailPageState extends CommonDynPageState<MusicDetailPage> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
GestureDetector( MarqueeText(
onTap: () => _searchMusic(item), item.musicTitle!,
onLongPress: () => Utils.copyText(item.musicTitle!), spacing: 30,
behavior: HitTestBehavior.opaque, style: textTheme.titleMedium,
child: MarqueeText(
item.musicTitle!,
spacing: 30,
style: textTheme.titleMedium,
),
), ),
Wrap( Wrap(
spacing: 8, spacing: 8,
@@ -558,18 +552,4 @@ 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) {
Utils.copyText(item.musicTitle!);
}
}
} }

View File

@@ -0,0 +1,19 @@
import 'package:PiliPlus/utils/android/bindings.g.dart';
import 'package:jni/jni.dart';
abstract final class PiliAndroidHelper {
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,342 @@
// 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: `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;';
}
/// 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_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_sdkInt = _class.staticMethodId(
r'sdkInt',
r'()I',
);
static final _sdkInt =
jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JniResult Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>
>('globalEnv_CallStaticIntMethod')
.asFunction<
jni$_.JniResult Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)
>();
/// from: `static public int sdkInt()`
static core$_.int sdkInt() {
final _$$classRef = _class.reference;
return _sdkInt(_$$classRef.pointer, _id_sdkInt.pointer).integer;
}
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_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 id, java.lang.String uri, java.lang.String label, java.lang.String icon)`
static void createShortcut(
jni$_.JString id,
jni$_.JString uri,
jni$_.JString label,
jni$_.JString icon,
) {
final _$$classRef = _class.reference;
final _$id = id.reference;
final _$uri = uri.reference;
final _$label = label.reference;
final _$icon = icon.reference;
_createShortcut(
_$$classRef.pointer,
_id_createShortcut.pointer,
_$id.pointer,
_$uri.pointer,
_$label.pointer,
_$icon.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;';
}

View File

@@ -1,8 +1,9 @@
import 'package:PiliPlus/utils/android/bindings.g.dart';
import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:flutter/widgets.dart' show WidgetsBinding, Size; import 'package:flutter/widgets.dart' show WidgetsBinding, Size;
abstract final class DeviceUtils { abstract final class DeviceUtils {
static late int sdkInt; static final int sdkInt = AndroidHelper.sdkInt();
static bool get isTablet { static bool get isTablet {
return size.shortestSide >= 600; return size.shortestSide >= 600;

View File

@@ -46,11 +46,11 @@ abstract final class ReplyUtils {
required int type, required int type,
required int id, required int id,
required String message, required String message,
dynamic root, required int root,
dynamic parent, required int parent,
dynamic ctime, required int ctime,
List? pictures, required List pictures,
dynamic mid, required int mid,
bool isManual = false, bool isManual = false,
}) async { }) async {
// CommAntifraud // CommAntifraud

View File

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

View File

@@ -269,6 +269,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.4" 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: cli_util:
dependency: transitive dependency: transitive
description: description:
@@ -948,7 +956,7 @@ packages:
source: hosted source: hosted
version: "0.6.1" version: "0.6.1"
jni: jni:
dependency: transitive dependency: "direct main"
description: description:
name: jni name: jni
sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f
@@ -963,6 +971,15 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.1" 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: js:
dependency: transitive dependency: transitive
description: description:

View File

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

1
tool/README.md Normal file
View File

@@ -0,0 +1 @@
run `dart run tool/jnigen.dart`

23
tool/jnigen.dart Normal file
View File

@@ -0,0 +1,23 @@
import 'dart:io' show Platform;
import 'package:jnigen/jnigen.dart';
void main(List<String> args) {
final packageRoot = Platform.script.resolve('../');
generateJniBindings(
Config(
outputConfig: OutputConfig(
dartConfig: DartCodeOutputConfig(
path: packageRoot.resolve('lib/utils/android/bindings.g.dart'),
structure: .singleFile,
),
),
androidSdkConfig: AndroidSdkConfig(addGradleDeps: true),
sourcePath: [packageRoot.resolve('android/app/src/main/java')],
classes: [
'com.example.piliplus',
'java.lang.Runnable',
],
),
);
}