mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-16 06:36:44 +02:00
Compare commits
5 Commits
v0.1.0-bet
...
v0.1.1-bet
Author | SHA1 | Date | |
---|---|---|---|
9dba372244 | |||
88b60fe362 | |||
0362cdf8ac | |||
aeada9635d | |||
ffe212ebf2 |
BIN
android/app/src/main/res/drawable/ic_notification.png
Normal file → Executable file
BIN
android/app/src/main/res/drawable/ic_notification.png
Normal file → Executable file
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.1 KiB |
@ -8,25 +8,49 @@ import 'package:provider/provider.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void backgroundUpdateCheck() {
|
||||
Workmanager().executeTask((task, inputData) async {
|
||||
var appsProvider = AppsProvider(bg: true);
|
||||
await appsProvider.loadApps();
|
||||
List<App> updates = await appsProvider.getUpdates();
|
||||
if (updates.isNotEmpty) {
|
||||
String message = updates.length == 1
|
||||
? '${updates[0].name} has an update.'
|
||||
: '${(updates.length == 2 ? '${updates[0].name} and ${updates[1].name}' : '${updates[0].name} and ${updates.length - 1} more apps')} have updates.';
|
||||
await appsProvider.downloaderNotifications.cancel(2);
|
||||
await appsProvider.notify(
|
||||
4,
|
||||
'Checking for Updates',
|
||||
'',
|
||||
'BG_UPDATE_CHECK',
|
||||
'Checking for Updates',
|
||||
'Transient notification that appears when checking for updates',
|
||||
important: false);
|
||||
try {
|
||||
await appsProvider.loadApps();
|
||||
List<App> updates = await appsProvider.checkUpdates();
|
||||
if (updates.isNotEmpty) {
|
||||
String message = updates.length == 1
|
||||
? '${updates[0].name} has an update.'
|
||||
: '${(updates.length == 2 ? '${updates[0].name} and ${updates[1].name}' : '${updates[0].name} and ${updates.length - 1} more apps')} have updates.';
|
||||
await appsProvider.downloaderNotifications.cancel(2);
|
||||
await appsProvider.notify(
|
||||
2,
|
||||
'Updates Available',
|
||||
message,
|
||||
'UPDATES_AVAILABLE',
|
||||
'Updates Available',
|
||||
'Notifies the user that updates are available for one or more Apps tracked by Obtainium');
|
||||
}
|
||||
return Future.value(true);
|
||||
} catch (e) {
|
||||
await appsProvider.downloaderNotifications.cancel(5);
|
||||
await appsProvider.notify(
|
||||
2,
|
||||
'Updates Available',
|
||||
message,
|
||||
'UPDATES_AVAILABLE',
|
||||
'Updates Available',
|
||||
'Notifies the user that updates are available for one or more Apps tracked by Obtainium');
|
||||
5,
|
||||
'Error Checking for Updates',
|
||||
e.toString(),
|
||||
'BG_UPDATE_CHECK_ERROR',
|
||||
'Error Checking for Updates',
|
||||
'A notification that shows when background update checking fails',
|
||||
important: false);
|
||||
return Future.value(false);
|
||||
} finally {
|
||||
await appsProvider.downloaderNotifications.cancel(4);
|
||||
}
|
||||
return Future.value(true);
|
||||
});
|
||||
}
|
||||
|
||||
@ -63,13 +87,14 @@ class MyApp extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return DynamicColorBuilder(
|
||||
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
||||
AppsProvider appsProvider = context.read<AppsProvider>();
|
||||
appsProvider.deleteSavedAPKs();
|
||||
// Initialize the settings provider (if needed) and perform first-run actions if needed
|
||||
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
||||
if (settingsProvider.prefs == null) {
|
||||
settingsProvider.initializeSettings().then((_) {
|
||||
bool isFirstRun = settingsProvider.checkAndFlipFirstRun();
|
||||
if (isFirstRun) {
|
||||
AppsProvider appsProvider = context.read<AppsProvider>();
|
||||
appsProvider
|
||||
.notify(
|
||||
3,
|
||||
@ -82,6 +107,14 @@ class MyApp extends StatelessWidget {
|
||||
.whenComplete(() {
|
||||
appsProvider.downloaderNotifications.cancel(3);
|
||||
});
|
||||
appsProvider.saveApp(App(
|
||||
'imranr98_obtainium_github',
|
||||
'https://github.com/ImranR98/Obtainium',
|
||||
'ImranR98',
|
||||
'Obtainium',
|
||||
'v0.1.1-beta', // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||
'v0.1.1-beta',
|
||||
''));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -14,46 +14,62 @@ class _AppsPageState extends State<AppsPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var appsProvider = context.watch<AppsProvider>();
|
||||
appsProvider.getUpdates();
|
||||
appsProvider.checkUpdates();
|
||||
var existingUpdateAppIds = appsProvider.getExistingUpdates();
|
||||
|
||||
return Center(
|
||||
child: appsProvider.loadingApps
|
||||
? const CircularProgressIndicator()
|
||||
: appsProvider.apps.isEmpty
|
||||
? Text(
|
||||
'No Apps',
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: appsProvider.getUpdates,
|
||||
child: ListView(
|
||||
children: appsProvider.apps.values
|
||||
.map(
|
||||
(e) => ListTile(
|
||||
title: Text('${e.app.author}/${e.app.name}'),
|
||||
subtitle:
|
||||
Text(e.app.installedVersion ?? 'Not Installed'),
|
||||
trailing: e.downloadProgress != null
|
||||
? Text(
|
||||
'Downloading - ${e.downloadProgress!.toInt()}%')
|
||||
: (e.app.installedVersion != null &&
|
||||
e.app.installedVersion !=
|
||||
e.app.latestVersion
|
||||
? const Text('Update Available')
|
||||
: null),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
AppPage(appId: e.app.id)),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
return Scaffold(
|
||||
floatingActionButton: existingUpdateAppIds.isEmpty
|
||||
? null
|
||||
: ElevatedButton.icon(
|
||||
onPressed: appsProvider.apps.values
|
||||
.where((element) => element.downloadProgress != null)
|
||||
.isNotEmpty
|
||||
? null
|
||||
: () {
|
||||
for (var e in existingUpdateAppIds) {
|
||||
appsProvider.downloadAndInstallLatestApp(e);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.update),
|
||||
label: const Text('Update All')),
|
||||
body: Center(
|
||||
child: appsProvider.loadingApps
|
||||
? const CircularProgressIndicator()
|
||||
: appsProvider.apps.isEmpty
|
||||
? Text(
|
||||
'No Apps',
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: appsProvider.checkUpdates,
|
||||
child: ListView(
|
||||
children: appsProvider.apps.values
|
||||
.map(
|
||||
(e) => ListTile(
|
||||
title: Text('${e.app.author}/${e.app.name}'),
|
||||
subtitle: Text(
|
||||
e.app.installedVersion ?? 'Not Installed'),
|
||||
trailing: e.downloadProgress != null
|
||||
? Text(
|
||||
'Downloading - ${e.downloadProgress!.toInt()}%')
|
||||
: (e.app.installedVersion != null &&
|
||||
e.app.installedVersion !=
|
||||
e.app.latestVersion
|
||||
? const Text('Update Available')
|
||||
: null),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
AppPage(appId: e.app.id)),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,15 @@ class AppsProvider with ChangeNotifier {
|
||||
return appsDir;
|
||||
}
|
||||
|
||||
Future<void> deleteSavedAPKs() async {
|
||||
(await getExternalStorageDirectory())
|
||||
?.listSync()
|
||||
.where((element) => element.path.endsWith('.apk'))
|
||||
.forEach((element) {
|
||||
element.deleteSync();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> loadApps() async {
|
||||
loadingApps = true;
|
||||
notifyListeners();
|
||||
@ -186,7 +195,7 @@ class AppsProvider with ChangeNotifier {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<List<App>> getUpdates() async {
|
||||
Future<List<App>> checkUpdates() async {
|
||||
List<App> updates = [];
|
||||
if (!gettingUpdates) {
|
||||
gettingUpdates = true;
|
||||
@ -203,14 +212,16 @@ class AppsProvider with ChangeNotifier {
|
||||
return updates;
|
||||
}
|
||||
|
||||
Future<void> installUpdates() async {
|
||||
List<String> getExistingUpdates() {
|
||||
List<String> updateAppIds = [];
|
||||
List<String> appIds = apps.keys.toList();
|
||||
for (int i = 0; i < appIds.length; i++) {
|
||||
App? app = apps[appIds[i]]!.app;
|
||||
if (app.installedVersion != app.latestVersion) {
|
||||
await downloadAndInstallLatestApp(app.id);
|
||||
updateAppIds.add(app.id);
|
||||
}
|
||||
}
|
||||
return updateAppIds;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -23,6 +23,7 @@ class APKDetails {
|
||||
// App Source abstract class (diff. implementations for GitHub, GitLab, etc.)
|
||||
|
||||
abstract class AppSource {
|
||||
late String sourceId;
|
||||
String standardizeURL(String url);
|
||||
Future<APKDetails> getLatestAPKDetails(String standardUrl);
|
||||
AppNames getAppNames(String standardUrl);
|
||||
@ -77,6 +78,9 @@ class App {
|
||||
// Specific App Source classes
|
||||
|
||||
class GitHub implements AppSource {
|
||||
@override
|
||||
String sourceId = 'github';
|
||||
|
||||
@override
|
||||
String standardizeURL(String url) {
|
||||
RegExp standardUrlRegEx = RegExp(r'^https?://github.com/[^/]*/[^/]*');
|
||||
@ -143,12 +147,15 @@ class SourceService {
|
||||
url.toLowerCase().indexOf('https://') != 0) {
|
||||
url = 'https://$url';
|
||||
}
|
||||
if (url.toLowerCase().indexOf('https://www.') == 0) {
|
||||
url = 'https://${url.substring(12)}';
|
||||
}
|
||||
AppSource source = getSource(url);
|
||||
String standardUrl = source.standardizeURL(url);
|
||||
AppNames names = source.getAppNames(standardUrl);
|
||||
APKDetails apk = await source.getLatestAPKDetails(standardUrl);
|
||||
return App(
|
||||
'${names.author}_${names.name}',
|
||||
'${names.author}_${names.name}_${source.sourceId}',
|
||||
standardUrl,
|
||||
names.author[0].toUpperCase() + names.author.substring(1),
|
||||
names.name[0].toUpperCase() + names.name.substring(1),
|
||||
|
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 0.1.0+1
|
||||
version: 0.1.1+2 # When changing this, update the tag in main() accordingly
|
||||
|
||||
environment:
|
||||
sdk: '>=2.19.0-79.0.dev <3.0.0'
|
||||
|
Reference in New Issue
Block a user