fix: add changelog (wip) on each app item

This commit is contained in:
Alberto Ponces 2022-08-29 15:01:51 +01:00
parent 45f4a5b207
commit 080ceae784
10 changed files with 103 additions and 167 deletions

View File

@ -397,7 +397,7 @@ class MainActivity : FlutterActivity() {
mapOf( mapOf(
"progress" to 1.0, "progress" to 1.0,
"header" to "Finished!", "header" to "Finished!",
"log" to "Finished" "log" to "Finished!"
) )
) )
} }

View File

@ -18,7 +18,9 @@ class PatchedApplication {
DateTime patchDate; DateTime patchDate;
final bool isRooted; final bool isRooted;
final bool isFromStorage; final bool isFromStorage;
bool hasUpdates;
List<String> appliedPatches; List<String> appliedPatches;
List<String> changelog;
PatchedApplication({ PatchedApplication({
required this.name, required this.name,
@ -27,9 +29,11 @@ class PatchedApplication {
required this.apkFilePath, required this.apkFilePath,
required this.icon, required this.icon,
required this.patchDate, required this.patchDate,
required this.isRooted, this.isRooted = false,
required this.isFromStorage, this.isFromStorage = false,
required this.appliedPatches, this.hasUpdates = false,
this.appliedPatches = const [],
this.changelog = const [],
}); });
factory PatchedApplication.fromJson(Map<String, dynamic> json) => factory PatchedApplication.fromJson(Map<String, dynamic> json) =>

View File

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:github/github.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:revanced_manager/constants.dart'; import 'package:revanced_manager/constants.dart';
@ -14,9 +15,11 @@ class ManagerAPI {
final GithubAPI _githubAPI = GithubAPI(); final GithubAPI _githubAPI = GithubAPI();
final RootAPI _rootAPI = RootAPI(); final RootAPI _rootAPI = RootAPI();
late SharedPreferences _prefs; late SharedPreferences _prefs;
late List<RepositoryCommit> _commits = [];
Future<void> initialize() async { Future<void> initialize() async {
_prefs = await SharedPreferences.getInstance(); _prefs = await SharedPreferences.getInstance();
_commits = (await _githubAPI.getCommits(ghOrg, patchesRepo)).toList();
} }
Future<File?> downloadPatches(String extension) async { Future<File?> downloadPatches(String extension) async {
@ -71,85 +74,58 @@ class ManagerAPI {
setPatchedApps(patchedApps); setPatchedApps(patchedApps);
} }
void saveApp(
ApplicationWithIcon application,
bool isRooted,
bool isFromStorage,
) {
savePatchedApp(
PatchedApplication(
name: application.appName,
packageName: application.packageName,
version: application.versionName!,
apkFilePath: application.apkFilePath,
icon: application.icon,
patchDate: DateTime.now(),
isRooted: isRooted,
isFromStorage: isFromStorage,
appliedPatches: [],
),
);
}
Future<void> reAssessSavedApps() async { Future<void> reAssessSavedApps() async {
List<PatchedApplication> patchedApps = getPatchedApps();
bool isRoot = isRooted() ?? false; bool isRoot = isRooted() ?? false;
List<PatchedApplication> patchedApps = getPatchedApps();
List<PatchedApplication> toRemove = []; List<PatchedApplication> toRemove = [];
for (PatchedApplication app in patchedApps) { for (PatchedApplication app in patchedApps) {
bool existsRoot = false; bool isRemove = await isAppUninstalled(app, isRoot);
if (isRoot) { if (isRemove) {
existsRoot = await _rootAPI.isAppInstalled(app.packageName);
}
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
if (!existsRoot && !existsNonRoot) {
toRemove.add(app); toRemove.add(app);
} else if (existsNonRoot) { } else {
ApplicationWithIcon? application = List<String> newChangelog = getAppChangelog(
await DeviceApps.getApp(app.packageName, true) app.packageName,
as ApplicationWithIcon?; app.patchDate,
if (application != null) { );
int savedVersionInt = if (newChangelog.isNotEmpty) {
int.parse(app.version.replaceAll(RegExp('[^0-9]'), '')); app.changelog = newChangelog;
int currentVersionInt = int.parse( app.hasUpdates = true;
application.versionName!.replaceAll(RegExp('[^0-9]'), '')); } else {
if (savedVersionInt < currentVersionInt) { app.hasUpdates = false;
toRemove.add(app);
}
} }
} }
} }
patchedApps.removeWhere((a) => toRemove.contains(a)); patchedApps.removeWhere((a) => toRemove.contains(a));
setPatchedApps(patchedApps); setPatchedApps(patchedApps);
List<String> apps = await _rootAPI.getInstalledApps(); }
for (String packageName in apps) {
if (!patchedApps.any((a) => a.packageName == packageName)) { Future<bool> isAppUninstalled(PatchedApplication app, bool isRoot) async {
ApplicationWithIcon? application = bool existsRoot = false;
await DeviceApps.getApp(packageName, true) as ApplicationWithIcon?; if (isRoot) {
if (application != null) { existsRoot = await _rootAPI.isAppInstalled(app.packageName);
saveApp(application, true, false);
}
}
} }
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
return !existsRoot && !existsNonRoot;
} }
Future<bool> hasAppUpdates(String packageName) async { List<String> getAppChangelog(String packageName, DateTime patchedDate) {
// TODO: get status based on last update time on the folder of this app? List<String> newCommits = _commits
return false;
}
Future<List<String>> getAppChangelog(
String packageName,
DateTime lastUpdated,
) async {
return (await _githubAPI.getCommits(ghOrg, patchesRepo))
.where((c) => .where((c) =>
c.commit != null && c.commit != null &&
c.commit!.message != null && c.commit!.message != null &&
!c.commit!.message!.startsWith('chore') &&
c.commit!.author != null && c.commit!.author != null &&
c.commit!.author!.date != null) c.commit!.author!.date != null &&
.map((c) => ' - ${c.commit!.message!}') c.commit!.author!.date!.isAfter(patchedDate))
.toList() .map((c) => c.commit!.message!)
.sublist(0, 3); .toList();
if (newCommits.isNotEmpty) {
int firstChore = newCommits.indexWhere((c) => c.startsWith('chore'));
int secondChore =
newCommits.indexWhere((c) => c.startsWith('chore'), firstChore + 1);
if (firstChore >= 0 && secondChore > firstChore) {
return newCommits.sublist(firstChore + 1, secondChore);
}
}
return List.empty();
} }
} }

View File

@ -35,8 +35,6 @@ class AppSelectorViewModel extends BaseViewModel {
icon: application.icon, icon: application.icon,
patchDate: DateTime.now(), patchDate: DateTime.now(),
isRooted: _isRooted, isRooted: _isRooted,
isFromStorage: false,
appliedPatches: [],
); );
locator<PatcherViewModel>().selectedPatches.clear(); locator<PatcherViewModel>().selectedPatches.clear();
locator<PatcherViewModel>().notifyListeners(); locator<PatcherViewModel>().notifyListeners();
@ -63,7 +61,6 @@ class AppSelectorViewModel extends BaseViewModel {
patchDate: DateTime.now(), patchDate: DateTime.now(),
isRooted: _isRooted, isRooted: _isRooted,
isFromStorage: true, isFromStorage: true,
appliedPatches: [],
); );
locator<PatcherViewModel>().selectedPatches.clear(); locator<PatcherViewModel>().selectedPatches.clear();
locator<PatcherViewModel>().notifyListeners(); locator<PatcherViewModel>().notifyListeners();

View File

@ -111,8 +111,8 @@ class HomeView extends StatelessWidget {
), ),
const SizedBox(height: 14), const SizedBox(height: 14),
model.showUpdatableApps model.showUpdatableApps
? AvailableUpdatesCard() ? const AvailableUpdatesCard()
: InstalledAppsCard() : const InstalledAppsCard()
], ],
), ),
), ),

View File

@ -22,8 +22,11 @@ class HomeViewModel extends BaseViewModel {
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin(); FlutterLocalNotificationsPlugin();
bool showUpdatableApps = true; bool showUpdatableApps = true;
List<PatchedApplication> patchedInstalledApps = [];
List<PatchedApplication> patchedUpdatableApps = [];
Future<void> initialize() async { Future<void> initialize() async {
await _getPatchedApps();
await _patcherAPI.loadPatches(); await _patcherAPI.loadPatches();
await flutterLocalNotificationsPlugin.initialize( await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings( const InitializationSettings(
@ -31,6 +34,7 @@ class HomeViewModel extends BaseViewModel {
), ),
onSelectNotification: (p) => DeviceApps.openApp('app.revanced.manager'), onSelectNotification: (p) => DeviceApps.openApp('app.revanced.manager'),
); );
_managerAPI.reAssessSavedApps().then((_) => notifyListeners());
} }
void toggleUpdatableApps(bool value) { void toggleUpdatableApps(bool value) {
@ -46,17 +50,16 @@ class HomeViewModel extends BaseViewModel {
locator<MainViewModel>().setIndex(1); locator<MainViewModel>().setIndex(1);
} }
Future<List<PatchedApplication>> getPatchedApps(bool isUpdatable) async { Future<void> _getPatchedApps() async {
await _managerAPI.reAssessSavedApps(); patchedInstalledApps = _managerAPI
List<PatchedApplication> list = []; .getPatchedApps()
List<PatchedApplication> patchedApps = _managerAPI.getPatchedApps(); .where((app) => app.hasUpdates == false)
for (PatchedApplication app in patchedApps) { .toList();
bool hasUpdates = await _managerAPI.hasAppUpdates(app.packageName); patchedUpdatableApps = _managerAPI
if (hasUpdates == isUpdatable) { .getPatchedApps()
list.add(app); .where((app) => app.hasUpdates == true)
} .toList();
} notifyListeners();
return list;
} }
Future<bool> hasManagerUpdates() async { Future<bool> hasManagerUpdates() async {

View File

@ -70,10 +70,10 @@ class InstallerViewModel extends BaseViewModel {
void update(double value, String header, String log) { void update(double value, String header, String log) {
progress = value; progress = value;
isInstalled = false;
isPatching = progress == 1.0 ? false : true; isPatching = progress == 1.0 ? false : true;
if (progress == 0.0) { if (progress == 0.0) {
logs = ''; logs = '';
isInstalled = false;
} }
if (header.isNotEmpty) { if (header.isNotEmpty) {
headerLogs = header; headerLogs = header;
@ -148,9 +148,9 @@ class InstallerViewModel extends BaseViewModel {
); );
isInstalled = await _patcherAPI.installPatchedFile(_app!); isInstalled = await _patcherAPI.installPatchedFile(_app!);
if (isInstalled) { if (isInstalled) {
update(1.0, 'Installed!', 'Installed'); update(1.0, 'Installed!', 'Installed!');
_app!.patchDate = DateTime.now(); _app!.patchDate = DateTime.now();
_app!.appliedPatches.addAll(_patches.map((p) => p.name).toList()); _app!.appliedPatches = _patches.map((p) => p.name).toList();
_managerAPI.savePatchedApp(_app!); _managerAPI.savePatchedApp(_app!);
} else { } else {
update(1.0, 'Aborting...', 'An error occurred! Aborting'); update(1.0, 'Aborting...', 'An error occurred! Aborting');

View File

@ -1,54 +1,32 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart'; import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/application_item.dart'; import 'package:revanced_manager/ui/widgets/shared/application_item.dart';
class AvailableUpdatesCard extends StatelessWidget { class AvailableUpdatesCard extends StatelessWidget {
AvailableUpdatesCard({ const AvailableUpdatesCard({
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final ManagerAPI _managerAPI = locator<ManagerAPI>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return ListView(
crossAxisAlignment: CrossAxisAlignment.start, shrinkWrap: true,
mainAxisAlignment: MainAxisAlignment.start, padding: EdgeInsets.zero,
children: [ physics: const NeverScrollableScrollPhysics(),
FutureBuilder<List<PatchedApplication>>( children: locator<HomeViewModel>()
future: locator<HomeViewModel>().getPatchedApps(true), .patchedUpdatableApps
builder: (context, snapshot) => snapshot.hasData && .map((app) => ApplicationItem(
snapshot.data!.isNotEmpty icon: app.icon,
? ListView.builder( name: app.name,
shrinkWrap: true, patchDate: app.patchDate,
physics: const NeverScrollableScrollPhysics(), changelog: app.changelog,
padding: EdgeInsets.zero, isUpdatableApp: true,
itemCount: snapshot.data!.length, onPressed: () => locator<HomeViewModel>().navigateToPatcher(
itemBuilder: (context, index) => FutureBuilder<List<String>>( app,
future: _managerAPI.getAppChangelog( ),
snapshot.data![index].packageName, ))
snapshot.data![index].patchDate, .toList(),
),
initialData: List.empty(),
builder: (context, snapshot2) => ApplicationItem(
icon: snapshot.data![index].icon,
name: snapshot.data![index].name,
patchDate: snapshot.data![index].patchDate,
changelog: '${snapshot2.data!.join('\n')}\n...',
isUpdatableApp: true,
onPressed: () =>
locator<HomeViewModel>().navigateToPatcher(
snapshot.data![index],
),
),
),
)
: Container(),
),
],
); );
} }
} }

View File

@ -1,54 +1,31 @@
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart'; import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/application_item.dart'; import 'package:revanced_manager/ui/widgets/shared/application_item.dart';
class InstalledAppsCard extends StatelessWidget { class InstalledAppsCard extends StatelessWidget {
InstalledAppsCard({ const InstalledAppsCard({
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final ManagerAPI _managerAPI = locator<ManagerAPI>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return ListView(
crossAxisAlignment: CrossAxisAlignment.start, shrinkWrap: true,
mainAxisAlignment: MainAxisAlignment.start, padding: EdgeInsets.zero,
children: [ physics: const NeverScrollableScrollPhysics(),
FutureBuilder<List<PatchedApplication>>( children: locator<HomeViewModel>()
future: locator<HomeViewModel>().getPatchedApps(false), .patchedInstalledApps
builder: (context, snapshot) => snapshot.hasData && .map((app) => ApplicationItem(
snapshot.data!.isNotEmpty icon: app.icon,
? ListView.builder( name: app.name,
shrinkWrap: true, patchDate: app.patchDate,
physics: const NeverScrollableScrollPhysics(), changelog: app.changelog,
padding: EdgeInsets.zero, isUpdatableApp: false,
itemCount: snapshot.data!.length, onPressed: () => DeviceApps.openApp(app.packageName),
itemBuilder: (context, index) => FutureBuilder<List<String>>( ))
future: _managerAPI.getAppChangelog( .toList(),
snapshot.data![index].packageName,
snapshot.data![index].patchDate,
),
initialData: const ['Loading'],
builder: (context, snapshot2) => ApplicationItem(
icon: snapshot.data![index].icon,
name: snapshot.data![index].name,
patchDate: snapshot.data![index].patchDate,
changelog: '${snapshot2.data!.join('\n')}\n(...)',
isUpdatableApp: false,
onPressed: () => DeviceApps.openApp(
snapshot.data![index].packageName,
),
),
),
)
: Container(),
),
],
); );
} }
} }

View File

@ -12,7 +12,7 @@ class ApplicationItem extends StatelessWidget {
final Uint8List icon; final Uint8List icon;
final String name; final String name;
final DateTime patchDate; final DateTime patchDate;
final String changelog; final List<String> changelog;
final bool isUpdatableApp; final bool isUpdatableApp;
final Function() onPressed; final Function() onPressed;
@ -103,8 +103,9 @@ class ApplicationItem extends StatelessWidget {
style: kRobotoTextStyle.copyWith(fontWeight: FontWeight.w700), style: kRobotoTextStyle.copyWith(fontWeight: FontWeight.w700),
), ),
), ),
const SizedBox(height: 4),
Text( Text(
changelog, ' - ${changelog.join('\n- ')}',
style: kRobotoTextStyle, style: kRobotoTextStyle,
), ),
], ],