Compare commits

...

7 Commits

Author SHA1 Message Date
Imran Remtulla
e3e945d13b Bugfix - Obtainium doesn't update with other Apps 2022-10-01 00:29:15 -04:00
Imran Remtulla
61f7f171b1 Upgraded a package 2022-09-30 23:23:23 -04:00
Imran Remtulla
de07583161 Fixed issue with backgorund task not starting 2022-09-30 23:21:35 -04:00
Imran Remtulla
49b9a65053 Updated version 2022-09-30 15:37:32 -04:00
Imran Remtulla
aebc8aed76 Clearer GitHub PAT instructions 2022-09-30 15:33:24 -04:00
Imran Remtulla
3958425c22 Removed outdated comment 2022-09-29 23:28:49 -04:00
Imran Remtulla
0a560871cb Fixed update checking on App page 2022-09-29 23:20:57 -04:00
8 changed files with 95 additions and 28 deletions

View File

@@ -1,9 +1,11 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
class GitHub implements AppSource { class GitHub implements AppSource {
@override @override
@@ -137,7 +139,7 @@ class GitHub implements AppSource {
@override @override
List<GeneratedFormItem> moreSourceSettingsFormItems = [ List<GeneratedFormItem> moreSourceSettingsFormItems = [
GeneratedFormItem( GeneratedFormItem(
label: 'GitHub Credentials (Increases Rate Limit)', label: 'GitHub Personal Access Token (Increases Rate Limit)',
id: 'github-creds', id: 'github-creds',
required: false, required: false,
additionalValidators: [ additionalValidators: [
@@ -153,6 +155,23 @@ class GitHub implements AppSource {
} }
return null; return null;
} }
],
hint: 'username:token',
belowWidgets: [
const SizedBox(
height: 8,
),
GestureDetector(
onTap: () {
launchUrlString(
'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token',
mode: LaunchMode.externalApplication);
},
child: const Text(
'About GitHub PATs',
style: TextStyle(
decoration: TextDecoration.underline, fontSize: 12),
))
]) ])
]; ];
} }

View File

@@ -11,6 +11,8 @@ class GeneratedFormItem {
late int max; late int max;
late List<String? Function(String? value)> additionalValidators; late List<String? Function(String? value)> additionalValidators;
late String id; late String id;
late List<Widget> belowWidgets;
late String? hint;
GeneratedFormItem( GeneratedFormItem(
{this.label = 'Input', {this.label = 'Input',
@@ -18,7 +20,9 @@ class GeneratedFormItem {
this.required = true, this.required = true,
this.max = 1, this.max = 1,
this.additionalValidators = const [], this.additionalValidators = const [],
this.id = 'input'}); this.id = 'input',
this.belowWidgets = const [],
this.hint});
} }
class GeneratedForm extends StatefulWidget { class GeneratedForm extends StatefulWidget {
@@ -91,7 +95,8 @@ class _GeneratedFormState extends State<GeneratedForm> {
}); });
}, },
decoration: InputDecoration( decoration: InputDecoration(
helperText: e.value.label + (e.value.required ? ' *' : '')), helperText: e.value.label + (e.value.required ? ' *' : ''),
hintText: e.value.hint),
minLines: e.value.max <= 1 ? null : e.value.max, minLines: e.value.max <= 1 ? null : e.value.max,
maxLines: e.value.max <= 1 ? 1 : e.value.max, maxLines: e.value.max <= 1 ? 1 : e.value.max,
validator: (value) { validator: (value) {
@@ -157,7 +162,13 @@ class _GeneratedFormState extends State<GeneratedForm> {
width: 20, width: 20,
)); ));
} }
rowItems.add(Expanded(child: rowInput.value)); rowItems.add(Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
rowInput.value,
...widget.items[rowInputs.key][rowInput.key].belowWidgets
])));
}); });
rows.add(rowItems); rows.add(rowItems);
}); });

View File

@@ -14,7 +14,7 @@ import 'package:dynamic_color/dynamic_color.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
const String currentReleaseTag = const String currentReleaseTag =
'v0.5.0-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES 'v0.5.4-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
const String bgUpdateCheckTaskName = 'bg-update-check'; const String bgUpdateCheckTaskName = 'bg-update-check';
@@ -28,8 +28,6 @@ bgUpdateCheck(int? ignoreAfterMicroseconds) async {
var appsProvider = AppsProvider(); var appsProvider = AppsProvider();
await notificationsProvider.cancel(ErrorCheckingUpdatesNotification('').id); await notificationsProvider.cancel(ErrorCheckingUpdatesNotification('').id);
await appsProvider.loadApps(); await appsProvider.loadApps();
// List<String> existingUpdateIds = // TODO: Uncomment this and below when it works
// appsProvider.getExistingUpdates(installedOnly: true);
List<String> existingUpdateIds = List<String> existingUpdateIds =
appsProvider.getExistingUpdates(installedOnly: true); appsProvider.getExistingUpdates(installedOnly: true);
DateTime nextIgnoreAfter = DateTime.now(); DateTime nextIgnoreAfter = DateTime.now();
@@ -37,7 +35,6 @@ bgUpdateCheck(int? ignoreAfterMicroseconds) async {
await appsProvider.checkUpdates(ignoreAfter: ignoreAfter); await appsProvider.checkUpdates(ignoreAfter: ignoreAfter);
} catch (e) { } catch (e) {
if (e is RateLimitError) { if (e is RateLimitError) {
// Ignore these (scheduling another task as below does not work)
String nextTaskName = String nextTaskName =
'$bgUpdateCheckTaskName-${nextIgnoreAfter.microsecondsSinceEpoch.toString()}'; '$bgUpdateCheckTaskName-${nextIgnoreAfter.microsecondsSinceEpoch.toString()}';
Workmanager().registerOneOffTask(nextTaskName, nextTaskName, Workmanager().registerOneOffTask(nextTaskName, nextTaskName,
@@ -53,11 +50,13 @@ bgUpdateCheck(int? ignoreAfterMicroseconds) async {
.where((id) => !existingUpdateIds.contains(id)) .where((id) => !existingUpdateIds.contains(id))
.map((e) => appsProvider.apps[e]!.app) .map((e) => appsProvider.apps[e]!.app)
.toList(); .toList();
// TODO: This silent update code doesn't work yet
// List<String> silentlyUpdated = await appsProvider // List<String> silentlyUpdated = await appsProvider
// .downloadAndInstallLatestApp( // .downloadAndInstallLatestApp(
// [...newUpdates.map((e) => e.id), ...existingUpdateIds], null); // [...newUpdates.map((e) => e.id), ...existingUpdateIds], null);
// if (silentlyUpdated.isNotEmpty) { // if (silentlyUpdated.isNotEmpty) {
// newUpdates // newUpdates = newUpdates
// .where((element) => !silentlyUpdated.contains(element.id)) // .where((element) => !silentlyUpdated.contains(element.id))
// .toList(); // .toList();
// notificationsProvider.notify( // notificationsProvider.notify(
@@ -65,6 +64,7 @@ bgUpdateCheck(int? ignoreAfterMicroseconds) async {
// silentlyUpdated.map((e) => appsProvider.apps[e]!.app).toList()), // silentlyUpdated.map((e) => appsProvider.apps[e]!.app).toList()),
// cancelExisting: true); // cancelExisting: true);
// } // }
if (newUpdates.isNotEmpty) { if (newUpdates.isNotEmpty) {
notificationsProvider.notify(UpdateNotification(newUpdates), notificationsProvider.notify(UpdateNotification(newUpdates),
cancelExisting: true); cancelExisting: true);
@@ -108,14 +108,21 @@ void main() async {
ChangeNotifierProvider(create: (context) => SettingsProvider()), ChangeNotifierProvider(create: (context) => SettingsProvider()),
Provider(create: (context) => NotificationsProvider()) Provider(create: (context) => NotificationsProvider())
], ],
child: const MyApp(), child: const Obtainium(),
)); ));
} }
var defaultThemeColour = Colors.deepPurple; var defaultThemeColour = Colors.deepPurple;
class MyApp extends StatelessWidget { class Obtainium extends StatefulWidget {
const MyApp({super.key}); const Obtainium({super.key});
@override
State<Obtainium> createState() => _ObtainiumState();
}
class _ObtainiumState extends State<Obtainium> {
var existingUpdateInterval = -1;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -144,18 +151,21 @@ class MyApp extends StatelessWidget {
]); ]);
} }
// Register the background update task according to the user's setting // Register the background update task according to the user's setting
if (settingsProvider.updateInterval == 0) { if (existingUpdateInterval != settingsProvider.updateInterval) {
Workmanager().cancelByUniqueName(bgUpdateCheckTaskName); existingUpdateInterval = settingsProvider.updateInterval;
} else { if (existingUpdateInterval == 0) {
Workmanager().registerPeriodicTask( Workmanager().cancelByUniqueName(bgUpdateCheckTaskName);
bgUpdateCheckTaskName, bgUpdateCheckTaskName, } else {
frequency: Duration(minutes: settingsProvider.updateInterval), Workmanager().registerPeriodicTask(
initialDelay: Duration(minutes: settingsProvider.updateInterval), bgUpdateCheckTaskName, bgUpdateCheckTaskName,
constraints: Constraints(networkType: NetworkType.connected), frequency: Duration(minutes: existingUpdateInterval),
existingWorkPolicy: ExistingWorkPolicy.keep, initialDelay: Duration(minutes: existingUpdateInterval),
backoffPolicy: BackoffPolicy.linear, constraints: Constraints(networkType: NetworkType.connected),
backoffPolicyDelay: existingWorkPolicy: ExistingWorkPolicy.replace,
const Duration(minutes: minUpdateIntervalMinutes)); backoffPolicy: BackoffPolicy.linear,
backoffPolicyDelay:
const Duration(minutes: minUpdateIntervalMinutes));
}
} }
} }

View File

@@ -8,6 +8,7 @@ import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:obtainium/app_sources/github.dart';
import 'package:obtainium/providers/notifications_provider.dart'; import 'package:obtainium/providers/notifications_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@@ -194,6 +195,26 @@ class AppsProvider with ChangeNotifier {
} }
} }
// If Obtainium is being installed, it should be the last one
List<ApkFile> moveObtainiumToEnd(List<ApkFile> items) {
String obtainiumId = 'imranr98_obtainium_${GitHub().host}';
ApkFile? temp;
items.removeWhere((element) {
bool res = element.appId == obtainiumId;
if (res) {
temp = element;
}
return res;
});
if (temp != null) {
items.add(temp!);
}
return items;
}
silentUpdates = moveObtainiumToEnd(silentUpdates);
regularInstalls = moveObtainiumToEnd(regularInstalls);
for (var u in silentUpdates) { for (var u in silentUpdates) {
await installApk(u); await installApk(u);
} }
@@ -293,6 +314,11 @@ class AppsProvider with ChangeNotifier {
} }
await saveApps([newApp]); await saveApps([newApp]);
return newApp; return newApp;
} else if ((newApp.lastUpdateCheck?.microsecondsSinceEpoch ?? 0) -
(currentApp.lastUpdateCheck?.microsecondsSinceEpoch ?? 0) >
5000000) {
currentApp.lastUpdateCheck = newApp.lastUpdateCheck;
await saveApps([currentApp]);
} }
return null; return null;
} }
@@ -306,7 +332,7 @@ class AppsProvider with ChangeNotifier {
if (ignoreAfter != null) { if (ignoreAfter != null) {
appIds = appIds appIds = appIds
.where((id) => .where((id) =>
apps[id]!.app.lastUpdateCheck != null && apps[id]!.app.lastUpdateCheck == null ||
apps[id]!.app.lastUpdateCheck!.isBefore(ignoreAfter)) apps[id]!.app.lastUpdateCheck!.isBefore(ignoreAfter))
.toList(); .toList();
} }

View File

@@ -129,5 +129,6 @@ class SettingsProvider with ChangeNotifier {
void setSettingString(String settingId, String value) { void setSettingString(String settingId, String value) {
prefs?.setString(settingId, value); prefs?.setString(settingId, value);
notifyListeners();
} }
} }

View File

@@ -407,7 +407,7 @@ packages:
name: permission_handler_android name: permission_handler_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "10.0.0" version: "10.1.0"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:

View File

@@ -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.5.0+21 # When changing this, update the tag in main() accordingly version: 0.5.4+25 # 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'

View File

@@ -13,7 +13,7 @@ import 'package:obtainium/main.dart';
void main() { void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async { testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame. // Build our app and trigger a frame.
await tester.pumpWidget(const MyApp()); await tester.pumpWidget(const Obtainium());
// Verify that our counter starts at 0. // Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget); expect(find.text('0'), findsOneWidget);