SelfUpdateWorker: Move self-updates logic into dedicated worker

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2023-12-21 13:16:51 +05:30
parent 59309783b1
commit a796c4c381
14 changed files with 209 additions and 333 deletions

View File

@@ -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 }
}

View File

@@ -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)
} }

View File

@@ -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
)
)
)
}
}
} }

View File

@@ -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) }
) )
} }

View File

@@ -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)
}
} }

View File

@@ -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() }
)
} }
} }
} }

View File

@@ -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

View 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()
}
}

View File

@@ -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

View File

@@ -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)
)
} }
} }

View File

@@ -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
} }

View File

@@ -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()
}
}
}

View File

@@ -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)
}
}
}
}

View File

@@ -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>