feat: get changelog from app's specific github path (ugly)

This commit is contained in:
Alberto Ponces 2022-08-30 02:07:28 +01:00
parent 03b45e0db0
commit 5c657fbed5
9 changed files with 77 additions and 51 deletions

View File

@ -6,6 +6,16 @@ import 'package:timeago/timeago.dart';
class GithubAPI { class GithubAPI {
final GitHub _github = GitHub(); final GitHub _github = GitHub();
final Map<String, String> repoAppPath = {
'com.google.android.youtube': 'youtube',
'com.google.android.apps.youtube.music': 'music',
'com.twitter.android': 'twitter',
'com.reddit.frontpage': 'reddit',
'com.zhiliaoapp.musically': 'tiktok',
'de.dwd.warnapp': 'warnwetter',
'com.garzotto.pflotsh.ecmwf_a': 'ecmwf',
};
Future<String?> latestReleaseVersion(String org, String repoName) async { Future<String?> latestReleaseVersion(String org, String repoName) async {
try { try {
var latestRelease = await _github.repositories.getLatestRelease( var latestRelease = await _github.repositories.getLatestRelease(
@ -61,9 +71,19 @@ class GithubAPI {
)).toList(); )).toList();
} }
Future<List<RepositoryCommit>> getCommits(String org, String repoName) async { Future<List<RepositoryCommit>> getCommits(
return await (_github.repositories.listCommits( String packageName,
RepositorySlug(org, repoName), String org,
String repoName,
) async {
String path =
'src/main/kotlin/app/revanced/patches/${repoAppPath[packageName]}';
return await (PaginationHelper(_github)
.objects<Map<String, dynamic>, RepositoryCommit>(
'GET',
'/repos/$org/$repoName/commits',
(i) => RepositoryCommit.fromJson(i),
params: <String, dynamic>{'path': path},
)).toList(); )).toList();
} }
} }

View File

@ -15,11 +15,9 @@ 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 {
@ -62,16 +60,16 @@ class ManagerAPI {
.toList(); .toList();
} }
void setPatchedApps(List<PatchedApplication> patchedApps) { Future<void> setPatchedApps(List<PatchedApplication> patchedApps) async {
_prefs.setStringList('patchedApps', await _prefs.setStringList('patchedApps',
patchedApps.map((a) => json.encode(a.toJson())).toList()); patchedApps.map((a) => json.encode(a.toJson())).toList());
} }
void savePatchedApp(PatchedApplication app) { Future<void> savePatchedApp(PatchedApplication app) async {
List<PatchedApplication> patchedApps = getPatchedApps(); List<PatchedApplication> patchedApps = getPatchedApps();
patchedApps.removeWhere((a) => a.packageName == app.packageName); patchedApps.removeWhere((a) => a.packageName == app.packageName);
patchedApps.add(app); patchedApps.add(app);
setPatchedApps(patchedApps); await setPatchedApps(patchedApps);
} }
Future<void> reAssessSavedApps() async { Future<void> reAssessSavedApps() async {
@ -83,20 +81,12 @@ class ManagerAPI {
if (isRemove) { if (isRemove) {
toRemove.add(app); toRemove.add(app);
} else { } else {
List<String> newChangelog = getAppChangelog( app.hasUpdates = await hasAppUpdates(app.packageName, app.patchDate);
app.packageName, app.changelog = await getAppChangelog(app.packageName, app.patchDate);
app.patchDate,
);
if (newChangelog.isNotEmpty) {
app.changelog = newChangelog;
app.hasUpdates = true;
} else {
app.hasUpdates = false;
}
} }
} }
patchedApps.removeWhere((a) => toRemove.contains(a)); patchedApps.removeWhere((a) => toRemove.contains(a));
setPatchedApps(patchedApps); await setPatchedApps(patchedApps);
} }
Future<bool> isAppUninstalled(PatchedApplication app, bool isRoot) async { Future<bool> isAppUninstalled(PatchedApplication app, bool isRoot) async {
@ -108,24 +98,38 @@ class ManagerAPI {
return !existsRoot && !existsNonRoot; return !existsRoot && !existsNonRoot;
} }
List<String> getAppChangelog(String packageName, DateTime patchedDate) { Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async {
List<String> newCommits = _commits List<RepositoryCommit> commits =
await _githubAPI.getCommits(packageName, ghOrg, patchesRepo);
return commits.any((c) =>
c.commit != null &&
c.commit!.author != null &&
c.commit!.author!.date != null &&
c.commit!.author!.date!.isAfter(patchDate));
}
Future<List<String>> getAppChangelog(
String packageName,
DateTime patchDate,
) async {
List<RepositoryCommit> commits =
await _githubAPI.getCommits(packageName, ghOrg, patchesRepo);
List<String> newCommits = commits
.where((c) => .where((c) =>
c.commit != null && c.commit != null &&
c.commit!.message != null &&
c.commit!.author != null && c.commit!.author != null &&
c.commit!.author!.date != null && c.commit!.author!.date != null &&
c.commit!.author!.date!.isAfter(patchedDate)) c.commit!.author!.date!.isAfter(patchDate) &&
c.commit!.message != null)
.map((c) => c.commit!.message!) .map((c) => c.commit!.message!)
.toList(); .toList();
if (newCommits.isNotEmpty) { if (newCommits.isEmpty) {
int firstChore = newCommits.indexWhere((c) => c.startsWith('chore')); newCommits = commits
int secondChore = .where((c) => c.commit != null && c.commit!.message != null)
newCommits.indexWhere((c) => c.startsWith('chore'), firstChore + 1); .take(3)
if (firstChore >= 0 && secondChore > firstChore) { .map((c) => c.commit!.message!)
return newCommits.sublist(firstChore + 1, secondChore); .toList();
}
} }
return List.empty(); return newCommits;
} }
} }

View File

@ -17,7 +17,6 @@ class HomeView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<HomeViewModel>.reactive( return ViewModelBuilder<HomeViewModel>.reactive(
disposeViewModel: false, disposeViewModel: false,
fireOnModelReadyOnce: true,
onModelReady: (model) => model.initialize(), onModelReady: (model) => model.initialize(),
viewModelBuilder: () => locator<HomeViewModel>(), viewModelBuilder: () => locator<HomeViewModel>(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
@ -111,8 +110,8 @@ class HomeView extends StatelessWidget {
), ),
const SizedBox(height: 14), const SizedBox(height: 14),
model.showUpdatableApps model.showUpdatableApps
? const AvailableUpdatesCard() ? AvailableUpdatesCard()
: const InstalledAppsCard() : InstalledAppsCard(),
], ],
), ),
), ),

View File

@ -26,14 +26,14 @@ class HomeViewModel extends BaseViewModel {
List<PatchedApplication> patchedUpdatableApps = []; List<PatchedApplication> patchedUpdatableApps = [];
Future<void> initialize() async { Future<void> initialize() async {
await _getPatchedApps();
await flutterLocalNotificationsPlugin.initialize( await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings( const InitializationSettings(
android: AndroidInitializationSettings('ic_notification'), android: AndroidInitializationSettings('ic_notification'),
), ),
onSelectNotification: (p) => DeviceApps.openApp('app.revanced.manager'), onSelectNotification: (p) => DeviceApps.openApp('app.revanced.manager'),
); );
_managerAPI.reAssessSavedApps().then((_) => notifyListeners()); _getPatchedApps();
_managerAPI.reAssessSavedApps().then((_) => _getPatchedApps());
} }
void toggleUpdatableApps(bool value) { void toggleUpdatableApps(bool value) {
@ -49,11 +49,8 @@ class HomeViewModel extends BaseViewModel {
locator<MainViewModel>().setIndex(1); locator<MainViewModel>().setIndex(1);
} }
Future<void> _getPatchedApps() async { void _getPatchedApps() {
patchedInstalledApps = _managerAPI patchedInstalledApps = _managerAPI.getPatchedApps().toList();
.getPatchedApps()
.where((app) => app.hasUpdates == false)
.toList();
patchedUpdatableApps = _managerAPI patchedUpdatableApps = _managerAPI
.getPatchedApps() .getPatchedApps()
.where((app) => app.hasUpdates == true) .where((app) => app.hasUpdates == true)

View File

@ -136,7 +136,7 @@ class InstallerViewModel extends BaseViewModel {
update(1.0, 'Installed!', 'Installed!'); update(1.0, 'Installed!', 'Installed!');
_app!.patchDate = DateTime.now(); _app!.patchDate = DateTime.now();
_app!.appliedPatches = _patches.map((p) => p.name).toList(); _app!.appliedPatches = _patches.map((p) => p.name).toList();
_managerAPI.savePatchedApp(_app!); await _managerAPI.savePatchedApp(_app!);
} else { } else {
update(1.0, 'Aborting...', 'An error occurred! Aborting'); update(1.0, 'Aborting...', 'An error occurred! Aborting');
} }

View File

@ -20,7 +20,7 @@ class RootCheckerViewModel extends BaseViewModel {
Future<void> navigateToHome() async { Future<void> navigateToHome() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
prefs.setBool('isRooted', isRooted); await prefs.setBool('isRooted', isRooted);
_navigationService.navigateTo(Routes.navigation); _navigationService.navigateTo(Routes.navigation);
notifyListeners(); notifyListeners();
} }

View File

@ -1,21 +1,24 @@
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/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 {
const AvailableUpdatesCard({ AvailableUpdatesCard({
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final List<PatchedApplication> apps =
locator<HomeViewModel>().patchedUpdatableApps;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return ListView(
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: locator<HomeViewModel>() children: apps
.patchedUpdatableApps
.map((app) => ApplicationItem( .map((app) => ApplicationItem(
icon: app.icon, icon: app.icon,
name: app.name, name: app.name,

View File

@ -1,22 +1,25 @@
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/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 {
const InstalledAppsCard({ InstalledAppsCard({
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final List<PatchedApplication> apps =
locator<HomeViewModel>().patchedInstalledApps;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return ListView(
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: locator<HomeViewModel>() children: apps
.patchedInstalledApps
.map((app) => ApplicationItem( .map((app) => ApplicationItem(
icon: app.icon, icon: app.icon,
name: app.name, name: app.name,

View File

@ -105,7 +105,7 @@ class ApplicationItem extends StatelessWidget {
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
' - ${changelog.join('\n- ')}', '\u2022 ${changelog.join('\n\u2022 ')}',
style: kRobotoTextStyle, style: kRobotoTextStyle,
), ),
], ],