mirror of
https://github.com/haorendashu/nowser.git
synced 2025-12-17 09:54:19 +01:00
nowser add contentresolver support
This commit is contained in:
@@ -30,11 +30,15 @@ if (flutterVersionName == null) {
|
||||
android {
|
||||
namespace = "com.github.haorendashu.nowser"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
ndkVersion = "26.1.10909125"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@@ -39,11 +39,17 @@
|
||||
<data android:scheme="nostrsigner" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
|
||||
<provider
|
||||
android:name="com.github.haorendashu.nowser.NowserSignerProvider"
|
||||
android:authorities="com.github.haorendashu.nowser.SIGN_EVENT;com.github.haorendashu.nowser.NIP04_ENCRYPT;com.github.haorendashu.nowser.NIP04_DECRYPT;com.github.haorendashu.nowser.NIP44_ENCRYPT;com.github.haorendashu.nowser.NIP44_DECRYPT;com.github.haorendashu.nowser.GET_PUBLIC_KEY"
|
||||
android:exported="true" />
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.github.haorendashu.nowser;
|
||||
|
||||
import com.nt4f04und.android_content_provider.AndroidContentProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NowserSignerProvider extends AndroidContentProvider {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return "com.github.haorendashu.nowser.SIGN_EVENT;com.github.haorendashu.nowser.NIP04_ENCRYPT;com.github.haorendashu.nowser.NIP04_DECRYPT;com.github.haorendashu.nowser.NIP44_ENCRYPT;com.github.haorendashu.nowser.NIP44_DECRYPT;com.github.haorendashu.nowser.GET_PUBLIC_KEY";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getEntrypointName() {
|
||||
return "nowserSignerProviderEntrypoint";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
package com.github.haorendashu.nowser
|
||||
|
||||
import android.content.Context
|
||||
import com.nt4f04und.android_content_provider.AndroidContentProvider
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
|
||||
class MainActivity: FlutterActivity()
|
||||
class MainActivity: FlutterActivity() {
|
||||
override fun provideFlutterEngine(context: Context): FlutterEngine? {
|
||||
return AndroidContentProvider.getFlutterEngineGroup(this)
|
||||
.createAndRunDefaultEngine(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,17 @@ allprojects {
|
||||
}
|
||||
|
||||
rootProject.buildDir = "../build"
|
||||
subprojects {
|
||||
afterEvaluate { project ->
|
||||
if (project.hasProperty('android')) {
|
||||
project.android {
|
||||
if (namespace == null) {
|
||||
namespace project.group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
||||
kotlin.jvm.target.validation.mode = IGNORE
|
||||
|
||||
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
|
||||
|
||||
@@ -18,8 +18,8 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.3.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
|
||||
id "com.android.application" version '8.1.1' apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
||||
@@ -34,6 +34,7 @@ import 'const/base.dart';
|
||||
import 'const/colors.dart';
|
||||
import 'const/router_path.dart';
|
||||
import 'generated/l10n.dart';
|
||||
import 'provider/android_signer_content_resolver_provider.dart';
|
||||
import 'provider/data_util.dart';
|
||||
import 'provider/remote_signing_provider.dart';
|
||||
import 'provider/setting_provider.dart';
|
||||
@@ -78,6 +79,12 @@ Future<void> main() async {
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
}
|
||||
|
||||
await doInit();
|
||||
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
Future<void> doInit() async {
|
||||
keyProvider = KeyProvider();
|
||||
appProvider = AppProvider();
|
||||
|
||||
@@ -92,8 +99,6 @@ Future<void> main() async {
|
||||
settingProvider = futureResultList[0] as SettingProvider;
|
||||
webProvider = WebProvider();
|
||||
remoteSigningProvider = RemoteSigningProvider();
|
||||
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
@@ -330,3 +335,11 @@ Color _getMainColor() {
|
||||
}
|
||||
return color500;
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> nowserSignerProviderEntrypoint() async {
|
||||
// if we call content resolver this should init first, to receive request.
|
||||
// so, doInit() method move to query method.
|
||||
AndroidSignerContentResolverProvider(
|
||||
'com.github.haorendashu.nowser.SIGN_EVENT;com.github.haorendashu.nowser.NIP04_ENCRYPT;com.github.haorendashu.nowser.NIP04_DECRYPT;com.github.haorendashu.nowser.NIP44_ENCRYPT;com.github.haorendashu.nowser.NIP44_DECRYPT;com.github.haorendashu.nowser.GET_PUBLIC_KEY');
|
||||
}
|
||||
|
||||
179
lib/provider/android_signer_content_resolver_provider.dart
Normal file
179
lib/provider/android_signer_content_resolver_provider.dart
Normal file
@@ -0,0 +1,179 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:android_content_provider/android_content_provider.dart';
|
||||
import 'package:nostr_sdk/event.dart';
|
||||
import 'package:nostr_sdk/nip19/nip19.dart';
|
||||
import 'package:nostr_sdk/signer/nostr_signer.dart';
|
||||
import 'package:nostr_sdk/utils/string_util.dart';
|
||||
import 'package:nowser/const/app_type.dart';
|
||||
import 'package:nowser/provider/permission_check_mixin.dart';
|
||||
|
||||
import '../const/auth_result.dart';
|
||||
import '../const/auth_type.dart';
|
||||
import '../data/app.dart';
|
||||
import '../main.dart';
|
||||
|
||||
class AndroidSignerContentResolverProvider extends AndroidContentProvider
|
||||
with PermissionCheckMixin {
|
||||
AndroidSignerContentResolverProvider(super.authority);
|
||||
|
||||
@override
|
||||
Future<int> delete(
|
||||
String uri, String? selection, List<String>? selectionArgs) async {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> getType(String uri) async {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> insert(String uri, ContentValues? values) async {
|
||||
return null;
|
||||
}
|
||||
|
||||
bool inited = false;
|
||||
|
||||
@override
|
||||
Future<CursorData?> query(String uri, List<String>? projection,
|
||||
String? selection, List<String>? selectionArgs, String? sortOrder) async {
|
||||
if (!inited) {
|
||||
await doInit();
|
||||
inited = true;
|
||||
}
|
||||
|
||||
var authTypeStr =
|
||||
uri.replaceAll("content://com.github.haorendashu.nowser.", "");
|
||||
String authDetail = "";
|
||||
String? pubkey;
|
||||
String? currentUser;
|
||||
if (projection != null && projection.isNotEmpty) {
|
||||
authDetail = projection.first;
|
||||
if (projection.length > 1) {
|
||||
pubkey = projection[1];
|
||||
}
|
||||
if (projection.length > 2) {
|
||||
currentUser = projection[2];
|
||||
}
|
||||
}
|
||||
|
||||
var authType = AuthType.GET_PUBLIC_KEY;
|
||||
if (authTypeStr == "GET_PUBLIC_KEY") {
|
||||
authType = AuthType.GET_PUBLIC_KEY;
|
||||
} else if (authTypeStr == "SIGN_EVENT") {
|
||||
authType = AuthType.SIGN_EVENT;
|
||||
} else if (authTypeStr == "GET_RELAYS") {
|
||||
authType = AuthType.GET_RELAYS;
|
||||
} else if (authTypeStr == "NIP04_ENCRYPT") {
|
||||
authType = AuthType.NIP04_ENCRYPT;
|
||||
} else if (authTypeStr == "NIP04_DECRYPT") {
|
||||
authType = AuthType.NIP04_DECRYPT;
|
||||
} else if (authTypeStr == "NIP44_ENCRYPT") {
|
||||
authType = AuthType.NIP44_ENCRYPT;
|
||||
} else if (authTypeStr == "NIP44_DECRYPT") {
|
||||
authType = AuthType.NIP44_DECRYPT;
|
||||
}
|
||||
|
||||
int appType = AppType.ANDROID_APP;
|
||||
String code = "com.github.haorendashu.nostrmo";
|
||||
|
||||
int? eventKind;
|
||||
dynamic eventObj;
|
||||
if (authType == AuthType.SIGN_EVENT) {
|
||||
eventObj = jsonDecode(authDetail);
|
||||
if (eventObj != null) {
|
||||
eventKind = eventObj["kind"];
|
||||
}
|
||||
}
|
||||
|
||||
App? app;
|
||||
NostrSigner? signer;
|
||||
var complete = Completer();
|
||||
|
||||
rejectFunc(_app) {
|
||||
saveAuthLog(_app, authType, eventKind, authDetail, AuthResult.REJECT);
|
||||
complete.complete();
|
||||
}
|
||||
|
||||
confirmFunc(_app, _signer) {
|
||||
app = _app;
|
||||
signer = _signer;
|
||||
complete.complete();
|
||||
}
|
||||
|
||||
checkPermission(null, appType, code, authType, rejectFunc, confirmFunc,
|
||||
eventKind: eventKind, authDetail: authDetail);
|
||||
|
||||
await complete.future;
|
||||
if (signer == null || app == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MatrixCursorData? data;
|
||||
|
||||
if (authType == AuthType.GET_PUBLIC_KEY) {
|
||||
// TODO should handle permissions
|
||||
// var permissions = extra["permissions"];
|
||||
var pubkey = await signer!.getPublicKey();
|
||||
data =
|
||||
MatrixCursorData(columnNames: ["signature"], notificationUris: [uri]);
|
||||
data.addRow([Nip19.encodePubKey(pubkey!)]);
|
||||
} else if (authType == AuthType.SIGN_EVENT) {
|
||||
var tags = eventObj["tags"];
|
||||
Event? event = Event(
|
||||
app!.pubkey!, eventObj["kind"], tags ?? [], eventObj["content"],
|
||||
createdAt: eventObj["created_at"]);
|
||||
log(jsonEncode(event.toJson()));
|
||||
event = await signer!.signEvent(event);
|
||||
if (event == null) {
|
||||
log("sign event fail");
|
||||
return null;
|
||||
}
|
||||
log("sig ${event.sig}");
|
||||
data = MatrixCursorData(
|
||||
columnNames: ["signature", "event"], notificationUris: [uri]);
|
||||
data.addRow([event.sig, jsonEncode(event.toJson())]);
|
||||
} else if (authType == AuthType.GET_RELAYS) {
|
||||
// TODO
|
||||
} else if (authType == AuthType.NIP04_ENCRYPT) {
|
||||
var result = await signer!.encrypt(pubkey, authDetail);
|
||||
if (StringUtil.isNotBlank(result)) {
|
||||
data = MatrixCursorData(
|
||||
columnNames: ["signature"], notificationUris: [uri]);
|
||||
data.addRow([result]);
|
||||
}
|
||||
} else if (authType == AuthType.NIP04_DECRYPT) {
|
||||
var result = await signer!.decrypt(pubkey, authDetail);
|
||||
if (StringUtil.isNotBlank(result)) {
|
||||
data = MatrixCursorData(
|
||||
columnNames: ["signature"], notificationUris: [uri]);
|
||||
data.addRow([result]);
|
||||
}
|
||||
} else if (authType == AuthType.NIP44_ENCRYPT) {
|
||||
var result = await signer!.nip44Encrypt(pubkey, authDetail);
|
||||
if (StringUtil.isNotBlank(result)) {
|
||||
data = MatrixCursorData(
|
||||
columnNames: ["signature"], notificationUris: [uri]);
|
||||
data.addRow([result]);
|
||||
}
|
||||
} else if (authType == AuthType.NIP44_DECRYPT) {
|
||||
var result = await signer!.nip44Decrypt(pubkey, authDetail);
|
||||
if (StringUtil.isNotBlank(result)) {
|
||||
data = MatrixCursorData(
|
||||
columnNames: ["signature"], notificationUris: [uri]);
|
||||
data.addRow([result]);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> update(String uri, ContentValues? values, String? selection,
|
||||
List<String>? selectionArgs) async {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,14 @@ import '../const/connect_type.dart';
|
||||
import '../data/app.dart';
|
||||
|
||||
mixin PermissionCheckMixin {
|
||||
Future<void> checkPermission(BuildContext context, int appType, String code,
|
||||
Future<void> checkPermission(BuildContext? context, int appType, String code,
|
||||
int authType, Function(App?) reject, Function(App, NostrSigner) confirm,
|
||||
{int? eventKind, String? authDetail}) async {
|
||||
if (keyProvider.keys.isEmpty) {
|
||||
// should add a key first
|
||||
await UserLoginDialog.show(context);
|
||||
if (context != null) {
|
||||
await UserLoginDialog.show(context);
|
||||
}
|
||||
if (keyProvider.keys.isEmpty) {
|
||||
return;
|
||||
}
|
||||
@@ -27,7 +29,9 @@ mixin PermissionCheckMixin {
|
||||
if (app == null) {
|
||||
// app is null, app connect
|
||||
var newApp = await getApp(appType, code);
|
||||
await AuthAppConnectDialog.show(context, newApp);
|
||||
if (context != null) {
|
||||
await AuthAppConnectDialog.show(context, newApp);
|
||||
}
|
||||
// reload from provider
|
||||
app = appProvider.getApp(appType, code);
|
||||
}
|
||||
@@ -63,12 +67,14 @@ mixin PermissionCheckMixin {
|
||||
return;
|
||||
}
|
||||
|
||||
var authResult = await AuthDialog.show(context, app, authType,
|
||||
eventKind: eventKind, authDetail: authDetail);
|
||||
if (authResult == AuthResult.OK) {
|
||||
saveAuthLog(app, authType, eventKind, authDetail, AuthResult.OK);
|
||||
confirm(app, signer);
|
||||
return;
|
||||
if (context != null) {
|
||||
var authResult = await AuthDialog.show(context, app, authType,
|
||||
eventKind: eventKind, authDetail: authDetail);
|
||||
if (authResult == AuthResult.OK) {
|
||||
saveAuthLog(app, authType, eventKind, authDetail, AuthResult.OK);
|
||||
confirm(app, signer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import flutter_secure_storage_macos
|
||||
import path_provider_foundation
|
||||
import screen_retriever
|
||||
import shared_preferences_foundation
|
||||
import sqflite
|
||||
import sqflite_darwin
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
|
||||
48
pubspec.lock
48
pubspec.lock
@@ -1,6 +1,14 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
android_content_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: android_content_provider
|
||||
sha256: c39dae7c8a520e11d210690d1340bbc86c871271b3f501a67724baff2f7da167
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -853,34 +861,58 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
|
||||
sha256: "79a297dc3cc137e758c6a4baf83342b039e5a6d2436fcdf3f96a00adaaf2ad62"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3+1"
|
||||
version: "2.4.0"
|
||||
sqflite_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_android
|
||||
sha256: "11821baf1d1bd3d71150d9b0d4c2e22cf680858c71add2113c919ca55cf9dc56"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0-1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
|
||||
sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
version: "2.5.4+5"
|
||||
sqflite_common_ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common_ffi
|
||||
sha256: "4d6137c29e930d6e4a8ff373989dd9de7bac12e3bc87bce950f6e844e8ad3bb5"
|
||||
sha256: a6057d4c87e9260ba1ec436ebac24760a110589b9c0a859e128842eb69a7ef04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
version: "2.3.3+1"
|
||||
sqflite_common_ffi_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common_ffi_web
|
||||
sha256: e9d1cb35a5ff7c43072968ed734e0a1a859564fd2b2c8654e0c6244a57dc82a8
|
||||
sha256: f540ad769e5fd31aabe77bfa6e774fdd36145a83e33cdc39239f310c5f8559c3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.4"
|
||||
version: "0.4.5+3"
|
||||
sqflite_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_darwin
|
||||
sha256: "5e325c925cbd63f27e0e538aed018a40852325e590b5d83165181e492d272f9b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1-0"
|
||||
sqflite_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_platform_interface
|
||||
sha256: b62ab81e1284341783222aefbbb44f984ebf4663d672ae10408c9a8ddab4bfb6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0-0"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user