Improve local manga directories config screen
This commit is contained in:
@@ -21,8 +21,8 @@ android {
|
|||||||
applicationId 'org.koitharu.kotatsu'
|
applicationId 'org.koitharu.kotatsu'
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 1031
|
versionCode = 1032
|
||||||
versionName = '9.3'
|
versionName = '9.4'
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
||||||
ksp {
|
ksp {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import android.view.View
|
|||||||
import androidx.annotation.Px
|
import androidx.annotation.Px
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
class SpacingItemDecoration(@Px private val spacing: Int) : RecyclerView.ItemDecoration() {
|
class SpacingItemDecoration(
|
||||||
|
@Px private val spacing: Int,
|
||||||
|
private val withBottomPadding: Boolean,
|
||||||
|
) : RecyclerView.ItemDecoration() {
|
||||||
|
|
||||||
override fun getItemOffsets(
|
override fun getItemOffsets(
|
||||||
outRect: Rect,
|
outRect: Rect,
|
||||||
@@ -13,6 +16,6 @@ class SpacingItemDecoration(@Px private val spacing: Int) : RecyclerView.ItemDec
|
|||||||
parent: RecyclerView,
|
parent: RecyclerView,
|
||||||
state: RecyclerView.State,
|
state: RecyclerView.State,
|
||||||
) {
|
) {
|
||||||
outRect.set(spacing, spacing, spacing, spacing)
|
outRect.set(spacing, spacing, spacing, if (withBottomPadding) spacing else 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ import androidx.annotation.WorkerThread
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import dagger.Reusable
|
import dagger.Reusable
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.Cache
|
import okhttp3.Cache
|
||||||
|
import org.koitharu.kotatsu.core.LocalizedAppContext
|
||||||
import org.koitharu.kotatsu.core.exceptions.NonFileUriException
|
import org.koitharu.kotatsu.core.exceptions.NonFileUriException
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.core.util.ext.computeSize
|
import org.koitharu.kotatsu.core.util.ext.computeSize
|
||||||
@@ -39,7 +39,7 @@ private const val CACHE_SIZE_MAX: Long = 250 * 1024 * 1024 // 250MB
|
|||||||
|
|
||||||
@Reusable
|
@Reusable
|
||||||
class LocalStorageManager @Inject constructor(
|
class LocalStorageManager @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@LocalizedAppContext private val context: Context,
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class WelcomeSheet : BaseAdaptiveSheet<SheetWelcomeBinding>(), ChipsView.OnChipC
|
|||||||
binding.chipsType.onChipClickListener = this
|
binding.chipsType.onChipClickListener = this
|
||||||
binding.chipBackup.setOnClickListener(this)
|
binding.chipBackup.setOnClickListener(this)
|
||||||
binding.chipSync.setOnClickListener(this)
|
binding.chipSync.setOnClickListener(this)
|
||||||
|
binding.chipDirectories.setOnClickListener(this)
|
||||||
|
|
||||||
viewModel.locales.observe(viewLifecycleOwner, ::onLocalesChanged)
|
viewModel.locales.observe(viewLifecycleOwner, ::onLocalesChanged)
|
||||||
viewModel.types.observe(viewLifecycleOwner, ::onTypesChanged)
|
viewModel.types.observe(viewLifecycleOwner, ::onTypesChanged)
|
||||||
@@ -86,6 +87,10 @@ class WelcomeSheet : BaseAdaptiveSheet<SheetWelcomeBinding>(), ChipsView.OnChipC
|
|||||||
val accountType = getString(R.string.account_type_sync)
|
val accountType = getString(R.string.account_type_sync)
|
||||||
am.addAccount(accountType, accountType, null, null, requireActivity(), null, null)
|
am.addAccount(accountType, accountType, null, null, requireActivity(), null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R.id.chip_directories -> {
|
||||||
|
router.openDirectoriesSettings()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ fun searchResultsAD(
|
|||||||
binding.recyclerView.addItemDecoration(selectionDecoration)
|
binding.recyclerView.addItemDecoration(selectionDecoration)
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
val spacing = context.resources.getDimensionPixelOffset(R.dimen.grid_spacing_outer)
|
val spacing = context.resources.getDimensionPixelOffset(R.dimen.grid_spacing_outer)
|
||||||
binding.recyclerView.addItemDecoration(SpacingItemDecoration(spacing))
|
binding.recyclerView.addItemDecoration(SpacingItemDecoration(spacing, withBottomPadding = true))
|
||||||
val eventListener = AdapterDelegateClickListenerAdapter(this, itemClickListener)
|
val eventListener = AdapterDelegateClickListenerAdapter(this, itemClickListener)
|
||||||
binding.buttonMore.setOnClickListener(eventListener)
|
binding.buttonMore.setOnClickListener(eventListener)
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fun searchSuggestionMangaListAD(
|
|||||||
left = recyclerView.paddingLeft - spacing,
|
left = recyclerView.paddingLeft - spacing,
|
||||||
right = recyclerView.paddingRight - spacing,
|
right = recyclerView.paddingRight - spacing,
|
||||||
)
|
)
|
||||||
recyclerView.addItemDecoration(SpacingItemDecoration(spacing))
|
recyclerView.addItemDecoration(SpacingItemDecoration(spacing, withBottomPadding = true))
|
||||||
val scrollResetCallback = RecyclerViewScrollCallback(recyclerView, 0, 0)
|
val scrollResetCallback = RecyclerViewScrollCallback(recyclerView, 0, 0)
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
|
|||||||
@@ -1,38 +1,66 @@
|
|||||||
package org.koitharu.kotatsu.settings.storage.directories
|
package org.koitharu.kotatsu.settings.storage.directories
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.text.bold
|
||||||
|
import androidx.core.text.buildSpannedString
|
||||||
|
import androidx.core.text.color
|
||||||
|
import androidx.core.view.isGone
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||||
import org.koitharu.kotatsu.core.util.ext.drawableStart
|
import org.koitharu.kotatsu.core.util.FileSize
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||||
import org.koitharu.kotatsu.core.util.ext.setTooltipCompat
|
import org.koitharu.kotatsu.core.util.ext.setTooltipCompat
|
||||||
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
||||||
import org.koitharu.kotatsu.databinding.ItemStorageConfigBinding
|
import org.koitharu.kotatsu.databinding.ItemStorageConfig2Binding
|
||||||
import org.koitharu.kotatsu.settings.storage.DirectoryModel
|
|
||||||
|
|
||||||
fun directoryConfigAD(
|
fun directoryConfigAD(
|
||||||
clickListener: OnListItemClickListener<DirectoryModel>,
|
clickListener: OnListItemClickListener<DirectoryConfigModel>,
|
||||||
) = adapterDelegateViewBinding<DirectoryModel, DirectoryModel, ItemStorageConfigBinding>(
|
) = adapterDelegateViewBinding<DirectoryConfigModel, DirectoryConfigModel, ItemStorageConfig2Binding>(
|
||||||
{ layoutInflater, parent -> ItemStorageConfigBinding.inflate(layoutInflater, parent, false) },
|
{ layoutInflater, parent -> ItemStorageConfig2Binding.inflate(layoutInflater, parent, false) },
|
||||||
) {
|
) {
|
||||||
|
|
||||||
binding.buttonRemove.setOnClickListener { v -> clickListener.onItemClick(item, v) }
|
binding.buttonRemove.setOnClickListener { v -> clickListener.onItemClick(item, v) }
|
||||||
binding.buttonRemove.setTooltipCompat(binding.buttonRemove.contentDescription)
|
binding.buttonRemove.setTooltipCompat(binding.buttonRemove.contentDescription)
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
binding.textViewTitle.text = item.title ?: getString(item.titleRes)
|
binding.textViewTitle.text = item.title
|
||||||
binding.textViewSubtitle.textAndVisible = item.file?.absolutePath
|
binding.textViewSubtitle.text = item.path.absolutePath
|
||||||
binding.buttonRemove.isVisible = item.isRemovable
|
binding.buttonRemove.isGone = item.isAppPrivate
|
||||||
binding.buttonRemove.isEnabled = !item.isChecked
|
binding.buttonRemove.isEnabled = !item.isDefault
|
||||||
binding.textViewTitle.drawableStart = if (!item.isAvailable) {
|
binding.spacer.visibility = if (item.isAppPrivate) {
|
||||||
ContextCompat.getDrawable(context, R.drawable.ic_alert_outline)?.apply {
|
View.INVISIBLE
|
||||||
setTint(ContextCompat.getColor(context, R.color.warning))
|
|
||||||
}
|
|
||||||
} else if (item.isChecked) {
|
|
||||||
ContextCompat.getDrawable(context, R.drawable.ic_download)
|
|
||||||
} else {
|
} else {
|
||||||
null
|
View.GONE
|
||||||
}
|
}
|
||||||
|
binding.textViewInfo.textAndVisible = buildSpannedString {
|
||||||
|
if (item.isDefault) {
|
||||||
|
bold {
|
||||||
|
append(getString(R.string.download_default_directory))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!item.isAccessible) {
|
||||||
|
if (isNotEmpty()) appendLine()
|
||||||
|
color(
|
||||||
|
context.getThemeColor(
|
||||||
|
androidx.appcompat.R.attr.colorError,
|
||||||
|
ContextCompat.getColor(context, R.color.common_red),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
append(getString(R.string.no_write_permission_to_file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.isAppPrivate) {
|
||||||
|
if (isNotEmpty()) appendLine()
|
||||||
|
append(getString(R.string.private_app_directory_warning))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.indicatorSize.max = FileSize.BYTES.convert(item.available, FileSize.KILOBYTES).toInt()
|
||||||
|
binding.indicatorSize.progress = FileSize.BYTES.convert(item.size, FileSize.KILOBYTES).toInt()
|
||||||
|
binding.textViewSize.text = context.getString(
|
||||||
|
R.string.available_pattern,
|
||||||
|
FileSize.BYTES.format(context, item.available),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.koitharu.kotatsu.settings.storage.directories
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||||
|
|
||||||
|
class DirectoryConfigDiffCallback : ItemCallback<DirectoryConfigModel>() {
|
||||||
|
|
||||||
|
override fun areItemsTheSame(oldItem: DirectoryConfigModel, newItem: DirectoryConfigModel): Boolean {
|
||||||
|
return oldItem.path == newItem.path
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: DirectoryConfigModel, newItem: DirectoryConfigModel): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChangePayload(oldItem: DirectoryConfigModel, newItem: DirectoryConfigModel): Any? {
|
||||||
|
return if (oldItem.isDefault != newItem.isDefault) {
|
||||||
|
Unit
|
||||||
|
} else {
|
||||||
|
super.getChangePayload(oldItem, newItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.koitharu.kotatsu.settings.storage.directories
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
data class DirectoryConfigModel(
|
||||||
|
val title: String,
|
||||||
|
val path: File,
|
||||||
|
val isDefault: Boolean,
|
||||||
|
val isAppPrivate: Boolean,
|
||||||
|
val isAccessible: Boolean,
|
||||||
|
val size: Long,
|
||||||
|
val available: Long,
|
||||||
|
) : ListModel {
|
||||||
|
|
||||||
|
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||||
|
return other is DirectoryConfigModel && path == other.path
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,18 +20,17 @@ import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
|||||||
import org.koitharu.kotatsu.core.os.OpenDocumentTreeHelper
|
import org.koitharu.kotatsu.core.os.OpenDocumentTreeHelper
|
||||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||||
|
import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration
|
||||||
import org.koitharu.kotatsu.core.util.ext.consumeAllSystemBarsInsets
|
import org.koitharu.kotatsu.core.util.ext.consumeAllSystemBarsInsets
|
||||||
import org.koitharu.kotatsu.core.util.ext.observe
|
import org.koitharu.kotatsu.core.util.ext.observe
|
||||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||||
import org.koitharu.kotatsu.core.util.ext.tryLaunch
|
import org.koitharu.kotatsu.core.util.ext.tryLaunch
|
||||||
import org.koitharu.kotatsu.databinding.ActivityMangaDirectoriesBinding
|
import org.koitharu.kotatsu.databinding.ActivityMangaDirectoriesBinding
|
||||||
import org.koitharu.kotatsu.settings.storage.DirectoryDiffCallback
|
|
||||||
import org.koitharu.kotatsu.settings.storage.DirectoryModel
|
|
||||||
import org.koitharu.kotatsu.settings.storage.RequestStorageManagerPermissionContract
|
import org.koitharu.kotatsu.settings.storage.RequestStorageManagerPermissionContract
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MangaDirectoriesActivity : BaseActivity<ActivityMangaDirectoriesBinding>(),
|
class MangaDirectoriesActivity : BaseActivity<ActivityMangaDirectoriesBinding>(),
|
||||||
OnListItemClickListener<DirectoryModel>, View.OnClickListener {
|
OnListItemClickListener<DirectoryConfigModel>, View.OnClickListener {
|
||||||
|
|
||||||
private val viewModel: MangaDirectoriesViewModel by viewModels()
|
private val viewModel: MangaDirectoriesViewModel by viewModels()
|
||||||
private val pickFileTreeLauncher = OpenDocumentTreeHelper(
|
private val pickFileTreeLauncher = OpenDocumentTreeHelper(
|
||||||
@@ -63,8 +62,10 @@ class MangaDirectoriesActivity : BaseActivity<ActivityMangaDirectoriesBinding>()
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(ActivityMangaDirectoriesBinding.inflate(layoutInflater))
|
setContentView(ActivityMangaDirectoriesBinding.inflate(layoutInflater))
|
||||||
setDisplayHomeAsUp(isEnabled = true, showUpAsClose = false)
|
setDisplayHomeAsUp(isEnabled = true, showUpAsClose = false)
|
||||||
val adapter = AsyncListDifferDelegationAdapter(DirectoryDiffCallback(), directoryConfigAD(this))
|
val adapter = AsyncListDifferDelegationAdapter(DirectoryConfigDiffCallback(), directoryConfigAD(this))
|
||||||
|
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing_large)
|
||||||
viewBinding.recyclerView.adapter = adapter
|
viewBinding.recyclerView.adapter = adapter
|
||||||
|
viewBinding.recyclerView.addItemDecoration(SpacingItemDecoration(spacing, withBottomPadding = false))
|
||||||
viewBinding.fabAdd.setOnClickListener(this)
|
viewBinding.fabAdd.setOnClickListener(this)
|
||||||
viewModel.items.observe(this) { adapter.items = it }
|
viewModel.items.observe(this) { adapter.items = it }
|
||||||
viewModel.isLoading.observe(this) { viewBinding.progressBar.isVisible = it }
|
viewModel.isLoading.observe(this) { viewBinding.progressBar.isVisible = it }
|
||||||
@@ -76,8 +77,8 @@ class MangaDirectoriesActivity : BaseActivity<ActivityMangaDirectoriesBinding>()
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClick(item: DirectoryModel, view: View) {
|
override fun onItemClick(item: DirectoryConfigModel, view: View) {
|
||||||
viewModel.onRemoveClick(item.file ?: return)
|
viewModel.onRemoveClick(item.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
override fun onClick(v: View?) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.koitharu.kotatsu.settings.storage.directories
|
package org.koitharu.kotatsu.settings.storage.directories
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.StatFs
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -8,10 +9,10 @@ import kotlinx.coroutines.cancelAndJoin
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.computeSize
|
||||||
import org.koitharu.kotatsu.core.util.ext.isReadable
|
import org.koitharu.kotatsu.core.util.ext.isReadable
|
||||||
import org.koitharu.kotatsu.core.util.ext.isWriteable
|
import org.koitharu.kotatsu.core.util.ext.isWriteable
|
||||||
import org.koitharu.kotatsu.local.data.LocalStorageManager
|
import org.koitharu.kotatsu.local.data.LocalStorageManager
|
||||||
import org.koitharu.kotatsu.settings.storage.DirectoryModel
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ class MangaDirectoriesViewModel @Inject constructor(
|
|||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
val items = MutableStateFlow(emptyList<DirectoryModel>())
|
val items = MutableStateFlow(emptyList<DirectoryConfigModel>())
|
||||||
private var loadingJob: Job? = null
|
private var loadingJob: Job? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -64,26 +65,31 @@ class MangaDirectoriesViewModel @Inject constructor(
|
|||||||
val customDirs = settings.userSpecifiedMangaDirectories - applicationDirs
|
val customDirs = settings.userSpecifiedMangaDirectories - applicationDirs
|
||||||
items.value = buildList(applicationDirs.size + customDirs.size) {
|
items.value = buildList(applicationDirs.size + customDirs.size) {
|
||||||
applicationDirs.mapTo(this) { dir ->
|
applicationDirs.mapTo(this) { dir ->
|
||||||
DirectoryModel(
|
dir.toDirectoryModel(
|
||||||
title = storageManager.getDirectoryDisplayName(dir, isFullPath = false),
|
isDefault = dir == downloadDir,
|
||||||
titleRes = 0,
|
isAppPrivate = true,
|
||||||
file = dir,
|
|
||||||
isChecked = dir == downloadDir,
|
|
||||||
isAvailable = dir.isReadable() && dir.isWriteable(),
|
|
||||||
isRemovable = false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
customDirs.mapTo(this) { dir ->
|
customDirs.mapTo(this) { dir ->
|
||||||
DirectoryModel(
|
dir.toDirectoryModel(
|
||||||
title = storageManager.getDirectoryDisplayName(dir, isFullPath = false),
|
isDefault = dir == downloadDir,
|
||||||
titleRes = 0,
|
isAppPrivate = false,
|
||||||
file = dir,
|
|
||||||
isChecked = dir == downloadDir,
|
|
||||||
isAvailable = dir.isReadable() && dir.isWriteable(),
|
|
||||||
isRemovable = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun File.toDirectoryModel(
|
||||||
|
isDefault: Boolean,
|
||||||
|
isAppPrivate: Boolean,
|
||||||
|
) = DirectoryConfigModel(
|
||||||
|
title = storageManager.getDirectoryDisplayName(this, isFullPath = false),
|
||||||
|
path = this,
|
||||||
|
isDefault = isDefault,
|
||||||
|
isAccessible = isReadable() && isWriteable(),
|
||||||
|
isAppPrivate = isAppPrivate,
|
||||||
|
size = computeSize(),
|
||||||
|
available = StatFs(this.absolutePath).availableBytes,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
@@ -27,9 +28,11 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/list_spacing_large"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
|
tools:listitem="@layout/item_storage_config2" />
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
|
|||||||
93
app/src/main/res/layout/item_storage_config2.xml
Normal file
93
app/src/main/res/layout/item_storage_config2.xml
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:layout_margin="@dimen/screen_padding">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/margin_small">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/screen_padding"
|
||||||
|
android:layout_marginTop="@dimen/screen_padding"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||||
|
tools:text="@tools:sample/lorem[3]" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/screen_padding"
|
||||||
|
android:layout_marginTop="@dimen/margin_small"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||||
|
tools:text="@tools:sample/lorem[5]" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/screen_padding"
|
||||||
|
android:layout_marginTop="@dimen/screen_padding"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_size"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
tools:text="250MB / 10GB" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/indicator_size"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:trackCornerRadius="5dp"
|
||||||
|
app:trackThickness="10dp"
|
||||||
|
tools:progress="40" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/screen_padding"
|
||||||
|
android:layout_marginTop="@dimen/margin_small"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
tools:text="Content will be removed within application" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_remove"
|
||||||
|
style="?buttonBarButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
|
android:text="@string/remove" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/spacer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/margin_small"
|
||||||
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
@@ -64,6 +64,14 @@
|
|||||||
android:text="@string/sync_auth"
|
android:text="@string/sync_auth"
|
||||||
app:chipIcon="@drawable/ic_sync" />
|
app:chipIcon="@drawable/ic_sync" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_directories"
|
||||||
|
style="@style/Widget.Kotatsu.Chip.Assist"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/local_manga_directories"
|
||||||
|
app:chipIcon="@drawable/ic_storage" />
|
||||||
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|||||||
@@ -900,4 +900,7 @@
|
|||||||
<string name="data_removal">Data removal</string>
|
<string name="data_removal">Data removal</string>
|
||||||
<string name="privacy">Privacy</string>
|
<string name="privacy">Privacy</string>
|
||||||
<string name="source_broken_warning">This manga source has been marked as broken. Some features may not work</string>
|
<string name="source_broken_warning">This manga source has been marked as broken. Some features may not work</string>
|
||||||
|
<string name="download_default_directory">Default directory for downloading manga</string>
|
||||||
|
<string name="private_app_directory_warning">This directory with all data will be deleted if you uninstall the application</string>
|
||||||
|
<string name="available_pattern">%1$s available</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user