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
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.os.Bundle
import androidx.databinding.PropertyChangeRegistry

View File

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

View File

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

View File

@ -6,24 +6,24 @@ import android.os.Build
import androidx.core.app.ServiceCompat
import androidx.core.content.IntentCompat
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
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 fun onCreate() {
super.onCreate()
dm = DownloadManager(this)
mEngine = DownloadEngine(this)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if (intent.action == DownloadManager.ACTION) {
if (intent.action == DownloadEngine.ACTION) {
IntentCompat
.getParcelableExtra(intent, DownloadManager.SUBJECT_KEY, Subject::class.java)
?.let { dm.download(it) }
.getParcelableExtra(intent, DownloadEngine.SUBJECT_KEY, Subject::class.java)
?.let { mEngine.download(it) }
}
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.UpdateInfo
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 REPO = "repo"

View File

@ -1,12 +1,16 @@
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.sqlite.db.SupportSQLiteDatabase
import com.topjohnwu.magisk.core.model.su.SuLog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.*
import java.util.Calendar
@Database(version = 2, entities = [SuLog::class], exportSchema = false)
abstract class SuLogDatabase : RoomDatabase() {

View File

@ -55,10 +55,27 @@ import java.util.zip.ZipFile
import java.util.zip.ZipInputStream
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
) {
interface Session {
val context: Context
@ -96,7 +113,6 @@ class DownloadManager(
.putExtra(SUBJECT_KEY, subject)
}
@SuppressLint("InlinedApi")
fun getPendingIntent(context: Context, subject: Subject): PendingIntent {
val flag = PendingIntent.FLAG_IMMUTABLE or
@ -167,7 +183,7 @@ class DownloadManager(
notifyFail(subject)
}
synchronized(this@DownloadManager) {
synchronized(this@DownloadEngine) {
if (notifications.isEmpty)
session.stop()
}

View File

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

View File

@ -8,7 +8,12 @@ import android.text.Spanned
import android.util.TypedValue
import android.view.View
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.appcompat.widget.Toolbar
import androidx.cardview.widget.CardView
@ -20,7 +25,11 @@ import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
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.card.MaterialCardView
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.ObservableList
import androidx.databinding.ObservableList.OnListChangedCallback
import java.util.*
import java.util.AbstractList
@Suppress("UNCHECKED_CAST")
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.di.AppContext
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.view.MagiskDialog
import java.io.File
@ -29,7 +29,7 @@ class ManagerInstallDialog : MarkDownDialog() {
setCancelable(true)
setButton(MagiskDialog.ButtonType.POSITIVE) {
text = R.string.install
onClick { DownloadManager.startWithActivity(activity, Subject.App()) }
onClick { DownloadEngine.startWithActivity(activity, Subject.App()) }
}
setButton(MagiskDialog.ButtonType.NEGATIVE) {
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.core.di.ServiceLocator
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.model.module.OnlineModule
import com.topjohnwu.magisk.view.MagiskDialog
@ -24,7 +24,7 @@ class OnlineModuleInstallDialog(private val item: OnlineModule) : MarkDownDialog
fun download(install: Boolean) {
val action = if (install) Action.Flash else Action.Download
val subject = Subject.Module(item, action)
DownloadManager.startWithActivity(activity, subject)
DownloadEngine.startWithActivity(activity, subject)
}
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.ComponentInfo
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.graphics.drawable.Drawable
import android.os.Build
@ -12,7 +17,7 @@ import android.os.Build.VERSION.SDK_INT
import androidx.core.os.ProcessCompat
import com.topjohnwu.magisk.core.ktx.getLabel
import com.topjohnwu.magisk.core.utils.currentLocale
import java.util.*
import java.util.TreeSet
class CmdlineListItem(line: String) {
val packageName: String

View File

@ -5,7 +5,11 @@ import android.content.Context
import android.content.pm.ActivityInfo
import android.net.Uri
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.isVisible
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.viewModel
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
class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
@ -25,7 +25,7 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
override fun onStart() {
super.onStart()
activity?.setTitle(R.string.section_home)
DownloadManager.observeProgress(this, viewModel::onProgressUpdate)
DownloadEngine.observeProgress(this, viewModel::onProgressUpdate)
}
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 com.topjohnwu.magisk.R
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.ktx.getBitmap
import com.topjohnwu.magisk.core.ktx.selfLaunchIntent
@ -67,7 +67,7 @@ object Notifications {
fun updateAvailable() {
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 builder = if (SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(this, UPDATE_CHANNEL)