Add legacy self-update so v3 users can get v4 update

This commit is contained in:
Rahul Kumar Patel
2021-03-01 22:02:09 +05:30
parent 09e75860a5
commit 90113ab338
10 changed files with 307 additions and 6 deletions

View File

@@ -150,6 +150,7 @@
<service
android:name="com.novoda.merlin.MerlinService"
android:exported="false" />
<service android:name=".data.service.SelfUpdateService" />
<provider
android:name="androidx.core.content.FileProvider"

View File

@@ -32,6 +32,7 @@ object Constants {
const val EXODUS_BASE_URL = "https://reports.exodus-privacy.eu.org/api/search/"
const val EXODUS_REPORT_URL = "https://reports.exodus-privacy.eu.org/reports/"
const val SHARE_URL = "http://play.google.com/store/apps/details?id="
const val UPDATE_URL = "https://gitlab.com/AuroraOSS/AuroraStore/raw/master/updates.json"
const val NOTIFICATION_CHANNEL_ALERT = "NOTIFICATION_CHANNEL_ALERT"
const val NOTIFICATION_CHANNEL_GENERAL = "NOTIFICATION_CHANNEL_GENERAL"

View File

@@ -54,6 +54,20 @@ fun Context.showDialog(title: String?, message: String?) {
}
}
fun Context.showDialog(title: String?, message: String?, listener: DialogInterface.OnDismissListener) {
runOnUiThread {
val backgroundColor: Int = getStyledAttributeColor(android.R.attr.colorBackground)
val builder = MaterialAlertDialogBuilder(this).apply {
setTitle(title)
setMessage(message)
setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _ -> listener }
background = ColorDrawable(backgroundColor)
}.create()
builder.show()
}
}
fun Fragment.showDialog(@StringRes titleId: Int, @StringRes messageId: Int) {
requireContext().showDialog(titleId, messageId)
}

View File

@@ -21,6 +21,7 @@ package com.aurora.store
import android.Manifest
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle
import android.os.Environment
@@ -39,13 +40,15 @@ import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.Navigation
import androidx.navigation.ui.NavigationUI
import com.aurora.extensions.getEmptyActivityBundle
import com.aurora.extensions.getStyledAttributeColor
import com.aurora.extensions.load
import com.aurora.extensions.open
import com.aurora.Constants
import com.aurora.extensions.*
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.store.data.model.SelfUpdate
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.data.service.SelfUpdateService
import com.aurora.store.databinding.ActivityMainBinding
import com.aurora.store.util.CertUtil.isFDroidApp
import com.aurora.store.util.Log
import com.aurora.store.util.Preferences
import com.aurora.store.view.ui.about.AboutActivity
@@ -60,7 +63,10 @@ import com.aurora.store.view.ui.search.SearchSuggestionActivity
import com.aurora.store.view.ui.spoof.SpoofActivity
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.livinglifetechway.quickpermissions_kotlin.runWithPermissions
import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.successUi
class MainActivity : BaseActivity() {
@@ -116,6 +122,9 @@ class MainActivity : BaseActivity() {
checkExternalStorageAccessPermission()
checkExternalStorageManagerPermission()
}
/* Check self update */
checkSelfUpdate()
}
private fun attachToolbar() {
@@ -237,4 +246,63 @@ class MainActivity : BaseActivity() {
startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))
}
}
private fun checkSelfUpdate() {
task {
HttpClient.getPreferredClient().get(Constants.UPDATE_URL, mapOf())
} successUi { playResponse ->
if (playResponse.isSuccessful) {
val selfUpdate = gson.fromJson(
String(playResponse.responseBytes),
SelfUpdate::class.java
)
selfUpdate?.let { it ->
if (it.versionCode > BuildConfig.VERSION_CODE) {
if (isFDroidApp(this, BuildConfig.APPLICATION_ID)) {
if (it.fdroidBuild.isNotEmpty()) {
showUpdatesDialog(it)
}
} else if (it.auroraBuild.isNotEmpty()) {
showUpdatesDialog(it)
} else {
Log.i(getString(R.string.details_no_updates))
}
} else {
Log.i(getString(R.string.details_no_updates))
}
}
} else {
Log.e("Failed to check self update")
}
}
}
private fun showUpdatesDialog(selfUpdate: SelfUpdate) {
val messages: List<String> = listOf(
selfUpdate.versionCode.toString(),
if (selfUpdate.changelog.isEmpty())
getString(R.string.details_changelog_unavailable)
else
selfUpdate.changelog,
getString(R.string.dialog_desc_self_update)
)
val builder: MaterialAlertDialogBuilder = MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.dialog_title_self_update))
.setMessage(messages.joinToString(separator = "\n"))
.setPositiveButton(getString(R.string.action_update)) { _, _ ->
val intent = Intent(this, SelfUpdateService::class.java)
intent.putExtra(Constants.STRING_EXTRA, gson.toJson(selfUpdate))
startService(intent)
}
.setNegativeButton(getString(R.string.action_later)) { dialog, _ ->
dialog.dismiss()
}
val backGroundColor: Int = getStyledAttributeColor(android.R.attr.colorBackground)
builder.background = ColorDrawable(backGroundColor)
builder.create()
builder.show()
}
}

View File

@@ -32,6 +32,7 @@ class NativeInstaller(context: Context) : InstallerBase(context) {
if (isAlreadyQueued(packageName)) {
Log.i("$packageName already queued")
} else {
Log.i("Received native install request for $packageName")
files.map {
when (it) {
is File -> it

View File

@@ -42,7 +42,7 @@ class SessionInstaller(context: Context) : InstallerBase(context) {
if (isAlreadyQueued(packageName)) {
Log.i("$packageName already queued")
} else {
Log.i("Received service install request for $packageName")
Log.i("Received session install request for $packageName")
val uriList = files.map {
when (it) {
is File -> getUri(it)

View File

@@ -0,0 +1,39 @@
/*
* 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/>.
*
*/
package com.aurora.store.data.model
import com.google.gson.annotations.SerializedName
class SelfUpdate {
@SerializedName("version_name")
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()
@SerializedName("changelog")
var changelog: String = String()
}

View File

@@ -23,12 +23,12 @@ import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.aurora.store.BuildConfig
import com.aurora.store.data.event.BusEvent.InstallEvent
import com.aurora.store.data.event.BusEvent.UninstallEvent
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.util.PathUtil
import com.aurora.store.util.Preferences
import com.aurora.store.util.isExternalStorageEnable
import org.greenrobot.eventbus.EventBus
import java.io.File
@@ -62,6 +62,10 @@ open class PackageManagerReceiver : BroadcastReceiver() {
if (isAutoDeleteAPKEnabled)
clearDownloads(context, packageName)
//Clear self update apk
if (packageName == BuildConfig.APPLICATION_ID)
clearDownloads(context, packageName)
}
}

View File

@@ -0,0 +1,168 @@
package com.aurora.store.data.service
import android.app.Notification
import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.File
import com.aurora.store.BuildConfig
import com.aurora.store.R
import com.aurora.store.data.downloader.DownloadManager
import com.aurora.store.data.downloader.RequestBuilder.buildRequest
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.model.SelfUpdate
import com.aurora.store.util.CertUtil.isFDroidApp
import com.aurora.store.util.Log
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.tonyodev.fetch2.*
import nl.komponents.kovenant.task
import org.apache.commons.lang3.StringUtils
import java.lang.reflect.Modifier
import java.util.*
import java.util.concurrent.TimeUnit
class SelfUpdateService : Service() {
private lateinit var app: App
private lateinit var fetch: Fetch
private lateinit var fetchListener: FetchListener
private var gson: Gson = GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
.create()
private val hashCode = BuildConfig.APPLICATION_ID.hashCode()
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val rawSelfUpdate = intent.getStringExtra(Constants.STRING_EXTRA)
if (StringUtils.isNotEmpty(rawSelfUpdate)) {
val selfUpdate = gson.fromJson(rawSelfUpdate, SelfUpdate::class.java)
selfUpdate?.let {
downloadAndUpdate(it)
}
}
return START_NOT_STICKY
}
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(1, notification)
} else {
val notification = getNotification(
NotificationCompat.Builder(
this,
Constants.NOTIFICATION_CHANNEL_GENERAL
)
)
startForeground(1, notification)
}
}
private fun destroyService() {
Log.d("Self-update service destroyed")
fetch.removeListener(fetchListener)
stopForeground(true)
stopSelf()
}
private fun downloadAndUpdate(update: SelfUpdate) {
app = App(BuildConfig.APPLICATION_ID)
app.id = hashCode
app.packageName = BuildConfig.APPLICATION_ID
app.displayName = getString(R.string.app_name)
app.versionName = update.versionName
app.versionCode = update.versionCode
val file = File().apply {
name = "AuroraStore.apk"
url = if (isFDroidVariant)
update.fdroidBuild
else
update.auroraBuild
type = File.FileType.BASE
}
val request = buildRequest(this, app, file)
request.enqueueAction = EnqueueAction.REPLACE_EXISTING
val requestList: MutableList<Request> = ArrayList()
requestList.add(request)
fetch = DownloadManager.with(this).fetch
fetch.enqueue(requestList) {
Log.i("Downloading latest update")
}
fetchListener = getFetchListener()
fetch.addListener(fetchListener)
}
private val isFDroidVariant: Boolean
get() = isFDroidApp(this, BuildConfig.APPLICATION_ID)
@get:RequiresApi(Build.VERSION_CODES.O)
private val notification: Notification
get() {
val notificationBuilder =
NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNEL_GENERAL)
return getNotification(notificationBuilder)
}
private fun getNotification(builder: NotificationCompat.Builder): Notification {
return builder.setAutoCancel(true)
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
.setContentTitle("Self update")
.setContentText("Updating Aurora Store in background")
.setOngoing(true)
.setSmallIcon(R.drawable.ic_notification_outlined)
.build()
}
private fun getFetchListener(): FetchListener {
return object : AbstractFetchGroupListener() {
override fun onError(
groupId: Int, download: Download, error: Error,
throwable: Throwable?, fetchGroup: FetchGroup
) {
if (groupId == app.id) {
Log.e("Error self-updating ${app.displayName}")
destroyService()
}
}
override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) {
if (groupId == app.id && fetchGroup.groupDownloadProgress == 100) {
Log.d("Calling installer ${app.displayName}")
AppInstaller(this@SelfUpdateService).getPreferredInstaller().install(
BuildConfig.APPLICATION_ID,
listOf(download.file)
)
task {
TimeUnit.SECONDS.sleep(10)
} success {
destroyService()
}
}
}
override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) {
if (groupId == app.id) {
Log.d("Self-update cancelled ${app.displayName}")
destroyService()
}
}
}
}
}

View File

@@ -238,6 +238,11 @@
<string name="device_miui_description">"Please, disable MIUI optimizations to allow installations, otherwise you can choose Root or Services installer."</string>
<string name="device_miui_extra">"Optionally you can choose Native installer, but then you can not install bundled (split) APKs, so choice is yours."</string>
<string name="dialog_title_self_update">"New update available"</string>
<string name="dialog_desc_self_update">"Do you wish to update now ?"</string>
<string name="list_update_all_txt_one">"update available"</string>
<string name="notification_channel_alert">"Quick notification"</string>
<string name="notification_channel_general">"General notification"</string>
<string name="notification_installation_auto">"Click to install"</string>