feat: show all apps and recommended versions

This commit is contained in:
Aunali321 2023-03-20 03:35:55 +05:30
parent 7e05bcac90
commit cbb36f2ec7
6 changed files with 184 additions and 36 deletions

View File

@ -72,7 +72,8 @@
"viewTitle": "Select an application",
"searchBarHint": "Search applications",
"storageButton": "Storage",
"errorMessage": "Unable to use selected application"
"errorMessage": "Unable to use selected application",
"downloadToast": "Download function is not available yet"
},
"patchesSelectorView": {
"viewTitle": "Select patches",

View File

@ -3,6 +3,7 @@ import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/not_installed_app_item.dart';
import 'package:revanced_manager/ui/widgets/shared/search_bar.dart';
import 'package:stacked/stacked.dart' hide SkeletonLoader;
@ -75,8 +76,8 @@ class _AppSelectorViewState extends State<AppSelectorView> {
),
SliverToBoxAdapter(
child: model.noApps
? Center(
child: I18nText('appSelectorCard.noAppsLabel'),
? const Center(
child: Text('No apps found.'),
)
: model.apps.isEmpty
? const AppSkeletonLoader()
@ -84,22 +85,42 @@ class _AppSelectorViewState extends State<AppSelectorView> {
padding: const EdgeInsets.symmetric(horizontal: 12.0)
.copyWith(bottom: 80),
child: Column(
children: model
.getFilteredApps(_query)
.map(
(app) => InstalledAppItem(
name: app.appName,
pkgName: app.packageName,
icon: app.icon,
patchesCount:
model.patchesCount(app.packageName),
onTap: () {
model.selectApp(app);
Navigator.of(context).pop();
},
),
)
.toList(),
children: [
...model
.getFilteredApps(_query)
.map(
(app) => InstalledAppItem(
name: app.appName,
pkgName: app.packageName,
icon: app.icon,
patchesCount:
model.patchesCount(app.packageName),
recommendedVersion:
model.getRecommendedVersion(
app.packageName,
),
onTap: () {
model.selectApp(app);
Navigator.of(context).pop();
},
),
)
.toList(),
...model
.getFilteredAppsNames(_query)
.map(
(app) => NotInstalledAppItem(
name: app,
patchesCount: model.patchesCount(app),
recommendedVersion:
model.getRecommendedVersion(app),
onTap: () {
model.showDownloadToast();
},
),
)
.toList(),
],
),
),
),

View File

@ -5,9 +5,11 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/services/revanced_api.dart';
import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:stacked/stacked.dart';
@ -15,14 +17,20 @@ import 'package:stacked/stacked.dart';
class AppSelectorViewModel extends BaseViewModel {
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final RevancedAPI _revancedAPI = locator<RevancedAPI>();
final Toast _toast = locator<Toast>();
final List<ApplicationWithIcon> apps = [];
List<String> allApps = [];
bool noApps = false;
int patchesCount(String packageName) {
return _patcherAPI.getFilteredPatches(packageName).length;
}
List<Patch> patches = [];
Future<void> initialize() async {
patches = await _revancedAPI.getPatches();
apps.addAll(
await _patcherAPI
.getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled()),
@ -34,9 +42,25 @@ class AppSelectorViewModel extends BaseViewModel {
.compareTo(_patcherAPI.getFilteredPatches(a.packageName).length),
);
noApps = apps.isEmpty;
getAllApps();
notifyListeners();
}
List<String> getAllApps() {
allApps = patches
.expand((e) => e.compatiblePackages.map((p) => p.name))
.toSet()
.where((name) => !apps.any((app) => app.packageName == name))
.toList();
return allApps;
}
String getRecommendedVersion(String packageName) {
return _patcherAPI.getRecommendedVersion(packageName);
}
Future<void> selectApp(ApplicationWithIcon application) async {
locator<PatcherViewModel>().selectedApp = PatchedApplication(
name: application.appName,
@ -105,4 +129,18 @@ class AppSelectorViewModel extends BaseViewModel {
)
.toList();
}
List<String> getFilteredAppsNames(String query) {
return allApps
.where(
(app) =>
query.isEmpty ||
query.length < 2 ||
app.toLowerCase().contains(query.toLowerCase()),
)
.toList();
}
void showDownloadToast() =>
_toast.showBottom('appSelectorView.downloadToast');
}

View File

@ -1,6 +1,4 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart';
@ -9,7 +7,6 @@ import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:stacked/stacked.dart';
class PatchesSelectorViewModel extends BaseViewModel {

View File

@ -9,12 +9,14 @@ class InstalledAppItem extends StatefulWidget {
required this.pkgName,
required this.icon,
required this.patchesCount,
required this.recommendedVersion,
this.onTap,
}) : super(key: key);
final String name;
final String pkgName;
final Uint8List icon;
final int patchesCount;
final String recommendedVersion;
final Function()? onTap;
@override
@ -46,31 +48,35 @@ class _InstalledAppItemState extends State<InstalledAppItem> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.name,
maxLines: 2,
overflow: TextOverflow.visible,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Text(widget.pkgName),
Row(
children: <Widget>[
children: [
Text(
widget.name,
maxLines: 2,
overflow: TextOverflow.visible,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
widget.recommendedVersion.isEmpty
? 'All versions'
: widget.recommendedVersion,
),
const SizedBox(width: 6),
const SizedBox(width: 4),
Text(
widget.patchesCount == 1
? '${widget.patchesCount} patch'
: '${widget.patchesCount} patches',
? '${widget.patchesCount} patch'
: '${widget.patchesCount} patches',
style: TextStyle(
fontSize: 8,
color: Theme.of(context).colorScheme.secondary,
),
),
],
),
const SizedBox(height: 4),
Text(widget.pkgName),
],
),
),

View File

@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
class NotInstalledAppItem extends StatefulWidget {
const NotInstalledAppItem({
Key? key,
required this.name,
required this.patchesCount,
required this.recommendedVersion,
this.onTap,
}) : super(key: key);
final String name;
final int patchesCount;
final String recommendedVersion;
final Function()? onTap;
@override
State<NotInstalledAppItem> createState() => _NotInstalledAppItem();
}
class _NotInstalledAppItem extends State<NotInstalledAppItem> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: CustomCard(
onTap: widget.onTap,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
height: 48,
padding: const EdgeInsets.symmetric(vertical: 4.0),
alignment: Alignment.center,
child: const CircleAvatar(
backgroundColor: Colors.transparent,
child: Icon(
Icons.square_rounded,
color: Colors.grey,
size: 44,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
const Text('App not installed.'),
const SizedBox(height: 4),
Row(
children: [
Text(
widget.recommendedVersion.isEmpty
? 'All versions'
: widget.recommendedVersion,
),
const SizedBox(width: 4),
Text(
widget.patchesCount == 1
? '${widget.patchesCount} patch'
: '${widget.patchesCount} patches',
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
),
),
],
),
],
),
),
],
),
),
);
}
}