Skip to content

Commit

Permalink
feat: add option to import/export keystore (#776)
Browse files Browse the repository at this point in the history
* feat: add option to import/export keystore

* change the order of import/export keystore buttons

* feat: add option to change the keystore password
  • Loading branch information
andrisas authored Apr 18, 2023
1 parent 3b677f8 commit dca2d4f
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class MainActivity : FlutterActivity() {
val selectedPatches = call.argument<List<String>>("selectedPatches")
val cacheDirPath = call.argument<String>("cacheDirPath")
val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
val keystorePassword = call.argument<String>("keystorePassword")

if (patchBundleFilePath != null &&
originalFilePath != null &&
inputFilePath != null &&
Expand All @@ -52,7 +54,8 @@ class MainActivity : FlutterActivity() {
integrationsPath != null &&
selectedPatches != null &&
cacheDirPath != null &&
keyStoreFilePath != null
keyStoreFilePath != null &&
keystorePassword != null
) {
runPatcher(
result,
Expand All @@ -64,7 +67,8 @@ class MainActivity : FlutterActivity() {
integrationsPath,
selectedPatches,
cacheDirPath,
keyStoreFilePath
keyStoreFilePath,
keystorePassword
)
} else {
result.notImplemented()
Expand All @@ -85,7 +89,8 @@ class MainActivity : FlutterActivity() {
integrationsPath: String,
selectedPatches: List<String>,
cacheDirPath: String,
keyStoreFilePath: String
keyStoreFilePath: String,
keystorePassword: String
) {
val originalFile = File(originalFilePath)
val inputFile = File(inputFilePath)
Expand Down Expand Up @@ -242,7 +247,7 @@ class MainActivity : FlutterActivity() {
// Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile)

try {
Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outFile, keyStoreFile)
Signer("ReVanced", keystorePassword).signApk(patchedFile, outFile, keyStoreFile)
} catch (e: Exception) {
//log to console
print("Error signing apk: ${e.message}")
Expand Down
12 changes: 11 additions & 1 deletion assets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,17 @@
"jsonSelectorErrorMessage": "Unable to use selected JSON file",
"deleteLogsLabel": "Delete logs",
"deleteLogsHint": "Delete collected manager logs",
"deletedLogs": "Logs deleted"
"deletedLogs": "Logs deleted",
"exportKeystoreLabel": "Export keystore",
"exportKeystoreHint": "Export keystore used to sign apps",
"exportedKeystore": "Keystore exported",
"noKeystoreExportFileFound": "No keystore to export",
"importKeystoreLabel": "Import keystore",
"importKeystoreHint": "Import keystore used to sign apps",
"importedKeystore": "Keystore imported",
"keystoreSelectorErrorMessage": "Unable to use selected KEYSTORE file",
"selectKeystorePassword": "Keystore Password",
"selectKeystorePasswordHint": "Select keystore password used to sign the apk"
},
"appInfoView": {
"widgetTitle": "App info",
Expand Down
12 changes: 11 additions & 1 deletion lib/services/manager_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class ManagerAPI {
final String cliRepo = 'revanced-cli';
late SharedPreferences _prefs;
String storedPatchesFile = '/selected-patches.json';
String keystoreFile = '/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore';
String defaultKeystorePassword = 's3cur3p@ssw0rd';
String defaultApiUrl = 'https://releases.revanced.app/';
String defaultRepoUrl = 'https://api.github.com';
String defaultPatcherRepo = 'revanced/revanced-patcher';
Expand Down Expand Up @@ -118,6 +120,14 @@ class ManagerAPI {
await _prefs.setBool('experimentalPatchesEnabled', value);
}

Future<void> setKeystorePassword(String password) async {
await _prefs.setString('keystorePassword', password);
}

String getKeystorePassword() {
return _prefs.getString('keystorePassword') ?? defaultKeystorePassword;
}

Future<void> deleteTempFolder() async {
final Directory dir = Directory('/data/local/tmp/revanced-manager');
if (await dir.exists()) {
Expand All @@ -127,7 +137,7 @@ class ManagerAPI {

Future<void> deleteKeystore() async {
final File keystore = File(
'/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore',
keystoreFile,
);
if (await keystore.exists()) {
await keystore.delete();
Expand Down
1 change: 1 addition & 0 deletions lib/services/patcher_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ class PatcherAPI {
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
'cacheDirPath': cacheDir.path,
'keyStoreFilePath': _keyStoreFile.path,
'keystorePassword': _managerAPI.getKeystorePassword(),
},
);
} on Exception catch (e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// ignore_for_file: use_build_context_synchronously

import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:stacked/stacked.dart';

class SManageKeystorePassword extends BaseViewModel {
final ManagerAPI _managerAPI = locator<ManagerAPI>();

final TextEditingController _keystorePasswordController = TextEditingController();

Future<void> showKeystoreDialog(BuildContext context) async {
final String keystorePasswordText = _managerAPI.getKeystorePassword();
_keystorePasswordController.text = keystorePasswordText;
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: <Widget>[
I18nText('settingsView.selectKeystorePassword'),
const Spacer(),
IconButton(
icon: const Icon(Icons.manage_history_outlined),
onPressed: () =>_keystorePasswordController.text = _managerAPI.defaultKeystorePassword,
color: Theme.of(context).colorScheme.secondary,
)
],
),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: SingleChildScrollView(
child: Column(
children: <Widget>[
CustomTextField(
inputController: _keystorePasswordController,
label: I18nText('settingsView.selectKeystorePassword'),
hint: "",
onChanged: (value) => notifyListeners(),
),
],
),
),
actions: <Widget>[
CustomMaterialButton(
isFilled: false,
label: I18nText('cancelButton'),
onPressed: () {
_keystorePasswordController.clear();
Navigator.of(context).pop();
},
),
CustomMaterialButton(
label: I18nText('okButton'),
onPressed: () {
String passwd = _keystorePasswordController.text;
_managerAPI.setKeystorePassword(passwd);
Navigator.of(context).pop();
},
)
],
),
);
}
}

final sManageKeystorePassword = SManageKeystorePassword();

class SManageKeystorePasswordUI extends StatelessWidget {
const SManageKeystorePasswordUI({super.key});

@override
Widget build(BuildContext context) {
return SettingsTileDialog(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
title: 'settingsView.selectKeystorePassword',
subtitle: 'settingsView.selectKeystorePasswordHint',
onTap: () => sManageKeystorePassword.showKeystoreDialog(context),
);
}
}
40 changes: 40 additions & 0 deletions lib/ui/views/settings/settings_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,46 @@ class SettingsViewModel extends BaseViewModel {
}
}

Future<void> exportKeystore() async {
try {
final File outFile = File(_managerAPI.keystoreFile);
if (outFile.existsSync()) {
final String dateTime =
DateTime.now().toString().replaceAll(' ', '_').split('.').first;
await CRFileSaver.saveFileWithDialog(
SaveFileDialogParams(
sourceFilePath: outFile.path,
destinationFileName: 'keystore_$dateTime.keystore',
),
);
_toast.showBottom('settingsView.exportedKeystore');
} else {
_toast.showBottom('settingsView.noKeystoreExportFileFound');
}
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
}
}

Future<void> importKeystore() async {
try {
final FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null && result.files.single.path != null) {
final File inFile = File(result.files.single.path!);
inFile.copySync(_managerAPI.keystoreFile);

_toast.showBottom('settingsView.importedKeystore');
}
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
_toast.showBottom('settingsView.keystoreSelectorErrorMessage');
}
}

void resetSelectedPatches() {
_managerAPI.resetLastSelectedPatches();
_toast.showBottom('settingsView.resetStoredPatches');
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/widgets/settingsView/settings_advanced_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_api_url.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_sources.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_keystore_password.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_experimental_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_experimental_universal_patches.dart';
Expand All @@ -22,6 +23,7 @@ class SAdvancedSection extends StatelessWidget {
children: <Widget>[
SManageApiUrlUI(),
SManageSourcesUI(),
// SManageKeystorePasswordUI(),
SExperimentalUniversalPatches(),
SExperimentalPatches(),
ListTile(
Expand Down
35 changes: 35 additions & 0 deletions lib/ui/widgets/settingsView/settings_export_section.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_keystore_password.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';

final _settingsViewModel = SettingsViewModel();
Expand Down Expand Up @@ -43,6 +44,40 @@ class SExportSection extends StatelessWidget {
subtitle: I18nText('settingsView.importPatchesHint'),
onTap: () => _settingsViewModel.importPatches(),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.exportKeystoreLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.exportKeystoreHint'),
onTap: () => _settingsViewModel.exportKeystore(),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.importKeystoreLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.importKeystoreHint'),
onTap: () {
_settingsViewModel.importKeystore();
final sManageKeystorePassword = SManageKeystorePassword();
sManageKeystorePassword.showKeystoreDialog(context);
},
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
Expand Down

0 comments on commit dca2d4f

Please sign in to comment.