diff --git a/lib/main.dart b/lib/main.dart index a2eb7cc..4d5207c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:obtainium/pages/home.dart'; import 'package:obtainium/services/apps_provider.dart'; import 'package:obtainium/services/source_service.dart'; @@ -30,6 +31,10 @@ void backgroundUpdateCheck() { void main() async { WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent), + ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); Workmanager().initialize( backgroundUpdateCheck, ); diff --git a/lib/pages/app.dart b/lib/pages/app.dart index 795b34a..aca64c8 100644 --- a/lib/pages/app.dart +++ b/lib/pages/app.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:obtainium/services/apps_provider.dart'; -import 'package:obtainium/services/source_service.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:provider/provider.dart'; @@ -23,77 +22,82 @@ class _AppPageState extends State { } return Scaffold( appBar: AppBar( - title: Text('${app!.app.author}/${app.app.name}'), + title: Text('${app?.app.author}/${app?.app.name}'), ), body: WebView( - initialUrl: app.app.url, - ), - bottomSheet: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: ElevatedButton( - onPressed: (app.app.installedVersion == null || - appsProvider.checkAppObjectForUpdate( - app.app)) && - app.downloadProgress == null - ? () { - appsProvider.downloadAndInstallLatestApp( - app.app.id); - } - : null, - child: Text(app.app.installedVersion == null - ? 'Install' - : 'Update'))), - const SizedBox(width: 16.0), - ElevatedButton( - onPressed: app.downloadProgress != null - ? null - : () { - showDialog( - context: context, - builder: (BuildContext ctx) { - return AlertDialog( - title: const Text('Remove App?'), - content: Text( - 'This will remove \'${app.app.name}\' from Obtainium.${app.app.installedVersion != null ? '\n\nNote that while Obtainium will no longer track its updates, the App will remain installed.' : ''}'), - actions: [ - TextButton( - onPressed: () { - appsProvider - .removeApp(app.app.id) - .then((_) { - int count = 0; - Navigator.of(context).popUntil( - (_) => count++ >= 2); - }); - }, - child: const Text('Remove')), - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancel')) - ], - ); - }); - }, - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).errorColor, - surfaceTintColor: Theme.of(context).errorColor), - child: const Text('Remove'), - ), - ])), - if (app.downloadProgress != null) - LinearProgressIndicator(value: app.downloadProgress) - ], + initialUrl: app?.app.url, ), + bottomSheet: Padding( + padding: EdgeInsets.fromLTRB( + 0, 0, 0, MediaQuery.of(context).padding.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: ElevatedButton( + onPressed: (app?.app.installedVersion == null || + appsProvider + .checkAppObjectForUpdate( + app!.app)) && + app?.downloadProgress == null + ? () { + appsProvider + .downloadAndInstallLatestApp( + app!.app.id); + } + : null, + child: Text(app?.app.installedVersion == null + ? 'Install' + : 'Update'))), + const SizedBox(width: 16.0), + ElevatedButton( + onPressed: app?.downloadProgress != null + ? null + : () { + showDialog( + context: context, + builder: (BuildContext ctx) { + return AlertDialog( + title: const Text('Remove App?'), + content: Text( + 'This will remove \'${app?.app.name}\' from Obtainium.${app?.app.installedVersion != null ? '\n\nNote that while Obtainium will no longer track its updates, the App will remain installed.' : ''}'), + actions: [ + TextButton( + onPressed: () { + appsProvider + .removeApp(app!.app.id) + .then((_) { + int count = 0; + Navigator.of(context) + .popUntil((_) => + count++ >= 2); + }); + }, + child: const Text('Remove')), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel')) + ], + ); + }); + }, + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).errorColor, + surfaceTintColor: Theme.of(context).errorColor), + child: const Text('Remove'), + ), + ])), + if (app?.downloadProgress != null) + LinearProgressIndicator(value: app!.downloadProgress! / 100) + ], + )), ); } } diff --git a/lib/pages/home.dart b/lib/pages/home.dart index ff78f00..d4ca4cd 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -21,21 +21,21 @@ class _HomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Obtainium')), - body: pages.elementAt(selectedIndex), - bottomNavigationBar: NavigationBar( - destinations: const [ - NavigationDestination( - icon: Icon(Icons.settings), label: 'Settings'), - NavigationDestination(icon: Icon(Icons.apps), label: 'Apps'), - NavigationDestination(icon: Icon(Icons.add), label: 'Add App'), - ], - onDestinationSelected: (int index) { - setState(() { - selectedIndex = index; - }); - }, - selectedIndex: selectedIndex, - )); + appBar: AppBar(title: const Text('Obtainium')), + body: pages.elementAt(selectedIndex), + bottomNavigationBar: NavigationBar( + destinations: const [ + NavigationDestination(icon: Icon(Icons.settings), label: 'Settings'), + NavigationDestination(icon: Icon(Icons.apps), label: 'Apps'), + NavigationDestination(icon: Icon(Icons.add), label: 'Add App'), + ], + onDestinationSelected: (int index) { + setState(() { + selectedIndex = index; + }); + }, + selectedIndex: selectedIndex, + ), + ); } } diff --git a/lib/services/apps_provider.dart b/lib/services/apps_provider.dart index 02c3eeb..793df32 100644 --- a/lib/services/apps_provider.dart +++ b/lib/services/apps_provider.dart @@ -72,7 +72,10 @@ class AppsProvider with ChangeNotifier { StreamedResponse response = await Client().send(Request('GET', Uri.parse(apps[appId]!.app.apkUrl))); File downloadFile = - File('${(await getTemporaryDirectory()).path}/$appId.apk'); + File('${(await getExternalStorageDirectory())!.path}/apks/$appId.apk'); + if (downloadFile.existsSync()) { + downloadFile.deleteSync(); + } var length = response.contentLength; var received = 0; var sink = downloadFile.openWrite(); @@ -94,14 +97,13 @@ class AppsProvider with ChangeNotifier { throw response.reasonPhrase ?? 'Unknown Error'; } - var res = await InstallPlugin.installApk( - downloadFile.path, 'dev.imranr.obtainium'); - print(res); + // Unfortunately this 'await' does not actually wait for the APK to finish installing + // So we only know that the install prompt was shown, but the user could still cancel w/o us knowing + // This also does not use the 'session-based' installer API, so background/silent updates are impossible + await InstallPlugin.installApk(downloadFile.path, 'dev.imranr.obtainium'); apps[appId]!.app.installedVersion = apps[appId]!.app.latestVersion; saveApp(apps[appId]!.app); - - downloadFile.deleteSync(); } Future getAppsDir() async {