From 2a89ef797f41eeee8472c426ceac688f3cda6034 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Sun, 24 Sep 2023 16:56:25 -0700 Subject: [PATCH 01/13] feat: share settings --- android/app/src/main/AndroidManifest.xml | 5 ++ .../revanced/manager/flutter/MainActivity.kt | 22 +++++ .../flutter/utils/share/ShareProvider.kt | 80 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/ShareProvider.kt diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 08e9e77c..8ae06734 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -54,5 +54,10 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> + + diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt index 22463f8c..444fa970 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt @@ -27,6 +27,13 @@ import java.io.StringWriter import java.util.logging.LogRecord import java.util.logging.Logger +import android.content.ContentResolver +import android.content.Context +import android.database.Cursor +import android.net.Uri + +import android.util.Log + class MainActivity : FlutterActivity() { private val handler = Handler(Looper.getMainLooper()) private lateinit var installerChannel: MethodChannel @@ -39,6 +46,21 @@ class MainActivity : FlutterActivity() { val patcherChannel = "app.revanced.manager.flutter/patcher" val installerChannel = "app.revanced.manager.flutter/installer" + val contentProviderUri = Uri.parse("content://app.revanced.manager.flutter.provider/settings") + val contentResolver: ContentResolver = context.contentResolver + val cursor: Cursor? = contentResolver.query(contentProviderUri, null, null, null, null) + + Log.d("app.revanced.manager.flutter.debug", "byhithere") + if (cursor != null) { + Log.d("app.revanced.manager.flutter.debug", "test2") + if (cursor.moveToFirst()) { + val helloValue = cursor.getString(cursor.getColumnIndex("settings")) + // Process the retrieved "hello" value + Log.d("testing2", helloValue) + } + cursor.close() + } + val mainChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, patcherChannel) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/ShareProvider.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/ShareProvider.kt new file mode 100644 index 00000000..9460b335 --- /dev/null +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/ShareProvider.kt @@ -0,0 +1,80 @@ +package app.revanced.manager.flutter.utils.share + +import android.content.ContentProvider +import android.content.ContentValues +import android.content.Context +import android.content.UriMatcher +import android.database.Cursor +import android.database.MatrixCursor +import android.net.Uri +import org.json.JSONObject + +import android.util.Log + +class ShareProvider : ContentProvider() { + private val authority = "app.revanced.manager.flutter.provider" + private val URI_CODE_SETTINGS = 1 + + private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { + addURI(authority, "settings", URI_CODE_SETTINGS) + } + + override fun onCreate(): Boolean { + return true + } + + fun getSettings(): String { + val json = JSONObject() + + // Default Data + + // TODO: load default data + + // Load Shared Preferences + val sharedPreferences = context!!.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) + val allEntries: Map = sharedPreferences.getAll() + for ((key, value) in allEntries.entries) { + Log.d("map values", key + ": " + value.toString()) + json.put(key.replace("flutter.", ""), value) + } + + // TODO: Load keystore + + return json.toString() + } + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String? + ):Cursor? { + when (uriMatcher.match(uri)) { + URI_CODE_SETTINGS -> { + val cursor = MatrixCursor(arrayOf("settings")) + val row = arrayOf(getSettings()) + cursor.addRow(row) + return cursor + } + else -> throw IllegalArgumentException("Unknown URI: $uri") + } + return null + } + + override fun insert(uri: Uri, values: ContentValues?): Uri? { + throw UnsupportedOperationException("Insert operation is not supported") + } + + override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int { + throw UnsupportedOperationException("Update operation is not supported") + } + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { + throw UnsupportedOperationException("Delete operation is not supported") + } + + override fun getType(uri: Uri): String? { + throw UnsupportedOperationException("Get type operation is not supported") + } +} From 99c92069b9f8302af3fd153e391cca7d41129a10 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Mon, 25 Sep 2023 08:43:10 -0700 Subject: [PATCH 02/13] export saved patches and keystore --- android/app/src/main/AndroidManifest.xml | 2 +- .../revanced/manager/flutter/MainActivity.kt | 22 ---------------- ...eProvider.kt => LegacySettingsProvider.kt} | 26 ++++++++++++++----- 3 files changed, 20 insertions(+), 30 deletions(-) rename android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/{ShareProvider.kt => LegacySettingsProvider.kt} (72%) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8ae06734..6db88397 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -55,7 +55,7 @@ android:resource="@xml/file_paths" /> diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt index 444fa970..22463f8c 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/MainActivity.kt @@ -27,13 +27,6 @@ import java.io.StringWriter import java.util.logging.LogRecord import java.util.logging.Logger -import android.content.ContentResolver -import android.content.Context -import android.database.Cursor -import android.net.Uri - -import android.util.Log - class MainActivity : FlutterActivity() { private val handler = Handler(Looper.getMainLooper()) private lateinit var installerChannel: MethodChannel @@ -46,21 +39,6 @@ class MainActivity : FlutterActivity() { val patcherChannel = "app.revanced.manager.flutter/patcher" val installerChannel = "app.revanced.manager.flutter/installer" - val contentProviderUri = Uri.parse("content://app.revanced.manager.flutter.provider/settings") - val contentResolver: ContentResolver = context.contentResolver - val cursor: Cursor? = contentResolver.query(contentProviderUri, null, null, null, null) - - Log.d("app.revanced.manager.flutter.debug", "byhithere") - if (cursor != null) { - Log.d("app.revanced.manager.flutter.debug", "test2") - if (cursor.moveToFirst()) { - val helloValue = cursor.getString(cursor.getColumnIndex("settings")) - // Process the retrieved "hello" value - Log.d("testing2", helloValue) - } - cursor.close() - } - val mainChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, patcherChannel) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/ShareProvider.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt similarity index 72% rename from android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/ShareProvider.kt rename to android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt index 9460b335..a5475b22 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/ShareProvider.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt @@ -7,11 +7,11 @@ import android.content.UriMatcher import android.database.Cursor import android.database.MatrixCursor import android.net.Uri +import android.util.Base64 import org.json.JSONObject +import java.io.File -import android.util.Log - -class ShareProvider : ContentProvider() { +class LegacySettingsProvider : ContentProvider() { private val authority = "app.revanced.manager.flutter.provider" private val URI_CODE_SETTINGS = 1 @@ -27,18 +27,30 @@ class ShareProvider : ContentProvider() { val json = JSONObject() // Default Data - - // TODO: load default data + json.put("keystorePassword", "s3cur3p@ssw0rd") // Load Shared Preferences val sharedPreferences = context!!.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) val allEntries: Map = sharedPreferences.getAll() for ((key, value) in allEntries.entries) { - Log.d("map values", key + ": " + value.toString()) json.put(key.replace("flutter.", ""), value) } - // TODO: Load keystore + // Load keystore + val keystoreFile = File(context!!.getExternalFilesDir(null), "/revanced-manager.keystore") + if (keystoreFile.exists()) { + val keystoreBytes = keystoreFile.readBytes() + val keystoreBase64 = Base64.encodeToString(keystoreBytes, Base64.DEFAULT) + json.put("keystore", keystoreBase64) + } + + // Load saved patches + val storedPatchesFile = File(context!!.filesDir.parentFile.absolutePath, "/app_flutter/selected-patches.json") + if (storedPatchesFile.exists()) { + val patchesBytes = storedPatchesFile.readBytes() + val patches = String(patchesBytes, Charsets.UTF_8) + json.put("savedPatches", patches) + } return json.toString() } From 378d62395a5f2de90da90fdff153a28ece14b1bd Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Mon, 25 Sep 2023 10:15:56 -0700 Subject: [PATCH 03/13] remove newlines from base64 output --- .../manager/flutter/utils/share/LegacySettingsProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt index a5475b22..b1b509ea 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt @@ -40,7 +40,7 @@ class LegacySettingsProvider : ContentProvider() { val keystoreFile = File(context!!.getExternalFilesDir(null), "/revanced-manager.keystore") if (keystoreFile.exists()) { val keystoreBytes = keystoreFile.readBytes() - val keystoreBase64 = Base64.encodeToString(keystoreBytes, Base64.DEFAULT) + val keystoreBase64 = Base64.encodeToString(keystoreBytes, Base64.DEFAULT).replace("\n", "") json.put("keystore", keystoreBase64) } From f1ea30629123c5fae7b3d216830135f5810e21d6 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Mon, 25 Sep 2023 14:42:40 -0700 Subject: [PATCH 04/13] change booleans to numbers --- .../manager/flutter/utils/share/LegacySettingsProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt index b1b509ea..64a85ed8 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt @@ -33,7 +33,7 @@ class LegacySettingsProvider : ContentProvider() { val sharedPreferences = context!!.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) val allEntries: Map = sharedPreferences.getAll() for ((key, value) in allEntries.entries) { - json.put(key.replace("flutter.", ""), value) + json.put(key.replace("flutter.", ""), if (value is Boolean) if (value) 1 else 0 else value) } // Load keystore From d9d5b746c3a677ba9f56f94f462ef6c25ab8c828 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Tue, 26 Sep 2023 16:21:46 -0700 Subject: [PATCH 05/13] Added ExportSettingsActivity --- android/app/src/main/AndroidManifest.xml | 21 ++++- .../manager/flutter/ExportSettingsActivity.kt | 40 ++++++++ .../utils/share/LegacySettingsProvider.kt | 92 ------------------- assets/i18n/en_US.json | 6 ++ lib/main.dart | 16 +++- .../export_settings/export_settings_view.dart | 35 +++++++ .../export_settings_viewmodel.dart | 71 ++++++++++++++ 7 files changed, 181 insertions(+), 100 deletions(-) create mode 100644 android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt delete mode 100644 android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt create mode 100644 lib/ui/views/export_settings/export_settings_view.dart create mode 100644 lib/ui/views/export_settings/export_settings_viewmodel.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6db88397..69587cd7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -42,6 +42,22 @@ + + + + + + + @@ -54,10 +70,5 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> - - diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt new file mode 100644 index 00000000..b9e8e5aa --- /dev/null +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt @@ -0,0 +1,40 @@ +package app.revanced.manager.flutter + +import android.app.Activity +import android.content.Intent +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel +import java.io.Serializable + +class ExportSettingsActivity : FlutterActivity() { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + + val settingsChannel = "app.revanced.manager.flutter/settings" + + val mainChannel = + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, settingsChannel) + + mainChannel.setMethodCallHandler { call, result -> + when (call.method) { + "accept" -> { + val data = call.argument("data") + val resultIntent = Intent() + resultIntent.putExtra("data", data as Serializable) + setResult(Activity.RESULT_OK, resultIntent) + finish() + } + "deny" -> { + setResult(Activity.RESULT_CANCELED) + finish() + } + else -> result.notImplemented() + } + } + } + + override fun getDartEntrypointFunctionName(): String { + return "mainExportSettings" + } +} diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt deleted file mode 100644 index 64a85ed8..00000000 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/utils/share/LegacySettingsProvider.kt +++ /dev/null @@ -1,92 +0,0 @@ -package app.revanced.manager.flutter.utils.share - -import android.content.ContentProvider -import android.content.ContentValues -import android.content.Context -import android.content.UriMatcher -import android.database.Cursor -import android.database.MatrixCursor -import android.net.Uri -import android.util.Base64 -import org.json.JSONObject -import java.io.File - -class LegacySettingsProvider : ContentProvider() { - private val authority = "app.revanced.manager.flutter.provider" - private val URI_CODE_SETTINGS = 1 - - private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { - addURI(authority, "settings", URI_CODE_SETTINGS) - } - - override fun onCreate(): Boolean { - return true - } - - fun getSettings(): String { - val json = JSONObject() - - // Default Data - json.put("keystorePassword", "s3cur3p@ssw0rd") - - // Load Shared Preferences - val sharedPreferences = context!!.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) - val allEntries: Map = sharedPreferences.getAll() - for ((key, value) in allEntries.entries) { - json.put(key.replace("flutter.", ""), if (value is Boolean) if (value) 1 else 0 else value) - } - - // Load keystore - val keystoreFile = File(context!!.getExternalFilesDir(null), "/revanced-manager.keystore") - if (keystoreFile.exists()) { - val keystoreBytes = keystoreFile.readBytes() - val keystoreBase64 = Base64.encodeToString(keystoreBytes, Base64.DEFAULT).replace("\n", "") - json.put("keystore", keystoreBase64) - } - - // Load saved patches - val storedPatchesFile = File(context!!.filesDir.parentFile.absolutePath, "/app_flutter/selected-patches.json") - if (storedPatchesFile.exists()) { - val patchesBytes = storedPatchesFile.readBytes() - val patches = String(patchesBytes, Charsets.UTF_8) - json.put("savedPatches", patches) - } - - return json.toString() - } - - override fun query( - uri: Uri, - projection: Array?, - selection: String?, - selectionArgs: Array?, - sortOrder: String? - ):Cursor? { - when (uriMatcher.match(uri)) { - URI_CODE_SETTINGS -> { - val cursor = MatrixCursor(arrayOf("settings")) - val row = arrayOf(getSettings()) - cursor.addRow(row) - return cursor - } - else -> throw IllegalArgumentException("Unknown URI: $uri") - } - return null - } - - override fun insert(uri: Uri, values: ContentValues?): Uri? { - throw UnsupportedOperationException("Insert operation is not supported") - } - - override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int { - throw UnsupportedOperationException("Update operation is not supported") - } - - override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { - throw UnsupportedOperationException("Delete operation is not supported") - } - - override fun getType(uri: Uri): String? { - throw UnsupportedOperationException("Get type operation is not supported") - } -} diff --git a/assets/i18n/en_US.json b/assets/i18n/en_US.json index 05694ced..cc398cb9 100644 --- a/assets/i18n/en_US.json +++ b/assets/i18n/en_US.json @@ -304,5 +304,11 @@ "integrationsContributors": "Integrations contributors", "cliContributors": "CLI contributors", "managerContributors": "Manager contributors" + }, + "exportSettingsView": { + "widgetTitle": "Export settings", + "description": "Would you like to export your settings to the latest version of ReVanced Manager?", + "exportButton": "Export", + "dismissButton": "No thanks" } } diff --git a/lib/main.dart b/lib/main.dart index 29b715b9..df883453 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart'; +import 'package:revanced_manager/ui/views/export_settings/export_settings_view.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_view.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:stacked_themes/stacked_themes.dart'; @@ -15,6 +16,14 @@ import 'package:timezone/data/latest.dart' as tz; late SharedPreferences prefs; Future main() async { + initialize(const NavigationView()); +} + +Future mainExportSettings() async { + initialize(const ExportSettingsView()); +} + +Future initialize(Widget homeView) async { await ThemeManager.initialise(); await setupLocator(); WidgetsFlutterBinding.ensureInitialized(); @@ -26,11 +35,12 @@ Future main() async { tz.initializeTimeZones(); prefs = await SharedPreferences.getInstance(); - runApp(const MyApp()); + runApp(MyApp(homeView: homeView)); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({Key? key, required this.homeView}) : super(key: key); + final Widget homeView; @override Widget build(BuildContext context) { @@ -42,7 +52,7 @@ class MyApp extends StatelessWidget { return DynamicThemeBuilder( title: 'ReVanced Manager', - home: const NavigationView(), + home: homeView, localizationsDelegates: [ FlutterI18nDelegate( translationLoader: FileTranslationLoader( diff --git a/lib/ui/views/export_settings/export_settings_view.dart b/lib/ui/views/export_settings/export_settings_view.dart new file mode 100644 index 00000000..631ee655 --- /dev/null +++ b/lib/ui/views/export_settings/export_settings_view.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:revanced_manager/ui/views/export_settings/export_settings_viewmodel.dart'; +import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; + +final _exportSettingsViewModel = ExportSettingsViewModel(); + +class ExportSettingsView extends StatelessWidget { + const ExportSettingsView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + _exportSettingsViewModel.init(context); + return Material( + child: AlertDialog( + title: I18nText('exportSettingsView.widgetTitle'), + content: I18nText('exportSettingsView.description'), + icon: const Icon(Icons.update), + actions: [ + CustomMaterialButton( + isFilled: false, + label: I18nText('exportSettingsView.dismissButton'), + onPressed: _exportSettingsViewModel.deny, + ), + CustomMaterialButton( + label: I18nText('exportSettingsView.exportButton'), + onPressed: () async { + await _exportSettingsViewModel.accept(); + }, + ), + ], + ), + ); + } +} diff --git a/lib/ui/views/export_settings/export_settings_viewmodel.dart b/lib/ui/views/export_settings/export_settings_viewmodel.dart new file mode 100644 index 00000000..ce450f36 --- /dev/null +++ b/lib/ui/views/export_settings/export_settings_viewmodel.dart @@ -0,0 +1,71 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:dynamic_themes/dynamic_themes.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:injectable/injectable.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:revanced_manager/app/app.locator.dart'; +import 'package:revanced_manager/services/manager_api.dart'; +import 'package:stacked/stacked.dart'; + +@lazySingleton +class ExportSettingsViewModel extends BaseViewModel { + final _channel = const MethodChannel('app.revanced.manager.flutter/settings'); + final ManagerAPI _managerAPI = locator(); + + void init(BuildContext context) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarIconBrightness: + DynamicTheme.of(context)!.theme.brightness == Brightness.light + ? Brightness.dark + : Brightness.light, + ), + ); + } + + Future accept() async { + final externalDir = await getExternalStorageDirectory(); + + final Map data = {}; + + data['themeMode'] = _managerAPI.getThemeMode(); + data['useDynamicTheme'] = _managerAPI.getUseDynamicTheme(); + + data['apiUrl'] = _managerAPI.getApiUrl(); + data['patchesRepo'] = _managerAPI.getPatchesRepo(); + data['integrationsRepo'] = _managerAPI.getIntegrationsRepo(); + + data['patchesAutoUpdate'] = _managerAPI.isPatchesAutoUpdate(); + data['patchesChangeEnabled'] = _managerAPI.isPatchesChangeEnabled(); + data['universalPatchesEnabled'] = _managerAPI.areUniversalPatchesEnabled(); + data['experimentalPatchesEnabled'] = _managerAPI.areExperimentalPatchesEnabled(); + + data['keystorePassword'] = _managerAPI.getKeystorePassword(); + + // Load keystore + if (externalDir != null) { + final keystoreFile = File('${externalDir.path}/revanced-manager.keystore'); + if (keystoreFile.existsSync()) { + final keystoreBytes = keystoreFile.readAsBytesSync(); + data['keystore'] = base64Encode(keystoreBytes); + } + } + + // Load patches + final patchFile = File(_managerAPI.storedPatchesFile); + if (patchFile.existsSync()) { + data['patches'] = patchFile.readAsStringSync(); + } + + _channel.invokeMethod('accept', {'data': jsonEncode(data)}); + } + + void deny() { + _channel.invokeMethod('deny'); + } +} From 2250e1bcab9145bfc6ae250a44af9a6d9f7fdb64 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Tue, 26 Sep 2023 16:46:49 -0700 Subject: [PATCH 06/13] convert Booleans to Ints --- .../export_settings/export_settings_viewmodel.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ui/views/export_settings/export_settings_viewmodel.dart b/lib/ui/views/export_settings/export_settings_viewmodel.dart index ce450f36..84999b66 100644 --- a/lib/ui/views/export_settings/export_settings_viewmodel.dart +++ b/lib/ui/views/export_settings/export_settings_viewmodel.dart @@ -34,16 +34,16 @@ class ExportSettingsViewModel extends BaseViewModel { final Map data = {}; data['themeMode'] = _managerAPI.getThemeMode(); - data['useDynamicTheme'] = _managerAPI.getUseDynamicTheme(); + data['useDynamicTheme'] = _managerAPI.getUseDynamicTheme() ? 1 : 0; data['apiUrl'] = _managerAPI.getApiUrl(); data['patchesRepo'] = _managerAPI.getPatchesRepo(); data['integrationsRepo'] = _managerAPI.getIntegrationsRepo(); - data['patchesAutoUpdate'] = _managerAPI.isPatchesAutoUpdate(); - data['patchesChangeEnabled'] = _managerAPI.isPatchesChangeEnabled(); - data['universalPatchesEnabled'] = _managerAPI.areUniversalPatchesEnabled(); - data['experimentalPatchesEnabled'] = _managerAPI.areExperimentalPatchesEnabled(); + data['patchesAutoUpdate'] = _managerAPI.isPatchesAutoUpdate() ? 1 : 0; + data['patchesChangeEnabled'] = _managerAPI.isPatchesChangeEnabled() ? 1 : 0; + data['universalPatchesEnabled'] = _managerAPI.areUniversalPatchesEnabled() ? 1 : 0; + data['experimentalPatchesEnabled'] = _managerAPI.areExperimentalPatchesEnabled() ? 1 : 0; data['keystorePassword'] = _managerAPI.getKeystorePassword(); From 72ae132fcd74bf2d2d7cd015562777328976df92 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Wed, 27 Sep 2023 12:21:27 -0700 Subject: [PATCH 07/13] make export settings activity transparent --- android/app/src/main/AndroidManifest.xml | 4 +-- .../manager/flutter/ExportSettingsActivity.kt | 5 +++ android/app/src/main/res/values/styles.xml | 6 ++++ assets/i18n/en_US.json | 6 ++-- .../export_settings/export_settings_view.dart | 35 +++++++++---------- .../export_settings_viewmodel.dart | 13 ------- 6 files changed, 32 insertions(+), 37 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 69587cd7..6102c540 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -46,13 +46,13 @@ android:name=".ExportSettingsActivity" android:exported="true" android:launchMode="singleTop" - android:theme="@style/LaunchTheme" + android:theme="@style/ExportSettingsTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> + android:resource="@style/ExportSettingsTheme"/> diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt index b9e8e5aa..6b9df401 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt @@ -3,6 +3,7 @@ package app.revanced.manager.flutter import android.app.Activity import android.content.Intent import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.android.TransparencyMode import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel import java.io.Serializable @@ -37,4 +38,8 @@ class ExportSettingsActivity : FlutterActivity() { override fun getDartEntrypointFunctionName(): String { return "mainExportSettings" } + + override fun getTransparencyMode(): TransparencyMode { + return TransparencyMode.transparent + } } diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index cb1ef880..ae451e0a 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -15,4 +15,10 @@ + + diff --git a/assets/i18n/en_US.json b/assets/i18n/en_US.json index cc398cb9..e1c1f848 100644 --- a/assets/i18n/en_US.json +++ b/assets/i18n/en_US.json @@ -306,9 +306,9 @@ "managerContributors": "Manager contributors" }, "exportSettingsView": { - "widgetTitle": "Export settings", - "description": "Would you like to export your settings to the latest version of ReVanced Manager?", - "exportButton": "Export", + "widgetTitle": "Import settings", + "description": "Would you like to import your settings to the previous version of ReVanced Manager?", + "exportButton": "Import", "dismissButton": "No thanks" } } diff --git a/lib/ui/views/export_settings/export_settings_view.dart b/lib/ui/views/export_settings/export_settings_view.dart index 631ee655..44e195be 100644 --- a/lib/ui/views/export_settings/export_settings_view.dart +++ b/lib/ui/views/export_settings/export_settings_view.dart @@ -10,26 +10,23 @@ class ExportSettingsView extends StatelessWidget { @override Widget build(BuildContext context) { - _exportSettingsViewModel.init(context); - return Material( - child: AlertDialog( - title: I18nText('exportSettingsView.widgetTitle'), - content: I18nText('exportSettingsView.description'), - icon: const Icon(Icons.update), - actions: [ - CustomMaterialButton( - isFilled: false, - label: I18nText('exportSettingsView.dismissButton'), - onPressed: _exportSettingsViewModel.deny, - ), - CustomMaterialButton( - label: I18nText('exportSettingsView.exportButton'), - onPressed: () async { - await _exportSettingsViewModel.accept(); - }, - ), - ], + return AlertDialog( + title: I18nText('exportSettingsView.widgetTitle'), + content: I18nText('exportSettingsView.description'), + icon: const Icon(Icons.update), + actions: [ + CustomMaterialButton( + isFilled: false, + label: I18nText('exportSettingsView.dismissButton'), + onPressed: _exportSettingsViewModel.deny, ), + CustomMaterialButton( + label: I18nText('exportSettingsView.exportButton'), + onPressed: () async { + await _exportSettingsViewModel.accept(); + }, + ), + ], ); } } diff --git a/lib/ui/views/export_settings/export_settings_viewmodel.dart b/lib/ui/views/export_settings/export_settings_viewmodel.dart index 84999b66..0aee70fd 100644 --- a/lib/ui/views/export_settings/export_settings_viewmodel.dart +++ b/lib/ui/views/export_settings/export_settings_viewmodel.dart @@ -15,19 +15,6 @@ class ExportSettingsViewModel extends BaseViewModel { final _channel = const MethodChannel('app.revanced.manager.flutter/settings'); final ManagerAPI _managerAPI = locator(); - void init(BuildContext context) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - SystemChrome.setSystemUIOverlayStyle( - SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - systemNavigationBarIconBrightness: - DynamicTheme.of(context)!.theme.brightness == Brightness.light - ? Brightness.dark - : Brightness.light, - ), - ); - } - Future accept() async { final externalDir = await getExternalStorageDirectory(); From 96736afb94b327c8dc3a5a592f055729f6f7c4e8 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Wed, 27 Sep 2023 12:32:19 -0700 Subject: [PATCH 08/13] make bars transparent --- lib/ui/views/export_settings/export_settings_view.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/ui/views/export_settings/export_settings_view.dart b/lib/ui/views/export_settings/export_settings_view.dart index 44e195be..c7e797b5 100644 --- a/lib/ui/views/export_settings/export_settings_view.dart +++ b/lib/ui/views/export_settings/export_settings_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/ui/views/export_settings/export_settings_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; @@ -10,6 +11,14 @@ class ExportSettingsView extends StatelessWidget { @override Widget build(BuildContext context) { + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + systemNavigationBarColor: Colors.black.withOpacity(0.002), + statusBarColor: Colors.black.withOpacity(0.002), + ), + ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + return AlertDialog( title: I18nText('exportSettingsView.widgetTitle'), content: I18nText('exportSettingsView.description'), From 02822f4b38946b92f4fc6585ff449e7233989b5c Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Wed, 27 Sep 2023 13:47:59 -0700 Subject: [PATCH 09/13] remove user interaction --- android/app/src/main/AndroidManifest.xml | 14 +--- .../manager/flutter/ExportSettingsActivity.kt | 75 +++++++++++-------- android/app/src/main/res/values/styles.xml | 6 -- assets/i18n/en_US.json | 6 -- lib/main.dart | 16 +--- .../export_settings/export_settings_view.dart | 41 ---------- .../export_settings_viewmodel.dart | 58 -------------- 7 files changed, 47 insertions(+), 169 deletions(-) delete mode 100644 lib/ui/views/export_settings/export_settings_view.dart delete mode 100644 lib/ui/views/export_settings/export_settings_viewmodel.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6102c540..3a2c4f8c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -44,19 +44,7 @@ - - - - - + android:exported="true"> - when (call.method) { - "accept" -> { - val data = call.argument("data") - val resultIntent = Intent() - resultIntent.putExtra("data", data as Serializable) - setResult(Activity.RESULT_OK, resultIntent) - finish() - } - "deny" -> { - setResult(Activity.RESULT_CANCELED) - finish() - } - else -> result.notImplemented() - } + // Default Data + json.put("keystorePassword", "s3cur3p@ssw0rd") + + // Load Shared Preferences + val sharedPreferences = getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) + val allEntries: Map = sharedPreferences.getAll() + for ((key, value) in allEntries.entries) { + json.put( + key.replace("flutter.", ""), + if (value is Boolean) if (value) 1 else 0 else value + ) } - } - override fun getDartEntrypointFunctionName(): String { - return "mainExportSettings" - } + // Load keystore + val keystoreFile = File(getExternalFilesDir(null), "/revanced-manager.keystore") + if (keystoreFile.exists()) { + val keystoreBytes = keystoreFile.readBytes() + val keystoreBase64 = + Base64.encodeToString(keystoreBytes, Base64.DEFAULT).replace("\n", "") + json.put("keystore", keystoreBase64) + } - override fun getTransparencyMode(): TransparencyMode { - return TransparencyMode.transparent + // Load saved patches + val storedPatchesFile = File(filesDir.parentFile.absolutePath, "/app_flutter/selected-patches.json") + if (storedPatchesFile.exists()) { + val patchesBytes = storedPatchesFile.readBytes() + val patches = String(patchesBytes, Charsets.UTF_8) + json.put("patches", patches) + } + + // Send data back + Log.e("ExportSettingsActivity", json.toString()) + val resultIntent = Intent() + resultIntent.putExtra("data", json.toString()) + setResult(Activity.RESULT_OK, resultIntent) + finish() } } diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index ae451e0a..cb1ef880 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -15,10 +15,4 @@ - - diff --git a/assets/i18n/en_US.json b/assets/i18n/en_US.json index e1c1f848..05694ced 100644 --- a/assets/i18n/en_US.json +++ b/assets/i18n/en_US.json @@ -304,11 +304,5 @@ "integrationsContributors": "Integrations contributors", "cliContributors": "CLI contributors", "managerContributors": "Manager contributors" - }, - "exportSettingsView": { - "widgetTitle": "Import settings", - "description": "Would you like to import your settings to the previous version of ReVanced Manager?", - "exportButton": "Import", - "dismissButton": "No thanks" } } diff --git a/lib/main.dart b/lib/main.dart index df883453..29b715b9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,7 +8,6 @@ import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart'; -import 'package:revanced_manager/ui/views/export_settings/export_settings_view.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_view.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:stacked_themes/stacked_themes.dart'; @@ -16,14 +15,6 @@ import 'package:timezone/data/latest.dart' as tz; late SharedPreferences prefs; Future main() async { - initialize(const NavigationView()); -} - -Future mainExportSettings() async { - initialize(const ExportSettingsView()); -} - -Future initialize(Widget homeView) async { await ThemeManager.initialise(); await setupLocator(); WidgetsFlutterBinding.ensureInitialized(); @@ -35,12 +26,11 @@ Future initialize(Widget homeView) async { tz.initializeTimeZones(); prefs = await SharedPreferences.getInstance(); - runApp(MyApp(homeView: homeView)); + runApp(const MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key, required this.homeView}) : super(key: key); - final Widget homeView; + const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -52,7 +42,7 @@ class MyApp extends StatelessWidget { return DynamicThemeBuilder( title: 'ReVanced Manager', - home: homeView, + home: const NavigationView(), localizationsDelegates: [ FlutterI18nDelegate( translationLoader: FileTranslationLoader( diff --git a/lib/ui/views/export_settings/export_settings_view.dart b/lib/ui/views/export_settings/export_settings_view.dart deleted file mode 100644 index c7e797b5..00000000 --- a/lib/ui/views/export_settings/export_settings_view.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_i18n/flutter_i18n.dart'; -import 'package:revanced_manager/ui/views/export_settings/export_settings_viewmodel.dart'; -import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; - -final _exportSettingsViewModel = ExportSettingsViewModel(); - -class ExportSettingsView extends StatelessWidget { - const ExportSettingsView({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - SystemChrome.setSystemUIOverlayStyle( - SystemUiOverlayStyle( - systemNavigationBarColor: Colors.black.withOpacity(0.002), - statusBarColor: Colors.black.withOpacity(0.002), - ), - ); - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - - return AlertDialog( - title: I18nText('exportSettingsView.widgetTitle'), - content: I18nText('exportSettingsView.description'), - icon: const Icon(Icons.update), - actions: [ - CustomMaterialButton( - isFilled: false, - label: I18nText('exportSettingsView.dismissButton'), - onPressed: _exportSettingsViewModel.deny, - ), - CustomMaterialButton( - label: I18nText('exportSettingsView.exportButton'), - onPressed: () async { - await _exportSettingsViewModel.accept(); - }, - ), - ], - ); - } -} diff --git a/lib/ui/views/export_settings/export_settings_viewmodel.dart b/lib/ui/views/export_settings/export_settings_viewmodel.dart deleted file mode 100644 index 0aee70fd..00000000 --- a/lib/ui/views/export_settings/export_settings_viewmodel.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:dynamic_themes/dynamic_themes.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:injectable/injectable.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:revanced_manager/app/app.locator.dart'; -import 'package:revanced_manager/services/manager_api.dart'; -import 'package:stacked/stacked.dart'; - -@lazySingleton -class ExportSettingsViewModel extends BaseViewModel { - final _channel = const MethodChannel('app.revanced.manager.flutter/settings'); - final ManagerAPI _managerAPI = locator(); - - Future accept() async { - final externalDir = await getExternalStorageDirectory(); - - final Map data = {}; - - data['themeMode'] = _managerAPI.getThemeMode(); - data['useDynamicTheme'] = _managerAPI.getUseDynamicTheme() ? 1 : 0; - - data['apiUrl'] = _managerAPI.getApiUrl(); - data['patchesRepo'] = _managerAPI.getPatchesRepo(); - data['integrationsRepo'] = _managerAPI.getIntegrationsRepo(); - - data['patchesAutoUpdate'] = _managerAPI.isPatchesAutoUpdate() ? 1 : 0; - data['patchesChangeEnabled'] = _managerAPI.isPatchesChangeEnabled() ? 1 : 0; - data['universalPatchesEnabled'] = _managerAPI.areUniversalPatchesEnabled() ? 1 : 0; - data['experimentalPatchesEnabled'] = _managerAPI.areExperimentalPatchesEnabled() ? 1 : 0; - - data['keystorePassword'] = _managerAPI.getKeystorePassword(); - - // Load keystore - if (externalDir != null) { - final keystoreFile = File('${externalDir.path}/revanced-manager.keystore'); - if (keystoreFile.existsSync()) { - final keystoreBytes = keystoreFile.readAsBytesSync(); - data['keystore'] = base64Encode(keystoreBytes); - } - } - - // Load patches - final patchFile = File(_managerAPI.storedPatchesFile); - if (patchFile.existsSync()) { - data['patches'] = patchFile.readAsStringSync(); - } - - _channel.invokeMethod('accept', {'data': jsonEncode(data)}); - } - - void deny() { - _channel.invokeMethod('deny'); - } -} From 7559c7b67e0025e6ad423acb9568868e11ef3dff Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Wed, 27 Sep 2023 14:17:29 -0700 Subject: [PATCH 10/13] verify fingerprint of calling app --- .../manager/flutter/ExportSettingsActivity.kt | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt index 87111a4b..f31819e3 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt @@ -3,17 +3,56 @@ package app.revanced.manager.flutter import android.app.Activity import android.content.Context import android.content.Intent +import android.content.pm.PackageInfo +import android.content.pm.PackageManager import android.os.Bundle import android.util.Base64 import org.json.JSONObject +import java.io.ByteArrayInputStream import java.io.File - -import android.util.Log +import java.security.cert.CertificateFactory +import java.security.cert.X509Certificate +import java.security.MessageDigest class ExportSettingsActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val composeFingerprint = "" + // Get the package name of the app that started the activity + val packageName = getCallingPackage()!! + + // Get the signature of the app that matches the package name + val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES) + val signatures = packageInfo.signatures + + // Loop through each signature and print its properties + for (signature in signatures) { + // Get the raw certificate data + val rawCert = signature.toByteArray() + + // Generate an X509Certificate from the data + val certFactory = CertificateFactory.getInstance("X509") + val x509Cert = certFactory.generateCertificate(ByteArrayInputStream(rawCert)) as X509Certificate + + // Get the SHA256 fingerprint + val fingerprint = MessageDigest.getInstance("SHA256").digest(x509Cert.encoded).joinToString("") { + "%02x".format(it) + } + + if (fingerprint == composeFingerprint) { + sendData() + } + } + + // Send data back + val resultIntent = Intent() + setResult(Activity.RESULT_CANCELED) + finish() + } + + fun sendData() { + // Create JSON Object val json = JSONObject() // Default Data @@ -47,7 +86,6 @@ class ExportSettingsActivity : Activity() { } // Send data back - Log.e("ExportSettingsActivity", json.toString()) val resultIntent = Intent() resultIntent.putExtra("data", json.toString()) setResult(Activity.RESULT_OK, resultIntent) From 83cbb34a5b3647c1e68d5c1921fd93ecf65dbd3a Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Wed, 27 Sep 2023 14:27:38 -0700 Subject: [PATCH 11/13] use revanced fingerprint --- .../app/revanced/manager/flutter/ExportSettingsActivity.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt index f31819e3..7a9f9f98 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt @@ -14,10 +14,12 @@ import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.security.MessageDigest +import android.util.Log + class ExportSettingsActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val composeFingerprint = "" + val revancedFingerprint = "b6362c6ea7888efd15c0800f480786ad0f5b133b4f84e12d46afba5f9eac1223" // Get the package name of the app that started the activity val packageName = getCallingPackage()!! @@ -40,7 +42,7 @@ class ExportSettingsActivity : Activity() { "%02x".format(it) } - if (fingerprint == composeFingerprint) { + if (fingerprint == revancedFingerprint) { sendData() } } From e7c8d0e78c31b8555e7e77fb048a929752188845 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Wed, 27 Sep 2023 14:36:39 -0700 Subject: [PATCH 12/13] use same fingerprint --- .../manager/flutter/ExportSettingsActivity.kt | 121 +++++++++--------- 1 file changed, 57 insertions(+), 64 deletions(-) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt index 7a9f9f98..72f72caf 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt @@ -19,78 +19,71 @@ import android.util.Log class ExportSettingsActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val revancedFingerprint = "b6362c6ea7888efd15c0800f480786ad0f5b133b4f84e12d46afba5f9eac1223" + val callingPackageName = getCallingPackage()!! - // Get the package name of the app that started the activity - val packageName = getCallingPackage()!! + if (getFingerprint(callingPackageName) == getFingerprint(getPackageName())) { + // Create JSON Object + val json = JSONObject() + // Default Data + json.put("keystorePassword", "s3cur3p@ssw0rd") + + // Load Shared Preferences + val sharedPreferences = getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) + val allEntries: Map = sharedPreferences.getAll() + for ((key, value) in allEntries.entries) { + json.put( + key.replace("flutter.", ""), + if (value is Boolean) if (value) 1 else 0 else value + ) + } + + // Load keystore + val keystoreFile = File(getExternalFilesDir(null), "/revanced-manager.keystore") + if (keystoreFile.exists()) { + val keystoreBytes = keystoreFile.readBytes() + val keystoreBase64 = + Base64.encodeToString(keystoreBytes, Base64.DEFAULT).replace("\n", "") + json.put("keystore", keystoreBase64) + } + + // Load saved patches + val storedPatchesFile = File(filesDir.parentFile.absolutePath, "/app_flutter/selected-patches.json") + if (storedPatchesFile.exists()) { + val patchesBytes = storedPatchesFile.readBytes() + val patches = String(patchesBytes, Charsets.UTF_8) + json.put("patches", patches) + } + + // Send data back + val resultIntent = Intent() + resultIntent.putExtra("data", json.toString()) + setResult(Activity.RESULT_OK, resultIntent) + finish() + } else { + val resultIntent = Intent() + setResult(Activity.RESULT_CANCELED) + finish() + } + } + + fun getFingerprint(packageName: String): String { // Get the signature of the app that matches the package name val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES) - val signatures = packageInfo.signatures + val signature = packageInfo.signatures[0] - // Loop through each signature and print its properties - for (signature in signatures) { - // Get the raw certificate data - val rawCert = signature.toByteArray() + // Get the raw certificate data + val rawCert = signature.toByteArray() - // Generate an X509Certificate from the data - val certFactory = CertificateFactory.getInstance("X509") - val x509Cert = certFactory.generateCertificate(ByteArrayInputStream(rawCert)) as X509Certificate + // Generate an X509Certificate from the data + val certFactory = CertificateFactory.getInstance("X509") + val x509Cert = certFactory.generateCertificate(ByteArrayInputStream(rawCert)) as X509Certificate - // Get the SHA256 fingerprint - val fingerprint = MessageDigest.getInstance("SHA256").digest(x509Cert.encoded).joinToString("") { - "%02x".format(it) - } - - if (fingerprint == revancedFingerprint) { - sendData() - } + // Get the SHA256 fingerprint + val fingerprint = MessageDigest.getInstance("SHA256").digest(x509Cert.encoded).joinToString("") { + "%02x".format(it) } - // Send data back - val resultIntent = Intent() - setResult(Activity.RESULT_CANCELED) - finish() - } - - fun sendData() { - // Create JSON Object - val json = JSONObject() - - // Default Data - json.put("keystorePassword", "s3cur3p@ssw0rd") - - // Load Shared Preferences - val sharedPreferences = getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) - val allEntries: Map = sharedPreferences.getAll() - for ((key, value) in allEntries.entries) { - json.put( - key.replace("flutter.", ""), - if (value is Boolean) if (value) 1 else 0 else value - ) - } - - // Load keystore - val keystoreFile = File(getExternalFilesDir(null), "/revanced-manager.keystore") - if (keystoreFile.exists()) { - val keystoreBytes = keystoreFile.readBytes() - val keystoreBase64 = - Base64.encodeToString(keystoreBytes, Base64.DEFAULT).replace("\n", "") - json.put("keystore", keystoreBase64) - } - - // Load saved patches - val storedPatchesFile = File(filesDir.parentFile.absolutePath, "/app_flutter/selected-patches.json") - if (storedPatchesFile.exists()) { - val patchesBytes = storedPatchesFile.readBytes() - val patches = String(patchesBytes, Charsets.UTF_8) - json.put("patches", patches) - } - - // Send data back - val resultIntent = Intent() - resultIntent.putExtra("data", json.toString()) - setResult(Activity.RESULT_OK, resultIntent) - finish() + return fingerprint } } From 2968d96fe9b301926db1e5f91a0b2033415645a5 Mon Sep 17 00:00:00 2001 From: Benjamin Halko Date: Wed, 27 Sep 2023 14:42:11 -0700 Subject: [PATCH 13/13] remove log import --- .../app/revanced/manager/flutter/ExportSettingsActivity.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt index 72f72caf..8a397f8c 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/flutter/ExportSettingsActivity.kt @@ -14,8 +14,6 @@ import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.security.MessageDigest -import android.util.Log - class ExportSettingsActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)