Skip to content

Commit

Permalink
Improvements: Installer (#394)
Browse files Browse the repository at this point in the history
  • Loading branch information
Iamlooker authored Aug 5, 2023
1 parent a5c9211 commit b77dc05
Show file tree
Hide file tree
Showing 18 changed files with 66 additions and 72 deletions.
4 changes: 2 additions & 2 deletions app/src/main/kotlin/com/looker/droidify/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import com.looker.droidify.sync.toJobNetworkType
import com.looker.droidify.utility.Utils.toInstalledItem
import com.looker.droidify.utility.extension.android.getInstalledPackagesCompat
import com.looker.droidify.work.CleanUpWorker
import com.looker.installer.Installer
import com.looker.installer.InstallManager
import com.topjohnwu.superuser.Shell
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -65,7 +65,7 @@ class MainApplication : Application(), ImageLoaderFactory {
private val userPreferenceFlow get() = userPreferencesRepository.userPreferencesFlow

@Inject
lateinit var installer: Installer
lateinit var installer: InstallManager

override fun onCreate() {
super.onCreate()
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/kotlin/com/looker/droidify/ScreenActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import com.looker.droidify.ui.repository.RepositoriesFragment
import com.looker.droidify.ui.repository.RepositoryFragment
import com.looker.droidify.ui.settings.SettingsFragment
import com.looker.droidify.ui.tabs_fragment.TabsFragment
import com.looker.installer.Installer
import com.looker.installer.InstallManager
import com.looker.installer.model.installFrom
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
Expand All @@ -60,7 +60,7 @@ abstract class ScreenActivity : AppCompatActivity() {
registerForActivityResult(ActivityResultContracts.RequestPermission()) { }

@Inject
lateinit var installer: Installer
lateinit var installer: InstallManager

private class FragmentStackItem(
val className: String, val arguments: Bundle?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import com.looker.core.model.Repository
import com.looker.droidify.BuildConfig
import com.looker.droidify.MainActivity
import com.looker.droidify.utility.extension.android.getPackageArchiveInfoCompat
import com.looker.installer.Installer
import com.looker.installer.InstallManager
import com.looker.installer.model.installFrom
import com.looker.network.Downloader
import com.looker.network.NetworkResponse
Expand Down Expand Up @@ -53,7 +53,7 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
}

@Inject
lateinit var installer: Installer
lateinit var installer: InstallManager

sealed class State(val packageName: String) {
object Idle : State("")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.looker.core.common.extension.firstItemVisible
import com.looker.core.common.extension.systemBarsPadding
import com.looker.core.datastore.UserPreferencesRepository
import com.looker.core.model.*
Expand All @@ -34,7 +35,7 @@ import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.Utils.startUpdate
import com.looker.droidify.utility.extension.android.getApplicationInfoCompat
import com.looker.droidify.utility.extension.screenActivity
import com.looker.installer.Installer
import com.looker.installer.InstallManager
import com.looker.installer.model.*
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.*
Expand Down Expand Up @@ -77,7 +78,7 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
get() = requireArguments().getString(EXTRA_PACKAGE_NAME)!!

@Inject
lateinit var installer: Installer
lateinit var installer: InstallManager

@Inject
lateinit var userPreferencesRepository: UserPreferencesRepository
Expand Down Expand Up @@ -131,7 +132,6 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
val adapter = AppDetailAdapter(this@AppDetailFragment)
this.adapter = adapter
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
addOnScrollListener(scrollListener)
savedInstanceState?.getParcelable<AppDetailAdapter.SavedState>(STATE_ADAPTER)
?.let(adapter::restoreState)
layoutManagerState = savedInstanceState?.getParcelable(STATE_LAYOUT_MANAGER)
Expand Down Expand Up @@ -226,6 +226,13 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
launch {
viewModel.installerState.collect { updateInstallState(it) }
}
launch {
recyclerView?.firstItemVisible?.collect { isFirstItemVisible ->
updateToolbarButtons()
toolbar.title = if (!isFirstItemVisible) products[0].first.name
else getString(stringRes.application)
}
}
}
}

Expand Down Expand Up @@ -366,21 +373,6 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
}
}

private val scrollListener = object : RecyclerView.OnScrollListener() {
private var lastPosition = -1

override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val position =
(recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
val lastPosition = lastPosition
this.lastPosition = position
if ((lastPosition == 0) != (position == 0)) {
updateToolbarTitle()
updateToolbarButtons()
}
}
}

override fun onActionClick(action: AppDetailAdapter.Action) {
when (action) {
AppDetailAdapter.Action.INSTALL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.looker.droidify.ui.app_detail

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.looker.installer.Installer
import com.looker.installer.InstallManager
import com.looker.installer.model.InstallerQueueState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
Expand All @@ -11,8 +11,8 @@ import javax.inject.Inject

@HiltViewModel
class AppDetailViewModel @Inject constructor(
installer: Installer
) : ViewModel(){
installer: InstallManager
) : ViewModel() {

val installerState = installer.getStatus().stateIn(
scope = viewModelScope,
Expand Down
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
plugins {
id("com.android.application") version "8.0.2" apply false
id("com.android.library") version "8.0.2" apply false
id("org.jetbrains.kotlin.android") version Version.kotlin apply false
kotlin("plugin.serialization") version Version.kotlin apply false
id("com.google.devtools.ksp") version Version.ksp apply false
id("com.google.dagger.hilt.android") version Hilt.version apply false
id("org.jetbrains.kotlin.android") version Kotlin.version apply false
kotlin("plugin.serialization") version Kotlin.version apply false
id(Ksp.plugin) version Ksp.version apply false
id(Hilt.plugin) version Hilt.version apply false
}

tasks.register("clean", Delete::class) {
Expand Down
11 changes: 6 additions & 5 deletions buildSrc/src/main/kotlin/Libs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ private object FDroid {
}

object Hilt {
const val version = "2.46.1"
const val version = "2.47"
const val android = "com.google.dagger:hilt-android:$version"

const val compiler = "com.google.dagger:hilt-compiler:$version"
const val plugin = "dagger.hilt.android.plugin"
const val plugin = "com.google.dagger.hilt.android"
private const val androidXHilt = "1.0.0"
const val work = "androidx.hilt:hilt-work:$androidXHilt"
const val androidX = "androidx.hilt:hilt-compiler:$androidXHilt"
Expand All @@ -76,6 +76,7 @@ object Jackson {
}

object Kotlin {
const val version = "1.9.0"
const val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1"
const val datetime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.0"
}
Expand Down Expand Up @@ -126,9 +127,9 @@ object Test {
const val espresso = "androidx.test.espresso:espresso-core:3.5.1"
}

object Version {
const val kotlin = "1.8.22"
const val ksp = "1.8.22-1.0.11"
object Ksp {
const val version = "${Kotlin.version}-1.0.12"
const val plugin = "com.google.devtools.ksp"
}

object Work {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
}

dependencies {
add("implementation", kotlin("stdlib"))
add("implementation", platform("org.jetbrains.kotlin:kotlin-bom:${Kotlin.version}"))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/plugins/HiltConventionPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class HiltConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("dagger.hilt.android.plugin")
apply(Hilt.plugin)
// KAPT must go last to avoid build warnings.
// See: https://stackoverflow.com/questions/70550883/warning-the-following-options-were-not-recognized-by-any-processor-dagger-f
apply("org.jetbrains.kotlin.kapt")
Expand All @@ -24,7 +24,7 @@ class HiltWorkerConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("dagger.hilt.android.plugin")
apply(Hilt.plugin)
apply("org.jetbrains.kotlin.kapt")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class Installer(
class InstallManager(
private val context: Context,
userPreferencesRepository: UserPreferencesRepository
) {
Expand All @@ -29,12 +29,12 @@ class Installer(
private val installState = MutableStateFlow(InstallItemState.EMPTY)
private val installQueue = MutableStateFlow(emptySet<String>())

private var _baseInstaller: BaseInstaller? = null
private var _installer: Installer? = null
set(value) {
field?.cleanup()
field = value
}
private val baseInstaller: BaseInstaller get() = _baseInstaller!!
private val installer: Installer get() = _installer!!

private val lock = Mutex()
private val installerPreference = userPreferencesRepository
Expand All @@ -48,7 +48,7 @@ class Installer(
}

fun close() {
_baseInstaller = null
_installer = null
uninstallItems.close()
installItems.close()
}
Expand Down Expand Up @@ -88,7 +88,7 @@ class Installer(
}
installState.emit(item statesTo InstallState.Installing)
val success = withTimeoutOrNull(20_000) {
baseInstaller.performInstall(item)
installer.install(item)
} ?: InstallState.Failed
installState.emit(item statesTo success)
lock.withLock { currentQueue.remove(item.packageName.name) }
Expand All @@ -101,13 +101,13 @@ class Installer(

private fun CoroutineScope.uninstaller() = launch {
uninstallItems.consumeEach {
baseInstaller.performUninstall(it)
installer.uninstall(it)
}
}

private suspend fun setInstaller(installerType: InstallerType) {
lock.withLock {
_baseInstaller = when (installerType) {
_installer = when (installerType) {
InstallerType.LEGACY -> LegacyInstaller(context)
InstallerType.SESSION -> SessionInstaller(context)
InstallerType.SHIZUKU -> ShizukuInstaller(context)
Expand All @@ -116,3 +116,12 @@ class Installer(
}
}
}

data class InstallerQueueState(
val currentItem: InstallItemState,
val queued: Set<String>
) {
companion object {
val EMPTY = InstallerQueueState(InstallItemState.EMPTY, emptySet())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ object InstallModule {
fun providesInstaller(
@ApplicationContext context: Context,
userPreferencesRepository: UserPreferencesRepository
): Installer = Installer(context, userPreferencesRepository)
): InstallManager = InstallManager(context, userPreferencesRepository)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import com.looker.core.model.newer.PackageName
import com.looker.installer.model.InstallItem
import com.looker.installer.model.InstallState

interface BaseInstaller {
interface Installer {

suspend fun performInstall(installItem: InstallItem): InstallState
suspend fun install(installItem: InstallItem): InstallState

suspend fun performUninstall(packageName: PackageName)
suspend fun uninstall(packageName: PackageName)

fun cleanup()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume

@Suppress("DEPRECATION")
internal class LegacyInstaller(private val context: Context) : BaseInstaller {
internal class LegacyInstaller(private val context: Context) : Installer {

companion object {
private const val APK_MIME = "application/vnd.android.package-archive"
}

override suspend fun performInstall(
override suspend fun install(
installItem: InstallItem
): InstallState = suspendCancellableCoroutine { cont ->
val (uri, flags) = if (SdkCheck.isNougat) {
Expand All @@ -46,7 +46,7 @@ internal class LegacyInstaller(private val context: Context) : BaseInstaller {
}
}

override suspend fun performUninstall(packageName: PackageName) =
override suspend fun uninstall(packageName: PackageName) =
context.uninstallPackage(packageName)

override fun cleanup() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import java.io.File
import kotlin.coroutines.resume

internal class RootInstaller(private val context: Context) : BaseInstaller {
internal class RootInstaller(private val context: Context) : Installer {

companion object {
private const val ROOT_INSTALL_PACKAGE = "cat %s | pm install --user %s -t -r -S %s"
Expand Down Expand Up @@ -54,7 +54,7 @@ internal class RootInstaller(private val context: Context) : BaseInstaller {
)
}

override suspend fun performInstall(
override suspend fun install(
installItem: InstallItem
): InstallState = suspendCancellableCoroutine { cont ->
val releaseFile = Cache.getReleaseFile(context, installItem.installFileName)
Expand All @@ -66,7 +66,7 @@ internal class RootInstaller(private val context: Context) : BaseInstaller {
}
}

override suspend fun performUninstall(packageName: PackageName) =
override suspend fun uninstall(packageName: PackageName) =
context.uninstallPackage(packageName)

override fun cleanup() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import com.looker.installer.model.InstallState
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume

internal class SessionInstaller(private val context: Context) : BaseInstaller {
internal class SessionInstaller(private val context: Context) : Installer {

private val sessionInstaller = context.packageManager.packageInstaller
private val intent = Intent(context, SessionInstallerService::class.java)
Expand All @@ -33,7 +33,7 @@ internal class SessionInstaller(private val context: Context) : BaseInstaller {
}
}

override suspend fun performInstall(
override suspend fun install(
installItem: InstallItem
): InstallState = suspendCancellableCoroutine { cont ->
val cacheFile = Cache.getReleaseFile(context, installItem.installFileName)
Expand Down Expand Up @@ -77,7 +77,7 @@ internal class SessionInstaller(private val context: Context) : BaseInstaller {
}

@SuppressLint("MissingPermission")
override suspend fun performUninstall(packageName: PackageName) =
override suspend fun uninstall(packageName: PackageName) =
suspendCancellableCoroutine { cont ->
intent.putExtra(
SessionInstallerService.KEY_ACTION,
Expand Down
Loading

0 comments on commit b77dc05

Please sign in to comment.