Skip to content

Commit

Permalink
Merge pull request #1643 from ImranR98/dev
Browse files Browse the repository at this point in the history
- Bugfix: Include GitLab token in APK request (#1622)
- Bugfix: Don't trim trailing slashes when parsing links (#1625)
- Improve contrast of placeholder icon in dark mode (#1637) 
- Improve app loading times
- Revert a previous change to background downloads
- Add a "clear logs" button
  • Loading branch information
ImranR98 authored May 24, 2024
2 parents 2b27902 + 6a73ade commit 7071e34
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 94 deletions.
8 changes: 8 additions & 0 deletions lib/app_sources/gitlab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ class GitLab extends AppSource {
}
}

@override
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
Map<String, dynamic> additionalSettings) async {
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
String optionalAuth = (PAT != null) ? 'private_token=$PAT' : '';
return '$apkUrl?$optionalAuth';
}

@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Expand Down
32 changes: 20 additions & 12 deletions lib/pages/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -226,18 +226,26 @@ class _AppPageState extends State<AppPage> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 20),
app?.icon != null
? Row(mainAxisAlignment: MainAxisAlignment.center, children: [
GestureDetector(
child: Image.memory(
app!.icon!,
height: 150,
gaplessPlayback: true,
),
onTap: () => pm.openApp(app.app.id),
)
])
: Container(),
FutureBuilder(
future: appsProvider.updateAppIcon(app?.app.id),
builder: (ctx, val) {
return app?.icon != null
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: app == null
? null
: () => pm.openApp(app.app.id),
child: Image.memory(
app!.icon!,
height: 150,
gaplessPlayback: true,
),
)
])
: Container();
}),
const SizedBox(
height: 25,
),
Expand Down
59 changes: 35 additions & 24 deletions lib/pages/apps.dart
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,11 @@ class AppsPageState extends State<AppsPage> {
SliverFillRemaining(
child: Center(
child: Text(
appsProvider.apps.isEmpty ? tr('noApps') : tr('noAppsForFilter'),
appsProvider.apps.isEmpty
? appsProvider.loadingApps
? tr('pleaseWait')
: tr('noApps')
: tr('noAppsForFilter'),
style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center,
))),
Expand Down Expand Up @@ -402,29 +406,36 @@ class AppsPageState extends State<AppsPage> {
}

getAppIcon(int appIndex) {
return listedApps[appIndex].icon != null
? Image.memory(
listedApps[appIndex].icon!,
gaplessPlayback: true,
)
: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(0.31),
child: Padding(
padding: const EdgeInsets.all(15),
child: Image(
image: const AssetImage(
'assets/graphics/icon_small.png'),
color: Colors.white.withOpacity(0.3),
colorBlendMode: BlendMode.modulate,
gaplessPlayback: true,
),
)),
]);
return FutureBuilder(
future: appsProvider.updateAppIcon(listedApps[appIndex].app.id),
builder: (ctx, val) {
return listedApps[appIndex].icon != null
? Image.memory(
listedApps[appIndex].icon!,
gaplessPlayback: true,
)
: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(0.31),
child: Padding(
padding: const EdgeInsets.all(15),
child: Image(
image: const AssetImage(
'assets/graphics/icon_small.png'),
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.white.withOpacity(0.4)
: Colors.white.withOpacity(0.3),
colorBlendMode: BlendMode.modulate,
gaplessPlayback: true,
),
)),
]);
});
}

getVersionText(int appIndex) {
Expand Down
20 changes: 20 additions & 0 deletions lib/pages/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart';
import 'package:obtainium/components/custom_app_bar.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/components/generated_form_modal.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/main.dart';
import 'package:obtainium/providers/apps_provider.dart';
Expand Down Expand Up @@ -945,6 +946,25 @@ class _LogsDialogState extends State<LogsDialog> {
],
),
actions: [
TextButton(
onPressed: () async {
var cont = (await showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext ctx) {
return GeneratedFormModal(
title: tr('appLogs'),
items: const [],
initValid: true,
message: tr('removeFromObtainium'),
);
})) !=
null;
if (cont) {
logsProvider.clear();
Navigator.of(context).pop();
}
},
child: Text(tr('remove'))),
TextButton(
onPressed: () {
Navigator.of(context).pop();
Expand Down
98 changes: 60 additions & 38 deletions lib/providers/apps_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ Future<Map<String, String>> getHeaders(String url,
return returnHeaders;
}

Future<List<PackageInfo>> getAllInstalledInfo() async {
return await pm.getInstalledPackages() ?? [];
}

Future<PackageInfo?> getInstalledInfo(String? packageName,
{bool printErr = true}) async {
if (packageName != null) {
Expand Down Expand Up @@ -364,7 +368,9 @@ class AppsProvider with ChangeNotifier {
foregroundStream = FGBGEvents.stream.asBroadcastStream();
foregroundSubscription = foregroundStream?.listen((event) async {
isForeground = event == FGBGType.foreground;
if (isForeground) loadApps();
if (isForeground) {
await loadApps();
}
});
() async {
await settingsProvider.initializeSettings();
Expand Down Expand Up @@ -1160,17 +1166,6 @@ class AppsProvider with ChangeNotifier {
: false;
}

Future<void> updateInstallStatusInMemory(AppInMemory app) async {
apps[app.app.id]?.installedInfo = await getInstalledInfo(app.app.id);
apps[app.app.id]?.icon =
await apps[app.app.id]?.installedInfo?.applicationInfo?.getAppIcon();
apps[app.app.id]?.app.name = await (apps[app.app.id]
?.installedInfo
?.applicationInfo
?.getAppLabel()) ??
app.name;
}

Future<void> loadApps({String? singleId}) async {
while (loadingApps) {
await Future.delayed(const Duration(microseconds: 1));
Expand All @@ -1179,6 +1174,8 @@ class AppsProvider with ChangeNotifier {
notifyListeners();
var sp = SourceProvider();
List<List<String>> errors = [];
var installedAppsData = await getAllInstalledInfo();
List<String> removedAppIds = [];
await Future.wait((await getAppsDir()) // Parse Apps from JSON
.listSync()
.map((item) async {
Expand All @@ -1199,43 +1196,53 @@ class AppsProvider with ChangeNotifier {
}
}
if (app != null) {
// Save the app to the in-memory list without grabbing any OS info first
apps.update(
app.id,
(value) => AppInMemory(
app!, value.downloadProgress, value.installedInfo, value.icon),
ifAbsent: () => AppInMemory(app!, null, null, null));
notifyListeners();
try {
// Try getting the app's source to ensure no invalid apps get loaded
sp.getSource(app.url, overrideSource: app.overrideSource);
// If the app is installed, grab its OS data and reconcile install statuses
PackageInfo? installedInfo;
try {
installedInfo =
installedAppsData.firstWhere((i) => i.packageName == app!.id);
} catch (e) {
// If the app isn't installed the above throws an error
}
// Reconcile differences between the installed and recorded install info
var moddedApp =
getCorrectedInstallStatusAppIfPossible(app, installedInfo);
if (moddedApp != null) {
app = moddedApp;
// Note the app ID if it was uninstalled externally
if (moddedApp.installedVersion == null) {
removedAppIds.add(moddedApp.id);
}
}
// Update the app in memory with install info and corrections
apps.update(
app.id,
(value) => AppInMemory(app!, value.downloadProgress,
value.installedInfo, value.icon),
ifAbsent: () => AppInMemory(app!, null, null, null));
(value) => AppInMemory(
app!, value.downloadProgress, installedInfo, value.icon),
ifAbsent: () => AppInMemory(app!, null, installedInfo, null));
notifyListeners();
} catch (e) {
errors.add([app.id, app.finalName, e.toString()]);
errors.add([app!.id, app.finalName, e.toString()]);
}
}
}));
notifyListeners();
if (errors.isNotEmpty) {
removeApps(errors.map((e) => e[0]).toList());
NotificationsProvider().notify(
AppsRemovedNotification(errors.map((e) => [e[1], e[2]]).toList()));
}
// Get install status and other OS info for each App (slow)
List<App> modifiedApps = [];
await Future.wait(apps.values.map((app) async {
await updateInstallStatusInMemory(app);
var moddedApp =
getCorrectedInstallStatusAppIfPossible(app.app, app.installedInfo);
if (moddedApp != null) {
modifiedApps.add(moddedApp);
}
}));
notifyListeners();
// Reconcile version differences
if (modifiedApps.isNotEmpty) {
await saveApps(modifiedApps, attemptToCorrectInstallStatus: false);
var removedAppIds = modifiedApps
.where((a) => a.installedVersion == null)
.map((e) => e.id)
.toList();
// After reconciliation, delete externally uninstalled Apps if needed
// Delete externally uninstalled Apps if needed
if (removedAppIds.isNotEmpty) {
if (removedAppIds.isNotEmpty) {
if (settingsProvider.removeOnExternalUninstall) {
await removeApps(removedAppIds);
Expand All @@ -1246,6 +1253,22 @@ class AppsProvider with ChangeNotifier {
notifyListeners();
}

Future<void> updateAppIcon(String? appId) async {
if (apps[appId]?.icon == null) {
var icon =
(await apps[appId]?.installedInfo?.applicationInfo?.getAppIcon());
if (icon != null) {
apps.update(
apps[appId]!.app.id,
(value) => AppInMemory(apps[appId]!.app, value.downloadProgress,
value.installedInfo, icon),
ifAbsent: () => AppInMemory(
apps[appId]!.app, null, apps[appId]?.installedInfo, icon));
notifyListeners();
}
}
}

Future<void> saveApps(List<App> apps,
{bool attemptToCorrectInstallStatus = true,
bool onlyIfExists = true}) async {
Expand Down Expand Up @@ -1941,8 +1964,7 @@ Future<void> bgUpdateCheck(String taskId, Map<String, dynamic>? params) async {
await appsProvider.downloadAndInstallLatestApps(
toInstall.map((e) => e.key).toList(), null,
notificationsProvider: notificationsProvider,
forceParallelDownloads: true,
useExisting: false);
forceParallelDownloads: true);
} catch (e) {
if (e is MultiAppMultiError) {
e.idsByErrorString.forEach((key, value) {
Expand Down
13 changes: 7 additions & 6 deletions lib/providers/source_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -354,11 +354,13 @@ preStandardizeUrl(String url) {
url.toLowerCase().indexOf('https://') != 0) {
url = 'https://$url';
}
var trailingSlash = Uri.tryParse(url)?.path.endsWith('/') ?? false;
url = url
.split('/')
.where((e) => e.isNotEmpty)
.join('/')
.replaceFirst(':/', '://');
.split('/')
.where((e) => e.isNotEmpty)
.join('/')
.replaceFirst(':/', '://') +
(trailingSlash ? '/' : '');
return url;
}

Expand Down Expand Up @@ -523,8 +525,7 @@ abstract class AppSource {
[GeneratedFormTextField('appName', label: tr('appName'), required: false)],
[
GeneratedFormSwitch('shizukuPretendToBeGooglePlay',
label: tr('shizukuPretendToBeGooglePlay'),
defaultValue: false)
label: tr('shizukuPretendToBeGooglePlay'), defaultValue: false)
],
[
GeneratedFormSwitch('exemptFromBackgroundUpdates',
Expand Down
Loading

0 comments on commit 7071e34

Please sign in to comment.