SelfUpdateWorker: Move self-updates logic into dedicated worker
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package com.aurora.extensions
|
package com.aurora.extensions
|
||||||
|
|
||||||
|
import com.aurora.gplayapi.data.models.File
|
||||||
|
|
||||||
fun <T> MutableList<T>.flushAndAdd(list: List<T>) {
|
fun <T> MutableList<T>.flushAndAdd(list: List<T>) {
|
||||||
clear()
|
clear()
|
||||||
addAll(list)
|
addAll(list)
|
||||||
@@ -28,3 +30,7 @@ fun <T> MutableSet<T>.flushAndAdd(list: Set<T>) {
|
|||||||
clear()
|
clear()
|
||||||
addAll(list)
|
addAll(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun List<File>.requiresObbDir(): Boolean {
|
||||||
|
return this.any { it.type == File.FileType.OBB || it.type == File.FileType.PATCH }
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import android.widget.ImageView
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.addCallback
|
import androidx.activity.addCallback
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.viewModels
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
@@ -60,16 +59,12 @@ import com.aurora.extensions.isMAndAbove
|
|||||||
import com.aurora.extensions.isRAndAbove
|
import com.aurora.extensions.isRAndAbove
|
||||||
import com.aurora.extensions.toast
|
import com.aurora.extensions.toast
|
||||||
import com.aurora.store.data.model.NetworkStatus
|
import com.aurora.store.data.model.NetworkStatus
|
||||||
import com.aurora.store.data.model.SelfUpdate
|
|
||||||
import com.aurora.store.data.providers.NetworkProvider
|
import com.aurora.store.data.providers.NetworkProvider
|
||||||
import com.aurora.store.databinding.ActivityMainBinding
|
import com.aurora.store.databinding.ActivityMainBinding
|
||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
import com.aurora.store.util.Preferences
|
import com.aurora.store.util.Preferences
|
||||||
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
|
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
|
||||||
import com.aurora.store.util.Preferences.PREFERENCE_SELF_UPDATE
|
|
||||||
import com.aurora.store.view.ui.sheets.NetworkDialogSheet
|
import com.aurora.store.view.ui.sheets.NetworkDialogSheet
|
||||||
import com.aurora.store.view.ui.sheets.SelfUpdateSheet
|
|
||||||
import com.aurora.store.viewmodel.MainViewModel
|
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -81,8 +76,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private lateinit var navController: NavController
|
private lateinit var navController: NavController
|
||||||
private lateinit var appConfig: AppBarConfiguration
|
private lateinit var appConfig: AppBarConfiguration
|
||||||
|
|
||||||
private val viewModel: MainViewModel by viewModels()
|
|
||||||
|
|
||||||
private val startForPermissions =
|
private val startForPermissions =
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||||
if (!it) toast(R.string.permissions_denied)
|
if (!it) toast(R.string.permissions_denied)
|
||||||
@@ -154,20 +147,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check self update only for stable release, skip debug & nightlies*/
|
|
||||||
if (Preferences.getBoolean(this, PREFERENCE_SELF_UPDATE)) {
|
|
||||||
if (BuildConfig.APPLICATION_ID == Constants.APP_ID) viewModel.checkSelfUpdate(this)
|
|
||||||
this.lifecycleScope.launch {
|
|
||||||
viewModel.selfUpdateAvailable.collect {
|
|
||||||
if (it != null) {
|
|
||||||
showUpdatesSheet(it)
|
|
||||||
} else {
|
|
||||||
Log.i("No Aurora Store update available")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle quick exit from back actions
|
// Handle quick exit from back actions
|
||||||
val defaultTab = when (Preferences.getInteger(this, PREFERENCE_DEFAULT_SELECTED_TAB)) {
|
val defaultTab = when (Preferences.getInteger(this, PREFERENCE_DEFAULT_SELECTED_TAB)) {
|
||||||
1 -> R.id.gamesContainerFragment
|
1 -> R.id.gamesContainerFragment
|
||||||
@@ -296,14 +275,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showUpdatesSheet(selfUpdate: SelfUpdate) {
|
|
||||||
if (!supportFragmentManager.isDestroyed) {
|
|
||||||
val sheet = SelfUpdateSheet.newInstance(selfUpdate)
|
|
||||||
sheet.isCancelable = false
|
|
||||||
sheet.show(supportFragmentManager, SelfUpdateSheet.TAG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isIntroDone(): Boolean {
|
private fun isIntroDone(): Boolean {
|
||||||
return Preferences.getBoolean(this@MainActivity, Preferences.PREFERENCE_INTRO)
|
return Preferences.getBoolean(this@MainActivity, Preferences.PREFERENCE_INTRO)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,21 +19,53 @@
|
|||||||
|
|
||||||
package com.aurora.store.data.model
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.gplayapi.data.models.Artwork
|
||||||
|
import com.aurora.gplayapi.data.models.File
|
||||||
|
import com.aurora.store.BuildConfig
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.util.CertUtil
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
class SelfUpdate {
|
data class SelfUpdate(
|
||||||
@SerializedName("version_name")
|
@SerializedName("version_name") var versionName: String = String(),
|
||||||
var versionName: String = String()
|
@SerializedName("version_code") var versionCode: Int = 0,
|
||||||
|
@SerializedName("aurora_build") var auroraBuild: String = String(),
|
||||||
|
@SerializedName("fdroid_build") var fdroidBuild: String = String(),
|
||||||
|
val changelog: String = String(),
|
||||||
|
val size: Long = 0L
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL = "https://gitlab.com/AuroraOSS/AuroraStore/-/raw/master"
|
||||||
|
|
||||||
@SerializedName("version_code")
|
fun toApp(selfUpdate: SelfUpdate, context: Context): App {
|
||||||
var versionCode: Int = 0
|
// Keep paths updated with fastlane data on project
|
||||||
|
val icon = "fastlane/metadata/android/en-US/images/icon.png"
|
||||||
|
|
||||||
@SerializedName("aurora_build")
|
val downloadURL = if (CertUtil.isFDroidApp(context, BuildConfig.APPLICATION_ID)) {
|
||||||
var auroraBuild: String = String()
|
selfUpdate.fdroidBuild
|
||||||
|
} else {
|
||||||
|
selfUpdate.auroraBuild
|
||||||
|
}
|
||||||
|
|
||||||
@SerializedName("fdroid_build")
|
return App(
|
||||||
var fdroidBuild: String = String()
|
packageName = Constants.APP_ID,
|
||||||
|
versionCode = selfUpdate.versionCode,
|
||||||
@SerializedName("changelog")
|
versionName = selfUpdate.versionName,
|
||||||
var changelog: String = String()
|
changes = selfUpdate.changelog,
|
||||||
|
size = selfUpdate.size,
|
||||||
|
displayName = context.getString(R.string.app_name),
|
||||||
|
iconArtwork = Artwork(url = "$BASE_URL/$icon"),
|
||||||
|
fileList = mutableListOf(
|
||||||
|
File(
|
||||||
|
name = "${Constants.APP_ID}.apk",
|
||||||
|
url = downloadURL,
|
||||||
|
size = selfUpdate.size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import android.os.Parcelable
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.gplayapi.data.models.File
|
||||||
import com.aurora.store.data.model.DownloadStatus
|
import com.aurora.store.data.model.DownloadStatus
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ data class Download(
|
|||||||
var timeRemaining: Long,
|
var timeRemaining: Long,
|
||||||
var totalFiles: Int,
|
var totalFiles: Int,
|
||||||
var downloadedFiles: Int,
|
var downloadedFiles: Int,
|
||||||
|
var fileList: List<File>,
|
||||||
val sharedLibs: List<SharedLib>
|
val sharedLibs: List<SharedLib>
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val isFinished get() = status in DownloadStatus.finished
|
val isFinished get() = status in DownloadStatus.finished
|
||||||
@@ -45,6 +47,7 @@ data class Download(
|
|||||||
0L,
|
0L,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
app.fileList.filterNot { it.url.isBlank() },
|
||||||
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) }
|
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.aurora.store.data.room.download
|
package com.aurora.store.data.room.download
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
|
import com.aurora.gplayapi.data.models.File
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
|
|
||||||
@@ -16,4 +17,15 @@ object DownloadConverter {
|
|||||||
fun fromSharedLibList(list: List<SharedLib>): String {
|
fun fromSharedLibList(list: List<SharedLib>): String {
|
||||||
return Gson().toJson(list)
|
return Gson().toJson(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun toGPlayFileList(string: String): List<File> {
|
||||||
|
val listType = object : TypeToken<List<File>>() {}.type
|
||||||
|
return Gson().fromJson(string, listType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun fromGPlayFileList(list: List<File>): String {
|
||||||
|
return Gson().toJson(list)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,22 @@ package com.aurora.store.data.room.download
|
|||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.gplayapi.data.models.File
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class SharedLib(
|
data class SharedLib(
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val versionCode: Int
|
val versionCode: Int,
|
||||||
|
var fileList: List<File>
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
companion object {
|
companion object {
|
||||||
fun fromApp(app: App): SharedLib {
|
fun fromApp(app: App): SharedLib {
|
||||||
return SharedLib(app.packageName, app.versionCode)
|
return SharedLib(
|
||||||
|
app.packageName,
|
||||||
|
app.versionCode,
|
||||||
|
app.fileList.filterNot { it.url.isBlank() }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.work.WorkerParameters
|
|||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.extensions.copyTo
|
import com.aurora.extensions.copyTo
|
||||||
import com.aurora.extensions.isQAndAbove
|
import com.aurora.extensions.isQAndAbove
|
||||||
|
import com.aurora.extensions.requiresObbDir
|
||||||
import com.aurora.gplayapi.helpers.PurchaseHelper
|
import com.aurora.gplayapi.helpers.PurchaseHelper
|
||||||
import com.aurora.store.data.model.DownloadInfo
|
import com.aurora.store.data.model.DownloadInfo
|
||||||
import com.aurora.store.data.model.DownloadStatus
|
import com.aurora.store.data.model.DownloadStatus
|
||||||
@@ -80,9 +81,10 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
appContext.getSystemService(Service.NOTIFICATION_SERVICE) as NotificationManager
|
appContext.getSystemService(Service.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
// Bail out if file list is empty
|
// Bail out if file list is empty
|
||||||
val files =
|
download.fileList = download.fileList.ifEmpty {
|
||||||
purchaseHelper.purchase(download.packageName, download.versionCode, download.offerType)
|
purchaseHelper.purchase(download.packageName, download.versionCode, download.offerType)
|
||||||
if (files.isEmpty()) {
|
}
|
||||||
|
if (download.fileList.isEmpty()) {
|
||||||
Log.i(TAG, "Nothing to download!")
|
Log.i(TAG, "Nothing to download!")
|
||||||
notifyStatus(DownloadStatus.FAILED)
|
notifyStatus(DownloadStatus.FAILED)
|
||||||
return Result.failure()
|
return Result.failure()
|
||||||
@@ -90,7 +92,7 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
|
|
||||||
// Create dirs & generate download request for files and shared libs (if any)
|
// Create dirs & generate download request for files and shared libs (if any)
|
||||||
PathUtil.getAppDownloadDir(appContext, download.packageName, download.versionCode).mkdirs()
|
PathUtil.getAppDownloadDir(appContext, download.packageName, download.versionCode).mkdirs()
|
||||||
if (files.any { it.type == GPlayFile.FileType.OBB || it.type == GPlayFile.FileType.PATCH }) {
|
if (download.fileList.requiresObbDir()) {
|
||||||
PathUtil.getObbDownloadDir(download.packageName).mkdirs()
|
PathUtil.getObbDownloadDir(download.packageName).mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,11 +106,13 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
download.versionCode,
|
download.versionCode,
|
||||||
it.packageName
|
it.packageName
|
||||||
).mkdirs()
|
).mkdirs()
|
||||||
val libs = purchaseHelper.purchase(it.packageName, it.versionCode, 0)
|
it.fileList = it.fileList.ifEmpty {
|
||||||
requestList.addAll(getDownloadRequest(libs, it.packageName))
|
purchaseHelper.purchase(it.packageName, it.versionCode, 0)
|
||||||
|
}
|
||||||
|
requestList.addAll(getDownloadRequest(it.fileList, it.packageName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requestList.addAll(getDownloadRequest(files, null))
|
requestList.addAll(getDownloadRequest(download.fileList, null))
|
||||||
|
|
||||||
// Update data for notification
|
// Update data for notification
|
||||||
download.totalFiles = requestList.size
|
download.totalFiles = requestList.size
|
||||||
|
|||||||
101
app/src/main/java/com/aurora/store/data/work/SelfUpdateWorker.kt
Normal file
101
app/src/main/java/com/aurora/store/data/work/SelfUpdateWorker.kt
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package com.aurora.store.data.work
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.hilt.work.HiltWorker
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.extensions.isMAndAbove
|
||||||
|
import com.aurora.store.BuildConfig
|
||||||
|
import com.aurora.store.data.model.SelfUpdate
|
||||||
|
import com.aurora.store.data.network.HttpClient
|
||||||
|
import com.aurora.store.util.CertUtil
|
||||||
|
import com.aurora.store.util.DownloadWorkerUtil
|
||||||
|
import com.aurora.store.util.Preferences
|
||||||
|
import com.aurora.store.util.Preferences.PREFERENCE_SELF_UPDATE
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import java.util.concurrent.TimeUnit.HOURS
|
||||||
|
import java.util.concurrent.TimeUnit.MINUTES
|
||||||
|
|
||||||
|
@HiltWorker
|
||||||
|
class SelfUpdateWorker @AssistedInject constructor(
|
||||||
|
private val downloadWorkerUtil: DownloadWorkerUtil,
|
||||||
|
private val gson: Gson,
|
||||||
|
@Assisted private val appContext: Context,
|
||||||
|
@Assisted workerParams: WorkerParameters
|
||||||
|
) : CoroutineWorker(appContext, workerParams) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "SelfUpdateWorker"
|
||||||
|
private const val SELF_UPDATE_WORKER = "SELF_UPDATE_WORKER"
|
||||||
|
|
||||||
|
fun cancelAutomatedCheck(context: Context) {
|
||||||
|
Log.i(TAG, "Cancelling periodic self-updates!")
|
||||||
|
WorkManager.getInstance(context).cancelUniqueWork(SELF_UPDATE_WORKER)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scheduleAutomatedCheck(context: Context) {
|
||||||
|
Log.i(TAG, "Scheduling periodic self-updates!")
|
||||||
|
|
||||||
|
val constraints = Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(NetworkType.UNMETERED)
|
||||||
|
.setRequiresBatteryNotLow(true)
|
||||||
|
|
||||||
|
if (isMAndAbove()) constraints.setRequiresDeviceIdle(true)
|
||||||
|
|
||||||
|
val workRequest = PeriodicWorkRequestBuilder<UpdateWorker>(
|
||||||
|
repeatInterval = 24,
|
||||||
|
repeatIntervalTimeUnit = HOURS,
|
||||||
|
flexTimeInterval = 30,
|
||||||
|
flexTimeIntervalUnit = MINUTES
|
||||||
|
).setConstraints(constraints.build()).build()
|
||||||
|
|
||||||
|
val workManager = WorkManager.getInstance(context)
|
||||||
|
workManager.enqueueUniquePeriodicWork(SELF_UPDATE_WORKER, KEEP, workRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val TAG = SelfUpdateWorker::class.java.simpleName
|
||||||
|
private val RELEASE = "release"
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
@Suppress("KotlinConstantConditions") // False-positive for build type always not being release
|
||||||
|
if (BuildConfig.BUILD_TYPE != RELEASE && Preferences.getBoolean(appContext, PREFERENCE_SELF_UPDATE)) {
|
||||||
|
Log.e(TAG, "Either build type is not release or self-update check is disabled")
|
||||||
|
return Result.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = HttpClient.getPreferredClient(appContext)
|
||||||
|
.get(Constants.UPDATE_URL, mapOf())
|
||||||
|
val selfUpdate = gson.fromJson(String(response.responseBytes), SelfUpdate::class.java)
|
||||||
|
|
||||||
|
if (selfUpdate.versionCode > BuildConfig.VERSION_CODE) {
|
||||||
|
if (CertUtil.isFDroidApp(appContext, BuildConfig.APPLICATION_ID)) {
|
||||||
|
if (selfUpdate.fdroidBuild.isNotEmpty()) {
|
||||||
|
downloadWorkerUtil.enqueueApp(SelfUpdate.toApp(selfUpdate, appContext))
|
||||||
|
}
|
||||||
|
} else if (selfUpdate.auroraBuild.isNotEmpty()) {
|
||||||
|
downloadWorkerUtil.enqueueApp(SelfUpdate.toApp(selfUpdate, appContext))
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Update file is missing!")
|
||||||
|
return Result.failure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e(TAG, "Failed to check Aurora Store updates", exception)
|
||||||
|
return Result.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "No updates found")
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@ import android.view.View
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.extensions.browse
|
||||||
import com.aurora.store.MobileNavigationDirections
|
import com.aurora.store.MobileNavigationDirections
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.room.download.Download
|
import com.aurora.store.data.room.download.Download
|
||||||
@@ -48,6 +50,8 @@ class DownloadFragment : BaseFragment(R.layout.fragment_download) {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var downloadWorkerUtil: DownloadWorkerUtil
|
lateinit var downloadWorkerUtil: DownloadWorkerUtil
|
||||||
|
|
||||||
|
private val AURORA_STORE_URL = "https://gitlab.com/AuroraOSS/AuroraStore"
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
@@ -101,7 +105,13 @@ class DownloadFragment : BaseFragment(R.layout.fragment_download) {
|
|||||||
DownloadViewModel_()
|
DownloadViewModel_()
|
||||||
.id(it.packageName)
|
.id(it.packageName)
|
||||||
.download(it)
|
.download(it)
|
||||||
.click { _ -> openDetailsFragment(it.packageName) }
|
.click { _ ->
|
||||||
|
if (it.packageName == Constants.APP_ID) {
|
||||||
|
requireContext().browse(AURORA_STORE_URL)
|
||||||
|
} else {
|
||||||
|
openDetailsFragment(it.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
.longClick { _ ->
|
.longClick { _ ->
|
||||||
openDownloadMenuSheet(it)
|
openDownloadMenuSheet(it)
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
|||||||
import com.aurora.extensions.isSAndAbove
|
import com.aurora.extensions.isSAndAbove
|
||||||
import com.aurora.store.BuildConfig
|
import com.aurora.store.BuildConfig
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.work.SelfUpdateWorker
|
||||||
import com.aurora.store.data.work.UpdateWorker
|
import com.aurora.store.data.work.UpdateWorker
|
||||||
import com.aurora.store.databinding.FragmentOnboardingBinding
|
import com.aurora.store.databinding.FragmentOnboardingBinding
|
||||||
import com.aurora.store.util.CertUtil
|
import com.aurora.store.util.CertUtil
|
||||||
@@ -63,6 +64,7 @@ import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
|
|||||||
import com.aurora.store.util.save
|
import com.aurora.store.util.save
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
|
class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
|
||||||
@@ -70,6 +72,7 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
|
|||||||
private var _binding: FragmentOnboardingBinding? = null
|
private var _binding: FragmentOnboardingBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
private var shouldSelfUpdate by Delegates.notNull<Boolean>()
|
||||||
private var lastPosition = 0
|
private var lastPosition = 0
|
||||||
|
|
||||||
internal class PagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
|
internal class PagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
|
||||||
@@ -95,6 +98,8 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
_binding = FragmentOnboardingBinding.bind(view)
|
_binding = FragmentOnboardingBinding.bind(view)
|
||||||
|
|
||||||
|
shouldSelfUpdate = CertUtil.isFDroidApp(requireContext(), BuildConfig.APPLICATION_ID)
|
||||||
|
|
||||||
val isDefaultPrefLoaded = Preferences.getBoolean(requireContext(), PREFERENCE_DEFAULT)
|
val isDefaultPrefLoaded = Preferences.getBoolean(requireContext(), PREFERENCE_DEFAULT)
|
||||||
if (!isDefaultPrefLoaded) {
|
if (!isDefaultPrefLoaded) {
|
||||||
save(PREFERENCE_DEFAULT, true)
|
save(PREFERENCE_DEFAULT, true)
|
||||||
@@ -144,6 +149,7 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
|
|||||||
binding.btnForward.setOnClickListener {
|
binding.btnForward.setOnClickListener {
|
||||||
save(PREFERENCE_INTRO, true)
|
save(PREFERENCE_INTRO, true)
|
||||||
UpdateWorker.scheduleAutomatedCheck(requireContext())
|
UpdateWorker.scheduleAutomatedCheck(requireContext())
|
||||||
|
if (shouldSelfUpdate) SelfUpdateWorker.scheduleAutomatedCheck(requireContext())
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
OnboardingFragmentDirections.actionOnboardingFragmentToSplashFragment()
|
OnboardingFragmentDirections.actionOnboardingFragmentToSplashFragment()
|
||||||
)
|
)
|
||||||
@@ -192,9 +198,6 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
|
|||||||
save(PREFERENCE_UPDATES_EXTENDED, false)
|
save(PREFERENCE_UPDATES_EXTENDED, false)
|
||||||
save(PREFERENCE_UPDATES_AUTO, 2)
|
save(PREFERENCE_UPDATES_AUTO, 2)
|
||||||
save(PREFERENCE_UPDATES_CHECK_INTERVAL, 3)
|
save(PREFERENCE_UPDATES_CHECK_INTERVAL, 3)
|
||||||
save(
|
save(PREFERENCE_SELF_UPDATE, shouldSelfUpdate)
|
||||||
PREFERENCE_SELF_UPDATE,
|
|
||||||
CertUtil.isFDroidApp(requireContext(), BuildConfig.APPLICATION_ID)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import androidx.preference.SeekBarPreference
|
|||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import com.aurora.store.BuildConfig
|
import com.aurora.store.BuildConfig
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.work.SelfUpdateWorker
|
||||||
import com.aurora.store.data.work.UpdateWorker
|
import com.aurora.store.data.work.UpdateWorker
|
||||||
import com.aurora.store.util.CertUtil
|
import com.aurora.store.util.CertUtil
|
||||||
import com.aurora.store.util.Preferences
|
import com.aurora.store.util.Preferences
|
||||||
@@ -55,6 +56,11 @@ class UpdatesPreference : PreferenceFragmentCompat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it.setOnPreferenceChangeListener { _, newValue ->
|
it.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
if (newValue.toString().toBoolean()) {
|
||||||
|
SelfUpdateWorker.scheduleAutomatedCheck(requireContext())
|
||||||
|
} else {
|
||||||
|
SelfUpdateWorker.cancelAutomatedCheck(requireContext())
|
||||||
|
}
|
||||||
save(PREFERENCE_SELF_UPDATE, newValue.toString().toBoolean())
|
save(PREFERENCE_SELF_UPDATE, newValue.toString().toBoolean())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2019, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package com.aurora.store.view.ui.sheets
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import com.aurora.Constants
|
|
||||||
import com.aurora.store.R
|
|
||||||
import com.aurora.store.data.model.SelfUpdate
|
|
||||||
import com.aurora.store.data.service.SelfUpdateService
|
|
||||||
import com.aurora.store.databinding.SheetSelfUpdateBinding
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class SelfUpdateSheet : BaseBottomSheet() {
|
|
||||||
|
|
||||||
private lateinit var B: SheetSelfUpdateBinding
|
|
||||||
private lateinit var selfUpdate: SelfUpdate
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
const val TAG = "ManualDownloadSheet"
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun newInstance(
|
|
||||||
selfUpdate: SelfUpdate
|
|
||||||
): SelfUpdateSheet {
|
|
||||||
return SelfUpdateSheet().apply {
|
|
||||||
arguments = Bundle().apply {
|
|
||||||
putString(Constants.STRING_EXTRA, gson.toJson(selfUpdate))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateContentView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
B = SheetSelfUpdateBinding.inflate(inflater)
|
|
||||||
return B.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
val bundle = arguments
|
|
||||||
bundle?.let {
|
|
||||||
val rawUpdate = bundle.getString(Constants.STRING_EXTRA, "{}")
|
|
||||||
selfUpdate = gson.fromJson(rawUpdate, SelfUpdate::class.java)
|
|
||||||
if (selfUpdate.versionName.isNotEmpty()) {
|
|
||||||
inflateData()
|
|
||||||
attachActions()
|
|
||||||
} else {
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun inflateData() {
|
|
||||||
B.txtLine2.text = ("${selfUpdate.versionName} (${selfUpdate.versionCode})")
|
|
||||||
|
|
||||||
val messages: String = if (selfUpdate.changelog.isEmpty())
|
|
||||||
getString(R.string.details_changelog_unavailable)
|
|
||||||
else
|
|
||||||
selfUpdate.changelog
|
|
||||||
|
|
||||||
B.txtChangelog.text = messages.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun attachActions() {
|
|
||||||
B.btnPrimary.setOnClickListener {
|
|
||||||
val intent = Intent(requireContext(), SelfUpdateService::class.java)
|
|
||||||
intent.putExtra(Constants.STRING_EXTRA, gson.toJson(selfUpdate))
|
|
||||||
requireContext().startService(intent)
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
}
|
|
||||||
|
|
||||||
B.btnSecondary.setOnClickListener {
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package com.aurora.store.viewmodel
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.aurora.Constants
|
|
||||||
import com.aurora.store.BuildConfig
|
|
||||||
import com.aurora.store.data.model.SelfUpdate
|
|
||||||
import com.aurora.store.data.network.HttpClient
|
|
||||||
import com.aurora.store.util.CertUtil
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.google.gson.GsonBuilder
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.lang.reflect.Modifier
|
|
||||||
|
|
||||||
class MainViewModel : ViewModel() {
|
|
||||||
|
|
||||||
private val TAG = MainViewModel::class.java.simpleName
|
|
||||||
|
|
||||||
private val _selfUpdateAvailable = MutableSharedFlow<SelfUpdate?>()
|
|
||||||
val selfUpdateAvailable = _selfUpdateAvailable.asSharedFlow()
|
|
||||||
|
|
||||||
fun checkSelfUpdate(context: Context) {
|
|
||||||
Log.i(TAG, "Checking for Aurora Store updates")
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
val gson: Gson =
|
|
||||||
GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create()
|
|
||||||
val response = HttpClient.getPreferredClient(context).get(Constants.UPDATE_URL, mapOf())
|
|
||||||
val selfUpdate =
|
|
||||||
gson.fromJson(String(response.responseBytes), SelfUpdate::class.java)
|
|
||||||
|
|
||||||
if (selfUpdate.versionCode > BuildConfig.VERSION_CODE) {
|
|
||||||
if (CertUtil.isFDroidApp(context, BuildConfig.APPLICATION_ID)) {
|
|
||||||
if (selfUpdate.fdroidBuild.isNotEmpty()) {
|
|
||||||
_selfUpdateAvailable.emit(selfUpdate)
|
|
||||||
}
|
|
||||||
} else if (selfUpdate.auroraBuild.isNotEmpty()) {
|
|
||||||
_selfUpdateAvailable.emit(selfUpdate)
|
|
||||||
} else {
|
|
||||||
_selfUpdateAvailable.emit(null)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_selfUpdateAvailable.emit(null)
|
|
||||||
}
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
_selfUpdateAvailable.emit(null)
|
|
||||||
Log.d(TAG, "Failed to check Aurora Store updates", exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ 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 <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:divider="@drawable/divider"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="@dimen/padding_large"
|
|
||||||
android:showDividers="middle">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/img_icon"
|
|
||||||
android:layout_width="@dimen/icon_size_category"
|
|
||||||
android:layout_height="@dimen/icon_size_category"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
app:srcCompat="@drawable/ic_logo" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txt_header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginStart="@dimen/margin_normal"
|
|
||||||
android:layout_marginBottom="@dimen/margin_normal"
|
|
||||||
android:layout_toEndOf="@id/img_icon"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/dialog_title_self_update"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.SubTitle" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txt_line1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txt_line2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/txt_line1"
|
|
||||||
android:layout_alignStart="@id/txt_line1"
|
|
||||||
android:layout_alignEnd="@id/txt_line1"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line2"
|
|
||||||
tools:text="Version" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txt_changelog"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/txt_line2"
|
|
||||||
android:layout_alignStart="@id/txt_line1"
|
|
||||||
android:layout_alignEnd="@id/txt_line1"
|
|
||||||
android:layout_marginTop="@dimen/margin_small"
|
|
||||||
android:background="@drawable/bg_outlined_padded"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line2"
|
|
||||||
tools:text="Base version" />
|
|
||||||
</RelativeLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:weightSum="2">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btn_secondary"
|
|
||||||
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/height_button"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/action_ignore" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btn_primary"
|
|
||||||
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/height_button"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/action_update" />
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
Reference in New Issue
Block a user