Rename DownloadManager to DownloadEngine

Also add some documentation
This commit is contained in:
topjohnwu 2024-02-06 17:54:15 -08:00
parent 4bf1c74164
commit 2aa923191e
16 changed files with 85 additions and 41 deletions

View File

@ -1,6 +1,8 @@
package com.topjohnwu.magisk.arch package com.topjohnwu.magisk.arch
import android.Manifest.permission.* import android.Manifest.permission.POST_NOTIFICATIONS
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import androidx.databinding.PropertyChangeRegistry import androidx.databinding.PropertyChangeRegistry

View File

@ -11,7 +11,7 @@ import androidx.core.content.getSystemService
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.core.base.BaseJobService import com.topjohnwu.magisk.core.base.BaseJobService
import com.topjohnwu.magisk.core.di.ServiceLocator import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.DownloadManager import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Notifications
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -22,12 +22,12 @@ import java.util.concurrent.TimeUnit
class JobService : BaseJobService() { class JobService : BaseJobService() {
private var mSession: Session? = null private var mSession: Session? = null
private var mDm: DownloadManager? = null private var mEngine: DownloadEngine? = null
@TargetApi(value = 34) @TargetApi(value = 34)
inner class Session( inner class Session(
var params: JobParameters var params: JobParameters
) : DownloadManager.Session { ) : DownloadEngine.Session {
override val context get() = this@JobService override val context get() = this@JobService
@ -55,7 +55,7 @@ class JobService : BaseJobService() {
private fun downloadFile(params: JobParameters): Boolean { private fun downloadFile(params: JobParameters): Boolean {
params.transientExtras.classLoader = Subject::class.java.classLoader params.transientExtras.classLoader = Subject::class.java.classLoader
val subject = params.transientExtras val subject = params.transientExtras
.getParcelable(DownloadManager.SUBJECT_KEY, Subject::class.java) ?: .getParcelable(DownloadEngine.SUBJECT_KEY, Subject::class.java) ?:
return false return false
val session = mSession?.also { val session = mSession?.also {
@ -64,13 +64,13 @@ class JobService : BaseJobService() {
Session(params).also { mSession = it } Session(params).also { mSession = it }
} }
val dm = mDm?.also { val engine = mEngine?.also {
it.reattach() it.reattach()
} ?: run { } ?: run {
DownloadManager(session).also { mDm = it } DownloadEngine(session).also { mEngine = it }
} }
dm.download(subject) engine.download(subject)
return true return true
} }

View File

@ -6,7 +6,7 @@ import android.content.Intent
import androidx.core.content.IntentCompat import androidx.core.content.IntentCompat
import com.topjohnwu.magisk.core.base.BaseReceiver import com.topjohnwu.magisk.core.base.BaseReceiver
import com.topjohnwu.magisk.core.di.ServiceLocator import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.DownloadManager import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.magisk.view.Shortcuts
@ -38,10 +38,10 @@ open class Receiver : BaseReceiver() {
} }
when (intent.action ?: return) { when (intent.action ?: return) {
DownloadManager.ACTION -> { DownloadEngine.ACTION -> {
IntentCompat.getParcelableExtra( IntentCompat.getParcelableExtra(
intent, DownloadManager.SUBJECT_KEY, Subject::class.java)?.let { intent, DownloadEngine.SUBJECT_KEY, Subject::class.java)?.let {
DownloadManager.start(context, it) DownloadEngine.start(context, it)
} }
} }
Intent.ACTION_PACKAGE_REPLACED -> { Intent.ACTION_PACKAGE_REPLACED -> {

View File

@ -6,24 +6,24 @@ import android.os.Build
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.core.content.IntentCompat import androidx.core.content.IntentCompat
import com.topjohnwu.magisk.core.base.BaseService import com.topjohnwu.magisk.core.base.BaseService
import com.topjohnwu.magisk.core.download.DownloadManager import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
class Service : BaseService(), DownloadManager.Session { class Service : BaseService(), DownloadEngine.Session {
private lateinit var dm: DownloadManager private lateinit var mEngine: DownloadEngine
override val context get() = this override val context get() = this
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
dm = DownloadManager(this) mEngine = DownloadEngine(this)
} }
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if (intent.action == DownloadManager.ACTION) { if (intent.action == DownloadEngine.ACTION) {
IntentCompat IntentCompat
.getParcelableExtra(intent, DownloadManager.SUBJECT_KEY, Subject::class.java) .getParcelableExtra(intent, DownloadEngine.SUBJECT_KEY, Subject::class.java)
?.let { dm.download(it) } ?.let { mEngine.download(it) }
} }
return START_NOT_STICKY return START_NOT_STICKY
} }

View File

@ -4,7 +4,11 @@ import com.topjohnwu.magisk.core.model.BranchInfo
import com.topjohnwu.magisk.core.model.ModuleJson import com.topjohnwu.magisk.core.model.ModuleJson
import com.topjohnwu.magisk.core.model.UpdateInfo import com.topjohnwu.magisk.core.model.UpdateInfo
import okhttp3.ResponseBody import okhttp3.ResponseBody
import retrofit2.http.* import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.Path
import retrofit2.http.Streaming
import retrofit2.http.Url
private const val BRANCH = "branch" private const val BRANCH = "branch"
private const val REPO = "repo" private const val REPO = "repo"

View File

@ -1,12 +1,16 @@
package com.topjohnwu.magisk.core.data package com.topjohnwu.magisk.core.data
import androidx.room.* import androidx.room.Dao
import androidx.room.Database
import androidx.room.Insert
import androidx.room.Query
import androidx.room.RoomDatabase
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import com.topjohnwu.magisk.core.model.su.SuLog import com.topjohnwu.magisk.core.model.su.SuLog
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.* import java.util.Calendar
@Database(version = 2, entities = [SuLog::class], exportSchema = false) @Database(version = 2, entities = [SuLog::class], exportSchema = false)
abstract class SuLogDatabase : RoomDatabase() { abstract class SuLogDatabase : RoomDatabase() {

View File

@ -55,7 +55,24 @@ import java.util.zip.ZipFile
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
class DownloadManager( /**
* This class drives the execution of file downloads and notification management.
*
* Each download engine instance has to be paired with a "session" that is managed by the operating
* system. A session is an Android component that allows executing long lasting operations and
* have its state tied to a notification to show progress.
*
* A session can only have one single notification representing its state, and the operating system
* also uses the notification to manage the lifecycle of a session. One goal of this class is
* to support concurrent download tasks using only one single session, so internally it manages
* all active tasks and notifications and properly re-assign notifications to be attached to
* the session to make sure all download operations can be completed without the operating system
* killing the session.
*
* For API 23 - 33, we use a foreground service as a session.
* For API 34 and higher, we use user-initiated job services as a session.
*/
class DownloadEngine(
private val session: Session private val session: Session
) { ) {
@ -96,7 +113,6 @@ class DownloadManager(
.putExtra(SUBJECT_KEY, subject) .putExtra(SUBJECT_KEY, subject)
} }
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
fun getPendingIntent(context: Context, subject: Subject): PendingIntent { fun getPendingIntent(context: Context, subject: Subject): PendingIntent {
val flag = PendingIntent.FLAG_IMMUTABLE or val flag = PendingIntent.FLAG_IMMUTABLE or
@ -167,7 +183,7 @@ class DownloadManager(
notifyFail(subject) notifyFail(subject)
} }
synchronized(this@DownloadManager) { synchronized(this@DownloadEngine) {
if (notifications.isEmpty) if (notifications.isEmpty)
session.stop() session.stop()
} }

View File

@ -12,7 +12,7 @@ import com.topjohnwu.magisk.core.createNewResources
import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.di.AppContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.* import java.util.Locale
var currentLocale: Locale = Locale.getDefault() var currentLocale: Locale = Locale.getDefault()

View File

@ -8,7 +8,12 @@ import android.text.Spanned
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.Spinner
import android.widget.TextView
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
@ -20,7 +25,11 @@ import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener import androidx.databinding.InverseBindingListener
import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.* import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip

View File

@ -3,7 +3,7 @@ package com.topjohnwu.magisk.databinding
import androidx.databinding.ListChangeRegistry import androidx.databinding.ListChangeRegistry
import androidx.databinding.ObservableList import androidx.databinding.ObservableList
import androidx.databinding.ObservableList.OnListChangedCallback import androidx.databinding.ObservableList.OnListChangedCallback
import java.util.* import java.util.AbstractList
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class MergeObservableList<T> : AbstractList<T>(), ObservableList<T> { class MergeObservableList<T> : AbstractList<T>(), ObservableList<T> {

View File

@ -4,7 +4,7 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.core.di.ServiceLocator import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.DownloadManager import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
import java.io.File import java.io.File
@ -29,7 +29,7 @@ class ManagerInstallDialog : MarkDownDialog() {
setCancelable(true) setCancelable(true)
setButton(MagiskDialog.ButtonType.POSITIVE) { setButton(MagiskDialog.ButtonType.POSITIVE) {
text = R.string.install text = R.string.install
onClick { DownloadManager.startWithActivity(activity, Subject.App()) } onClick { DownloadEngine.startWithActivity(activity, Subject.App()) }
} }
setButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
text = android.R.string.cancel text = android.R.string.cancel

View File

@ -3,7 +3,7 @@ package com.topjohnwu.magisk.dialog
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.di.ServiceLocator import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.Action import com.topjohnwu.magisk.core.download.Action
import com.topjohnwu.magisk.core.download.DownloadManager import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.core.model.module.OnlineModule import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
@ -24,7 +24,7 @@ class OnlineModuleInstallDialog(private val item: OnlineModule) : MarkDownDialog
fun download(install: Boolean) { fun download(install: Boolean) {
val action = if (install) Action.Flash else Action.Download val action = if (install) Action.Flash else Action.Download
val subject = Subject.Module(item, action) val subject = Subject.Module(item, action)
DownloadManager.startWithActivity(activity, subject) DownloadEngine.startWithActivity(activity, subject)
} }
val title = context.getString(R.string.repo_install_title, val title = context.getString(R.string.repo_install_title,

View File

@ -4,7 +4,12 @@ import android.annotation.SuppressLint
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.ComponentInfo import android.content.pm.ComponentInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.PackageManager.* import android.content.pm.PackageManager.GET_ACTIVITIES
import android.content.pm.PackageManager.GET_PROVIDERS
import android.content.pm.PackageManager.GET_RECEIVERS
import android.content.pm.PackageManager.GET_SERVICES
import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import android.content.pm.ServiceInfo import android.content.pm.ServiceInfo
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
@ -12,7 +17,7 @@ import android.os.Build.VERSION.SDK_INT
import androidx.core.os.ProcessCompat import androidx.core.os.ProcessCompat
import com.topjohnwu.magisk.core.ktx.getLabel import com.topjohnwu.magisk.core.ktx.getLabel
import com.topjohnwu.magisk.core.utils.currentLocale import com.topjohnwu.magisk.core.utils.currentLocale
import java.util.* import java.util.TreeSet
class CmdlineListItem(line: String) { class CmdlineListItem(line: String) {
val packageName: String val packageName: String

View File

@ -5,7 +5,11 @@ import android.content.Context
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.KeyEvent
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.navigation.NavDeepLinkBuilder import androidx.navigation.NavDeepLinkBuilder

View File

@ -14,7 +14,7 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseFragment import com.topjohnwu.magisk.arch.BaseFragment
import com.topjohnwu.magisk.arch.viewModel import com.topjohnwu.magisk.arch.viewModel
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.DownloadManager import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider { class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
@ -25,7 +25,7 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
activity?.setTitle(R.string.section_home) activity?.setTitle(R.string.section_home)
DownloadManager.observeProgress(this, viewModel::onProgressUpdate) DownloadEngine.observeProgress(this, viewModel::onProgressUpdate)
} }
private fun checkTitle(text: TextView, icon: ImageView) { private fun checkTitle(text: TextView, icon: ImageView) {

View File

@ -11,7 +11,7 @@ import androidx.core.content.getSystemService
import androidx.core.graphics.drawable.toIcon import androidx.core.graphics.drawable.toIcon
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.core.download.DownloadManager import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.core.ktx.getBitmap import com.topjohnwu.magisk.core.ktx.getBitmap
import com.topjohnwu.magisk.core.ktx.selfLaunchIntent import com.topjohnwu.magisk.core.ktx.selfLaunchIntent
@ -67,7 +67,7 @@ object Notifications {
fun updateAvailable() { fun updateAvailable() {
AppContext.apply { AppContext.apply {
val intent = DownloadManager.getPendingIntent(this, Subject.App()) val intent = DownloadEngine.getPendingIntent(this, Subject.App())
val bitmap = getBitmap(R.drawable.ic_magisk_outline) val bitmap = getBitmap(R.drawable.ic_magisk_outline)
val builder = if (SDK_INT >= Build.VERSION_CODES.O) { val builder = if (SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(this, UPDATE_CHANNEL) Notification.Builder(this, UPDATE_CHANNEL)