From 5d72af5b2034e23e19529d0374f706ec955e9710 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 27 Sep 2024 18:28:37 -0400 Subject: [PATCH 1/5] Add Tencent App Store (#1848) --- README.md | 1 + lib/app_sources/tencent.dart | 78 ++++++++++++++++++++++++++++++ lib/providers/source_provider.dart | 14 ++++-- 3 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 lib/app_sources/tencent.dart diff --git a/README.md b/README.md index 15720dc6..471207f3 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Currently supported App sources: - [Aptoide](https://aptoide.com/) - [Uptodown](https://uptodown.com/) - [Huawei AppGallery](https://appgallery.huawei.com/) + - [Tencent App Store](https://sj.qq.com/) - Jenkins Jobs - [APKMirror](https://apkmirror.com/) (Track-Only) - Open Source - App-Specific: diff --git a/lib/app_sources/tencent.dart b/lib/app_sources/tencent.dart new file mode 100644 index 00000000..6552498c --- /dev/null +++ b/lib/app_sources/tencent.dart @@ -0,0 +1,78 @@ +import 'dart:convert'; + +import 'package:obtainium/custom_errors.dart'; +import 'package:obtainium/providers/source_provider.dart'; + +class Tencent extends AppSource { + Tencent() { + name = 'Tencent App Store'; + hosts = ['sj.qq.com']; + naiveStandardVersionDetection = true; + showReleaseDateAsVersionToggle = true; + } + + @override + String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) { + RegExp standardUrlRegEx = RegExp( + '^https?://${getSourceRegex(hosts)}/appdetail/[^/]+', + caseSensitive: false); + var match = standardUrlRegEx.firstMatch(url); + if (match == null) { + throw InvalidURLError(name); + } + return match.group(0)!; + } + + @override + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { + return Uri.parse(standardUrl).pathSegments.last; + } + + @override + Future getLatestAPKDetails( + String standardUrl, + Map additionalSettings, + ) async { + String appId = (await tryInferringAppId(standardUrl))!; + String baseHost = Uri.parse(standardUrl) + .host + .split('.') + .reversed + .toList() + .sublist(0, 2) + .reversed + .join('.'); + + var res = await sourceRequest( + 'https://upage.html5.$baseHost/wechat-apkinfo', additionalSettings, + followRedirects: false, postBody: {"packagename": appId}); + + if (res.statusCode == 200) { + var json = jsonDecode(res.body); + if (json['app_detail_records'][appId] == null) { + throw NoReleasesError(); + } + var version = + json['app_detail_records'][appId]['apk_all_data']['version_name']; + var apkUrl = json['app_detail_records'][appId]['apk_all_data']['url']; + if (apkUrl == null) { + throw NoAPKError(); + } + var appName = json['app_detail_records'][appId]['app_info']['name']; + var author = json['app_detail_records'][appId]['app_info']['author']; + var releaseDate = + json['app_detail_records'][appId]['app_info']['update_time']; + + return APKDetails( + version, + [MapEntry(Uri.parse(apkUrl).queryParameters['fsname']!, apkUrl)], + AppNames(author, appName), + releaseDate: releaseDate != null + ? DateTime.fromMillisecondsSinceEpoch(releaseDate * 1000) + : null); + } else { + throw getObtainiumHttpError(res); + } + } +} diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 43b90baf..3c592975 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -28,6 +28,7 @@ import 'package:obtainium/app_sources/sourceforge.dart'; import 'package:obtainium/app_sources/sourcehut.dart'; import 'package:obtainium/app_sources/steammobile.dart'; import 'package:obtainium/app_sources/telegramapp.dart'; +import 'package:obtainium/app_sources/tencent.dart'; import 'package:obtainium/app_sources/uptodown.dart'; import 'package:obtainium/app_sources/vlc.dart'; import 'package:obtainium/app_sources/whatsapp.dart'; @@ -465,19 +466,25 @@ abstract class AppSource { Future sourceRequest( String url, Map additionalSettings, - {bool followRedirects = true}) async { + {bool followRedirects = true, Object? postBody}) async { var requestHeaders = await getRequestHeaders(additionalSettings); if (requestHeaders != null || followRedirects == false) { - var req = Request('GET', Uri.parse(url)); + var req = Request(postBody == null ? 'GET' : 'POST', Uri.parse(url)); req.followRedirects = followRedirects; if (requestHeaders != null) { req.headers.addAll(requestHeaders); } + if (postBody != null) { + req.headers[HttpHeaders.contentTypeHeader] = 'application/json'; + req.body = jsonEncode(postBody); + } return Response.fromStream(await IOClient( createHttpClient(additionalSettings['allowInsecure'] == true)) .send(req)); } else { - return get(Uri.parse(url)); + return postBody == null + ? get(Uri.parse(url)) + : post(Uri.parse(url), body: jsonEncode(postBody)); } } @@ -782,6 +789,7 @@ class SourceProvider { Aptoide(), Uptodown(), HuaweiAppGallery(), + Tencent(), Jenkins(), APKMirror(), Signal(), From cad14dd6a4bc71481dee179c81abf6c7f6ac6e33 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 27 Sep 2024 18:34:52 -0400 Subject: [PATCH 2/5] Update Flutter, upgrade packages --- .flutter | 2 +- pubspec.lock | 64 ++++++++++++++++++++++++++-------------------------- pubspec.yaml | 4 ++-- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.flutter b/.flutter index 4cf269e3..2663184a 160000 --- a/.flutter +++ b/.flutter @@ -1 +1 @@ -Subproject commit 4cf269e36de2573851eaef3c763994f8f9be494d +Subproject commit 2663184aa79047d0a33a14a3b607954f8fdd8730 diff --git a/pubspec.lock b/pubspec.lock index b600543f..2137698d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -103,10 +103,10 @@ packages: dependency: "direct main" description: name: background_fetch - sha256: f910c1c7c67a55f242daf78e9e9835d26eb01d39fc7f5d77f57dd84d009a6bab + sha256: e9f26ae54d88310b7ac2a68f2f9fcee0081a4d5f11100f233a70702021e7ac4f url: "https://pub.dev" source: hosted - version: "1.3.6" + version: "1.3.7" boolean_selector: dependency: transitive description: @@ -303,18 +303,18 @@ packages: dependency: "direct main" description: name: flex_color_picker - sha256: "809af4ec82ede3b140ed0219b97d548de99e47aa4b99b14a10f705a2dbbcba5e" + sha256: "12dc855ae8ef5491f529b1fc52c655f06dcdf4114f1f7fdecafa41eec2ec8d79" url: "https://pub.dev" source: hosted - version: "3.5.1" + version: "3.6.0" flex_seed_scheme: dependency: transitive description: name: flex_seed_scheme - sha256: "7d97ba5c20f0e5cb1e3e2c17c865e1f797d129de31fc1f75d2dcce9470d6373c" + sha256: "7639d2c86268eff84a909026eb169f008064af0fb3696a651b24b0fa24a40334" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.4.1" flutter: dependency: "direct main" description: flutter @@ -388,26 +388,26 @@ packages: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77" url: "https://pub.dev" source: hosted - version: "0.13.1" + version: "0.14.1" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" flutter_local_notifications: dependency: "direct main" description: name: flutter_local_notifications - sha256: c500d5d9e7e553f06b61877ca6b9c8b92c570a4c8db371038702e8ce57f8a50f + sha256: "49eeef364fddb71515bc78d5a8c51435a68bccd6e4d68e25a942c5e47761ae71" url: "https://pub.dev" source: hosted - version: "17.2.2" + version: "17.2.3" flutter_local_notifications_linux: dependency: transitive description: @@ -433,10 +433,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: a23c41ee57573e62fc2190a1f36a0480c4d90bde3a8a8d7126e5d5992fb53fb7 + sha256: e17575ca576a34b46c58c91f9948891117a1bd97815d2e661813c7f90c647a78 url: "https://pub.dev" source: hosted - version: "0.7.3+1" + version: "0.7.3+2" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -571,10 +571,10 @@ packages: dependency: transitive description: name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" markdown: dependency: "direct main" description: @@ -723,10 +723,10 @@ packages: dependency: transitive description: name: permission_handler_platform_interface - sha256: fe0ffe274d665be8e34f9c59705441a7d248edebbe5d9e3ec2665f88b79358ea + sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9 url: "https://pub.dev" source: hosted - version: "4.2.2" + version: "4.2.3" permission_handler_windows: dependency: transitive description: @@ -913,18 +913,18 @@ packages: dependency: "direct main" description: name: sqflite - sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d + sha256: ff5a2436ef8ebdfda748fbfe957f9981524cb5ff11e7bafa8c42771840e8a788 url: "https://pub.dev" source: hosted - version: "2.3.3+1" + version: "2.3.3+2" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "7b41b6c3507854a159e24ae90a8e3e9cc01eb26a477c118d6dca065b5f55453e" + sha256: "2d8e607db72e9cb7748c9c6e739e2c9618320a5517de693d5a24609c4671b1a4" url: "https://pub.dev" source: hosted - version: "2.5.4+2" + version: "2.5.4+4" stack_trace: dependency: transitive description: @@ -953,10 +953,10 @@ packages: dependency: transitive description: name: synchronized - sha256: a824e842b8a054f91a728b783c177c1e4731f6b124f9192468457a8913371255 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.3.0+3" term_glyph: dependency: transitive description: @@ -1025,10 +1025,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" + sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.1" url_launcher_platform_interface: dependency: transitive description: @@ -1057,10 +1057,10 @@ packages: dependency: transitive description: name: uuid - sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77 + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.5.1" vector_math: dependency: transitive description: @@ -1081,10 +1081,10 @@ packages: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" webview_flutter: dependency: "direct main" description: @@ -1129,10 +1129,10 @@ packages: dependency: transitive description: name: win32_registry - sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6" + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.1.5" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3a66aed7..c9d0f8a9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -84,14 +84,14 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_launcher_icons: ^0.13.1 + flutter_launcher_icons: ^0.14.1 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^4.0.0 + flutter_lints: ^5.0.0 flutter_launcher_icons: android: "ic_launcher" From 77bebc48bcd878787e743ea9f9ca15eea2f9da3a Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 27 Sep 2024 19:01:57 -0400 Subject: [PATCH 3/5] Enable icon caching (#1837) --- lib/pages/app.dart | 3 ++- lib/pages/apps.dart | 2 ++ lib/providers/apps_provider.dart | 26 +++++++++++++++++++++++--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/pages/app.dart b/lib/pages/app.dart index cda1d8d4..00a4b2e4 100644 --- a/lib/pages/app.dart +++ b/lib/pages/app.dart @@ -248,7 +248,8 @@ class _AppPageState extends State { children: [ const SizedBox(height: 20), FutureBuilder( - future: appsProvider.updateAppIcon(app?.app.id), + future: + appsProvider.updateAppIcon(app?.app.id, ignoreCache: true), builder: (ctx, val) { return app?.icon != null ? Row( diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index d2086c9d..505a98db 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -416,6 +416,8 @@ class AppsPageState extends State { ? Image.memory( listedApps[appIndex].icon!, gaplessPlayback: true, + opacity: AlwaysStoppedAnimation( + listedApps[appIndex].installedInfo == null ? 0.6 : 1), ) : Row( mainAxisSize: MainAxisSize.min, diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 2f17835f..a444ab08 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -375,6 +375,7 @@ class AppsProvider with ChangeNotifier { late Stream? foregroundStream; late StreamSubscription? foregroundSubscription; late Directory APKDir; + late Directory iconsCacheDir; late SettingsProvider settingsProvider = SettingsProvider(); Iterable getAppValues() => apps.values.map((a) => a.deepCopy()); @@ -393,12 +394,21 @@ class AppsProvider with ChangeNotifier { var cacheDirs = await getExternalCacheDirectories(); if (cacheDirs?.isNotEmpty ?? false) { APKDir = cacheDirs!.first; + iconsCacheDir = Directory('${cacheDirs.first.path}/icons'); + if (!iconsCacheDir.existsSync()) { + iconsCacheDir.createSync(); + } } else { APKDir = Directory('${(await getExternalStorageDirectory())!.path}/apks'); if (!APKDir.existsSync()) { APKDir.createSync(); } + iconsCacheDir = + Directory('${(await getExternalStorageDirectory())!.path}/icons'); + if (!iconsCacheDir.existsSync()) { + iconsCacheDir.createSync(); + } } if (!isBg) { // Load Apps into memory (in background processes, this is done later instead of in the constructor) @@ -1297,10 +1307,16 @@ class AppsProvider with ChangeNotifier { notifyListeners(); } - Future updateAppIcon(String? appId) async { + Future updateAppIcon(String? appId, {bool ignoreCache = false}) async { if (apps[appId]?.icon == null) { - var icon = - (await apps[appId]?.installedInfo?.applicationInfo?.getAppIcon()); + var cachedIcon = File('${iconsCacheDir.path}/$appId.png'); + var alreadyCached = cachedIcon.existsSync() && !ignoreCache; + var icon = alreadyCached + ? (await cachedIcon.readAsBytes()) + : (await apps[appId]?.installedInfo?.applicationInfo?.getAppIcon()); + if (icon != null && !alreadyCached) { + cachedIcon.writeAsBytes(icon.toList()); + } if (icon != null) { apps.update( apps[appId]!.app.id, @@ -1351,6 +1367,10 @@ class AppsProvider with ChangeNotifier { if (file.existsSync()) { file.deleteSync(recursive: true); } + File iconFile = File('$iconsCacheDir/$appId.png'); + if (iconFile.existsSync()) { + iconFile.deleteSync(recursive: true); + } apkFiles .where( (element) => element.path.split('/').last.startsWith('$appId-')) From dcf69578424f0ab43a794f42160f5de113102383 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 27 Sep 2024 19:02:54 -0400 Subject: [PATCH 4/5] Remove unused code --- lib/providers/apps_provider.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index a444ab08..bea62d15 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -1367,10 +1367,6 @@ class AppsProvider with ChangeNotifier { if (file.existsSync()) { file.deleteSync(recursive: true); } - File iconFile = File('$iconsCacheDir/$appId.png'); - if (iconFile.existsSync()) { - iconFile.deleteSync(recursive: true); - } apkFiles .where( (element) => element.path.split('/').last.startsWith('$appId-')) From 10c558faaf9c76e6647ef6faf7c0257c36be9fb2 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 27 Sep 2024 19:03:17 -0400 Subject: [PATCH 5/5] Increment version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index c9d0f8a9..44371c60 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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 # 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. -version: 1.1.21+2278 +version: 1.1.22+2279 environment: sdk: '>=3.0.0 <4.0.0'