diff --git a/android/build.gradle b/android/build.gradle index 249165164c..47890036d0 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -24,6 +24,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index bd58abbe77..7a29ce4677 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -253,7 +253,9 @@ "privacy": "Privacy", "terms": "Terms", "checkForUpdates": "Check for updates", - "installManually": "Install manually", + "downloadUpdate": "Download", + "criticalUpdateAvailable": "Critical update available", + "updateAvailable": "Update available", "update": "Update", "checking": "Checking...", "youAreOnTheLatestVersion": "You are on the latest version", diff --git a/lib/store/offline_authenticator_db.dart b/lib/store/offline_authenticator_db.dart deleted file mode 100644 index 393e5302c7..0000000000 --- a/lib/store/offline_authenticator_db.dart +++ /dev/null @@ -1,170 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:ente_auth/models/authenticator/auth_entity.dart'; -import 'package:ente_auth/models/authenticator/local_auth_entity.dart'; -import 'package:flutter/foundation.dart'; -import 'package:path/path.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:sqflite/sqflite.dart'; - -class OfflineAuthenticatorDB { - static const _databaseName = "ente.offline_authenticator.db"; - static const _databaseVersion = 1; - - static const entityTable = 'entities'; - - OfflineAuthenticatorDB._privateConstructor(); - static final OfflineAuthenticatorDB instance = OfflineAuthenticatorDB._privateConstructor(); - - static Future? _dbFuture; - - Future get database async { - _dbFuture ??= _initDatabase(); - return _dbFuture!; - } - - Future _initDatabase() async { - final Directory documentsDirectory = - await getApplicationDocumentsDirectory(); - final String path = join(documentsDirectory.path, _databaseName); - debugPrint(path); - return await openDatabase( - path, - version: _databaseVersion, - onCreate: _onCreate, - ); - } - - Future _onCreate(Database db, int version) async { - await db.execute( - ''' - CREATE TABLE $entityTable ( - _generatedID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - id TEXT, - encryptedData TEXT NOT NULL, - header TEXT NOT NULL, - createdAt INTEGER NOT NULL, - updatedAt INTEGER NOT NULL, - shouldSync INTEGER DEFAULT 0, - UNIQUE(id) - ); - ''', - ); - } - - Future insert(String encData, String header) async { - final db = await instance.database; - final int timeInMicroSeconds = DateTime.now().microsecondsSinceEpoch; - final insertedID = await db.insert( - entityTable, - { - "encryptedData": encData, - "header": header, - "shouldSync": 1, - "createdAt": timeInMicroSeconds, - "updatedAt": timeInMicroSeconds, - }, - ); - return insertedID; - } - - Future updateEntry( - int generatedID, - String encData, - String header, - ) async { - final db = await instance.database; - final int timeInMicroSeconds = DateTime.now().microsecondsSinceEpoch; - int affectedRows = await db.update( - entityTable, - { - "encryptedData": encData, - "header": header, - "shouldSync": 1, - "updatedAt": timeInMicroSeconds, - }, - where: '_generatedID = ?', - whereArgs: [generatedID], - ); - return affectedRows; - } - - Future insertOrReplace(List authEntities) async { - final db = await instance.database; - final batch = db.batch(); - for (AuthEntity authEntity in authEntities) { - final insertRow = authEntity.toMap(); - insertRow.remove('isDeleted'); - insertRow.putIfAbsent('shouldSync', () => 0); - batch.insert( - entityTable, - insertRow, - conflictAlgorithm: ConflictAlgorithm.replace, - ); - } - await batch.commit(noResult: true); - } - - Future updateLocalEntity(LocalAuthEntity localAuthEntity) async { - final db = await instance.database; - await db.update( - entityTable, - localAuthEntity.toMap(), - where: '_generatedID = ?', - whereArgs: [localAuthEntity.generatedID], - conflictAlgorithm: ConflictAlgorithm.replace, - ); - } - - Future getEntryByID(int genID) async { - final db = await instance.database; - final rows = await db - .query(entityTable, where: '_generatedID = ?', whereArgs: [genID]); - final listOfAuthEntities = _convertRows(rows); - if (listOfAuthEntities.isEmpty) { - return null; - } else { - return listOfAuthEntities.first; - } - } - - Future> getAll() async { - final db = await instance.database; - final rows = await db.rawQuery("SELECT * from $entityTable"); - return _convertRows(rows); - } - -// deleteByID will prefer generated id if both ids are passed during deletion - Future deleteByIDs({List? generatedIDs, List? ids}) async { - final db = await instance.database; - final batch = db.batch(); - const whereGenID = '_generatedID = ?'; - const whereID = 'id = ?'; - if (generatedIDs != null) { - for (int genId in generatedIDs) { - batch.delete(entityTable, where: whereGenID, whereArgs: [genId]); - } - } - if (ids != null) { - for (String id in ids) { - batch.delete(entityTable, where: whereID, whereArgs: [id]); - } - } - final result = await batch.commit(); - debugPrint("Done"); - } - - Future clearTable() async { - final db = await instance.database; - await db.delete(entityTable); - } - - List _convertRows(List> rows) { - final keys = []; - for (final row in rows) { - keys.add(LocalAuthEntity.fromMap(row)); - } - return keys; - } -} diff --git a/lib/ui/settings/app_update_dialog.dart b/lib/ui/settings/app_update_dialog.dart index 39fa04bc86..eba357285b 100644 --- a/lib/ui/settings/app_update_dialog.dart +++ b/lib/ui/settings/app_update_dialog.dart @@ -1,10 +1,11 @@ - +import 'dart:io'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/update_service.dart'; +import 'package:ente_auth/theme/ente_theme.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:open_filex/open_filex.dart'; @@ -22,6 +23,7 @@ class AppUpdateDialog extends StatefulWidget { class _AppUpdateDialogState extends State { @override Widget build(BuildContext context) { + final enteTextTheme = getEnteTextTheme(context); final List changelog = []; for (final log in widget.latestVersionInfo!.changelog) { changelog.add( @@ -43,18 +45,19 @@ class _AppUpdateDialogState extends State { Text( widget.latestVersionInfo!.name!, style: const TextStyle( - fontSize: 20, + fontSize: 14, fontWeight: FontWeight.bold, ), ), const Padding(padding: EdgeInsets.all(8)), - const Text( - "Changelog", - style: TextStyle( - fontSize: 18, + if (changelog.isNotEmpty) + const Text( + "Changelog", + style: TextStyle( + fontSize: 18, + ), ), - ), - const Padding(padding: EdgeInsets.all(4)), + if (changelog.isNotEmpty) const Padding(padding: EdgeInsets.all(4)), Column( crossAxisAlignment: CrossAxisAlignment.start, children: changelog, @@ -71,36 +74,13 @@ class _AppUpdateDialogState extends State { }, ), ), - onPressed: () async { - Navigator.pop(context); - showDialog( - context: context, - builder: (BuildContext context) { - return ApkDownloaderDialog(widget.latestVersionInfo); - }, - barrierDismissible: false, - ); - }, - child: const Text( - "Update", - ), - - ), - ), - const Padding(padding: EdgeInsets.all(8)), - Center( - child: InkWell( - child: Text( - context.l10n.installManually, - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(decoration: TextDecoration.underline), - ), - onTap: () => launchUrlString( + onPressed: () => launchUrlString( widget.latestVersionInfo!.url!, mode: LaunchMode.externalApplication, ), + child: Text( + context.l10n.downloadUpdate, + ), ), ), ], @@ -110,8 +90,24 @@ class _AppUpdateDialogState extends State { return WillPopScope( onWillPop: () async => !shouldForceUpdate, child: AlertDialog( - title: Text( - shouldForceUpdate ? "Critical update available" : "Update available", + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + Icons.auto_awesome_outlined, + size: 24, + color: getEnteColorScheme(context).strokeMuted, + ), + const SizedBox( + height: 16, + ), + Text( + shouldForceUpdate + ? context.l10n.criticalUpdateAvailable + : context.l10n.updateAvailable, + style: enteTextTheme.h3Bold, + ), + ], ), content: content, ), @@ -166,15 +162,17 @@ class _ApkDownloaderDialogState extends State { Future _downloadApk() async { try { - await Network.instance.getDio().download( - widget.versionInfo!.url!, - _saveUrl, - onReceiveProgress: (count, _) { - setState(() { - _downloadProgress = count / widget.versionInfo!.size!; - }); - }, - ); + if (!File(_saveUrl!).existsSync()) { + await Network.instance.getDio().download( + widget.versionInfo!.url!, + _saveUrl, + onReceiveProgress: (count, _) { + setState(() { + _downloadProgress = count / widget.versionInfo!.size!; + }); + }, + ); + } Navigator.of(context, rootNavigator: true).pop('dialog'); OpenFilex.open(_saveUrl); } catch (e) {