LeOS-Droid/app/src/main/kotlin/com/leos/droidify/ui/MessageDialog.kt

266 lines
10 KiB
Kotlin

package com.leos.droidify.ui
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.leos.core.common.SdkCheck
import com.leos.core.common.nullIfEmpty
import com.leos.core.domain.Release
import com.leos.droidify.ui.repository.RepositoryFragment
import com.leos.droidify.utility.PackageItemResolver
import com.leos.droidify.utility.extension.android.Android
import kotlinx.parcelize.Parceler
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.TypeParceler
import com.leos.core.common.R.string as stringRes
class MessageDialog() : DialogFragment() {
companion object {
private const val EXTRA_MESSAGE = "message"
}
constructor(message: Message) : this() {
arguments = bundleOf(EXTRA_MESSAGE to message)
}
fun show(fragmentManager: FragmentManager) {
show(fragmentManager, this::class.java.name)
}
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog {
val dialog = MaterialAlertDialogBuilder(requireContext())
val message = if (SdkCheck.isTiramisu) {
arguments?.getParcelable(EXTRA_MESSAGE, Message::class.java)!!
} else {
arguments?.getParcelable(EXTRA_MESSAGE)!!
}
when (message) {
is Message.DeleteRepositoryConfirm -> {
dialog.setTitle(stringRes.confirmation)
dialog.setMessage(stringRes.delete_repository_DESC)
dialog.setPositiveButton(stringRes.delete) { _, _ ->
(parentFragment as RepositoryFragment).onDeleteConfirm()
}
dialog.setNegativeButton(stringRes.cancel, null)
}
is Message.CantEditSyncing -> {
dialog.setTitle(stringRes.action_failed)
dialog.setMessage(stringRes.cant_edit_sync_DESC)
dialog.setPositiveButton(stringRes.ok, null)
}
is Message.Link -> {
dialog.setTitle(stringRes.confirmation)
dialog.setMessage(getString(stringRes.open_DESC_FORMAT, message.uri.toString()))
dialog.setPositiveButton(stringRes.ok) { _, _ ->
try {
startActivity(Intent(Intent.ACTION_VIEW, message.uri))
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
}
}
dialog.setNegativeButton(stringRes.cancel, null)
}
is Message.Permissions -> {
val packageManager = requireContext().packageManager
val builder = StringBuilder()
val localCache = PackageItemResolver.LocalCache()
val title = if (message.group != null) {
val name = try {
val permissionGroupInfo =
packageManager.getPermissionGroupInfo(message.group, 0)
PackageItemResolver.loadLabel(
requireContext(),
localCache,
permissionGroupInfo
)?.nullIfEmpty()?.let { if (it == message.group) null else it }
} catch (e: Exception) {
null
}
name ?: getString(stringRes.unknown)
} else {
getString(stringRes.other)
}
for (permission in message.permissions) {
kotlin.runCatching {
val permissionInfo = packageManager.getPermissionInfo(permission, 0)
PackageItemResolver.loadDescription(
requireContext(),
localCache,
permissionInfo
)?.nullIfEmpty()?.let { if (it == permission) null else it }
?: error("Invalid Permission Description")
}.onSuccess {
builder.append(it).append("\n\n")
}
}
if (builder.isNotEmpty()) {
builder.delete(builder.length - 2, builder.length)
} else {
builder.append(getString(stringRes.no_description_available_DESC))
}
dialog.setTitle(title)
dialog.setMessage(builder)
dialog.setPositiveButton(stringRes.ok, null)
}
is Message.ReleaseIncompatible -> {
val builder = StringBuilder()
val minSdkVersion =
if (Release.Incompatibility.MinSdk in message.incompatibilities) {
message.minSdkVersion
} else {
null
}
val maxSdkVersion =
if (Release.Incompatibility.MaxSdk in message.incompatibilities) {
message.maxSdkVersion
} else {
null
}
if (minSdkVersion != null || maxSdkVersion != null) {
val versionMessage = minSdkVersion?.let {
getString(
stringRes.incompatible_api_min_DESC_FORMAT,
it
)
}
?: maxSdkVersion?.let {
getString(
stringRes.incompatible_api_max_DESC_FORMAT,
it
)
}
builder.append(
getString(
stringRes.incompatible_api_DESC_FORMAT,
Android.name,
SdkCheck.sdk,
versionMessage.orEmpty()
)
).append("\n\n")
}
if (Release.Incompatibility.Platform in message.incompatibilities) {
builder.append(
getString(
stringRes.incompatible_platforms_DESC_FORMAT,
Android.primaryPlatform ?: getString(stringRes.unknown),
message.platforms.joinToString(separator = ", ")
)
).append("\n\n")
}
val features =
message.incompatibilities.mapNotNull { it as? Release.Incompatibility.Feature }
if (features.isNotEmpty()) {
builder.append(getString(stringRes.incompatible_features_DESC))
for (feature in features) {
builder.append("\n\u2022 ").append(feature.feature)
}
builder.append("\n\n")
}
if (builder.isNotEmpty()) {
builder.delete(builder.length - 2, builder.length)
}
dialog.setTitle(stringRes.incompatible_version)
dialog.setMessage(builder)
dialog.setPositiveButton(stringRes.ok, null)
}
is Message.ReleaseOlder -> {
dialog.setTitle(stringRes.incompatible_version)
dialog.setMessage(stringRes.incompatible_older_DESC)
dialog.setPositiveButton(stringRes.ok, null)
}
is Message.ReleaseSignatureMismatch -> {
dialog.setTitle(stringRes.incompatible_version)
dialog.setMessage(stringRes.incompatible_signature_DESC)
dialog.setPositiveButton(stringRes.ok, null)
}
}::class
return dialog.create()
}
}
@Parcelize
sealed interface Message : Parcelable {
@Parcelize
data object DeleteRepositoryConfirm : Message
@Parcelize
data object CantEditSyncing : Message
@Parcelize
class Link(val uri: Uri) : Message
@Parcelize
class Permissions(val group: String?, val permissions: List<String>) : Message
@Parcelize
@TypeParceler<Release.Incompatibility, ReleaseIncompatibilityParceler>
class ReleaseIncompatible(
val incompatibilities: List<Release.Incompatibility>,
val platforms: List<String>,
val minSdkVersion: Int,
val maxSdkVersion: Int
) : Message
@Parcelize
data object ReleaseOlder : Message
@Parcelize
data object ReleaseSignatureMismatch : Message
}
class ReleaseIncompatibilityParceler : Parceler<Release.Incompatibility> {
private companion object {
// Incompatibility indices in `Parcel`
const val MIN_SDK_INDEX = 0
const val MAX_SDK_INDEX = 1
const val PLATFORM_INDEX = 2
const val FEATURE_INDEX = 3
}
override fun create(parcel: Parcel): Release.Incompatibility {
return when (parcel.readInt()) {
MIN_SDK_INDEX -> Release.Incompatibility.MinSdk
MAX_SDK_INDEX -> Release.Incompatibility.MaxSdk
PLATFORM_INDEX -> Release.Incompatibility.Platform
FEATURE_INDEX -> Release.Incompatibility.Feature(requireNotNull(parcel.readString()))
else -> error("Invalid Index for Incompatibility")
}
}
override fun Release.Incompatibility.write(parcel: Parcel, flags: Int) {
when (this) {
is Release.Incompatibility.MinSdk -> {
parcel.writeInt(MIN_SDK_INDEX)
}
is Release.Incompatibility.MaxSdk -> {
parcel.writeInt(MAX_SDK_INDEX)
}
is Release.Incompatibility.Platform -> {
parcel.writeInt(PLATFORM_INDEX)
}
is Release.Incompatibility.Feature -> {
parcel.writeInt(FEATURE_INDEX)
parcel.writeString(feature)
}
}
}
}