Loading app/src/main/java/foundation/e/apps/MainActivity.kt +5 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.updates.UpdatesNotifier import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.enums.User Loading Loading @@ -115,6 +116,10 @@ class MainActivity : AppCompatActivity() { } } if (intent.hasExtra(UpdatesNotifier.UPDATES_NOTIFICATION_CLICK_EXTRA)) { bottomNavigationView.selectedItemId = R.id.updatesFragment } // Create notification channel on post-nougat devices if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { viewModel.createNotificationChannels() Loading app/src/main/java/foundation/e/apps/updates/UpdatesNotifier.kt 0 → 100644 +128 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019-2021 E FOUNDATION * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.updates import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import foundation.e.apps.MainActivity import foundation.e.apps.R class UpdatesNotifier { companion object { const val UPDATES_NOTIFICATION_ID = 76 const val UPDATES_NOTIFICATION_CHANNEL_ID = "updates_notification" const val UPDATES_NOTIFICATION_CHANNEL_TITLE = "App updates" const val UPDATES_NOTIFICATION_CLICK_EXTRA = "updates_notification_click_extra" } private fun getNotification( context: Context, numberOfApps: Int, installAutomatically: Boolean, unmeteredNetworkOnly: Boolean, isConnectedToUnmeteredNetwork: Boolean ): Notification { val notificationBuilder = NotificationCompat.Builder(context, UPDATES_NOTIFICATION_CHANNEL_ID) notificationBuilder.setSmallIcon(R.drawable.ic_app_updated_on) notificationBuilder.priority = NotificationCompat.PRIORITY_DEFAULT if (numberOfApps == 1) { notificationBuilder.setContentTitle( context.resources.getQuantityString( R.plurals.updates_notification_title, 1, numberOfApps ) ) } else { notificationBuilder.setContentTitle( context.resources.getQuantityString( R.plurals.updates_notification_title, numberOfApps, numberOfApps ) ) } if (installAutomatically) { notificationBuilder.setContentText(context.getString(R.string.AUTOMATICALLY_INSTALL_updates_notification_text)) if (unmeteredNetworkOnly && !isConnectedToUnmeteredNetwork) { notificationBuilder.setSubText( context .getString(R.string.updates_notification_unmetered_network_warning) ) } } else { notificationBuilder.setContentText(context.getString(R.string.MANUALLY_INSTALL_updates_notification_text)) } notificationBuilder.setContentIntent(getClickIntent(context)) notificationBuilder.setAutoCancel(true) return notificationBuilder.build() } private fun getClickIntent(context: Context): PendingIntent { val intent = Intent(context, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra(UPDATES_NOTIFICATION_CLICK_EXTRA, true) } return PendingIntent.getActivity(context, 0, intent, 0) } private fun createNotificationChannel(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val importance = NotificationManager.IMPORTANCE_DEFAULT val channel = NotificationChannel( UPDATES_NOTIFICATION_CHANNEL_ID, UPDATES_NOTIFICATION_CHANNEL_TITLE, importance ) val notificationManager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } fun showNotification( context: Context, numberOfApps: Int, installAutomatically: Boolean, unmeteredNetworkOnly: Boolean, isConnectedToUnmeteredNetwork: Boolean ) { with(NotificationManagerCompat.from(context)) { createNotificationChannel(context) notify( UPDATES_NOTIFICATION_ID, getNotification( context, numberOfApps, installAutomatically, unmeteredNetworkOnly, isConnectedToUnmeteredNetwork ) ) } } } app/src/main/java/foundation/e/apps/updates/UpdatesWorker.kt +126 −19 Original line number Diff line number Diff line package foundation.e.apps.updates import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.util.Base64 import android.util.Log import androidx.hilt.work.HiltWorker import androidx.preference.PreferenceManager import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.aurora.gplayapi.data.models.AuthData import com.google.gson.Gson import dagger.assisted.Assisted import dagger.assisted.AssistedInject import foundation.e.apps.R import foundation.e.apps.api.cleanapk.CleanAPKInterface import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.manager.database.fusedDownload.FusedDownload import foundation.e.apps.manager.fused.FusedManagerRepository import foundation.e.apps.updates.manager.UpdatesManagerRepository import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.modules.DataStoreModule import kotlinx.coroutines.flow.collect import java.io.ByteArrayOutputStream import java.net.URL Loading @@ -35,19 +42,75 @@ class UpdatesWorker @AssistedInject constructor( private val gson: Gson, ) : CoroutineWorker(context, params) { val TAG = UpdatesWorker::class.simpleName private var shouldShowNotification = true private var automaticInstallEnabled = true private var onlyOnUnmeteredNetwork = false override suspend fun doWork(): Result { Log.d(TAG, "doWork: triggered") val authDataJson = dataStoreModule.getAuthDataSync() Log.d(TAG, "doWork: authdata: $authDataJson") val authData = gson.fromJson(authDataJson, AuthData::class.java) return try { checkForUpdates() Result.success() } catch (e: Throwable) { Result.failure() } } private suspend fun checkForUpdates() { loadSettings() val authData = getAuthData() val appsNeededToUpdate = updatesManagerRepository.getUpdates(authData) Log.d(TAG, "doWork: update needs: ${appsNeededToUpdate.size}") val isConnectedToUnmeteredNetwork = isConnectedToUnmeteredNetwork(applicationContext) handleNotification(appsNeededToUpdate, isConnectedToUnmeteredNetwork) triggerUpdateProcessOnSettings( isConnectedToUnmeteredNetwork, appsNeededToUpdate, authData ) } private suspend fun triggerUpdateProcessOnSettings( isConnectedToUnmeteredNetwork: Boolean, appsNeededToUpdate: List<FusedApp>, authData: AuthData ) { if (automaticInstallEnabled && applicationContext.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED ) { if (onlyOnUnmeteredNetwork && isConnectedToUnmeteredNetwork) { startUpdateProcess(appsNeededToUpdate, authData) } else if (!onlyOnUnmeteredNetwork) { startUpdateProcess(appsNeededToUpdate, authData) } } } private fun handleNotification( appsNeededToUpdate: List<FusedApp>, isConnectedToUnmeteredNetwork: Boolean ) { if (appsNeededToUpdate.isNotEmpty()) { UpdatesNotifier().showNotification( applicationContext, appsNeededToUpdate.size, automaticInstallEnabled, onlyOnUnmeteredNetwork, isConnectedToUnmeteredNetwork ) } } private fun getAuthData(): AuthData { val authDataJson = dataStoreModule.getAuthDataSync() return gson.fromJson(authDataJson, AuthData::class.java) } private suspend fun startUpdateProcess( appsNeededToUpdate: List<FusedApp>, authData: AuthData ) { appsNeededToUpdate.forEach { fusedApp -> Log.d(TAG, "doWork: triggering update for: ${fusedApp.name}") val downloadList = getAppDownloadLink(fusedApp, authData).toMutableList() val iconBase64 = fusedApp.getIconImageToBase64() val iconBase64 = getIconImageToBase64(fusedApp) val fusedDownload = FusedDownload( fusedApp._id, Loading @@ -63,16 +126,16 @@ class UpdatesWorker @AssistedInject constructor( ) fusedManagerRepository.addDownload(fusedDownload) Log.d(TAG, "doWork: triggering update for: ${fusedApp.name} added download in db") Log.d(TAG, "doWork: triggering update for: ${fusedApp.name} downloading...") fusedManagerRepository.downloadApp(fusedDownload) Log.d(TAG, "doWork: triggering update for: ${fusedApp.name} downloaded...") } observeFusedDownload() } private suspend fun observeFusedDownload() { fusedManagerRepository.getDownloadListFlow().collect { Log.d(TAG, "doWork: updated downloadlist ${it.size}") it.forEach { fusedDownload -> Log.d(TAG, "doWork: updated downloadlistitem ${fusedDownload.name}") if (fusedDownload.type == Type.NATIVE && fusedDownload.status == Status.INSTALLING && fusedDownload.downloadIdMap.all { it.value }) { Log.d(TAG, "doWork: triggering update for: ${fusedDownload.name} installing...") fusedManagerRepository.installApp(fusedDownload) Loading @@ -80,7 +143,30 @@ class UpdatesWorker @AssistedInject constructor( } } } return Result.success() } private fun loadSettings() { val preferences = PreferenceManager.getDefaultSharedPreferences(applicationContext) shouldShowNotification = preferences.getBoolean( applicationContext.getString( R.string.updateNotify ), true ) automaticInstallEnabled = preferences.getBoolean( applicationContext.getString( R.string.auto_install_enabled ), true ) onlyOnUnmeteredNetwork = preferences.getBoolean( applicationContext.getString( R.string.only_unmetered_network ), false ) } private suspend fun getAppDownloadLink(app: FusedApp, authData: AuthData): List<String> { Loading @@ -101,12 +187,33 @@ class UpdatesWorker @AssistedInject constructor( } return downloadList } } fun FusedApp.getIconImageToBase64(): String { val stream = URL(icon_image_path).openStream() private fun getIconImageToBase64(fusedApp: FusedApp): String { val url = if (fusedApp.origin == Origin.CLEANAPK) "${CleanAPKInterface.ASSET_URL}${fusedApp.icon_image_path}" else fusedApp.icon_image_path val stream = URL(url).openStream() val bitmap = BitmapFactory.decodeStream(stream) val byteArrayOS = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOS) return Base64.encodeToString(byteArrayOS.toByteArray(), Base64.DEFAULT) } /* * Checks if the device is connected to a metered connection or not * @param context current Context * @return returns true if the connections is not metered, false otherwise */ private fun isConnectedToUnmeteredNetwork(context: Context): Boolean { val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) if (capabilities != null) { if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) { return true } } return false } } app/src/main/res/drawable-nodpi/ic_app_updated_on.xml 0 → 100644 +26 −0 Original line number Diff line number Diff line <!-- ~ Copyright (C) 2019-2021 E FOUNDATION ~ ~ This program 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 3 of the License, or ~ (at your option) any later version. ~ ~ This program 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 this program. If not, see <https://www.gnu.org/licenses/>. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="640dp" android:height="512dp" android:viewportWidth="640" android:viewportHeight="512"> <path android:fillColor="@color/colorAccent" android:pathData="M272,80c53.473,0 99.279,32.794 118.426,79.363C401.611,149.793 416.125,144 432,144c35.346,0 64,28.654 64,64 0,11.829 -3.222,22.9 -8.817,32.407A96.998,96.998 0,0 1,496 240c53.019,0 96,42.981 96,96s-42.981,96 -96,96L160,432c-61.856,0 -112,-50.144 -112,-112 0,-56.428 41.732,-103.101 96.014,-110.859 -0.003,-0.381 -0.014,-0.76 -0.014,-1.141 0,-70.692 57.308,-128 128,-128m0,-48c-84.587,0 -155.5,59.732 -172.272,139.774C39.889,196.13 0,254.416 0,320c0,88.374 71.642,160 160,160h336c79.544,0 144,-64.487 144,-144 0,-61.805 -39.188,-115.805 -96.272,-135.891C539.718,142.116 491.432,96 432,96c-7.558,0 -15.051,0.767 -22.369,2.262C377.723,58.272 328.091,32 272,32zM312,420L312,232.535l54.545,55.762c4.671,4.775 12.341,4.817 17.064,0.094l16.877,-16.877c4.686,-4.686 4.686,-12.284 0,-16.971l-104,-104c-4.686,-4.686 -12.284,-4.686 -16.971,0l-104,104c-4.686,4.686 -4.686,12.284 0,16.971l16.877,16.877c4.723,4.723 12.393,4.681 17.064,-0.094L264,232.535L264,372c0,6.627 5.373,12 12,12h24c6.627,0 12,-5.373 12,-12z"/> </vector> app/src/main/res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,9 @@ <!-- Notification --> <string name="downloads">Downloads</string> <string name="updates">Updates</string> <string name="AUTOMATICALLY_INSTALL_updates_notification_text">App updates will be installed automatically</string> <string name="MANUALLY_INSTALL_updates_notification_text">App updates will not be installed automatically</string> <string name="updates_notification_unmetered_network_warning">Waiting for un-metered network</string> <!-- trackers_dialog--> <string name="privacy_computed_using_text">Computed using <a href="https://exodus-privacy.eu.org/en/">Exodus Privacy Analysis</a></string> Loading @@ -134,5 +137,8 @@ <!-- Settings Preferences--> <string name="update_check_intervals">updateCheckIntervals</string> <string name="updateNotify">updateNotify</string> <string name="auto_install_enabled">updateInstallAuto</string> <string name="only_unmetered_network">updateUnmeteredOnly</string> </resources> No newline at end of file Loading
app/src/main/java/foundation/e/apps/MainActivity.kt +5 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.updates.UpdatesNotifier import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.enums.User Loading Loading @@ -115,6 +116,10 @@ class MainActivity : AppCompatActivity() { } } if (intent.hasExtra(UpdatesNotifier.UPDATES_NOTIFICATION_CLICK_EXTRA)) { bottomNavigationView.selectedItemId = R.id.updatesFragment } // Create notification channel on post-nougat devices if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { viewModel.createNotificationChannels() Loading
app/src/main/java/foundation/e/apps/updates/UpdatesNotifier.kt 0 → 100644 +128 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019-2021 E FOUNDATION * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.updates import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import foundation.e.apps.MainActivity import foundation.e.apps.R class UpdatesNotifier { companion object { const val UPDATES_NOTIFICATION_ID = 76 const val UPDATES_NOTIFICATION_CHANNEL_ID = "updates_notification" const val UPDATES_NOTIFICATION_CHANNEL_TITLE = "App updates" const val UPDATES_NOTIFICATION_CLICK_EXTRA = "updates_notification_click_extra" } private fun getNotification( context: Context, numberOfApps: Int, installAutomatically: Boolean, unmeteredNetworkOnly: Boolean, isConnectedToUnmeteredNetwork: Boolean ): Notification { val notificationBuilder = NotificationCompat.Builder(context, UPDATES_NOTIFICATION_CHANNEL_ID) notificationBuilder.setSmallIcon(R.drawable.ic_app_updated_on) notificationBuilder.priority = NotificationCompat.PRIORITY_DEFAULT if (numberOfApps == 1) { notificationBuilder.setContentTitle( context.resources.getQuantityString( R.plurals.updates_notification_title, 1, numberOfApps ) ) } else { notificationBuilder.setContentTitle( context.resources.getQuantityString( R.plurals.updates_notification_title, numberOfApps, numberOfApps ) ) } if (installAutomatically) { notificationBuilder.setContentText(context.getString(R.string.AUTOMATICALLY_INSTALL_updates_notification_text)) if (unmeteredNetworkOnly && !isConnectedToUnmeteredNetwork) { notificationBuilder.setSubText( context .getString(R.string.updates_notification_unmetered_network_warning) ) } } else { notificationBuilder.setContentText(context.getString(R.string.MANUALLY_INSTALL_updates_notification_text)) } notificationBuilder.setContentIntent(getClickIntent(context)) notificationBuilder.setAutoCancel(true) return notificationBuilder.build() } private fun getClickIntent(context: Context): PendingIntent { val intent = Intent(context, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra(UPDATES_NOTIFICATION_CLICK_EXTRA, true) } return PendingIntent.getActivity(context, 0, intent, 0) } private fun createNotificationChannel(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val importance = NotificationManager.IMPORTANCE_DEFAULT val channel = NotificationChannel( UPDATES_NOTIFICATION_CHANNEL_ID, UPDATES_NOTIFICATION_CHANNEL_TITLE, importance ) val notificationManager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } fun showNotification( context: Context, numberOfApps: Int, installAutomatically: Boolean, unmeteredNetworkOnly: Boolean, isConnectedToUnmeteredNetwork: Boolean ) { with(NotificationManagerCompat.from(context)) { createNotificationChannel(context) notify( UPDATES_NOTIFICATION_ID, getNotification( context, numberOfApps, installAutomatically, unmeteredNetworkOnly, isConnectedToUnmeteredNetwork ) ) } } }
app/src/main/java/foundation/e/apps/updates/UpdatesWorker.kt +126 −19 Original line number Diff line number Diff line package foundation.e.apps.updates import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.util.Base64 import android.util.Log import androidx.hilt.work.HiltWorker import androidx.preference.PreferenceManager import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.aurora.gplayapi.data.models.AuthData import com.google.gson.Gson import dagger.assisted.Assisted import dagger.assisted.AssistedInject import foundation.e.apps.R import foundation.e.apps.api.cleanapk.CleanAPKInterface import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.manager.database.fusedDownload.FusedDownload import foundation.e.apps.manager.fused.FusedManagerRepository import foundation.e.apps.updates.manager.UpdatesManagerRepository import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.modules.DataStoreModule import kotlinx.coroutines.flow.collect import java.io.ByteArrayOutputStream import java.net.URL Loading @@ -35,19 +42,75 @@ class UpdatesWorker @AssistedInject constructor( private val gson: Gson, ) : CoroutineWorker(context, params) { val TAG = UpdatesWorker::class.simpleName private var shouldShowNotification = true private var automaticInstallEnabled = true private var onlyOnUnmeteredNetwork = false override suspend fun doWork(): Result { Log.d(TAG, "doWork: triggered") val authDataJson = dataStoreModule.getAuthDataSync() Log.d(TAG, "doWork: authdata: $authDataJson") val authData = gson.fromJson(authDataJson, AuthData::class.java) return try { checkForUpdates() Result.success() } catch (e: Throwable) { Result.failure() } } private suspend fun checkForUpdates() { loadSettings() val authData = getAuthData() val appsNeededToUpdate = updatesManagerRepository.getUpdates(authData) Log.d(TAG, "doWork: update needs: ${appsNeededToUpdate.size}") val isConnectedToUnmeteredNetwork = isConnectedToUnmeteredNetwork(applicationContext) handleNotification(appsNeededToUpdate, isConnectedToUnmeteredNetwork) triggerUpdateProcessOnSettings( isConnectedToUnmeteredNetwork, appsNeededToUpdate, authData ) } private suspend fun triggerUpdateProcessOnSettings( isConnectedToUnmeteredNetwork: Boolean, appsNeededToUpdate: List<FusedApp>, authData: AuthData ) { if (automaticInstallEnabled && applicationContext.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED ) { if (onlyOnUnmeteredNetwork && isConnectedToUnmeteredNetwork) { startUpdateProcess(appsNeededToUpdate, authData) } else if (!onlyOnUnmeteredNetwork) { startUpdateProcess(appsNeededToUpdate, authData) } } } private fun handleNotification( appsNeededToUpdate: List<FusedApp>, isConnectedToUnmeteredNetwork: Boolean ) { if (appsNeededToUpdate.isNotEmpty()) { UpdatesNotifier().showNotification( applicationContext, appsNeededToUpdate.size, automaticInstallEnabled, onlyOnUnmeteredNetwork, isConnectedToUnmeteredNetwork ) } } private fun getAuthData(): AuthData { val authDataJson = dataStoreModule.getAuthDataSync() return gson.fromJson(authDataJson, AuthData::class.java) } private suspend fun startUpdateProcess( appsNeededToUpdate: List<FusedApp>, authData: AuthData ) { appsNeededToUpdate.forEach { fusedApp -> Log.d(TAG, "doWork: triggering update for: ${fusedApp.name}") val downloadList = getAppDownloadLink(fusedApp, authData).toMutableList() val iconBase64 = fusedApp.getIconImageToBase64() val iconBase64 = getIconImageToBase64(fusedApp) val fusedDownload = FusedDownload( fusedApp._id, Loading @@ -63,16 +126,16 @@ class UpdatesWorker @AssistedInject constructor( ) fusedManagerRepository.addDownload(fusedDownload) Log.d(TAG, "doWork: triggering update for: ${fusedApp.name} added download in db") Log.d(TAG, "doWork: triggering update for: ${fusedApp.name} downloading...") fusedManagerRepository.downloadApp(fusedDownload) Log.d(TAG, "doWork: triggering update for: ${fusedApp.name} downloaded...") } observeFusedDownload() } private suspend fun observeFusedDownload() { fusedManagerRepository.getDownloadListFlow().collect { Log.d(TAG, "doWork: updated downloadlist ${it.size}") it.forEach { fusedDownload -> Log.d(TAG, "doWork: updated downloadlistitem ${fusedDownload.name}") if (fusedDownload.type == Type.NATIVE && fusedDownload.status == Status.INSTALLING && fusedDownload.downloadIdMap.all { it.value }) { Log.d(TAG, "doWork: triggering update for: ${fusedDownload.name} installing...") fusedManagerRepository.installApp(fusedDownload) Loading @@ -80,7 +143,30 @@ class UpdatesWorker @AssistedInject constructor( } } } return Result.success() } private fun loadSettings() { val preferences = PreferenceManager.getDefaultSharedPreferences(applicationContext) shouldShowNotification = preferences.getBoolean( applicationContext.getString( R.string.updateNotify ), true ) automaticInstallEnabled = preferences.getBoolean( applicationContext.getString( R.string.auto_install_enabled ), true ) onlyOnUnmeteredNetwork = preferences.getBoolean( applicationContext.getString( R.string.only_unmetered_network ), false ) } private suspend fun getAppDownloadLink(app: FusedApp, authData: AuthData): List<String> { Loading @@ -101,12 +187,33 @@ class UpdatesWorker @AssistedInject constructor( } return downloadList } } fun FusedApp.getIconImageToBase64(): String { val stream = URL(icon_image_path).openStream() private fun getIconImageToBase64(fusedApp: FusedApp): String { val url = if (fusedApp.origin == Origin.CLEANAPK) "${CleanAPKInterface.ASSET_URL}${fusedApp.icon_image_path}" else fusedApp.icon_image_path val stream = URL(url).openStream() val bitmap = BitmapFactory.decodeStream(stream) val byteArrayOS = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOS) return Base64.encodeToString(byteArrayOS.toByteArray(), Base64.DEFAULT) } /* * Checks if the device is connected to a metered connection or not * @param context current Context * @return returns true if the connections is not metered, false otherwise */ private fun isConnectedToUnmeteredNetwork(context: Context): Boolean { val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) if (capabilities != null) { if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) { return true } } return false } }
app/src/main/res/drawable-nodpi/ic_app_updated_on.xml 0 → 100644 +26 −0 Original line number Diff line number Diff line <!-- ~ Copyright (C) 2019-2021 E FOUNDATION ~ ~ This program 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 3 of the License, or ~ (at your option) any later version. ~ ~ This program 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 this program. If not, see <https://www.gnu.org/licenses/>. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="640dp" android:height="512dp" android:viewportWidth="640" android:viewportHeight="512"> <path android:fillColor="@color/colorAccent" android:pathData="M272,80c53.473,0 99.279,32.794 118.426,79.363C401.611,149.793 416.125,144 432,144c35.346,0 64,28.654 64,64 0,11.829 -3.222,22.9 -8.817,32.407A96.998,96.998 0,0 1,496 240c53.019,0 96,42.981 96,96s-42.981,96 -96,96L160,432c-61.856,0 -112,-50.144 -112,-112 0,-56.428 41.732,-103.101 96.014,-110.859 -0.003,-0.381 -0.014,-0.76 -0.014,-1.141 0,-70.692 57.308,-128 128,-128m0,-48c-84.587,0 -155.5,59.732 -172.272,139.774C39.889,196.13 0,254.416 0,320c0,88.374 71.642,160 160,160h336c79.544,0 144,-64.487 144,-144 0,-61.805 -39.188,-115.805 -96.272,-135.891C539.718,142.116 491.432,96 432,96c-7.558,0 -15.051,0.767 -22.369,2.262C377.723,58.272 328.091,32 272,32zM312,420L312,232.535l54.545,55.762c4.671,4.775 12.341,4.817 17.064,0.094l16.877,-16.877c4.686,-4.686 4.686,-12.284 0,-16.971l-104,-104c-4.686,-4.686 -12.284,-4.686 -16.971,0l-104,104c-4.686,4.686 -4.686,12.284 0,16.971l16.877,16.877c4.723,4.723 12.393,4.681 17.064,-0.094L264,232.535L264,372c0,6.627 5.373,12 12,12h24c6.627,0 12,-5.373 12,-12z"/> </vector>
app/src/main/res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,9 @@ <!-- Notification --> <string name="downloads">Downloads</string> <string name="updates">Updates</string> <string name="AUTOMATICALLY_INSTALL_updates_notification_text">App updates will be installed automatically</string> <string name="MANUALLY_INSTALL_updates_notification_text">App updates will not be installed automatically</string> <string name="updates_notification_unmetered_network_warning">Waiting for un-metered network</string> <!-- trackers_dialog--> <string name="privacy_computed_using_text">Computed using <a href="https://exodus-privacy.eu.org/en/">Exodus Privacy Analysis</a></string> Loading @@ -134,5 +137,8 @@ <!-- Settings Preferences--> <string name="update_check_intervals">updateCheckIntervals</string> <string name="updateNotify">updateNotify</string> <string name="auto_install_enabled">updateInstallAuto</string> <string name="only_unmetered_network">updateUnmeteredOnly</string> </resources> No newline at end of file