From e9080614af17e04c641f2782bc3c98b2384da1e9 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Mon, 25 Dec 2023 20:04:34 +0530 Subject: [PATCH] Drop dependency upon Fetch2 library Signed-off-by: Aayush Gupta --- app/build.gradle.kts | 3 - app/src/main/AndroidManifest.xml | 8 - .../com/aurora/store/AuroraApplication.kt | 8 - .../store/data/downloader/DownloadManager.kt | 92 --- .../store/data/downloader/RequestBuilder.kt | 71 -- .../data/downloader/RequestGroupIdBuilder.kt | 54 -- .../aurora/store/data/model/DownloadFile.kt | 39 - .../com/aurora/store/data/model/UpdateFile.kt | 49 -- .../data/receiver/DownloadCancelReceiver.kt | 40 - .../data/receiver/DownloadPauseReceiver.kt | 40 - .../data/receiver/DownloadResumeReceiver.kt | 40 - .../data/receiver/PackageManagerReceiver.kt | 13 - .../data/service/AppMetadataStatusListener.kt | 7 - .../store/data/service/NotificationService.kt | 234 ------ .../store/data/service/SelfUpdateService.kt | 189 ----- .../store/data/service/UpdateService.kt | 760 ------------------ .../com/aurora/store/util/NotificationUtil.kt | 158 ---- .../store/view/ui/sheets/DownloadMenuSheet.kt | 5 - 18 files changed, 1810 deletions(-) delete mode 100644 app/src/main/java/com/aurora/store/data/downloader/DownloadManager.kt delete mode 100644 app/src/main/java/com/aurora/store/data/downloader/RequestBuilder.kt delete mode 100644 app/src/main/java/com/aurora/store/data/downloader/RequestGroupIdBuilder.kt delete mode 100644 app/src/main/java/com/aurora/store/data/model/DownloadFile.kt delete mode 100644 app/src/main/java/com/aurora/store/data/model/UpdateFile.kt delete mode 100644 app/src/main/java/com/aurora/store/data/receiver/DownloadCancelReceiver.kt delete mode 100644 app/src/main/java/com/aurora/store/data/receiver/DownloadPauseReceiver.kt delete mode 100644 app/src/main/java/com/aurora/store/data/receiver/DownloadResumeReceiver.kt delete mode 100644 app/src/main/java/com/aurora/store/data/service/AppMetadataStatusListener.kt delete mode 100644 app/src/main/java/com/aurora/store/data/service/NotificationService.kt delete mode 100644 app/src/main/java/com/aurora/store/data/service/SelfUpdateService.kt delete mode 100644 app/src/main/java/com/aurora/store/data/service/UpdateService.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 87fea133d..26aac19fa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -156,9 +156,6 @@ dependencies { //HTTP Clients implementation("com.squareup.okhttp3:okhttp:4.12.0") - //Fetch - Downloader - implementation("androidx.tonyodev.fetch2:xfetch2:3.1.6") - //EventBus implementation("org.greenrobot:eventbus:3.3.1") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b09d0debf..a648d4eac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -97,11 +97,6 @@ android:exported="false" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> - - - - - - - - diff --git a/app/src/main/java/com/aurora/store/AuroraApplication.kt b/app/src/main/java/com/aurora/store/AuroraApplication.kt index f6dfdf591..96ca17199 100644 --- a/app/src/main/java/com/aurora/store/AuroraApplication.kt +++ b/app/src/main/java/com/aurora/store/AuroraApplication.kt @@ -25,14 +25,11 @@ import androidx.core.content.ContextCompat import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import com.aurora.extensions.isPAndAbove -import com.aurora.store.data.downloader.DownloadManager import com.aurora.store.data.receiver.PackageManagerReceiver -import com.aurora.store.data.service.NotificationService import com.aurora.store.util.CommonUtil import com.aurora.store.util.DownloadWorkerUtil import com.aurora.store.util.NotificationUtil import com.aurora.store.util.PackageUtil -import com.tonyodev.fetch2.Fetch import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject import org.lsposed.hiddenapibypass.HiddenApiBypass @@ -52,8 +49,6 @@ class AuroraApplication : Application(), Configuration.Provider { .setWorkerFactory(workerFactory) .build() - private lateinit var fetch: Fetch - companion object { val enqueuedInstalls: MutableSet = mutableSetOf() } @@ -69,9 +64,6 @@ class AuroraApplication : Application(), Configuration.Provider { //Create Notification Channels : General & Alert NotificationUtil.createNotificationChannel(this) - NotificationService.startService(this) - - fetch = DownloadManager.with(this).fetch // Initialize DownloadWorker to observe and trigger downloads downloadWorkerUtil.init() diff --git a/app/src/main/java/com/aurora/store/data/downloader/DownloadManager.kt b/app/src/main/java/com/aurora/store/data/downloader/DownloadManager.kt deleted file mode 100644 index bfb43b443..000000000 --- a/app/src/main/java/com/aurora/store/data/downloader/DownloadManager.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ - -package com.aurora.store.data.downloader - -import android.content.Context -import com.aurora.store.data.SingletonHolder -import com.aurora.store.util.Preferences -import com.tonyodev.fetch2.BuildConfig -import com.tonyodev.fetch2.Download -import com.tonyodev.fetch2.Fetch -import com.tonyodev.fetch2.FetchConfiguration -import com.tonyodev.fetch2.FetchGroup -import com.tonyodev.fetch2.FetchListener -import com.tonyodev.fetch2core.DefaultStorageResolver -import com.tonyodev.fetch2core.getFileTempDir - -class DownloadManager private constructor(var context: Context) { - - companion object : SingletonHolder(::DownloadManager) - - var fetch: Fetch - - init { - fetch = Fetch.getInstance(getFetchConfiguration(context)) - } - - fun getFetchInstance(): Fetch { - return fetch - } - - private fun getFetchConfiguration(context: Context): FetchConfiguration { - return FetchConfiguration.Builder(context) - .enableLogging(BuildConfig.DEBUG) - .enableHashCheck(true) - .enableFileExistChecks(true) - .enableRetryOnNetworkGain(true) - .enableAutoStart(true) - .setAutoRetryMaxAttempts(3) - .setProgressReportingInterval(3000) - .setNamespace(BuildConfig.APPLICATION_ID) - .setStorageResolver(DefaultStorageResolver(context, getFileTempDir(context))) - .build() - } - - fun isDownloading(fetchGroup: FetchGroup): Boolean { - return fetchGroup.downloadingDownloads.isNotEmpty() - || fetchGroup.queuedDownloads.isNotEmpty() - || fetchGroup.addedDownloads.isNotEmpty() - } - - fun isCanceled(fetchGroup: FetchGroup): Boolean { - return fetchGroup.cancelledDownloads.isNotEmpty() - || fetchGroup.removedDownloads.isNotEmpty() - || fetchGroup.deletedDownloads.isNotEmpty() - } - - fun updateOngoingDownloads( - fetch: Fetch, packageList: MutableList, download: Download, - fetchListener: FetchListener? - ) { - if (packageList.contains(download.tag)) { - val packageName = download.tag - if (packageName != null) { - val groupIDsOfPackageName = RequestGroupIdBuilder.getGroupIDsForApp(context, packageName.hashCode()) - groupIDsOfPackageName.forEach { - fetch.deleteGroup(it) - } - packageList.remove(packageName) - } - } - if (packageList.size == 0) { - fetch.removeListener(fetchListener!!) - } - } -} diff --git a/app/src/main/java/com/aurora/store/data/downloader/RequestBuilder.kt b/app/src/main/java/com/aurora/store/data/downloader/RequestBuilder.kt deleted file mode 100644 index adba57b56..000000000 --- a/app/src/main/java/com/aurora/store/data/downloader/RequestBuilder.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ - -package com.aurora.store.data.downloader - -import android.content.Context -import com.aurora.Constants -import com.aurora.gplayapi.data.models.App -import com.aurora.gplayapi.data.models.File -import com.aurora.store.util.PathUtil -import com.aurora.store.util.Preferences -import com.google.gson.GsonBuilder -import com.tonyodev.fetch2.EnqueueAction -import com.tonyodev.fetch2.NetworkType -import com.tonyodev.fetch2.Request -import com.tonyodev.fetch2core.Extras -import java.lang.reflect.Modifier - -private fun Request.attachMetaData(context: Context, app: App) { - val isWifiOnly = Preferences.getBoolean(context, Preferences.PREFERENCE_DOWNLOAD_WIFI_ONLY) - - apply { - groupId = app.getGroupId(context) - tag = app.packageName - enqueueAction = EnqueueAction.UPDATE_ACCORDINGLY - networkType = if (isWifiOnly) NetworkType.WIFI_ONLY else NetworkType.ALL - } -} - -private fun Request.attachExtra(app: App) { - val stringMap: MutableMap = mutableMapOf() - val gson = GsonBuilder() - .excludeFieldsWithModifiers(Modifier.TRANSIENT) - .create() - stringMap[Constants.STRING_EXTRA] = gson.toJson(app) - apply { - extras = Extras(stringMap) - } -} - -object RequestBuilder { - - fun buildRequest(context: Context, app: App, file: File): Request { - val fileName = when (file.type) { - File.FileType.BASE, - File.FileType.SPLIT -> PathUtil.getApkDownloadFile(context, app.packageName, app.versionCode, file) - File.FileType.OBB, - File.FileType.PATCH -> PathUtil.getObbDownloadFile(app.packageName, file) - } - return Request(file.url, fileName).apply { - attachMetaData(context, app) - attachExtra(app) - } - } -} diff --git a/app/src/main/java/com/aurora/store/data/downloader/RequestGroupIdBuilder.kt b/app/src/main/java/com/aurora/store/data/downloader/RequestGroupIdBuilder.kt deleted file mode 100644 index d94d098a4..000000000 --- a/app/src/main/java/com/aurora/store/data/downloader/RequestGroupIdBuilder.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.aurora.store.data.downloader - -import android.content.Context -import com.aurora.gplayapi.data.models.App -import com.aurora.store.util.Preferences -import com.aurora.store.util.Preferences.PREFERENCE_UNIQUE_GROUP_IDS -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken - -class RequestGroupIdBuilder { - - data class AppIDnVersion(val id: Int, val versionCode: Int) - - companion object { - fun getGroupIDsForApp(context: Context, appID: Int): MutableList { - val data = Preferences.getPrefs(context).getString(PREFERENCE_UNIQUE_GROUP_IDS, "")!! - val gson = Gson() - var groupIDMap = HashMap() - if (data.isNotEmpty()) { - val empMapType = object : TypeToken?>() {}.type - groupIDMap = HashMap(gson.fromJson(data, empMapType) ?: HashMap()) - } - val out = mutableListOf() - for (item in groupIDMap.entries) { - if (item.value.id == appID) { - out.add(item.key) - } - } - return out - } - } -} - -fun App.getGroupId(context: Context): Int { - val data = Preferences.getPrefs(context).getString(PREFERENCE_UNIQUE_GROUP_IDS, "")!! - val gson = Gson() - var groupIDMap = HashMap() - if (data.isNotEmpty()) { - val empMapType = object : TypeToken?>() {}.type - groupIDMap = HashMap(gson.fromJson(data, empMapType) ?: HashMap()) - } - for (item in groupIDMap.entries) { - if (item.value.id == this.id && item.value.versionCode == this.versionCode) { - return item.key - } - } - var randomGroupID = (0 until Int.MAX_VALUE).random() - while (groupIDMap.containsKey(randomGroupID)) { - randomGroupID = (0 until Int.MAX_VALUE).random() - } - groupIDMap[randomGroupID] = RequestGroupIdBuilder.AppIDnVersion(this.id, this.versionCode) - Preferences.getPrefs(context).edit().putString(PREFERENCE_UNIQUE_GROUP_IDS, gson.toJson(groupIDMap)).apply() - return randomGroupID -} \ No newline at end of file diff --git a/app/src/main/java/com/aurora/store/data/model/DownloadFile.kt b/app/src/main/java/com/aurora/store/data/model/DownloadFile.kt deleted file mode 100644 index 01a1e2ac3..000000000 --- a/app/src/main/java/com/aurora/store/data/model/DownloadFile.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ - -package com.aurora.store.data.model - -import android.os.Parcelable -import com.tonyodev.fetch2.Download -import kotlinx.parcelize.Parcelize - -@Parcelize -data class DownloadFile(val download: Download) : Parcelable { - - override fun hashCode(): Int { - return download.id - } - - override fun equals(other: Any?): Boolean { - return when (other) { - is DownloadFile -> other.download.status == download.status && other.download.progress == download.progress - else -> false - } - } -} diff --git a/app/src/main/java/com/aurora/store/data/model/UpdateFile.kt b/app/src/main/java/com/aurora/store/data/model/UpdateFile.kt deleted file mode 100644 index 28dd5817c..000000000 --- a/app/src/main/java/com/aurora/store/data/model/UpdateFile.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ - -package com.aurora.store.data.model - -import com.aurora.gplayapi.data.models.App -import com.aurora.store.State -import com.tonyodev.fetch2.FetchGroup - -data class UpdateFile(val app: App) { - - var group: FetchGroup? = null - var state: State = State.IDLE - - override fun hashCode(): Int { - return app.id - } - - override fun equals(other: Any?): Boolean { - return when (other) { - is UpdateFile -> other.app.id == app.id - && other.state == state - && isGroupSame(group, other.group) - else -> false - } - } - - private fun isGroupSame(oldGroup: FetchGroup?, newGroup: FetchGroup?): Boolean { - if (oldGroup != null && newGroup == null) - return true - return oldGroup?.groupDownloadProgress != newGroup?.groupDownloadProgress - } -} \ No newline at end of file diff --git a/app/src/main/java/com/aurora/store/data/receiver/DownloadCancelReceiver.kt b/app/src/main/java/com/aurora/store/data/receiver/DownloadCancelReceiver.kt deleted file mode 100644 index cee04c7fc..000000000 --- a/app/src/main/java/com/aurora/store/data/receiver/DownloadCancelReceiver.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ -package com.aurora.store.data.receiver - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import com.aurora.Constants.FETCH_GROUP_ID -import com.aurora.store.data.downloader.DownloadManager -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class DownloadCancelReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val extras = intent.extras - if (extras != null) { - val groupId = extras.getInt(FETCH_GROUP_ID, -1) - DownloadManager - .with(context) - .getFetchInstance() - .cancelGroup(groupId) - } - } -} diff --git a/app/src/main/java/com/aurora/store/data/receiver/DownloadPauseReceiver.kt b/app/src/main/java/com/aurora/store/data/receiver/DownloadPauseReceiver.kt deleted file mode 100644 index 83e5edc93..000000000 --- a/app/src/main/java/com/aurora/store/data/receiver/DownloadPauseReceiver.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ -package com.aurora.store.data.receiver - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import com.aurora.Constants.FETCH_GROUP_ID -import com.aurora.store.data.downloader.DownloadManager -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class DownloadPauseReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val extras = intent.extras - if (extras != null) { - val groupId: Int = extras.getInt(FETCH_GROUP_ID, -1) - DownloadManager - .with(context) - .getFetchInstance() - .pauseGroup(groupId) - } - } -} diff --git a/app/src/main/java/com/aurora/store/data/receiver/DownloadResumeReceiver.kt b/app/src/main/java/com/aurora/store/data/receiver/DownloadResumeReceiver.kt deleted file mode 100644 index 1b535b2cb..000000000 --- a/app/src/main/java/com/aurora/store/data/receiver/DownloadResumeReceiver.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ -package com.aurora.store.data.receiver - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import com.aurora.Constants.FETCH_GROUP_ID -import com.aurora.store.data.downloader.DownloadManager -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class DownloadResumeReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val extras = intent.extras - if (extras != null) { - val groupId: Int = extras.getInt(FETCH_GROUP_ID, -1) - DownloadManager - .with(context) - .getFetchInstance() - .resumeGroup(groupId) - } - } -} diff --git a/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt b/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt index 7dafd4822..b5f22ae3b 100644 --- a/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt +++ b/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt @@ -19,12 +19,10 @@ package com.aurora.store.data.receiver -import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.aurora.store.BuildConfig -import com.aurora.store.data.downloader.RequestGroupIdBuilder import com.aurora.store.data.event.BusEvent.InstallEvent import com.aurora.store.data.event.BusEvent.UninstallEvent import com.aurora.store.data.installer.AppInstaller @@ -56,8 +54,6 @@ open class PackageManagerReceiver : BroadcastReceiver() { .getPreferredInstaller() .removeFromInstallQueue(packageName) - //clearNotification(context, packageName) - val isAutoDeleteAPKEnabled = Preferences.getBoolean( context, Preferences.PREFERENCE_AUTO_DELETE @@ -72,15 +68,6 @@ open class PackageManagerReceiver : BroadcastReceiver() { } } - private fun clearNotification(context: Context, packageName: String) { - val notificationManager = context.applicationContext - .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val groupIDsOfPackageName = RequestGroupIdBuilder.getGroupIDsForApp(context, packageName.hashCode()) - groupIDsOfPackageName.forEach { - notificationManager.cancel(packageName, it) - } - } - private fun clearDownloads(context: Context, packageName: String) { try { val rootDirPath = PathUtil.getPackageDirectory(context, packageName) diff --git a/app/src/main/java/com/aurora/store/data/service/AppMetadataStatusListener.kt b/app/src/main/java/com/aurora/store/data/service/AppMetadataStatusListener.kt deleted file mode 100644 index fd927502b..000000000 --- a/app/src/main/java/com/aurora/store/data/service/AppMetadataStatusListener.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.aurora.store.data.service - -import com.aurora.gplayapi.data.models.App - -interface AppMetadataStatusListener { - fun onAppMetadataStatusError(reason: String, app: App) -} \ No newline at end of file diff --git a/app/src/main/java/com/aurora/store/data/service/NotificationService.kt b/app/src/main/java/com/aurora/store/data/service/NotificationService.kt deleted file mode 100644 index 570da62d0..000000000 --- a/app/src/main/java/com/aurora/store/data/service/NotificationService.kt +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * Copyright (C) 2022, The Calyx Institute - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ - -package com.aurora.store.data.service - -import android.app.NotificationManager -import android.app.Service -import android.content.Context -import android.content.Intent -import android.os.IBinder -import android.util.ArrayMap -import androidx.core.app.NotificationManagerCompat -import com.aurora.Constants -import com.aurora.gplayapi.data.models.App -import com.aurora.store.data.downloader.DownloadManager -import com.aurora.store.data.downloader.RequestGroupIdBuilder -import com.aurora.store.data.event.InstallerEvent -import com.aurora.store.data.installer.AppInstaller -import com.aurora.store.util.Log -import com.aurora.store.util.NotificationUtil -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import com.tonyodev.fetch2.AbstractFetchGroupListener -import com.tonyodev.fetch2.Download -import com.tonyodev.fetch2.Error -import com.tonyodev.fetch2.Fetch -import com.tonyodev.fetch2.FetchGroup -import com.tonyodev.fetch2.Status -import dagger.hilt.android.AndroidEntryPoint -import java.lang.reflect.Modifier -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe - -@AndroidEntryPoint -class NotificationService : Service() { - - companion object { - fun startService(context: Context) { - try { - context.startService(Intent(context, NotificationService::class.java)) - } catch (e: Exception) { - Log.e("Failed to start notification service : %s", e.message) - } - } - } - - private lateinit var fetch: Fetch - private lateinit var fetchListener: AbstractFetchGroupListener - private lateinit var notificationManager: NotificationManager - - private val appMap = ArrayMap() - private val gson: Gson = GsonBuilder() - .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT) - .create() - - override fun onBind(intent: Intent): IBinder? { - return null - } - - override fun onCreate() { - super.onCreate() - - Log.i("Notification Service Started") - - EventBus.getDefault().register(this) - - notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager - - fetch = DownloadManager.with(this).fetch - - fetchListener = object : AbstractFetchGroupListener() { - override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) { - showNotification(groupId, download, fetchGroup) - } - - override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) { - showNotification(groupId, download, fetchGroup) - } - - override fun onError( - groupId: Int, - download: Download, - error: Error, - throwable: Throwable?, - fetchGroup: FetchGroup - ) { - showNotification(groupId, download, fetchGroup) - } - - override fun onProgress( - groupId: Int, - download: Download, - etaInMilliSeconds: Long, - downloadedBytesPerSecond: Long, - fetchGroup: FetchGroup - ) { - showNotification(groupId, download, fetchGroup) - } - - override fun onQueued( - groupId: Int, - download: Download, - waitingNetwork: Boolean, - fetchGroup: FetchGroup - ) { - showNotification(groupId, download, fetchGroup) - } - - override fun onPaused(groupId: Int, download: Download, fetchGroup: FetchGroup) { - showNotification(groupId, download, fetchGroup) - } - - override fun onDeleted(groupId: Int, download: Download, fetchGroup: FetchGroup) { - showNotification(groupId, download, fetchGroup) - } - } - - fetch.addListener(fetchListener) - } - - private fun showNotification(groupId: Int, download: Download, fetchGroup: FetchGroup) { - //Ignore notifications for completion of sub-parts of a bundled apk - if (download.status == Status.COMPLETED && fetchGroup.groupDownloadProgress < 100) - return - - //synchronized(appMap) { - var app: App? = appMap[groupId] - - if (app == null) { - app = gson.fromJson( - download.extras.getString(Constants.STRING_EXTRA, "{}"), - App::class.java - ) - appMap[groupId] = app - } - - if (app == null) - return - - if (download.status == Status.DELETED) { - notificationManager.cancel(app.id) - return - } - - if (NotificationManagerCompat.from(this).areNotificationsEnabled()) { - notificationManager.notify( - app.packageName, - app.id, - NotificationUtil.getDownloadNotification(this, app, groupId, download, fetchGroup) - ) - } - } - - @Subscribe() - fun onEventMainThread(event: Any) { - when (event) { - is InstallerEvent.Success -> { - val groupIDsOfPackageName = RequestGroupIdBuilder.getGroupIDsForApp(this, event.packageName.hashCode()) - var app: App? = null - for (item in groupIDsOfPackageName) { - app = appMap[item] - if (app != null) { - break - } - } - if (app != null) - notifyInstallationStatus(app, event.extra) - } - is InstallerEvent.Failed -> { - val groupIDsOfPackageName = RequestGroupIdBuilder.getGroupIDsForApp(this, event.packageName.hashCode()) - var app: App? = null - for (item in groupIDsOfPackageName) { - app = appMap[item] - if (app != null) { - break - } - } - if (app != null) - notifyInstallationStatus(app, event.error) - } - else -> { - - } - } - } - - @Synchronized - private fun install(packageName: String, files: List) { - AppInstaller.getInstance(this) - .getPreferredInstaller() - .install( - packageName, - files - .filter { it.file.endsWith(".apk") } - .map { - it.file - }.toList() - ) - } - - private fun notifyInstallationStatus(app: App, status: String?) { - if (NotificationManagerCompat.from(this).areNotificationsEnabled()) { - notificationManager.notify( - app.packageName, - app.id, - NotificationUtil.getInstallNotification(this, app, status) - ) - } - } - - override fun onDestroy() { - Log.i("Notification Service Stopped") - fetch.removeListener(fetchListener) - EventBus.getDefault().unregister(this); - super.onDestroy() - } -} diff --git a/app/src/main/java/com/aurora/store/data/service/SelfUpdateService.kt b/app/src/main/java/com/aurora/store/data/service/SelfUpdateService.kt deleted file mode 100644 index a8c219964..000000000 --- a/app/src/main/java/com/aurora/store/data/service/SelfUpdateService.kt +++ /dev/null @@ -1,189 +0,0 @@ -package com.aurora.store.data.service - -import android.app.Notification -import android.content.Intent -import android.os.Build -import android.os.IBinder -import androidx.annotation.RequiresApi -import androidx.core.app.NotificationCompat -import androidx.core.app.ServiceCompat -import androidx.core.content.ContextCompat -import androidx.lifecycle.LifecycleService -import com.aurora.Constants -import com.aurora.gplayapi.data.models.App -import com.aurora.gplayapi.data.models.File -import com.aurora.store.BuildConfig -import com.aurora.store.R -import com.aurora.store.data.downloader.DownloadManager -import com.aurora.store.data.downloader.RequestBuilder.buildRequest -import com.aurora.store.data.downloader.getGroupId -import com.aurora.store.data.installer.NativeInstaller -import com.aurora.store.data.model.SelfUpdate -import com.aurora.store.util.CertUtil.isFDroidApp -import com.aurora.store.util.Log -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import com.tonyodev.fetch2.* -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import java.lang.reflect.Modifier -import java.util.* - -@AndroidEntryPoint -class SelfUpdateService : LifecycleService() { - private lateinit var app: App - private lateinit var fetch: Fetch - private lateinit var fetchListener: FetchListener - - private var gson: Gson = GsonBuilder() - .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT) - .create() - - private val hashCode = BuildConfig.APPLICATION_ID.hashCode() - - // Coroutine - private val job = SupervisorJob() - private val serviceScope = CoroutineScope(Dispatchers.IO + job) - - override fun onBind(intent: Intent): IBinder? { - super.onBind(intent) - return null - } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - super.onStartCommand(intent, flags, startId) - val rawSelfUpdate = intent?.getStringExtra(Constants.STRING_EXTRA) - if (rawSelfUpdate?.isNotBlank() == true) { - val selfUpdate = gson.fromJson(rawSelfUpdate, SelfUpdate::class.java) - selfUpdate?.let { - downloadAndUpdate(it) - } - } - return START_NOT_STICKY - } - - override fun onCreate() { - super.onCreate() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForeground(1, notification) - } else { - val notification = getNotification( - NotificationCompat.Builder( - this, - Constants.NOTIFICATION_CHANNEL_GENERAL - ) - ) - startForeground(1, notification) - } - } - - override fun onDestroy() { - job.cancel() - super.onDestroy() - } - - private fun destroyService() { - Log.d("Self-update service destroyed") - fetch.removeListener(fetchListener) - ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) - stopSelf() - } - - private fun downloadAndUpdate(update: SelfUpdate) { - app = App(BuildConfig.APPLICATION_ID) - app.id = hashCode - app.packageName = BuildConfig.APPLICATION_ID - app.displayName = getString(R.string.app_name) - app.versionName = update.versionName - app.versionCode = update.versionCode - - val file = File().apply { - name = "AuroraStore.apk" - url = if (isFDroidVariant) - update.fdroidBuild - else - update.auroraBuild - type = File.FileType.BASE - } - - val request = buildRequest(this, app, file) - request.enqueueAction = EnqueueAction.REPLACE_EXISTING - - val requestList: MutableList = ArrayList() - requestList.add(request) - - fetch = DownloadManager.with(this).fetch - fetch.enqueue(requestList) { - Log.i("Downloading latest update") - } - - fetchListener = getFetchListener() - fetch.addListener(fetchListener) - } - - private val isFDroidVariant: Boolean - get() = isFDroidApp(this, BuildConfig.APPLICATION_ID) - - @get:RequiresApi(Build.VERSION_CODES.O) - private val notification: Notification - get() { - val notificationBuilder = - NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNEL_GENERAL) - return getNotification(notificationBuilder) - } - - private fun getNotification(builder: NotificationCompat.Builder): Notification { - return builder.setAutoCancel(true) - .setColor(ContextCompat.getColor(this, R.color.colorAccent)) - .setContentTitle("Self update") - .setContentText("Updating Aurora Store in background") - .setOngoing(true) - .setSmallIcon(R.drawable.ic_notification_outlined) - .build() - } - - private fun getFetchListener(): FetchListener { - return object : AbstractFetchGroupListener() { - override fun onError( - groupId: Int, download: Download, error: Error, - throwable: Throwable?, fetchGroup: FetchGroup - ) { - if (groupId == app.getGroupId(this@SelfUpdateService)) { - Log.e("Error self-updating ${app.displayName}") - destroyService() - } - } - - override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) { - if (groupId == app.getGroupId(this@SelfUpdateService) && fetchGroup.groupDownloadProgress == 100) { - Log.d("Calling installer ${app.displayName}") - - try { - NativeInstaller(this@SelfUpdateService).install( - app.packageName, - fetchGroup.downloads.map { it.file } - ) - } catch (e: Exception) { - Log.e("Self update : ${e.stackTraceToString()}") - } - - serviceScope.launch { - delay(10000) - destroyService() - } - } - } - - override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) { - if (groupId == app.getGroupId(this@SelfUpdateService)) { - Log.d("Self-update cancelled ${app.displayName}") - destroyService() - } - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/data/service/UpdateService.kt b/app/src/main/java/com/aurora/store/data/service/UpdateService.kt deleted file mode 100644 index 23be032fa..000000000 --- a/app/src/main/java/com/aurora/store/data/service/UpdateService.kt +++ /dev/null @@ -1,760 +0,0 @@ -package com.aurora.store.data.service - -import android.app.Notification -import android.content.Intent -import android.os.* -import androidx.annotation.RequiresApi -import androidx.core.app.NotificationCompat -import androidx.core.app.ServiceCompat -import androidx.core.content.ContextCompat -import androidx.lifecycle.LifecycleService -import androidx.lifecycle.MutableLiveData -import com.aurora.Constants -import com.aurora.extensions.isOAndAbove -import com.aurora.extensions.stackTraceToString -import com.aurora.gplayapi.data.models.App -import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.exceptions.ApiException -import com.aurora.gplayapi.helpers.PurchaseHelper -import com.aurora.store.R -import com.aurora.store.data.downloader.DownloadManager -import com.aurora.store.data.downloader.RequestBuilder -import com.aurora.store.data.downloader.getGroupId -import com.aurora.store.data.event.InstallerEvent -import com.aurora.store.data.installer.AppInstaller -import com.aurora.store.data.model.UpdateFile -import com.aurora.store.data.network.HttpClient -import com.aurora.store.data.providers.AuthProvider -import com.aurora.store.util.Log -import com.aurora.store.util.PackageUtil.isSharedLibraryInstalled -import com.aurora.store.util.PathUtil -import com.aurora.store.util.Preferences -import com.tonyodev.fetch2.* -import com.tonyodev.fetch2core.DownloadBlock -import com.tonyodev.fetch2core.FetchObserver -import com.tonyodev.fetch2core.Reason -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode -import java.io.File -import java.util.* -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.timerTask - -@AndroidEntryPoint -class UpdateService: LifecycleService() { - - lateinit var fetch: Fetch - lateinit var downloadManager: DownloadManager - private lateinit var fetchListener: FetchGroupListener - private var fetchActiveDownloadObserver = object : FetchObserver { - override fun onChanged(data: Boolean, reason: Reason) { - if (!data && isEmptyInstalling() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) { - Handler(Looper.getMainLooper()).postDelayed ({ - if (isEmptyInstalling() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) { - ServiceCompat.stopForeground( - this@UpdateService, - ServiceCompat.STOP_FOREGROUND_REMOVE - ) - stopSelf() - } - }, 5 * 1000) - } - } - } - private var hasActiveDownloadObserver = false - - private val fetchListeners: ArrayList = ArrayList() - - private val fetchPendingEvents: MutableMap = mutableMapOf() - - private val appMetadataListeners: ArrayList = ArrayList() - - private val appMetadataPendingEvents: ArrayList = ArrayList() - - var liveUpdateData: MutableLiveData> = MutableLiveData() - - inner class UpdateServiceBinder : Binder() { - fun getUpdateService() : UpdateService { - return this@UpdateService - } - } - - // HashMap> - private var downloadsInCompletedGroup = HashMap>() - - private var installing = HashSet() - private var lock = ReentrantLock() - - // Coroutine - private val job = SupervisorJob() - private val serviceScope = CoroutineScope(Dispatchers.IO + job) - - fun putInInstalling(packageName: String?) { - if (packageName == null) { - return - } - if (lock.tryLock()) { - installing.add(packageName) - try { - lock.unlock() - } catch (th: Throwable) { - th.printStackTrace() - } - } else { - Thread { - while (!lock.tryLock()) { - Thread.sleep(50) - } - installing.add(packageName) - try { - lock.unlock() - } catch (th: Throwable) { - th.printStackTrace() - } - }.start() - } - } - - fun removeFromInstalling(packageName: String?, runFromCurrentThread: Boolean = false) { - if (packageName == null) { - return - } - if (lock.tryLock()) { - installing.remove(packageName) - try { - lock.unlock() - } catch (th: Throwable) { - th.printStackTrace() - } - } else { - val toRun = Runnable { - while (!lock.tryLock()) { - Thread.sleep(50) - } - installing.remove(packageName) - try { - lock.unlock() - } catch (th: Throwable) { - th.printStackTrace() - } - } - if (runFromCurrentThread) { - toRun.run() - } else { - Thread(toRun).start() - } - } - } - - fun isEmptyInstalling(): Boolean { - while (!lock.tryLock()) { - Thread.sleep(50) - } - val out: Boolean = installing.isEmpty() - try { - lock.unlock() - } catch (th: Throwable) { - th.printStackTrace() - } - return out - } - - fun containsInInstalling(packageName: String?): Boolean { - if (packageName == null) { - return false - } - while (!lock.tryLock()) { - Thread.sleep(50) - } - val out = installing.contains(packageName) - try { - lock.unlock() - } catch (th: Throwable) { - th.printStackTrace() - } - return out - } - - - private lateinit var purchaseHelper: PurchaseHelper - - private lateinit var authData: AuthData - - - data class AppDownloadStatus(val download: Download, val fetchGroup: FetchGroup, - val isCancelled: Boolean = false, - val isComplete: Boolean = false) - - data class AppMetadataStatus(val reason: String, val app: App) - - @get:RequiresApi(Build.VERSION_CODES.O) - private val notification: Notification - get() { - val notificationBuilder = - NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNEL_UPDATER_SERVICE) - return getNotification(notificationBuilder) - } - - private fun getNotification(builder: NotificationCompat.Builder): Notification { - return builder.setAutoCancel(true) - .setColor(ContextCompat.getColor(this, R.color.colorAccent)) - .setContentTitle(getString(R.string.app_updater_service_notif_title)) - .setContentText(getString(R.string.app_updater_service_notif_text)) - .setOngoing(true) - .setSmallIcon(R.drawable.ic_notification_outlined) - .build() - } - - override fun onCreate() { - super.onCreate() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForeground(1, notification) - } else { - val notification = getNotification( - NotificationCompat.Builder( - this, - Constants.NOTIFICATION_CHANNEL_UPDATER_SERVICE - ) - ) - startForeground(1, notification) - } - EventBus.getDefault().register(this) - authData = AuthProvider.with(this).getAuthData() - purchaseHelper = PurchaseHelper(authData).using(HttpClient.getPreferredClient(this)) - downloadManager = DownloadManager.with(this) - fetch = downloadManager.fetch - fetchListener = object : FetchGroupListener { - - override fun onAdded(groupId: Int, download: Download, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onAdded(groupId, download, fetchGroup) - } - if (download.tag != null) { - removeFromInstalling(download.tag, runFromCurrentThread = true) - } - if (!hasActiveDownloadObserver) { - hasActiveDownloadObserver = true - fetch.addActiveDownloadsObserver(true, fetchActiveDownloadObserver) - } - } - - override fun onAdded(download: Download) { - fetchListeners.forEach { - it.onAdded(download) - } - } - - override fun onProgress( - groupId: Int, - download: Download, - etaInMilliSeconds: Long, - downloadedBytesPerSecond: Long, - fetchGroup: FetchGroup - ) { - fetchListeners.forEach { - it.onProgress(groupId, download, etaInMilliSeconds, downloadedBytesPerSecond, fetchGroup) - } - } - - override fun onProgress(download: Download, etaInMilliSeconds: Long, downloadedBytesPerSecond: Long) { - fetchListeners.forEach { - it.onProgress(download, etaInMilliSeconds, downloadedBytesPerSecond) - } - } - - override fun onQueued(groupId: Int, download: Download, waitingNetwork: Boolean, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onQueued(groupId, download, waitingNetwork, fetchGroup) - } - } - - override fun onQueued(download: Download, waitingOnNetwork: Boolean) { - fetchListeners.forEach { - it.onQueued(download, waitingOnNetwork) - } - } - - override fun onRemoved(groupId: Int, download: Download, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onRemoved(groupId, download, fetchGroup) - } - } - - override fun onRemoved(download: Download) { - fetchListeners.forEach { - it.onRemoved(download) - } - } - - override fun onResumed(groupId: Int, download: Download, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onResumed(groupId, download, fetchGroup) - } - } - - override fun onResumed(download: Download) { - fetchListeners.forEach { - it.onResumed(download) - } - } - - override fun onStarted( - groupId: Int, - download: Download, - downloadBlocks: List, - totalBlocks: Int, - fetchGroup: FetchGroup - ) { - fetchListeners.forEach { - it.onStarted( - groupId, - download, - downloadBlocks, - totalBlocks, - fetchGroup) - } - } - - override fun onStarted(download: Download, downloadBlocks: List, totalBlocks: Int) { - fetchListeners.forEach { - it.onStarted( - download, - downloadBlocks, - totalBlocks) - } - } - - override fun onWaitingNetwork(groupId: Int, download: Download, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onWaitingNetwork(groupId, download, fetchGroup) - } - } - - override fun onWaitingNetwork(download: Download) { - fetchListeners.forEach { - it.onWaitingNetwork(download) - } - } - - override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onCompleted(groupId, download, fetchGroup) - } - if (fetchListeners.isEmpty()) { - fetchPendingEvents[groupId] = AppDownloadStatus(download, fetchGroup, isComplete = true) - } - var packageDownloadFilesWhichCompleted = downloadsInCompletedGroup[download.tag!!] - if (packageDownloadFilesWhichCompleted == null) { - packageDownloadFilesWhichCompleted = HashSet() - downloadsInCompletedGroup[download.tag!!] = packageDownloadFilesWhichCompleted - } - packageDownloadFilesWhichCompleted.add(download.file) - if (fetchGroup.groupDownloadProgress == 100 && fetchGroup.downloads.all { packageDownloadFilesWhichCompleted.contains(it.file) }) { - downloadsInCompletedGroup.remove(download.tag!!) - if (download.tag != null) { - removeFromInstalling(download.tag, runFromCurrentThread = true) - } - Log.d("Group (${download.tag!!}) downloaded and verified all downloaded!") - Handler(Looper.getMainLooper()).post { - try { - install(download.tag!!, fetchGroup.downloads) - } catch (e: Exception) { - Log.e(e.stackTraceToString()) - } - } - } - if (fetchGroup.groupDownloadProgress == 100) { - Log.d("Group (${download.tag!!}) downloaded but NOT verified all downloaded!") - } /* else if (fetchGroup.groupDownloadProgress == -1) { - fetch.deleteGroup(fetchGroup.id) - }*/ - } - - override fun onCompleted(download: Download) { - fetchListeners.forEach { - it.onCompleted(download) - } - } - - override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onCancelled(groupId, download, fetchGroup) - } - if (fetchListeners.isEmpty()) { - fetchPendingEvents[groupId] = AppDownloadStatus(download, fetchGroup, isCancelled = true) - } - } - - override fun onCancelled(download: Download) { - fetchListeners.forEach { - it.onCancelled(download) - } - } - - override fun onDeleted(groupId: Int, download: Download, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onDeleted(groupId, download, fetchGroup) - } - if (fetchListeners.isEmpty()) { - fetchPendingEvents[groupId] = AppDownloadStatus(download, fetchGroup, isCancelled = true) - } - } - - override fun onDeleted(download: Download) { - fetchListeners.forEach { - it.onDeleted(download) - } - } - - override fun onDownloadBlockUpdated( - groupId: Int, - download: Download, - downloadBlock: DownloadBlock, - totalBlocks: Int, - fetchGroup: FetchGroup - ) { - fetchListeners.forEach { - it.onDownloadBlockUpdated( - groupId, - download, - downloadBlock, - totalBlocks, - fetchGroup - ) - } - } - - override fun onDownloadBlockUpdated(download: Download, downloadBlock: DownloadBlock, totalBlocks: Int) { - fetchListeners.forEach { - it.onDownloadBlockUpdated( - download, - downloadBlock, - totalBlocks - ) - } - } - - override fun onError( - groupId: Int, - download: Download, - error: Error, - throwable: Throwable?, - fetchGroup: FetchGroup - ) { - fetchListeners.forEach { - it.onError( - groupId, - download, - error, - throwable, - fetchGroup - ) - } - } - - override fun onError(download: Download, error: Error, throwable: Throwable?) { - fetchListeners.forEach { - it.onError( - download, - error, - throwable - ) - } - } - - override fun onPaused(groupId: Int, download: Download, fetchGroup: FetchGroup) { - fetchListeners.forEach { - it.onPaused(groupId, download, fetchGroup) - } - } - - override fun onPaused(download: Download) { - fetchListeners.forEach { - it.onPaused(download) - } - } - } - /*liveUpdateData.observe(this) { updateData -> - - }*/ - if (::fetch.isInitialized && ::fetchListener.isInitialized) { - fetch.addListener(fetchListener) - } - } - - fun updateApp(app: App, removeExisiting: Boolean = false) { - putInInstalling(app.packageName) - serviceScope.launch { - try { - val files = purchaseHelper.purchase( - app.packageName, - app.versionCode, - app.offerType - ) - - if (app.dependencies.dependentLibraries.isNotEmpty() && isOAndAbove()) { - app.dependencies.dependentLibraries.forEach { - if (!isSharedLibraryInstalled( - this@UpdateService, - it.packageName, - it.versionCode - ) - ) { - it.displayName = getString(R.string.downloading_dep, app.displayName) - it.iconArtwork = app.iconArtwork - - updateApp(it, removeExisiting) - while (containsInInstalling(it.packageName) || - !isSharedLibraryInstalled( - this@UpdateService, - it.packageName, - it.versionCode - ) - ) { - delay(1000) - } - - // Clear library downloads - clearDownloadsIfRequired(it.packageName) - } - } - } - - val requests = files.filter { it.url.isNotEmpty() } - .map { RequestBuilder.buildRequest(this@UpdateService, app, it) } - .toList() - - if (requests.isNotEmpty()) { - if (removeExisiting) { - /*Remove old fetch group if downloaded earlier, mostly in case of updates*/ - fetch.deleteGroup(app.getGroupId(this@UpdateService)) - } - fetch.enqueue(requests) { - Log.i("Updating ${app.displayName}") - } - } else { - removeFromInstalling(app.packageName) - Log.e("Failed to download : ${app.displayName}") - appMetadataListeners.forEach { - it.onAppMetadataStatusError( - getString(R.string.purchase_session_expired), - app - ) - } - - if (appMetadataListeners.isEmpty()) { - appMetadataPendingEvents.add( - AppMetadataStatus( - getString(R.string.purchase_session_expired), - app - ) - ) - } - } - } catch (exception: Exception) { - removeFromInstalling(app.packageName) - var reason = "Unknown" - - when (exception) { - is ApiException.AppNotPurchased -> { - reason = getString(R.string.purchase_invalid) - } - - is ApiException.AppNotFound -> { - reason = getString(R.string.purchase_not_found) - } - - is ApiException.AppNotSupported -> { - reason = getString(R.string.purchase_unsupported) - } - - is ApiException.EmptyDownloads -> { - reason = getString(R.string.purchase_no_file) - } - } - - appMetadataListeners.forEach { - it.onAppMetadataStatusError(reason, app) - } - - if (appMetadataListeners.isEmpty()) { - appMetadataPendingEvents.add(AppMetadataStatus(reason, app)) - } - - Log.e("Failed to purchase ${app.displayName} : $reason") - } - } - } - - var timer: Timer? = null - val timerTaskRun: Runnable = Runnable { - Handler(Looper.getMainLooper()).post { - if (isEmptyInstalling() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) { - fetch.hasActiveDownloads(true) { hasActiveDownloads -> - if (!hasActiveDownloads && isEmptyInstalling() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) { - Handler(Looper.getMainLooper()).post { - ServiceCompat.stopForeground( - this@UpdateService, - ServiceCompat.STOP_FOREGROUND_REMOVE - ) - stopSelf() - } - } - } - } - } - } - - @Synchronized - private fun install(packageName: String, files: List?) { - if (containsInInstalling(packageName)) { - println("Already installing $packageName!") - return - } - putInInstalling(packageName) - files?.let { downloads -> - if (downloads.all { File(it.file).exists() }) { - serviceScope.launch { - try { - AppInstaller.getInstance(this@UpdateService).getPreferredInstaller() - .install( - packageName, - files.filter { it.file.endsWith(".apk") }.map { it.file }.toList() - ) - } catch (exception: Exception) { - removeFromInstalling(packageName) - Log.e(exception.stackTraceToString()) - } - } - } else { - removeFromInstalling(packageName) - } - } - } - - var timerLock = Object() - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - fun onEventBackgroundThreadExec(event: Any) { - when (event) { - is InstallerEvent.Success, - is InstallerEvent.Cancelled, - is InstallerEvent.Failed -> { - when (event) { - is InstallerEvent.Success -> event.packageName - is InstallerEvent.Cancelled -> event.packageName - is InstallerEvent.Failed -> event.packageName - else -> null - }?.run { - removeFromInstalling(this) - } - synchronized(timerLock) { - if (timer != null) { - timer!!.cancel() - timer = null - } - if (timer == null) { - timer = Timer() - } - timer!!.schedule(timerTask { timerTaskRun.run() }, 5 * 1000) - } - } - else -> { - - } - } - } - - fun registerFetchListener(listener: FetchGroupListener) { - fetchListeners.add(listener) - val iterator = fetchPendingEvents.iterator() - while (iterator.hasNext()) { - val item = iterator.next() - if (item.value.isCancelled && !item.value.isComplete) { - listener.onCancelled(item.key, item.value.download, item.value.fetchGroup) - } else if (!item.value.isCancelled && item.value.isComplete) { - listener.onCompleted(item.key, item.value.download, item.value.fetchGroup) - } - iterator.remove() - } - } - - fun registerAppMetadataListener(listener: AppMetadataStatusListener) { - appMetadataListeners.add(listener) - val iterator = appMetadataPendingEvents.iterator() - while (iterator.hasNext()) { - val item = iterator.next() - listener.onAppMetadataStatusError(item.reason, item.app) - iterator.remove() - } - } - - fun unregisterAppMetadataListener(listener: AppMetadataStatusListener) { - appMetadataListeners.remove(listener) - } - - fun unregisterFetchListener(listener: AbstractFetchGroupListener) { - fetchListeners.remove(listener) - } - - private var binder: UpdateServiceBinder = UpdateServiceBinder() - - override fun onBind(intent: Intent): IBinder { - super.onBind(intent) - return binder - } - - override fun onUnbind(intent: Intent?): Boolean { - fetchListeners.clear() - appMetadataListeners.clear() - synchronized(timerLock) { - if (timer != null) { - timer!!.cancel() - timer = null - } - if (timer == null) { - timer = Timer() - } - timer!!.schedule(timerTask { timerTaskRun.run() }, 5 * 1000) - } - return true - } - - override fun onRebind(intent: Intent?) { - super.onRebind(intent) - } - - override fun onDestroy() { - super.onDestroy() - if (hasActiveDownloadObserver) { - hasActiveDownloadObserver = false - fetch.removeActiveDownloadsObserver(fetchActiveDownloadObserver) - } - if (::fetch.isInitialized && ::fetchListener.isInitialized) { - fetch.removeListener(fetchListener) - } - } - - fun updateAll(updateFileMap: MutableMap) { - updateFileMap.values.forEach { updateApp(it.app) } - } - - private fun clearDownloadsIfRequired(packageName: String) { - if (Preferences.getBoolean(this, Preferences.PREFERENCE_AUTO_DELETE)) { - try { - val rootDirPath = PathUtil.getPackageDirectory(this, packageName) - val rootDir = File(rootDirPath) - if (rootDir.exists()) rootDir.deleteRecursively() - } catch (e: Exception) { - Log.d("Failed to clear downloads!", e) - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/util/NotificationUtil.kt b/app/src/main/java/com/aurora/store/util/NotificationUtil.kt index 2583cca3b..c7c528428 100644 --- a/app/src/main/java/com/aurora/store/util/NotificationUtil.kt +++ b/app/src/main/java/com/aurora/store/util/NotificationUtil.kt @@ -25,12 +25,6 @@ import com.aurora.store.data.room.download.Download as AuroraDownload import com.aurora.store.R import com.aurora.store.data.activity.InstallActivity import com.aurora.store.data.model.DownloadStatus -import com.aurora.store.data.receiver.DownloadCancelReceiver -import com.aurora.store.data.receiver.DownloadPauseReceiver -import com.aurora.store.data.receiver.DownloadResumeReceiver -import com.tonyodev.fetch2.Download -import com.tonyodev.fetch2.FetchGroup -import com.tonyodev.fetch2.Status import java.net.URL import java.util.UUID @@ -89,131 +83,6 @@ object NotificationUtil { .build() } - fun getDownloadNotification( - context: Context, - app: App, - groupId: Int, - download: Download, - fetchGroup: FetchGroup - ): Notification { - val status = download.status - - val builder = NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_GENERAL) - builder.setContentTitle(app.displayName) - builder.setSmallIcon(R.drawable.ic_notification_outlined) - builder.color = ContextCompat.getColor(context, R.color.colorAccent) - builder.setWhen(download.created) - builder.setContentIntent(getContentIntentForDownloads(context)) - - when (status) { - Status.PAUSED -> { - builder.setSmallIcon(R.drawable.ic_download_pause) - builder.setContentText(context.getString(R.string.download_paused)) - } - - Status.CANCELLED -> { - builder.setSmallIcon(R.drawable.ic_download_cancel) - builder.setContentText(context.getString(R.string.download_canceled)) - builder.color = Color.RED - } - - Status.FAILED -> { - builder.setSmallIcon(R.drawable.ic_download_fail) - builder.setContentText(context.getString(R.string.download_failed)) - builder.color = Color.RED - } - - Status.COMPLETED -> if (fetchGroup.groupDownloadProgress == 100) { - builder.setSmallIcon(android.R.drawable.stat_sys_download_done) - builder.setContentText(context.getString(R.string.download_completed)) - } - - else -> { - builder.setSmallIcon(android.R.drawable.stat_sys_download) - builder.setContentText(context.getString(R.string.download_metadata)) - } - } - val progress = fetchGroup.groupDownloadProgress - val progressBigText = NotificationCompat.BigTextStyle() - - when (status) { - Status.QUEUED -> { - builder.setProgress(100, 0, true) - progressBigText.bigText(context.getString(R.string.download_queued)) - builder.setStyle(progressBigText) - } - - Status.DOWNLOADING -> { - val speedString: String = - CommonUtil.humanReadableByteSpeed(download.downloadedBytesPerSecond, true) - progressBigText.bigText( - context.getString( - R.string.download_progress, - fetchGroup.completedDownloads.size + 1, - fetchGroup.downloads.size, - speedString - ) - ) - builder.setStyle(progressBigText) - builder.addAction( - NotificationCompat.Action.Builder( - R.drawable.ic_download_pause, - context.getString(R.string.action_pause), - getPauseIntent(context, groupId) - ).build() - ) - builder.addAction( - NotificationCompat.Action.Builder( - R.drawable.ic_download_cancel, - context.getString(R.string.action_cancel), - getCancelIntent(context, groupId) - ).build() - ) - if (progress < 0) builder.setProgress( - 100, - 0, - true - ) else builder.setProgress(100, progress, false) - } - - Status.PAUSED -> { - progressBigText.bigText( - context.getString( - R.string.download_paused, - fetchGroup.completedDownloads.size, - fetchGroup.downloads.size - ) - ) - builder.setStyle(progressBigText) - builder.addAction( - NotificationCompat.Action.Builder( - R.drawable.ic_download_pause, - context.getString(R.string.action_resume), - getResumeIntent(context, groupId) - ).build() - ) - } - - Status.COMPLETED -> if (fetchGroup.groupDownloadProgress == 100) { - builder.setAutoCancel(true) - builder.setContentIntent(getContentIntentForDetails(context, app.packageName)) - builder.setStyle(progressBigText) - } - - else -> { - - } - } - - when (status) { - Status.DOWNLOADING -> builder.setCategory(Notification.CATEGORY_PROGRESS) - Status.FAILED, Status.CANCELLED -> builder.setCategory(Notification.CATEGORY_ERROR) - else -> builder.setCategory(Notification.CATEGORY_STATUS) - } - - return builder.build() - } - fun getDownloadNotification( context: Context, download: AuroraDownload, @@ -307,33 +176,6 @@ object NotificationUtil { return builder.build() } - private fun getPauseIntent(context: Context, groupId: Int): PendingIntent { - val intent = Intent(context, DownloadPauseReceiver::class.java) - intent.putExtra(Constants.FETCH_GROUP_ID, groupId) - val flags = if (isMAndAbove()) - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE - else PendingIntent.FLAG_CANCEL_CURRENT - return PendingIntent.getBroadcast(context, groupId, intent, flags) - } - - private fun getResumeIntent(context: Context, groupId: Int): PendingIntent { - val intent = Intent(context, DownloadResumeReceiver::class.java) - intent.putExtra(Constants.FETCH_GROUP_ID, groupId) - val flags = if (isMAndAbove()) - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE - else PendingIntent.FLAG_CANCEL_CURRENT - return PendingIntent.getBroadcast(context, groupId, intent, flags) - } - - private fun getCancelIntent(context: Context, groupId: Int): PendingIntent { - val intent = Intent(context, DownloadCancelReceiver::class.java) - intent.putExtra(Constants.FETCH_GROUP_ID, groupId) - val flags = if (isMAndAbove()) - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE - else PendingIntent.FLAG_CANCEL_CURRENT - return PendingIntent.getBroadcast(context, groupId, intent, flags) - } - private fun getContentIntentForDetails(context: Context, packageName: String): PendingIntent { return NavDeepLinkBuilder(context) .setGraph(R.navigation.mobile_navigation) diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt index 268a3159e..9ffa9e430 100644 --- a/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt +++ b/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt @@ -29,17 +29,12 @@ import androidx.navigation.fragment.navArgs import com.aurora.extensions.copyToClipBoard import com.aurora.extensions.toast import com.aurora.store.R -import com.aurora.store.data.downloader.DownloadManager -import com.aurora.store.data.model.DownloadStatus import com.aurora.store.databinding.SheetDownloadMenuBinding import com.aurora.store.util.DownloadWorkerUtil -import com.tonyodev.fetch2.Fetch -import com.tonyodev.fetch2.Status import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.launch -import kotlin.properties.Delegates @AndroidEntryPoint class DownloadMenuSheet : BaseBottomSheet() {