Repo created
This commit is contained in:
parent
2fd78f5dc9
commit
489cf0b2ea
148 changed files with 30898 additions and 2 deletions
2612
lib/providers/apps_provider.dart
Normal file
2612
lib/providers/apps_provider.dart
Normal file
File diff suppressed because it is too large
Load diff
127
lib/providers/logs_provider.dart
Normal file
127
lib/providers/logs_provider.dart
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
const String logTable = 'logs';
|
||||
const String idColumn = '_id';
|
||||
const String levelColumn = 'level';
|
||||
const String messageColumn = 'message';
|
||||
const String timestampColumn = 'timestamp';
|
||||
const String dbPath = 'logs.db';
|
||||
|
||||
enum LogLevels { debug, info, warning, error }
|
||||
|
||||
class Log {
|
||||
int? id;
|
||||
late LogLevels level;
|
||||
late String message;
|
||||
DateTime timestamp = DateTime.now();
|
||||
|
||||
Map<String, Object?> toMap() {
|
||||
var map = <String, Object?>{
|
||||
idColumn: id,
|
||||
levelColumn: level.index,
|
||||
messageColumn: message,
|
||||
timestampColumn: timestamp.millisecondsSinceEpoch,
|
||||
};
|
||||
return map;
|
||||
}
|
||||
|
||||
Log(this.message, this.level);
|
||||
|
||||
Log.fromMap(Map<String, Object?> map) {
|
||||
id = map[idColumn] as int;
|
||||
level = LogLevels.values.elementAt(map[levelColumn] as int);
|
||||
message = map[messageColumn] as String;
|
||||
timestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||
map[timestampColumn] as int,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '${timestamp.toString()}: ${level.name}: $message';
|
||||
}
|
||||
}
|
||||
|
||||
class LogsProvider {
|
||||
LogsProvider({bool runDefaultClear = true}) {
|
||||
clear(before: DateTime.now().subtract(const Duration(days: 7)));
|
||||
}
|
||||
|
||||
Database? db;
|
||||
|
||||
Future<Database> getDB() async {
|
||||
db ??= await openDatabase(
|
||||
dbPath,
|
||||
version: 1,
|
||||
onCreate: (Database db, int version) async {
|
||||
await db.execute('''
|
||||
create table if not exists $logTable (
|
||||
$idColumn integer primary key autoincrement,
|
||||
$levelColumn integer not null,
|
||||
$messageColumn text not null,
|
||||
$timestampColumn integer not null)
|
||||
''');
|
||||
},
|
||||
);
|
||||
return db!;
|
||||
}
|
||||
|
||||
Future<Log> add(String message, {LogLevels level = LogLevels.info}) async {
|
||||
Log l = Log(message, level);
|
||||
l.id = await (await getDB()).insert(logTable, l.toMap());
|
||||
if (kDebugMode) {
|
||||
print(l);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
Future<List<Log>> get({DateTime? before, DateTime? after}) async {
|
||||
var where = getWhereDates(before: before, after: after);
|
||||
return (await (await getDB()).query(
|
||||
logTable,
|
||||
where: where.key,
|
||||
whereArgs: where.value,
|
||||
)).map((e) => Log.fromMap(e)).toList();
|
||||
}
|
||||
|
||||
Future<int> clear({DateTime? before, DateTime? after}) async {
|
||||
var where = getWhereDates(before: before, after: after);
|
||||
var res = await (await getDB()).delete(
|
||||
logTable,
|
||||
where: where.key,
|
||||
whereArgs: where.value,
|
||||
);
|
||||
if (res > 0) {
|
||||
add(
|
||||
plural(
|
||||
'clearedNLogsBeforeXAfterY',
|
||||
res,
|
||||
namedArgs: {'before': before.toString(), 'after': after.toString()},
|
||||
name: 'n',
|
||||
),
|
||||
);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
MapEntry<String?, List<int>?> getWhereDates({
|
||||
DateTime? before,
|
||||
DateTime? after,
|
||||
}) {
|
||||
List<String> where = [];
|
||||
List<int> whereArgs = [];
|
||||
if (before != null) {
|
||||
where.add('$timestampColumn < ?');
|
||||
whereArgs.add(before.millisecondsSinceEpoch);
|
||||
}
|
||||
if (after != null) {
|
||||
where.add('$timestampColumn > ?');
|
||||
whereArgs.add(after.millisecondsSinceEpoch);
|
||||
}
|
||||
return whereArgs.isEmpty
|
||||
? const MapEntry(null, null)
|
||||
: MapEntry(where.join(' and '), whereArgs);
|
||||
}
|
||||
22
lib/providers/native_provider.dart
Normal file
22
lib/providers/native_provider.dart
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:android_system_font/android_system_font.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class NativeFeatures {
|
||||
static bool _systemFontLoaded = false;
|
||||
|
||||
static Future<ByteData> _readFileBytes(String path) async {
|
||||
var bytes = await File(path).readAsBytes();
|
||||
return ByteData.view(bytes.buffer);
|
||||
}
|
||||
|
||||
static Future loadSystemFont() async {
|
||||
if (_systemFontLoaded) return;
|
||||
var fontLoader = FontLoader('SystemFont');
|
||||
var fontFilePath = await AndroidSystemFont().getFilePath();
|
||||
fontLoader.addFont(_readFileBytes(fontFilePath!));
|
||||
fontLoader.load();
|
||||
_systemFontLoaded = true;
|
||||
}
|
||||
}
|
||||
323
lib/providers/notifications_provider.dart
Normal file
323
lib/providers/notifications_provider.dart
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
// Exposes functions that can be used to send notifications to the user
|
||||
// Contains a set of pre-defined ObtainiumNotification objects that should be used throughout the app
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:obtainium/main.dart';
|
||||
import 'package:obtainium/providers/settings_provider.dart';
|
||||
import 'package:obtainium/providers/source_provider.dart';
|
||||
|
||||
class ObtainiumNotification {
|
||||
late int id;
|
||||
late String title;
|
||||
late String message;
|
||||
late String channelCode;
|
||||
late String channelName;
|
||||
late String channelDescription;
|
||||
Importance importance;
|
||||
int? progPercent;
|
||||
bool onlyAlertOnce;
|
||||
String? payload;
|
||||
|
||||
ObtainiumNotification(
|
||||
this.id,
|
||||
this.title,
|
||||
this.message,
|
||||
this.channelCode,
|
||||
this.channelName,
|
||||
this.channelDescription,
|
||||
this.importance, {
|
||||
this.onlyAlertOnce = false,
|
||||
this.progPercent,
|
||||
this.payload,
|
||||
});
|
||||
}
|
||||
|
||||
class UpdateNotification extends ObtainiumNotification {
|
||||
UpdateNotification(List<App> updates, {int? id})
|
||||
: super(
|
||||
id ?? 2,
|
||||
tr('updatesAvailable'),
|
||||
'',
|
||||
'UPDATES_AVAILABLE',
|
||||
tr('updatesAvailableNotifChannel'),
|
||||
tr('updatesAvailableNotifDescription'),
|
||||
Importance.max,
|
||||
) {
|
||||
message = updates.isEmpty
|
||||
? tr('noNewUpdates')
|
||||
: updates.length == 1
|
||||
? tr('xHasAnUpdate', args: [updates[0].finalName])
|
||||
: plural(
|
||||
'xAndNMoreUpdatesAvailable',
|
||||
updates.length - 1,
|
||||
args: [updates[0].finalName, (updates.length - 1).toString()],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SilentUpdateNotification extends ObtainiumNotification {
|
||||
SilentUpdateNotification(List<App> updates, bool succeeded, {int? id})
|
||||
: super(
|
||||
id ?? 3,
|
||||
succeeded ? tr('appsUpdated') : tr('appsNotUpdated'),
|
||||
'',
|
||||
'APPS_UPDATED',
|
||||
tr('appsUpdatedNotifChannel'),
|
||||
tr('appsUpdatedNotifDescription'),
|
||||
Importance.defaultImportance,
|
||||
) {
|
||||
message = updates.length == 1
|
||||
? tr(
|
||||
succeeded ? 'xWasUpdatedToY' : 'xWasNotUpdatedToY',
|
||||
args: [updates[0].finalName, updates[0].latestVersion],
|
||||
)
|
||||
: plural(
|
||||
succeeded ? 'xAndNMoreUpdatesInstalled' : "xAndNMoreUpdatesFailed",
|
||||
updates.length - 1,
|
||||
args: [updates[0].finalName, (updates.length - 1).toString()],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SilentUpdateAttemptNotification extends ObtainiumNotification {
|
||||
SilentUpdateAttemptNotification(List<App> updates, {int? id})
|
||||
: super(
|
||||
id ?? 3,
|
||||
tr('appsPossiblyUpdated'),
|
||||
'',
|
||||
'APPS_POSSIBLY_UPDATED',
|
||||
tr('appsPossiblyUpdatedNotifChannel'),
|
||||
tr('appsPossiblyUpdatedNotifDescription'),
|
||||
Importance.defaultImportance,
|
||||
) {
|
||||
message = updates.length == 1
|
||||
? tr(
|
||||
'xWasPossiblyUpdatedToY',
|
||||
args: [updates[0].finalName, updates[0].latestVersion],
|
||||
)
|
||||
: plural(
|
||||
'xAndNMoreUpdatesPossiblyInstalled',
|
||||
updates.length - 1,
|
||||
args: [updates[0].finalName, (updates.length - 1).toString()],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorCheckingUpdatesNotification extends ObtainiumNotification {
|
||||
ErrorCheckingUpdatesNotification(String error, {int? id})
|
||||
: super(
|
||||
id ?? 5,
|
||||
tr('errorCheckingUpdates'),
|
||||
error,
|
||||
'BG_UPDATE_CHECK_ERROR',
|
||||
tr('errorCheckingUpdatesNotifChannel'),
|
||||
tr('errorCheckingUpdatesNotifDescription'),
|
||||
Importance.high,
|
||||
payload: "${tr('errorCheckingUpdates')}\n$error",
|
||||
);
|
||||
}
|
||||
|
||||
class AppsRemovedNotification extends ObtainiumNotification {
|
||||
AppsRemovedNotification(List<List<String>> namedReasons)
|
||||
: super(
|
||||
6,
|
||||
tr('appsRemoved'),
|
||||
'',
|
||||
'APPS_REMOVED',
|
||||
tr('appsRemovedNotifChannel'),
|
||||
tr('appsRemovedNotifDescription'),
|
||||
Importance.max,
|
||||
) {
|
||||
message = '';
|
||||
for (var r in namedReasons) {
|
||||
message += '${tr('xWasRemovedDueToErrorY', args: [r[0], r[1]])} \n';
|
||||
}
|
||||
message = message.trim();
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadNotification extends ObtainiumNotification {
|
||||
DownloadNotification(String appName, int progPercent)
|
||||
: super(
|
||||
appName.hashCode,
|
||||
tr('downloadingX', args: [appName]),
|
||||
'',
|
||||
'APP_DOWNLOADING',
|
||||
tr('downloadingXNotifChannel', args: [tr('app')]),
|
||||
tr('downloadNotifDescription'),
|
||||
Importance.low,
|
||||
onlyAlertOnce: true,
|
||||
progPercent: progPercent,
|
||||
);
|
||||
}
|
||||
|
||||
class DownloadedNotification extends ObtainiumNotification {
|
||||
DownloadedNotification(String fileName, String downloadUrl)
|
||||
: super(
|
||||
downloadUrl.hashCode,
|
||||
tr('downloadedX', args: [fileName]),
|
||||
'',
|
||||
'FILE_DOWNLOADED',
|
||||
tr('downloadedXNotifChannel', args: [tr('app')]),
|
||||
tr('downloadedX', args: [tr('app')]),
|
||||
Importance.defaultImportance,
|
||||
);
|
||||
}
|
||||
|
||||
final completeInstallationNotification = ObtainiumNotification(
|
||||
1,
|
||||
tr('completeAppInstallation'),
|
||||
tr('obtainiumMustBeOpenToInstallApps'),
|
||||
'COMPLETE_INSTALL',
|
||||
tr('completeAppInstallationNotifChannel'),
|
||||
tr('completeAppInstallationNotifDescription'),
|
||||
Importance.max,
|
||||
);
|
||||
|
||||
class CheckingUpdatesNotification extends ObtainiumNotification {
|
||||
CheckingUpdatesNotification(String appName)
|
||||
: super(
|
||||
4,
|
||||
tr('checkingForUpdates'),
|
||||
appName,
|
||||
'BG_UPDATE_CHECK',
|
||||
tr('checkingForUpdatesNotifChannel'),
|
||||
tr('checkingForUpdatesNotifDescription'),
|
||||
Importance.min,
|
||||
);
|
||||
}
|
||||
|
||||
class NotificationsProvider {
|
||||
FlutterLocalNotificationsPlugin notifications =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
bool isInitialized = false;
|
||||
|
||||
Map<Importance, Priority> importanceToPriority = {
|
||||
Importance.defaultImportance: Priority.defaultPriority,
|
||||
Importance.high: Priority.high,
|
||||
Importance.low: Priority.low,
|
||||
Importance.max: Priority.max,
|
||||
Importance.min: Priority.min,
|
||||
Importance.none: Priority.min,
|
||||
Importance.unspecified: Priority.defaultPriority,
|
||||
};
|
||||
|
||||
Future<void> initialize() async {
|
||||
isInitialized =
|
||||
await notifications.initialize(
|
||||
const InitializationSettings(
|
||||
android: AndroidInitializationSettings('ic_notification'),
|
||||
),
|
||||
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
||||
_showNotificationPayload(response.payload);
|
||||
},
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
Future<void> checkLaunchByNotif() async {
|
||||
final NotificationAppLaunchDetails? launchDetails = await notifications
|
||||
.getNotificationAppLaunchDetails();
|
||||
if (launchDetails?.didNotificationLaunchApp ?? false) {
|
||||
_showNotificationPayload(
|
||||
launchDetails!.notificationResponse?.payload,
|
||||
doublePop: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _showNotificationPayload(String? payload, {bool doublePop = false}) {
|
||||
if (payload?.isNotEmpty == true) {
|
||||
var title = (payload ?? '\n\n').split('\n').first;
|
||||
var content = (payload ?? '\n\n').split('\n').sublist(1).join('\n');
|
||||
globalNavigatorKey.currentState?.push(
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, _, __) => AlertDialog(
|
||||
title: Text(title),
|
||||
content: Text(content),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(null);
|
||||
if (doublePop) {
|
||||
Navigator.of(context).pop(null);
|
||||
}
|
||||
},
|
||||
child: Text(tr('ok')),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cancel(int id) async {
|
||||
if (!isInitialized) {
|
||||
await initialize();
|
||||
}
|
||||
await notifications.cancel(id);
|
||||
}
|
||||
|
||||
Future<void> notifyRaw(
|
||||
int id,
|
||||
String title,
|
||||
String message,
|
||||
String channelCode,
|
||||
String channelName,
|
||||
String channelDescription,
|
||||
Importance importance, {
|
||||
bool cancelExisting = false,
|
||||
int? progPercent,
|
||||
bool onlyAlertOnce = false,
|
||||
String? payload,
|
||||
}) async {
|
||||
if (cancelExisting) {
|
||||
await cancel(id);
|
||||
}
|
||||
if (!isInitialized) {
|
||||
await initialize();
|
||||
}
|
||||
await notifications.show(
|
||||
id,
|
||||
title,
|
||||
message,
|
||||
NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
channelCode,
|
||||
channelName,
|
||||
channelDescription: channelDescription,
|
||||
importance: importance,
|
||||
priority: importanceToPriority[importance]!,
|
||||
groupKey: '$obtainiumId.$channelCode',
|
||||
progress: progPercent ?? 0,
|
||||
maxProgress: 100,
|
||||
showProgress: progPercent != null,
|
||||
onlyAlertOnce: onlyAlertOnce,
|
||||
indeterminate: progPercent != null && progPercent < 0,
|
||||
),
|
||||
),
|
||||
payload: payload,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> notify(
|
||||
ObtainiumNotification notif, {
|
||||
bool cancelExisting = false,
|
||||
}) => notifyRaw(
|
||||
notif.id,
|
||||
notif.title,
|
||||
notif.message,
|
||||
notif.channelCode,
|
||||
notif.channelName,
|
||||
notif.channelDescription,
|
||||
notif.importance,
|
||||
cancelExisting: cancelExisting,
|
||||
onlyAlertOnce: notif.onlyAlertOnce,
|
||||
progPercent: notif.progPercent,
|
||||
payload: notif.payload,
|
||||
);
|
||||
}
|
||||
525
lib/providers/settings_provider.dart
Normal file
525
lib/providers/settings_provider.dart
Normal file
|
|
@ -0,0 +1,525 @@
|
|||
// Exposes functions used to save/load app settings
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:obtainium/app_sources/github.dart';
|
||||
import 'package:obtainium/main.dart';
|
||||
import 'package:obtainium/providers/apps_provider.dart';
|
||||
import 'package:obtainium/providers/source_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:shared_storage/shared_storage.dart' as saf;
|
||||
|
||||
String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}';
|
||||
String obtainiumId = 'dev.imranr.obtainium';
|
||||
String obtainiumUrl = 'https://github.com/ImranR98/Obtainium';
|
||||
Color obtainiumThemeColor = const Color(0xFF6438B5);
|
||||
|
||||
enum ThemeSettings { system, light, dark }
|
||||
|
||||
enum SortColumnSettings { added, nameAuthor, authorName, releaseDate }
|
||||
|
||||
enum SortOrderSettings { ascending, descending }
|
||||
|
||||
class SettingsProvider with ChangeNotifier {
|
||||
SharedPreferences? prefs;
|
||||
String? defaultAppDir;
|
||||
bool justStarted = true;
|
||||
|
||||
String sourceUrl = 'https://github.com/ImranR98/Obtainium';
|
||||
|
||||
// Not done in constructor as we want to be able to await it
|
||||
Future<void> initializeSettings() async {
|
||||
prefs = await SharedPreferences.getInstance();
|
||||
defaultAppDir = (await getAppStorageDir()).path;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get useSystemFont {
|
||||
return prefs?.getBool('useSystemFont') ?? false;
|
||||
}
|
||||
|
||||
set useSystemFont(bool useSystemFont) {
|
||||
prefs?.setBool('useSystemFont', useSystemFont);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get useShizuku {
|
||||
return prefs?.getBool('useShizuku') ?? false;
|
||||
}
|
||||
|
||||
set useShizuku(bool useShizuku) {
|
||||
prefs?.setBool('useShizuku', useShizuku);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
ThemeSettings get theme {
|
||||
return ThemeSettings.values[prefs?.getInt('theme') ??
|
||||
ThemeSettings.system.index];
|
||||
}
|
||||
|
||||
set theme(ThemeSettings t) {
|
||||
prefs?.setInt('theme', t.index);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Color get themeColor {
|
||||
int? colorCode = prefs?.getInt('themeColor');
|
||||
return (colorCode != null) ? Color(colorCode) : obtainiumThemeColor;
|
||||
}
|
||||
|
||||
set themeColor(Color themeColor) {
|
||||
prefs?.setInt('themeColor', themeColor.value);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get useMaterialYou {
|
||||
return prefs?.getBool('useMaterialYou') ?? false;
|
||||
}
|
||||
|
||||
set useMaterialYou(bool useMaterialYou) {
|
||||
prefs?.setBool('useMaterialYou', useMaterialYou);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get useBlackTheme {
|
||||
return prefs?.getBool('useBlackTheme') ?? false;
|
||||
}
|
||||
|
||||
set useBlackTheme(bool useBlackTheme) {
|
||||
prefs?.setBool('useBlackTheme', useBlackTheme);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
int get updateInterval {
|
||||
return prefs?.getInt('updateInterval') ?? 360;
|
||||
}
|
||||
|
||||
set updateInterval(int min) {
|
||||
prefs?.setInt('updateInterval', min);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
double get updateIntervalSliderVal {
|
||||
return prefs?.getDouble('updateIntervalSliderVal') ?? 6.0;
|
||||
}
|
||||
|
||||
set updateIntervalSliderVal(double val) {
|
||||
prefs?.setDouble('updateIntervalSliderVal', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get checkOnStart {
|
||||
return prefs?.getBool('checkOnStart') ?? false;
|
||||
}
|
||||
|
||||
set checkOnStart(bool checkOnStart) {
|
||||
prefs?.setBool('checkOnStart', checkOnStart);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SortColumnSettings get sortColumn {
|
||||
return SortColumnSettings.values[prefs?.getInt('sortColumn') ??
|
||||
SortColumnSettings.nameAuthor.index];
|
||||
}
|
||||
|
||||
set sortColumn(SortColumnSettings s) {
|
||||
prefs?.setInt('sortColumn', s.index);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SortOrderSettings get sortOrder {
|
||||
return SortOrderSettings.values[prefs?.getInt('sortOrder') ??
|
||||
SortOrderSettings.ascending.index];
|
||||
}
|
||||
|
||||
set sortOrder(SortOrderSettings s) {
|
||||
prefs?.setInt('sortOrder', s.index);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool checkAndFlipFirstRun() {
|
||||
bool result = prefs?.getBool('firstRun') ?? true;
|
||||
if (result) {
|
||||
prefs?.setBool('firstRun', false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool get welcomeShown {
|
||||
return prefs?.getBool('welcomeShown') ?? false;
|
||||
}
|
||||
|
||||
set welcomeShown(bool welcomeShown) {
|
||||
prefs?.setBool('welcomeShown', welcomeShown);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool checkJustStarted() {
|
||||
if (justStarted) {
|
||||
justStarted = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> getInstallPermission({bool enforce = false}) async {
|
||||
while (!(await Permission.requestInstallPackages.isGranted)) {
|
||||
// Explicit request as InstallPlugin request sometimes bugged
|
||||
Fluttertoast.showToast(
|
||||
msg: tr('pleaseAllowInstallPerm'),
|
||||
toastLength: Toast.LENGTH_LONG,
|
||||
);
|
||||
if ((await Permission.requestInstallPackages.request()) ==
|
||||
PermissionStatus.granted) {
|
||||
return true;
|
||||
}
|
||||
if (!enforce) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get showAppWebpage {
|
||||
return prefs?.getBool('showAppWebpage') ?? false;
|
||||
}
|
||||
|
||||
set showAppWebpage(bool show) {
|
||||
prefs?.setBool('showAppWebpage', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get pinUpdates {
|
||||
return prefs?.getBool('pinUpdates') ?? true;
|
||||
}
|
||||
|
||||
set pinUpdates(bool show) {
|
||||
prefs?.setBool('pinUpdates', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get buryNonInstalled {
|
||||
return prefs?.getBool('buryNonInstalled') ?? false;
|
||||
}
|
||||
|
||||
set buryNonInstalled(bool show) {
|
||||
prefs?.setBool('buryNonInstalled', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get groupByCategory {
|
||||
return prefs?.getBool('groupByCategory') ?? false;
|
||||
}
|
||||
|
||||
set groupByCategory(bool show) {
|
||||
prefs?.setBool('groupByCategory', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get hideTrackOnlyWarning {
|
||||
return prefs?.getBool('hideTrackOnlyWarning') ?? false;
|
||||
}
|
||||
|
||||
set hideTrackOnlyWarning(bool show) {
|
||||
prefs?.setBool('hideTrackOnlyWarning', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get hideAPKOriginWarning {
|
||||
return prefs?.getBool('hideAPKOriginWarning') ?? false;
|
||||
}
|
||||
|
||||
set hideAPKOriginWarning(bool show) {
|
||||
prefs?.setBool('hideAPKOriginWarning', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
String? getSettingString(String settingId) {
|
||||
String? str = prefs?.getString(settingId);
|
||||
return str?.isNotEmpty == true ? str : null;
|
||||
}
|
||||
|
||||
void setSettingString(String settingId, String value) {
|
||||
prefs?.setString(settingId, value);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool? getSettingBool(String settingId) {
|
||||
return prefs?.getBool(settingId) ?? false;
|
||||
}
|
||||
|
||||
void setSettingBool(String settingId, bool value) {
|
||||
prefs?.setBool(settingId, value);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Map<String, int> get categories =>
|
||||
Map<String, int>.from(jsonDecode(prefs?.getString('categories') ?? '{}'));
|
||||
|
||||
void setCategories(Map<String, int> cats, {AppsProvider? appsProvider}) {
|
||||
if (appsProvider != null) {
|
||||
List<App> changedApps = appsProvider
|
||||
.getAppValues()
|
||||
.map((a) {
|
||||
var n1 = a.app.categories.length;
|
||||
a.app.categories.removeWhere((c) => !cats.keys.contains(c));
|
||||
return n1 > a.app.categories.length ? a.app : null;
|
||||
})
|
||||
.where((element) => element != null)
|
||||
.map((e) => e as App)
|
||||
.toList();
|
||||
if (changedApps.isNotEmpty) {
|
||||
appsProvider.saveApps(changedApps);
|
||||
}
|
||||
}
|
||||
prefs?.setString('categories', jsonEncode(cats));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Locale? get forcedLocale {
|
||||
var flSegs = prefs?.getString('forcedLocale')?.split('-');
|
||||
var fl = flSegs != null && flSegs.isNotEmpty
|
||||
? Locale(flSegs[0], flSegs.length > 1 ? flSegs[1] : null)
|
||||
: null;
|
||||
var set = supportedLocales.where((element) => element.key == fl).isNotEmpty
|
||||
? fl
|
||||
: null;
|
||||
return set;
|
||||
}
|
||||
|
||||
set forcedLocale(Locale? fl) {
|
||||
if (fl == null) {
|
||||
prefs?.remove('forcedLocale');
|
||||
} else if (supportedLocales
|
||||
.where((element) => element.key == fl)
|
||||
.isNotEmpty) {
|
||||
prefs?.setString('forcedLocale', fl.toLanguageTag());
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool setEqual(Set<String> a, Set<String> b) =>
|
||||
a.length == b.length && a.union(b).length == a.length;
|
||||
|
||||
void resetLocaleSafe(BuildContext context) {
|
||||
if (context.supportedLocales.contains(context.deviceLocale)) {
|
||||
context.resetLocale();
|
||||
} else {
|
||||
context.setLocale(context.fallbackLocale!);
|
||||
context.deleteSaveLocale();
|
||||
}
|
||||
}
|
||||
|
||||
bool get removeOnExternalUninstall {
|
||||
return prefs?.getBool('removeOnExternalUninstall') ?? false;
|
||||
}
|
||||
|
||||
set removeOnExternalUninstall(bool show) {
|
||||
prefs?.setBool('removeOnExternalUninstall', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get checkUpdateOnDetailPage {
|
||||
return prefs?.getBool('checkUpdateOnDetailPage') ?? true;
|
||||
}
|
||||
|
||||
set checkUpdateOnDetailPage(bool show) {
|
||||
prefs?.setBool('checkUpdateOnDetailPage', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get disablePageTransitions {
|
||||
return prefs?.getBool('disablePageTransitions') ?? false;
|
||||
}
|
||||
|
||||
set disablePageTransitions(bool show) {
|
||||
prefs?.setBool('disablePageTransitions', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get reversePageTransitions {
|
||||
return prefs?.getBool('reversePageTransitions') ?? false;
|
||||
}
|
||||
|
||||
set reversePageTransitions(bool show) {
|
||||
prefs?.setBool('reversePageTransitions', show);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get enableBackgroundUpdates {
|
||||
return prefs?.getBool('enableBackgroundUpdates') ?? true;
|
||||
}
|
||||
|
||||
set enableBackgroundUpdates(bool val) {
|
||||
prefs?.setBool('enableBackgroundUpdates', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get bgUpdatesOnWiFiOnly {
|
||||
return prefs?.getBool('bgUpdatesOnWiFiOnly') ?? false;
|
||||
}
|
||||
|
||||
set bgUpdatesOnWiFiOnly(bool val) {
|
||||
prefs?.setBool('bgUpdatesOnWiFiOnly', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get bgUpdatesWhileChargingOnly {
|
||||
return prefs?.getBool('bgUpdatesWhileChargingOnly') ?? false;
|
||||
}
|
||||
|
||||
set bgUpdatesWhileChargingOnly(bool val) {
|
||||
prefs?.setBool('bgUpdatesWhileChargingOnly', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
DateTime get lastCompletedBGCheckTime {
|
||||
int? temp = prefs?.getInt('lastCompletedBGCheckTime');
|
||||
return temp != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(temp)
|
||||
: DateTime.fromMillisecondsSinceEpoch(0);
|
||||
}
|
||||
|
||||
set lastCompletedBGCheckTime(DateTime val) {
|
||||
prefs?.setInt('lastCompletedBGCheckTime', val.millisecondsSinceEpoch);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get showDebugOpts {
|
||||
return prefs?.getBool('showDebugOpts') ?? false;
|
||||
}
|
||||
|
||||
set showDebugOpts(bool val) {
|
||||
prefs?.setBool('showDebugOpts', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get highlightTouchTargets {
|
||||
return prefs?.getBool('highlightTouchTargets') ?? false;
|
||||
}
|
||||
|
||||
set highlightTouchTargets(bool val) {
|
||||
prefs?.setBool('highlightTouchTargets', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<Uri?> getExportDir() async {
|
||||
var uriString = prefs?.getString('exportDir');
|
||||
if (uriString != null) {
|
||||
Uri? uri = Uri.parse(uriString);
|
||||
if (!(await saf.canRead(uri) ?? false) ||
|
||||
!(await saf.canWrite(uri) ?? false)) {
|
||||
uri = null;
|
||||
prefs?.remove('exportDir');
|
||||
notifyListeners();
|
||||
}
|
||||
return uri;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> pickExportDir({bool remove = false}) async {
|
||||
var existingSAFPerms = (await saf.persistedUriPermissions()) ?? [];
|
||||
var currentOneWayDataSyncDir = await getExportDir();
|
||||
Uri? newOneWayDataSyncDir;
|
||||
if (!remove) {
|
||||
newOneWayDataSyncDir = (await saf.openDocumentTree());
|
||||
}
|
||||
if (currentOneWayDataSyncDir?.path != newOneWayDataSyncDir?.path) {
|
||||
if (newOneWayDataSyncDir == null) {
|
||||
prefs?.remove('exportDir');
|
||||
} else {
|
||||
prefs?.setString('exportDir', newOneWayDataSyncDir.toString());
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
for (var e in existingSAFPerms) {
|
||||
await saf.releasePersistableUriPermission(e.uri);
|
||||
}
|
||||
}
|
||||
|
||||
bool get autoExportOnChanges {
|
||||
return prefs?.getBool('autoExportOnChanges') ?? false;
|
||||
}
|
||||
|
||||
set autoExportOnChanges(bool val) {
|
||||
prefs?.setBool('autoExportOnChanges', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get onlyCheckInstalledOrTrackOnlyApps {
|
||||
return prefs?.getBool('onlyCheckInstalledOrTrackOnlyApps') ?? false;
|
||||
}
|
||||
|
||||
set onlyCheckInstalledOrTrackOnlyApps(bool val) {
|
||||
prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
int get exportSettings {
|
||||
try {
|
||||
return prefs?.getInt('exportSettings') ??
|
||||
1; // 0 for no, 1 for yes but no secrets, 2 for everything
|
||||
} catch (e) {
|
||||
var val = prefs?.getBool('exportSettings') == true ? 1 : 0;
|
||||
prefs?.setInt('exportSettings', val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
set exportSettings(int val) {
|
||||
prefs?.setInt('exportSettings', val > 2 || val < 0 ? 1 : val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get parallelDownloads {
|
||||
return prefs?.getBool('parallelDownloads') ?? false;
|
||||
}
|
||||
|
||||
set parallelDownloads(bool val) {
|
||||
prefs?.setBool('parallelDownloads', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<String> get searchDeselected {
|
||||
return prefs?.getStringList('searchDeselected') ??
|
||||
SourceProvider().sources.map((s) => s.name).toList();
|
||||
}
|
||||
|
||||
set searchDeselected(List<String> list) {
|
||||
prefs?.setStringList('searchDeselected', list);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get beforeNewInstallsShareToAppVerifier {
|
||||
return prefs?.getBool('beforeNewInstallsShareToAppVerifier') ?? true;
|
||||
}
|
||||
|
||||
set beforeNewInstallsShareToAppVerifier(bool val) {
|
||||
prefs?.setBool('beforeNewInstallsShareToAppVerifier', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get shizukuPretendToBeGooglePlay {
|
||||
return prefs?.getBool('shizukuPretendToBeGooglePlay') ?? false;
|
||||
}
|
||||
|
||||
set shizukuPretendToBeGooglePlay(bool val) {
|
||||
prefs?.setBool('shizukuPretendToBeGooglePlay', val);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get useFGService {
|
||||
return prefs?.getBool('useFGService') ?? false;
|
||||
}
|
||||
|
||||
set useFGService(bool val) {
|
||||
prefs?.setBool('useFGService', val);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
1331
lib/providers/source_provider.dart
Normal file
1331
lib/providers/source_provider.dart
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue