Remove: Using AppDetailAdapter in AppSheet

This commit is contained in:
machiav3lli 2022-05-29 03:06:04 +02:00
parent 31d988ed2e
commit 1920820f1d

View File

@ -1,6 +1,9 @@
package com.looker.droidify.ui.fragments
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@ -8,32 +11,44 @@ import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import com.looker.droidify.R
import com.looker.droidify.content.Preferences
import com.looker.droidify.content.ProductPreferences
import com.looker.droidify.database.entity.Installed
import com.looker.droidify.database.entity.Product
import com.looker.droidify.database.entity.Release
import com.looker.droidify.database.entity.Repository
import com.looker.droidify.databinding.SheetAppXBinding
import com.looker.droidify.entity.Action
import com.looker.droidify.entity.Cancelable
import com.looker.droidify.entity.Connecting
import com.looker.droidify.entity.Details
import com.looker.droidify.entity.Install
import com.looker.droidify.entity.Launch
import com.looker.droidify.entity.PackageState
import com.looker.droidify.entity.Pending
import com.looker.droidify.entity.ProductPreference
import com.looker.droidify.entity.Screenshot
import com.looker.droidify.entity.Share
import com.looker.droidify.entity.Uninstall
import com.looker.droidify.entity.Update
import com.looker.droidify.installer.AppInstaller
import com.looker.droidify.screen.MessageDialog
import com.looker.droidify.screen.ScreenshotsFragment
import com.looker.droidify.service.Connection
import com.looker.droidify.service.DownloadService
import com.looker.droidify.ui.activities.MainActivityX
import com.looker.droidify.ui.adapters.AppDetailAdapter
import com.looker.droidify.ui.compose.theme.AppTheme
import com.looker.droidify.ui.compose.utils.Callbacks
import com.looker.droidify.ui.viewmodels.AppViewModelX
import com.looker.droidify.utility.Utils.rootInstallerEnabled
import com.looker.droidify.utility.Utils.startUpdate
import com.looker.droidify.utility.extension.android.Android
import com.looker.droidify.utility.findSuggestedProduct
import com.looker.droidify.utility.isDarkTheme
import com.looker.droidify.utility.onLaunchClick
import io.reactivex.rxjava3.disposables.Disposable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
@ -43,10 +58,9 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
// TODO clean up and replace dropped functions from AppDetailFragment
class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Callbacks {
class AppSheetX() : FullscreenBottomSheetDialogFragment(), Callbacks {
companion object {
private const val EXTRA_PACKAGE_NAME = "packageName"
private const val STATE_ADAPTER = "adapter"
}
constructor(packageName: String) : this() {
@ -55,7 +69,6 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
}
}
private lateinit var binding: SheetAppXBinding
val viewModel: AppViewModelX by viewModels {
AppViewModelX.Factory(mainActivityX.db, packageName)
}
@ -65,13 +78,7 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
val packageName: String
get() = requireArguments().getString(EXTRA_PACKAGE_NAME)!!
private var actions = Pair(emptySet<Action>(), null as Action?)
private var productRepos = emptyList<Pair<Product, Repository>>()
private var products = emptyList<Product>()
private var installed: Installed? = null
private var downloading = false
private var productDisposable: Disposable? = null
private val downloadConnection = Connection(DownloadService::class.java, onBind = { _, binder ->
binder.stateSubject
.filter { it.packageName == packageName }
@ -86,83 +93,53 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = SheetAppXBinding.inflate(layoutInflater)
binding.lifecycleOwner = this
super.onCreate(savedInstanceState)
setupAdapters()
return binding.root
}
private fun setupAdapters() {
downloadConnection.bind(requireContext())
binding.recyclerView.apply {
id = android.R.id.list
this.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
this.adapter = AppDetailAdapter(this@AppSheetX)
}
}
override fun setupLayout() {
// TODO simplify observing and updating
viewModel.installedItem.observe(viewLifecycleOwner) {
installed = it
updateSheet()
}
viewModel.repositories.observe(viewLifecycleOwner) {
if (it.isNotEmpty() && products.isNotEmpty()) updateSheet()
}
viewModel.products.observe(viewLifecycleOwner) {
products = it.filterNotNull()
viewModel.repositories.value?.let { repos ->
if (repos.isNotEmpty() && products.isNotEmpty()) updateSheet()
return ComposeView(requireContext()).apply {
setContent {
AppTheme(
darkTheme = when (Preferences[Preferences.Key.Theme]) {
is Preferences.Theme.System -> isSystemInDarkTheme()
is Preferences.Theme.AmoledSystem -> isSystemInDarkTheme()
else -> isDarkTheme
}
) {
AppSheet()
}
}
}
}
override fun updateSheet() {
products.mapNotNull { product ->
viewModel.repositories.value
?.firstOrNull { it.id == product.repositoryId }
?.let { Pair(product, it) }
}.apply {
productRepos = this
private fun setupAdapters() {
downloadConnection.bind(requireContext())
}
val adapter = binding.recyclerView.adapter as AppDetailAdapter
adapter.setProducts(
binding.recyclerView.context,
packageName,
this,
installed
)
override fun setupLayout() {
viewModel._productRepos.observe(this) {
lifecycleScope.launch {
updateButtons()
}
}
}
override fun updateSheet() {
}
override fun onDestroyView() {
super.onDestroyView()
productDisposable?.dispose()
productDisposable = null
downloadConnection.unbind(requireContext())
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val adapterState = (binding.recyclerView.adapter as? AppDetailAdapter)?.saveState()
adapterState?.let { outState.putParcelable(STATE_ADAPTER, it) }
}
private suspend fun updateButtons() {
updateButtons(ProductPreferences[packageName])
}
// TODO rename to updateActions
private suspend fun updateButtons(preference: ProductPreference) =
withContext(Dispatchers.Default) {
val installed = installed
val installed = viewModel.installedItem.value
val productRepos = viewModel.productRepos
val product = findSuggestedProduct(productRepos, installed) { it.first }?.first
val compatible = product != null && product.selectedReleases.firstOrNull()
.let { it != null && it.incompatibilities.isEmpty() }
@ -175,80 +152,76 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
product != null && installed != null && installed.launcherActivities.isNotEmpty()
val canShare = product != null && productRepos[0].second.name == "F-Droid"
val actions = mutableSetOf<Action>()
val actions = mutableSetOf<PackageState>()
launch {
if (canInstall) {
actions += Action.INSTALL
actions += Install
}
}
launch {
if (canUpdate) {
actions += Action.UPDATE
actions += Update
}
}
launch {
if (canLaunch) {
actions += Action.LAUNCH
actions += Launch
}
}
launch {
if (installed != null) {
actions += Action.DETAILS
actions += Details
}
}
launch {
if (canUninstall) {
actions += Action.UNINSTALL
actions += Uninstall
}
}
launch {
if (canShare) {
actions += Action.SHARE
actions += Share
}
}
val primaryAction = when {
canUpdate -> Action.UPDATE
canLaunch -> Action.LAUNCH
canInstall -> Action.INSTALL
canShare -> Action.SHARE
canUpdate -> Update
canLaunch -> Launch
canInstall -> Install
canShare -> Share
else -> null
}
val secondaryAction = when {
primaryAction != Action.SHARE && canShare -> Action.SHARE
primaryAction != Action.LAUNCH && canLaunch -> Action.LAUNCH
installed != null && canUninstall -> Action.UNINSTALL
primaryAction != Share && canShare -> Share
primaryAction != Launch && canLaunch -> Launch
installed != null && canUninstall -> Uninstall
else -> null
}
launch(Dispatchers.Main) {
val adapterAction =
if (downloading) Action.CANCEL else primaryAction
val adapterSecondaryAction =
if (downloading) null else secondaryAction
(binding.recyclerView.adapter as? AppDetailAdapter)
?.setAction(adapterAction)
(binding.recyclerView.adapter as? AppDetailAdapter)
?.setSecondaryAction(adapterSecondaryAction)
withContext(Dispatchers.Main) {
viewModel.actions.value = actions
if (!downloading) {
viewModel.state.value = primaryAction
viewModel.secondaryAction.value = secondaryAction
} else {
viewModel.secondaryAction.value = null
}
}
launch { this@AppSheetX.actions = Pair(actions, primaryAction) }
}
private suspend fun updateDownloadState(state: DownloadService.State?) {
val status = when (state) {
is DownloadService.State.Pending -> AppDetailAdapter.Status.Pending
is DownloadService.State.Connecting -> AppDetailAdapter.Status.Connecting
is DownloadService.State.Downloading -> AppDetailAdapter.Status.Downloading(
is DownloadService.State.Pending -> Pending
is DownloadService.State.Connecting -> Connecting
is DownloadService.State.Downloading -> com.looker.droidify.entity.Downloading(
state.read,
state.total
)
is DownloadService.State.Success, is DownloadService.State.Error, is DownloadService.State.Cancel, null -> null
else -> null
}
val downloading = status != null
if (this.downloading != downloading) {
this.downloading = downloading
updateButtons()
}
(binding.recyclerView.adapter as? AppDetailAdapter)?.setStatus(status)
val downloading = status is Cancelable
this.downloading = downloading
updateButtons()
viewModel.state.value = status
if (state is DownloadService.State.Success && isResumed && !rootInstallerEnabled) {
withContext(Dispatchers.Default) {
AppInstaller.getInstance(context)?.defaultInstaller?.install(state.release.cacheFileName)
@ -256,12 +229,13 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
}
}
override fun onActionClick(action: Action) {
override fun onActionClick(action: PackageState?) {
val productRepos = viewModel.productRepos
when (action) {
Action.INSTALL,
Action.UPDATE,
Install,
Update,
-> {
val installedItem = installed
val installedItem = viewModel.installedItem.value
lifecycleScope.launch {
startUpdate(
packageName,
@ -272,31 +246,37 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
}
Unit
}
Action.LAUNCH -> {
installed?.let { requireContext().onLaunchClick(it, childFragmentManager) }
Launch -> {
viewModel.installedItem.value?.let {
requireContext().onLaunchClick(
it,
childFragmentManager
)
}
Unit
}
Action.DETAILS -> {
Details -> {
startActivity(
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.parse("package:$packageName"))
)
}
Action.UNINSTALL -> {
Uninstall -> {
lifecycleScope.launch {
AppInstaller.getInstance(context)?.defaultInstaller?.uninstall(packageName)
}
Unit
}
Action.CANCEL -> {
is Cancelable -> {
val binder = downloadConnection.binder
if (downloading && binder != null) {
binder.cancel(packageName)
} else Unit
}
Action.SHARE -> {
Share -> {
shareIntent(packageName, productRepos[0].first.label)
}
else -> Unit
}::class
}
@ -325,8 +305,9 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
)
}
// TODO fix in compose implementation
override fun onScreenshotClick(screenshot: Screenshot) {
val pair = productRepos.asSequence()
val pair = viewModel.productRepos.asSequence()
.map { it ->
Pair(
it.second,
@ -345,7 +326,7 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
}
override fun onReleaseClick(release: Release) {
val installedItem = installed
val installedItem = viewModel.installedItem.value
when {
release.incompatibilities.isNotEmpty() -> {
MessageDialog(
@ -365,7 +346,7 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
}
else -> {
val productRepository =
productRepos.asSequence()
viewModel.productRepos.asSequence()
.filter { it -> it.first.releases.any { it === release } }
.firstOrNull()
if (productRepository != null) {
@ -392,4 +373,17 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), AppDetailAdapter.Call
}
}
}
private fun copyLinkToClipboard(view: View, link: String) {
val clipboardManager =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, link))
Snackbar.make(view, R.string.link_copied_to_clipboard, Snackbar.LENGTH_SHORT).show()
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppSheet() {
}
}