mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 22:14:25 +02:00
Added ExportSettingsActivity
This commit is contained in:
parent
f1ea306291
commit
d9d5b746c3
@ -42,6 +42,22 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".ExportSettingsActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
@ -54,10 +70,5 @@
|
|||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
<provider
|
|
||||||
android:name=".utils.share.LegacySettingsProvider"
|
|
||||||
android:authorities="app.revanced.manager.flutter.provider"
|
|
||||||
android:exported="true">
|
|
||||||
</provider>
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -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<String>("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"
|
||||||
|
}
|
||||||
|
}
|
@ -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<String, *> = 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<out String>?,
|
|
||||||
selection: String?,
|
|
||||||
selectionArgs: Array<out String>?,
|
|
||||||
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<out String>?): Int {
|
|
||||||
throw UnsupportedOperationException("Update operation is not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
|
|
||||||
throw UnsupportedOperationException("Delete operation is not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getType(uri: Uri): String? {
|
|
||||||
throw UnsupportedOperationException("Get type operation is not supported")
|
|
||||||
}
|
|
||||||
}
|
|
@ -304,5 +304,11 @@
|
|||||||
"integrationsContributors": "Integrations contributors",
|
"integrationsContributors": "Integrations contributors",
|
||||||
"cliContributors": "CLI contributors",
|
"cliContributors": "CLI contributors",
|
||||||
"managerContributors": "Manager 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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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/manager_api.dart';
|
||||||
import 'package:revanced_manager/services/revanced_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/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:revanced_manager/ui/views/navigation/navigation_view.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:stacked_themes/stacked_themes.dart';
|
import 'package:stacked_themes/stacked_themes.dart';
|
||||||
@ -15,6 +16,14 @@ import 'package:timezone/data/latest.dart' as tz;
|
|||||||
|
|
||||||
late SharedPreferences prefs;
|
late SharedPreferences prefs;
|
||||||
Future main() async {
|
Future main() async {
|
||||||
|
initialize(const NavigationView());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future mainExportSettings() async {
|
||||||
|
initialize(const ExportSettingsView());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future initialize(Widget homeView) async {
|
||||||
await ThemeManager.initialise();
|
await ThemeManager.initialise();
|
||||||
await setupLocator();
|
await setupLocator();
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@ -26,11 +35,12 @@ Future main() async {
|
|||||||
tz.initializeTimeZones();
|
tz.initializeTimeZones();
|
||||||
prefs = await SharedPreferences.getInstance();
|
prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
runApp(const MyApp());
|
runApp(MyApp(homeView: homeView));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -42,7 +52,7 @@ class MyApp extends StatelessWidget {
|
|||||||
|
|
||||||
return DynamicThemeBuilder(
|
return DynamicThemeBuilder(
|
||||||
title: 'ReVanced Manager',
|
title: 'ReVanced Manager',
|
||||||
home: const NavigationView(),
|
home: homeView,
|
||||||
localizationsDelegates: [
|
localizationsDelegates: [
|
||||||
FlutterI18nDelegate(
|
FlutterI18nDelegate(
|
||||||
translationLoader: FileTranslationLoader(
|
translationLoader: FileTranslationLoader(
|
||||||
|
35
lib/ui/views/export_settings/export_settings_view.dart
Normal file
35
lib/ui/views/export_settings/export_settings_view.dart
Normal file
@ -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: <Widget> [
|
||||||
|
CustomMaterialButton(
|
||||||
|
isFilled: false,
|
||||||
|
label: I18nText('exportSettingsView.dismissButton'),
|
||||||
|
onPressed: _exportSettingsViewModel.deny,
|
||||||
|
),
|
||||||
|
CustomMaterialButton(
|
||||||
|
label: I18nText('exportSettingsView.exportButton'),
|
||||||
|
onPressed: () async {
|
||||||
|
await _exportSettingsViewModel.accept();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
71
lib/ui/views/export_settings/export_settings_viewmodel.dart
Normal file
71
lib/ui/views/export_settings/export_settings_viewmodel.dart
Normal file
@ -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<ManagerAPI>();
|
||||||
|
|
||||||
|
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<void> accept() async {
|
||||||
|
final externalDir = await getExternalStorageDirectory();
|
||||||
|
|
||||||
|
final Map<String, dynamic> 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');
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user