mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-27 19:39:42 +02:00
Compare commits
7 Commits
v0.1.0-bet
...
v0.1.2-bet
Author | SHA1 | Date | |
---|---|---|---|
|
21ca18ce75 | ||
|
7afcf6a37b | ||
|
9dba372244 | ||
|
88b60fe362 | ||
|
0362cdf8ac | ||
|
aeada9635d | ||
|
ffe212ebf2 |
@@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion 33
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@@ -54,7 +54,7 @@ android {
|
|||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 32
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
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 |
3
android/app/src/main/res/raw/keep.xml
Normal file
3
android/app/src/main/res/raw/keep.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:keep="@drawable/*" />
|
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:obtainium/pages/home.dart';
|
import 'package:obtainium/pages/home.dart';
|
||||||
import 'package:obtainium/services/apps_provider.dart';
|
import 'package:obtainium/services/apps_provider.dart';
|
||||||
import 'package:obtainium/services/settings_provider.dart';
|
import 'package:obtainium/services/settings_provider.dart';
|
||||||
@@ -8,11 +9,21 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:workmanager/workmanager.dart';
|
import 'package:workmanager/workmanager.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
void backgroundUpdateCheck() {
|
void backgroundUpdateCheck() {
|
||||||
Workmanager().executeTask((task, inputData) async {
|
Workmanager().executeTask((task, inputData) async {
|
||||||
var appsProvider = AppsProvider(bg: true);
|
var appsProvider = AppsProvider(bg: true);
|
||||||
|
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();
|
await appsProvider.loadApps();
|
||||||
List<App> updates = await appsProvider.getUpdates();
|
List<App> updates = await appsProvider.checkUpdates();
|
||||||
if (updates.isNotEmpty) {
|
if (updates.isNotEmpty) {
|
||||||
String message = updates.length == 1
|
String message = updates.length == 1
|
||||||
? '${updates[0].name} has an update.'
|
? '${updates[0].name} has an update.'
|
||||||
@@ -27,6 +38,20 @@ void backgroundUpdateCheck() {
|
|||||||
'Notifies the user that updates are available for one or more Apps tracked by Obtainium');
|
'Notifies the user that updates are available for one or more Apps tracked by Obtainium');
|
||||||
}
|
}
|
||||||
return Future.value(true);
|
return Future.value(true);
|
||||||
|
} catch (e) {
|
||||||
|
await appsProvider.downloaderNotifications.cancel(5);
|
||||||
|
await appsProvider.notify(
|
||||||
|
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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,25 +88,26 @@ class MyApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DynamicColorBuilder(
|
return DynamicColorBuilder(
|
||||||
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
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
|
// Initialize the settings provider (if needed) and perform first-run actions if needed
|
||||||
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
||||||
if (settingsProvider.prefs == null) {
|
if (settingsProvider.prefs == null) {
|
||||||
settingsProvider.initializeSettings().then((_) {
|
settingsProvider.initializeSettings().then((_) {
|
||||||
bool isFirstRun = settingsProvider.checkAndFlipFirstRun();
|
bool isFirstRun = settingsProvider.checkAndFlipFirstRun();
|
||||||
if (isFirstRun) {
|
if (isFirstRun) {
|
||||||
AppsProvider appsProvider = context.read<AppsProvider>();
|
appsProvider.downloaderNotifications
|
||||||
appsProvider
|
.resolvePlatformSpecificImplementation<
|
||||||
.notify(
|
AndroidFlutterLocalNotificationsPlugin>()!
|
||||||
3,
|
.requestPermission();
|
||||||
'Permission Notification',
|
appsProvider.saveApp(App(
|
||||||
'This is a transient notification used to trigger the Android 13 notification permission prompt',
|
'imranr98_obtainium_github',
|
||||||
'PERMISSION_NOTIFICATION',
|
'https://github.com/ImranR98/Obtainium',
|
||||||
'Permission Notifications',
|
'ImranR98',
|
||||||
'A transient notification used to trigger the Android 13 notification permission prompt',
|
'Obtainium',
|
||||||
important: false)
|
'v0.1.2-beta', // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||||
.whenComplete(() {
|
'v0.1.2-beta',
|
||||||
appsProvider.downloaderNotifications.cancel(3);
|
''));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -14,9 +14,25 @@ class _AppsPageState extends State<AppsPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var appsProvider = context.watch<AppsProvider>();
|
var appsProvider = context.watch<AppsProvider>();
|
||||||
appsProvider.getUpdates();
|
appsProvider.checkUpdates();
|
||||||
|
var existingUpdateAppIds = appsProvider.getExistingUpdates();
|
||||||
|
|
||||||
return Center(
|
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
|
child: appsProvider.loadingApps
|
||||||
? const CircularProgressIndicator()
|
? const CircularProgressIndicator()
|
||||||
: appsProvider.apps.isEmpty
|
: appsProvider.apps.isEmpty
|
||||||
@@ -25,14 +41,14 @@ class _AppsPageState extends State<AppsPage> {
|
|||||||
style: Theme.of(context).textTheme.headline4,
|
style: Theme.of(context).textTheme.headline4,
|
||||||
)
|
)
|
||||||
: RefreshIndicator(
|
: RefreshIndicator(
|
||||||
onRefresh: appsProvider.getUpdates,
|
onRefresh: appsProvider.checkUpdates,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: appsProvider.apps.values
|
children: appsProvider.apps.values
|
||||||
.map(
|
.map(
|
||||||
(e) => ListTile(
|
(e) => ListTile(
|
||||||
title: Text('${e.app.author}/${e.app.name}'),
|
title: Text('${e.app.author}/${e.app.name}'),
|
||||||
subtitle:
|
subtitle: Text(
|
||||||
Text(e.app.installedVersion ?? 'Not Installed'),
|
e.app.installedVersion ?? 'Not Installed'),
|
||||||
trailing: e.downloadProgress != null
|
trailing: e.downloadProgress != null
|
||||||
? Text(
|
? Text(
|
||||||
'Downloading - ${e.downloadProgress!.toInt()}%')
|
'Downloading - ${e.downloadProgress!.toInt()}%')
|
||||||
@@ -54,6 +70,6 @@ class _AppsPageState extends State<AppsPage> {
|
|||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -132,6 +132,15 @@ class AppsProvider with ChangeNotifier {
|
|||||||
return appsDir;
|
return appsDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteSavedAPKs() async {
|
||||||
|
(await getExternalStorageDirectory())
|
||||||
|
?.listSync()
|
||||||
|
.where((element) => element.path.endsWith('.apk'))
|
||||||
|
.forEach((element) {
|
||||||
|
element.deleteSync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadApps() async {
|
Future<void> loadApps() async {
|
||||||
loadingApps = true;
|
loadingApps = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@@ -186,7 +195,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<App>> getUpdates() async {
|
Future<List<App>> checkUpdates() async {
|
||||||
List<App> updates = [];
|
List<App> updates = [];
|
||||||
if (!gettingUpdates) {
|
if (!gettingUpdates) {
|
||||||
gettingUpdates = true;
|
gettingUpdates = true;
|
||||||
@@ -203,14 +212,16 @@ class AppsProvider with ChangeNotifier {
|
|||||||
return updates;
|
return updates;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> installUpdates() async {
|
List<String> getExistingUpdates() {
|
||||||
|
List<String> updateAppIds = [];
|
||||||
List<String> appIds = apps.keys.toList();
|
List<String> appIds = apps.keys.toList();
|
||||||
for (int i = 0; i < appIds.length; i++) {
|
for (int i = 0; i < appIds.length; i++) {
|
||||||
App? app = apps[appIds[i]]!.app;
|
App? app = apps[appIds[i]]!.app;
|
||||||
if (app.installedVersion != app.latestVersion) {
|
if (app.installedVersion != app.latestVersion) {
|
||||||
await downloadAndInstallLatestApp(app.id);
|
updateAppIds.add(app.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return updateAppIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@@ -23,6 +23,7 @@ class APKDetails {
|
|||||||
// App Source abstract class (diff. implementations for GitHub, GitLab, etc.)
|
// App Source abstract class (diff. implementations for GitHub, GitLab, etc.)
|
||||||
|
|
||||||
abstract class AppSource {
|
abstract class AppSource {
|
||||||
|
late String sourceId;
|
||||||
String standardizeURL(String url);
|
String standardizeURL(String url);
|
||||||
Future<APKDetails> getLatestAPKDetails(String standardUrl);
|
Future<APKDetails> getLatestAPKDetails(String standardUrl);
|
||||||
AppNames getAppNames(String standardUrl);
|
AppNames getAppNames(String standardUrl);
|
||||||
@@ -77,6 +78,9 @@ class App {
|
|||||||
// Specific App Source classes
|
// Specific App Source classes
|
||||||
|
|
||||||
class GitHub implements AppSource {
|
class GitHub implements AppSource {
|
||||||
|
@override
|
||||||
|
String sourceId = 'github';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String standardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp(r'^https?://github.com/[^/]*/[^/]*');
|
RegExp standardUrlRegEx = RegExp(r'^https?://github.com/[^/]*/[^/]*');
|
||||||
@@ -143,12 +147,15 @@ class SourceService {
|
|||||||
url.toLowerCase().indexOf('https://') != 0) {
|
url.toLowerCase().indexOf('https://') != 0) {
|
||||||
url = 'https://$url';
|
url = 'https://$url';
|
||||||
}
|
}
|
||||||
|
if (url.toLowerCase().indexOf('https://www.') == 0) {
|
||||||
|
url = 'https://${url.substring(12)}';
|
||||||
|
}
|
||||||
AppSource source = getSource(url);
|
AppSource source = getSource(url);
|
||||||
String standardUrl = source.standardizeURL(url);
|
String standardUrl = source.standardizeURL(url);
|
||||||
AppNames names = source.getAppNames(standardUrl);
|
AppNames names = source.getAppNames(standardUrl);
|
||||||
APKDetails apk = await source.getLatestAPKDetails(standardUrl);
|
APKDetails apk = await source.getLatestAPKDetails(standardUrl);
|
||||||
return App(
|
return App(
|
||||||
'${names.author}_${names.name}',
|
'${names.author}_${names.name}_${source.sourceId}',
|
||||||
standardUrl,
|
standardUrl,
|
||||||
names.author[0].toUpperCase() + names.author.substring(1),
|
names.author[0].toUpperCase() + names.author.substring(1),
|
||||||
names.name[0].toUpperCase() + names.name.substring(1),
|
names.name[0].toUpperCase() + names.name.substring(1),
|
||||||
|
12
pubspec.lock
12
pubspec.lock
@@ -91,7 +91,7 @@ packages:
|
|||||||
name: dbus
|
name: dbus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.7"
|
version: "0.7.8"
|
||||||
dynamic_color:
|
dynamic_color:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -119,7 +119,7 @@ packages:
|
|||||||
name: file
|
name: file
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.4"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -152,7 +152,7 @@ packages:
|
|||||||
name: flutter_local_notifications
|
name: flutter_local_notifications
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.7.0"
|
version: "9.8.0+1"
|
||||||
flutter_local_notifications_linux:
|
flutter_local_notifications_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -281,7 +281,7 @@ packages:
|
|||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.17"
|
version: "2.0.20"
|
||||||
path_provider_ios:
|
path_provider_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -552,7 +552,7 @@ packages:
|
|||||||
name: webview_flutter_platform_interface
|
name: webview_flutter_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.2"
|
||||||
webview_flutter_wkwebview:
|
webview_flutter_wkwebview:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -580,7 +580,7 @@ packages:
|
|||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+1"
|
version: "0.2.0+2"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -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
|
# 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
|
# 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.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.1.0+1
|
version: 0.1.2+3 # When changing this, update the tag in main() accordingly
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.19.0-79.0.dev <3.0.0'
|
sdk: '>=2.19.0-79.0.dev <3.0.0'
|
||||||
@@ -38,7 +38,7 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
flutter_fgbg: ^0.2.0 # Try removing reliance on this
|
flutter_fgbg: ^0.2.0 # Try removing reliance on this
|
||||||
flutter_local_notifications: ^9.7.0
|
flutter_local_notifications: ^9.8.0+1
|
||||||
provider: ^6.0.3
|
provider: ^6.0.3
|
||||||
http: ^0.13.5
|
http: ^0.13.5
|
||||||
webview_flutter: ^3.0.4
|
webview_flutter: ^3.0.4
|
||||||
|
Reference in New Issue
Block a user