Implement automated updates check

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2023-06-17 22:14:05 +05:30
parent 42c54c5d1d
commit 9b8e3cf9c4
10 changed files with 294 additions and 5 deletions

View File

@@ -37,6 +37,7 @@ object Constants {
const val NOTIFICATION_CHANNEL_ALERT = "NOTIFICATION_CHANNEL_ALERT"
const val NOTIFICATION_CHANNEL_GENERAL = "NOTIFICATION_CHANNEL_GENERAL"
const val NOTIFICATION_CHANNEL_UPDATES = "NOTIFICATION_CHANNEL_UPDATES"
const val NOTIFICATION_CHANNEL_UPDATER_SERVICE = "NOTIFICATION_CHANNEL_UPDATER_SERVICE"
const val URL_DISPENSER = "https://auroraoss.com/api/auth"
@@ -53,4 +54,7 @@ object Constants {
const val PAGE_TYPE = "PAGE_TYPE"
const val TOP_CHART_TYPE = "TOP_CHART_TYPE"
const val TOP_CHART_CATEGORY = "TOP_CHART_CATEGORY"
//NAVIGATION
const val NAVIGATION_UPDATES = "NAVIGATION_UPDATES"
}

View File

@@ -113,6 +113,13 @@ class AuroraApplication : MultiDexApplication() {
NotificationManager.IMPORTANCE_MIN
)
)
channels.add(
NotificationChannel(
Constants.NOTIFICATION_CHANNEL_UPDATES,
getString(R.string.notification_channel_updates),
NotificationManager.IMPORTANCE_DEFAULT
)
)
notificationManager.createNotificationChannels(channels)
}
}

View File

@@ -165,6 +165,12 @@ class MainActivity : BaseActivity() {
B.drawerLayout.close()
}
}
// Handle intents
when (intent?.action) {
Constants.NAVIGATION_UPDATES -> B.navView.selectedItemId = R.id.navigation_updates
else -> Log.i("Unhandled intent action: ${intent.action}")
}
}
private fun attachToolbar() {

View File

@@ -0,0 +1,239 @@
package com.aurora.store.data.work
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
import androidx.core.content.pm.PackageInfoCompat
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
import androidx.work.ForegroundInfo
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.helpers.AppDetailsHelper
import com.aurora.gplayapi.helpers.AuthValidator
import com.aurora.store.MainActivity
import com.aurora.store.R
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.data.providers.BlacklistProvider
import com.aurora.store.util.Log
import com.aurora.store.util.PackageUtil
import com.aurora.store.util.Preferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit.HOURS
import java.util.concurrent.TimeUnit.MINUTES
class UpdateWorker(private val appContext: Context, workerParams: WorkerParameters) :
CoroutineWorker(appContext, workerParams) {
companion object {
private const val WORK_NAME_CHECK = "WORK_NAME_CHECK"
fun cancelAutomatedCheck(context: Context) {
Log.i("Cancelling periodic app updates check!")
WorkManager.getInstance(context)
.cancelUniqueWork(WORK_NAME_CHECK)
}
fun scheduleAutomatedCheck(context: Context) {
Log.i("Scheduling periodic app updates check!")
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresDeviceIdle(true)
.setRequiresBatteryNotLow(true)
.build()
val workRequest = PeriodicWorkRequestBuilder<UpdateWorker>(
repeatInterval = 3,
repeatIntervalTimeUnit = HOURS,
flexTimeInterval = 30,
flexTimeIntervalUnit = MINUTES
).setConstraints(constraints).build()
val workManager = WorkManager.getInstance(context)
workManager.enqueueUniquePeriodicWork(WORK_NAME_CHECK, KEEP, workRequest)
}
}
private val notificationID = 100
private val workerID = 101
private val gapps: MutableSet<String> = hashSetOf(
"com.chrome.beta",
"com.chrome.canary",
"com.chrome.dev",
"com.android.chrome",
"com.niksoftware.snapseed",
"com.google.toontastic"
)
private val authData = AuthProvider.with(appContext)
.getAuthData()
private val blackList = BlacklistProvider.with(appContext)
.getBlackList()
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(workerID, getOngoingNotification())
}
override suspend fun doWork(): Result {
withContext(Dispatchers.IO) {
if (!isValid(authData)) {
Log.i("AuthData is not valid, retrying later!")
return@withContext Result.retry()
}
Log.i("Checking for app updates")
val appDetailsHelper = AppDetailsHelper(authData)
.using(HttpClient.getPreferredClient())
val isGoogleFilterEnabled = Preferences.getBoolean(
appContext,
Preferences.PREFERENCE_FILTER_GOOGLE
)
val packageInfoMap = PackageUtil.getPackageInfoMap(appContext)
packageInfoMap.keys.let { packages ->
/*Filter black list*/
var filtersPackages = packages.filter { !blackList.contains(it) }
/*Filter google apps*/
if (isGoogleFilterEnabled) {
filtersPackages = filtersPackages
.filter { !it.startsWith("com.google") }
.filter { !gapps.contains(it) }
}
val appList = appDetailsHelper.getAppByPackageName(filtersPackages)
.filter { it.displayName.isNotEmpty() }
val updatesList = appList.filter {
val packageInfo = packageInfoMap[it.packageName]
if (packageInfo != null) {
it.versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo)
} else {
false
}
}
if (updatesList.isNotEmpty()) {
Log.i("Found updates, notifying!")
val notifyManager = appContext.getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
notifyManager.notify(notificationID, getUpdateNotification(updatesList))
return@withContext Result.success()
}
Log.i("No updates found!")
return@withContext Result.success()
}
}
return Result.failure()
}
private fun isValid(authData: AuthData): Boolean {
return try {
AuthValidator(authData)
.using(HttpClient.getPreferredClient())
.isValid()
} catch (e: Exception) {
false
}
}
private fun getUpdateNotification(updatesList: List<App>): Notification {
val contentIntent = PendingIntent.getActivity(
appContext,
0,
Intent(appContext, MainActivity::class.java).apply {
action = Constants.NAVIGATION_UPDATES
},
PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Builder(appContext, Constants.NOTIFICATION_CHANNEL_UPDATES)
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle(
appContext.getString(
R.string.notification_updates_available,
updatesList.size
)
)
.setContentText(
when (updatesList.size) {
1 -> {
appContext.getString(
R.string.notification_updates_available_desc_1,
updatesList[0].displayName
)
}
2 -> {
appContext.getString(
R.string.notification_updates_available_desc_2,
updatesList[0].displayName,
updatesList[1].displayName
)
}
3 -> {
appContext.getString(
R.string.notification_updates_available_desc_3,
updatesList[0].displayName,
updatesList[1].displayName,
updatesList[2].displayName
)
}
else -> {
appContext.getString(
R.string.notification_updates_available_desc_4,
updatesList[0].displayName,
updatesList[1].displayName,
updatesList[2].displayName,
updatesList.size - 3
)
}
}
)
.setContentIntent(contentIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_RECOMMENDATION)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setAutoCancel(true)
.build()
}
private fun getOngoingNotification(): Notification {
val contentIntent = PendingIntent.getActivity(
appContext,
0,
Intent(appContext, MainActivity::class.java),
PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Builder(appContext, Constants.NOTIFICATION_CHANNEL_UPDATES)
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle(appContext.getString(R.string.checking_for_updates))
.setContentIntent(contentIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setForegroundServiceBehavior(FOREGROUND_SERVICE_IMMEDIATE)
.setProgress(100, 0, true)
.setOngoing(true)
.build()
}
}

View File

@@ -58,6 +58,7 @@ object Preferences {
const val PREFERENCE_INSECURE_ANONYMOUS = "PREFERENCE_INSECURE_ANONYMOUS"
const val PREFERENCE_UPDATES_EXTENDED = "PREFERENCE_UPDATES_EXTENDED"
const val PREFERENCE_UPDATES_CHECK = "PREFERENCE_UPDATES_CHECK"
const val PREFERENCE_ADVANCED_SEARCH_IN_CTT = "PREFERENCE_ADVANCED_SEARCH_IN_CTT"

View File

@@ -30,6 +30,7 @@ import com.aurora.Constants
import com.aurora.extensions.isSAndAbove
import com.aurora.extensions.open
import com.aurora.store.R
import com.aurora.store.data.work.UpdateWorker
import com.aurora.store.databinding.ActivityOnboardingBinding
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
@@ -48,6 +49,7 @@ import com.aurora.store.util.Preferences.PREFERENCE_QUICK_EXIT
import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR
import com.aurora.store.util.Preferences.PREFERENCE_THEME_ACCENT
import com.aurora.store.util.Preferences.PREFERENCE_THEME_TYPE
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
import com.aurora.store.util.save
import com.aurora.store.view.ui.commons.BaseActivity
@@ -152,6 +154,7 @@ class OnboardingActivity : BaseActivity() {
B.btnForward.isEnabled = true
B.btnForward.setOnClickListener {
save(PREFERENCE_INTRO, true)
UpdateWorker.scheduleAutomatedCheck(this)
open(SplashActivity::class.java, true)
}
} else {
@@ -191,6 +194,7 @@ class OnboardingActivity : BaseActivity() {
/*Updates*/
save(PREFERENCE_UPDATES_EXTENDED, false)
save(PREFERENCE_UPDATES_CHECK, true)
}
internal class PagerAdapter(fragmentActivity: FragmentActivity) :

View File

@@ -20,16 +20,25 @@
package com.aurora.store.view.ui.preferences
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.aurora.extensions.runOnUiThread
import com.aurora.extensions.toast
import androidx.preference.SwitchPreferenceCompat
import com.aurora.store.R
import com.aurora.store.util.CommonUtil
import com.aurora.store.util.Preferences
import com.aurora.store.data.work.UpdateWorker
import com.aurora.store.util.Log
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK
class UpdatesPreference : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_updates, rootKey)
findPreference<SwitchPreferenceCompat>(PREFERENCE_UPDATES_CHECK)
?.setOnPreferenceChangeListener { _, newValue ->
if (newValue.toString().toBoolean()) {
UpdateWorker.scheduleAutomatedCheck(requireContext())
} else {
UpdateWorker.cancelAutomatedCheck(requireContext())
}
true
}
}
}

View File

@@ -355,4 +355,13 @@
<string name="requesting_new_session">Requesting new session</string>
<string name="server_maintenance">Server down for maintenance</string>
<string name="downloading_dep">Downloading additional files for <xliff:g id="app">%1$s</xliff:g></string>
<string name="notification_channel_updates">Updates notifications</string>
<string name="notification_updates_available"><xliff:g id="apps_count">%1$d</xliff:g> updates available</string>
<string name="notification_updates_available_desc_1">A new version of <xliff:g id="app_one_name">%1$s</xliff:g> is available</string>
<string name="notification_updates_available_desc_2"><xliff:g id="app_one_name">%1$s</xliff:g> and <xliff:g id="app_two_name">%2$s</xliff:g></string>
<string name="notification_updates_available_desc_3"><xliff:g id="app_one_name">%1$s</xliff:g>, <xliff:g id="app_two_name">%2$s</xliff:g> and <xliff:g id="app_three_name">%3$s</xliff:g></string>
<string name="notification_updates_available_desc_4"><xliff:g id="app_one_name">%1$s</xliff:g>, <xliff:g id="app_two_name">%2$s</xliff:g>, <xliff:g id="app_three_name">%3$s</xliff:g> and <xliff:g id="apps_count">%4$d</xliff:g> more</string>
<string name="checking_for_updates">Checking for updates</string>
<string name="pref_updates_check">Automated updates check</string>
<string name="pref_updates_check_desc">Automatically check &amp; notify for new app updates periodically</string>
</resources>

View File

@@ -27,4 +27,11 @@
app:summary="@string/pref_updates_extended_desc"
app:title="@string/pref_updates_extended" />
<SwitchPreferenceCompat
app:defaultValue="true"
app:iconSpaceReserved="false"
app:key="PREFERENCE_UPDATES_CHECK"
app:summary="@string/pref_updates_check_desc"
app:title="@string/pref_updates_check" />
</androidx.preference.PreferenceScreen>