Reformated all the code

This commit is contained in:
Mohit
2021-06-08 21:18:44 +05:30
parent 74e8287cf1
commit 29ef88853d
50 changed files with 6043 additions and 4857 deletions

View File

@ -12,220 +12,264 @@ import com.looker.droidify.R
import com.looker.droidify.entity.Release
import com.looker.droidify.utility.KParcelable
import com.looker.droidify.utility.PackageItemResolver
import com.looker.droidify.utility.extension.android.*
import com.looker.droidify.utility.extension.text.*
import com.looker.droidify.utility.extension.android.Android
import com.looker.droidify.utility.extension.text.nullIfEmpty
class MessageDialog(): DialogFragment() {
companion object {
private const val EXTRA_MESSAGE = "message"
}
sealed class Message: KParcelable {
object DeleteRepositoryConfirm: Message() {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator { DeleteRepositoryConfirm }
class MessageDialog() : DialogFragment() {
companion object {
private const val EXTRA_MESSAGE = "message"
}
object CantEditSyncing: Message() {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator { CantEditSyncing }
}
class Link(val uri: Uri): Message() {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(uri.toString())
}
companion object {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator {
val uri = Uri.parse(it.readString()!!)
Link(uri)
sealed class Message : KParcelable {
object DeleteRepositoryConfirm : Message() {
@Suppress("unused")
@JvmField
val CREATOR = KParcelable.creator { DeleteRepositoryConfirm }
}
}
}
class Permissions(val group: String?, val permissions: List<String>): Message() {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(group)
dest.writeStringList(permissions)
}
companion object {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator {
val group = it.readString()
val permissions = it.createStringArrayList()!!
Permissions(group, permissions)
object CantEditSyncing : Message() {
@Suppress("unused")
@JvmField
val CREATOR = KParcelable.creator { CantEditSyncing }
}
}
}
class ReleaseIncompatible(val incompatibilities: List<Release.Incompatibility>,
val platforms: List<String>, val minSdkVersion: Int, val maxSdkVersion: Int): Message() {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeInt(incompatibilities.size)
for (incompatibility in incompatibilities) {
when (incompatibility) {
is Release.Incompatibility.MinSdk -> {
dest.writeInt(0)
class Link(val uri: Uri) : Message() {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(uri.toString())
}
is Release.Incompatibility.MaxSdk -> {
dest.writeInt(1)
}
is Release.Incompatibility.Platform -> {
dest.writeInt(2)
}
is Release.Incompatibility.Feature -> {
dest.writeInt(3)
dest.writeString(incompatibility.feature)
}
}::class
}
dest.writeStringList(platforms)
dest.writeInt(minSdkVersion)
dest.writeInt(maxSdkVersion)
}
companion object {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator {
val count = it.readInt()
val incompatibilities = generateSequence {
when (it.readInt()) {
0 -> Release.Incompatibility.MinSdk
1 -> Release.Incompatibility.MaxSdk
2 -> Release.Incompatibility.Platform
3 -> Release.Incompatibility.Feature(it.readString()!!)
else -> throw RuntimeException()
companion object {
@Suppress("unused")
@JvmField
val CREATOR = KParcelable.creator {
val uri = Uri.parse(it.readString()!!)
Link(uri)
}
}
}.take(count).toList()
val platforms = it.createStringArrayList()!!
val minSdkVersion = it.readInt()
val maxSdkVersion = it.readInt()
ReleaseIncompatible(incompatibilities, platforms, minSdkVersion, maxSdkVersion)
}
}
class Permissions(val group: String?, val permissions: List<String>) : Message() {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(group)
dest.writeStringList(permissions)
}
companion object {
@Suppress("unused")
@JvmField
val CREATOR = KParcelable.creator {
val group = it.readString()
val permissions = it.createStringArrayList()!!
Permissions(group, permissions)
}
}
}
class ReleaseIncompatible(
val incompatibilities: List<Release.Incompatibility>,
val platforms: List<String>, val minSdkVersion: Int, val maxSdkVersion: Int
) : Message() {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeInt(incompatibilities.size)
for (incompatibility in incompatibilities) {
when (incompatibility) {
is Release.Incompatibility.MinSdk -> {
dest.writeInt(0)
}
is Release.Incompatibility.MaxSdk -> {
dest.writeInt(1)
}
is Release.Incompatibility.Platform -> {
dest.writeInt(2)
}
is Release.Incompatibility.Feature -> {
dest.writeInt(3)
dest.writeString(incompatibility.feature)
}
}::class
}
dest.writeStringList(platforms)
dest.writeInt(minSdkVersion)
dest.writeInt(maxSdkVersion)
}
companion object {
@Suppress("unused")
@JvmField
val CREATOR = KParcelable.creator {
val count = it.readInt()
val incompatibilities = generateSequence {
when (it.readInt()) {
0 -> Release.Incompatibility.MinSdk
1 -> Release.Incompatibility.MaxSdk
2 -> Release.Incompatibility.Platform
3 -> Release.Incompatibility.Feature(it.readString()!!)
else -> throw RuntimeException()
}
}.take(count).toList()
val platforms = it.createStringArrayList()!!
val minSdkVersion = it.readInt()
val maxSdkVersion = it.readInt()
ReleaseIncompatible(incompatibilities, platforms, minSdkVersion, maxSdkVersion)
}
}
}
object ReleaseOlder : Message() {
@Suppress("unused")
@JvmField
val CREATOR = KParcelable.creator { ReleaseOlder }
}
object ReleaseSignatureMismatch : Message() {
@Suppress("unused")
@JvmField
val CREATOR = KParcelable.creator { ReleaseSignatureMismatch }
}
}
object ReleaseOlder: Message() {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator { ReleaseOlder }
constructor(message: Message) : this() {
arguments = Bundle().apply {
putParcelable(EXTRA_MESSAGE, message)
}
}
object ReleaseSignatureMismatch: Message() {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator { ReleaseSignatureMismatch }
fun show(fragmentManager: FragmentManager) {
show(fragmentManager, this::class.java.name)
}
}
constructor(message: Message): this() {
arguments = Bundle().apply {
putParcelable(EXTRA_MESSAGE, message)
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog {
val dialog = AlertDialog.Builder(requireContext())
when (val message = requireArguments().getParcelable<Message>(EXTRA_MESSAGE)!!) {
is Message.DeleteRepositoryConfirm -> {
dialog.setTitle(R.string.confirmation)
dialog.setMessage(R.string.delete_repository_DESC)
dialog.setPositiveButton(R.string.delete) { _, _ -> (parentFragment as RepositoryFragment).onDeleteConfirm() }
dialog.setNegativeButton(R.string.cancel, null)
}
is Message.CantEditSyncing -> {
dialog.setTitle(R.string.action_failed)
dialog.setMessage(R.string.cant_edit_sync_DESC)
dialog.setPositiveButton(R.string.ok, null)
}
is Message.Link -> {
dialog.setTitle(R.string.confirmation)
dialog.setMessage(getString(R.string.open_DESC_FORMAT, message.uri.toString()))
dialog.setPositiveButton(R.string.ok) { _, _ ->
try {
startActivity(Intent(Intent.ACTION_VIEW, message.uri))
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
}
}
dialog.setNegativeButton(R.string.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(R.string.unknown)
} else {
getString(R.string.other)
}
for (permission in message.permissions) {
val description = try {
val permissionInfo = packageManager.getPermissionInfo(permission, 0)
PackageItemResolver.loadDescription(
requireContext(),
localCache,
permissionInfo
)
?.nullIfEmpty()?.let { if (it == permission) null else it }
} catch (e: Exception) {
null
}
description?.let { builder.append(it).append("\n\n") }
}
if (builder.isNotEmpty()) {
builder.delete(builder.length - 2, builder.length)
} else {
builder.append(getString(R.string.no_description_available_DESC))
}
dialog.setTitle(title)
dialog.setMessage(builder)
dialog.setPositiveButton(R.string.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(
R.string.incompatible_api_min_DESC_FORMAT,
it
)
}
?: maxSdkVersion?.let {
getString(
R.string.incompatible_api_max_DESC_FORMAT,
it
)
}
builder.append(
getString(
R.string.incompatible_api_DESC_FORMAT,
Android.name, Android.sdk, versionMessage.orEmpty()
)
).append("\n\n")
}
if (Release.Incompatibility.Platform in message.incompatibilities) {
builder.append(
getString(
R.string.incompatible_platforms_DESC_FORMAT,
Android.primaryPlatform ?: getString(R.string.unknown),
message.platforms.joinToString(separator = ", ")
)
).append("\n\n")
}
val features =
message.incompatibilities.mapNotNull { it as? Release.Incompatibility.Feature }
if (features.isNotEmpty()) {
builder.append(getString(R.string.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(R.string.incompatible_version)
dialog.setMessage(builder)
dialog.setPositiveButton(R.string.ok, null)
}
is Message.ReleaseOlder -> {
dialog.setTitle(R.string.incompatible_version)
dialog.setMessage(R.string.incompatible_older_DESC)
dialog.setPositiveButton(R.string.ok, null)
}
is Message.ReleaseSignatureMismatch -> {
dialog.setTitle(R.string.incompatible_version)
dialog.setMessage(R.string.incompatible_signature_DESC)
dialog.setPositiveButton(R.string.ok, null)
}
}::class
return dialog.create()
}
}
fun show(fragmentManager: FragmentManager) {
show(fragmentManager, this::class.java.name)
}
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog {
val dialog = AlertDialog.Builder(requireContext())
when (val message = requireArguments().getParcelable<Message>(EXTRA_MESSAGE)!!) {
is Message.DeleteRepositoryConfirm -> {
dialog.setTitle(R.string.confirmation)
dialog.setMessage(R.string.delete_repository_DESC)
dialog.setPositiveButton(R.string.delete) { _, _ -> (parentFragment as RepositoryFragment).onDeleteConfirm() }
dialog.setNegativeButton(R.string.cancel, null)
}
is Message.CantEditSyncing -> {
dialog.setTitle(R.string.action_failed)
dialog.setMessage(R.string.cant_edit_sync_DESC)
dialog.setPositiveButton(R.string.ok, null)
}
is Message.Link -> {
dialog.setTitle(R.string.confirmation)
dialog.setMessage(getString(R.string.open_DESC_FORMAT, message.uri.toString()))
dialog.setPositiveButton(R.string.ok) { _, _ ->
try {
startActivity(Intent(Intent.ACTION_VIEW, message.uri))
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
}
}
dialog.setNegativeButton(R.string.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(R.string.unknown)
} else {
getString(R.string.other)
}
for (permission in message.permissions) {
val description = try {
val permissionInfo = packageManager.getPermissionInfo(permission, 0)
PackageItemResolver.loadDescription(requireContext(), localCache, permissionInfo)
?.nullIfEmpty()?.let { if (it == permission) null else it }
} catch (e: Exception) {
null
}
description?.let { builder.append(it).append("\n\n") }
}
if (builder.isNotEmpty()) {
builder.delete(builder.length - 2, builder.length)
} else {
builder.append(getString(R.string.no_description_available_DESC))
}
dialog.setTitle(title)
dialog.setMessage(builder)
dialog.setPositiveButton(R.string.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(R.string.incompatible_api_min_DESC_FORMAT, it) }
?: maxSdkVersion?.let { getString(R.string.incompatible_api_max_DESC_FORMAT, it) }
builder.append(getString(R.string.incompatible_api_DESC_FORMAT,
Android.name, Android.sdk, versionMessage.orEmpty())).append("\n\n")
}
if (Release.Incompatibility.Platform in message.incompatibilities) {
builder.append(getString(R.string.incompatible_platforms_DESC_FORMAT,
Android.primaryPlatform ?: getString(R.string.unknown),
message.platforms.joinToString(separator = ", "))).append("\n\n")
}
val features = message.incompatibilities.mapNotNull { it as? Release.Incompatibility.Feature }
if (features.isNotEmpty()) {
builder.append(getString(R.string.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(R.string.incompatible_version)
dialog.setMessage(builder)
dialog.setPositiveButton(R.string.ok, null)
}
is Message.ReleaseOlder -> {
dialog.setTitle(R.string.incompatible_version)
dialog.setMessage(R.string.incompatible_older_DESC)
dialog.setPositiveButton(R.string.ok, null)
}
is Message.ReleaseSignatureMismatch -> {
dialog.setTitle(R.string.incompatible_version)
dialog.setMessage(R.string.incompatible_signature_DESC)
dialog.setPositiveButton(R.string.ok, null)
}
}::class
return dialog.create()
}
}

View File

@ -2,9 +2,9 @@ package com.looker.droidify.screen
import androidx.fragment.app.Fragment
open class ScreenFragment: Fragment() {
val screenActivity: ScreenActivity
get() = requireActivity() as ScreenActivity
open class ScreenFragment : Fragment() {
val screenActivity: ScreenActivity
get() = requireActivity() as ScreenActivity
open fun onBackPressed(): Boolean = false
open fun onBackPressed(): Boolean = false
}

View File

@ -15,10 +15,6 @@ import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.MarginPageTransformer
import androidx.viewpager2.widget.ViewPager2
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import com.looker.droidify.R
import com.looker.droidify.database.Database
import com.looker.droidify.entity.Product
@ -26,208 +22,245 @@ import com.looker.droidify.entity.Repository
import com.looker.droidify.graphics.PaddingDrawable
import com.looker.droidify.network.PicassoDownloader
import com.looker.droidify.utility.RxUtils
import com.looker.droidify.utility.extension.android.*
import com.looker.droidify.utility.extension.android.Android
import com.looker.droidify.utility.extension.resources.*
import com.looker.droidify.widget.StableRecyclerAdapter
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
class ScreenshotsFragment(): DialogFragment() {
companion object {
private const val EXTRA_PACKAGE_NAME = "packageName"
private const val EXTRA_REPOSITORY_ID = "repositoryId"
private const val EXTRA_IDENTIFIER = "identifier"
class ScreenshotsFragment() : DialogFragment() {
companion object {
private const val EXTRA_PACKAGE_NAME = "packageName"
private const val EXTRA_REPOSITORY_ID = "repositoryId"
private const val EXTRA_IDENTIFIER = "identifier"
private const val STATE_IDENTIFIER = "identifier"
}
constructor(packageName: String, repositoryId: Long, identifier: String): this() {
arguments = Bundle().apply {
putString(EXTRA_PACKAGE_NAME, packageName)
putLong(EXTRA_REPOSITORY_ID, repositoryId)
putString(EXTRA_IDENTIFIER, identifier)
private const val STATE_IDENTIFIER = "identifier"
}
}
fun show(fragmentManager: FragmentManager) {
show(fragmentManager, this::class.java.name)
}
private var viewPager: ViewPager2? = null
private var productDisposable: Disposable? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val packageName = requireArguments().getString(EXTRA_PACKAGE_NAME)!!
val repositoryId = requireArguments().getLong(EXTRA_REPOSITORY_ID)
val dialog = Dialog(requireContext(), R.style.Theme_Main_Dark)
val window = dialog.window!!
val decorView = window.decorView
val background = dialog.context.getColorFromAttr(android.R.attr.colorBackground).defaultColor
decorView.setBackgroundColor(background.let { ColorUtils.blendARGB(0x00ffffff and it, it, 0.9f) })
decorView.setPadding(0, 0, 0, 0)
background.let { ColorUtils.blendARGB(0x00ffffff and it, it, 0.8f) }.let {
window.statusBarColor = it
window.navigationBarColor = it
}
window.attributes = window.attributes.apply {
title = ScreenshotsFragment::class.java.name
format = PixelFormat.TRANSLUCENT
windowAnimations = run {
val typedArray = dialog.context.obtainStyledAttributes(null,
intArrayOf(android.R.attr.windowAnimationStyle), android.R.attr.dialogTheme, 0)
try {
typedArray.getResourceId(0, 0)
} finally {
typedArray.recycle()
constructor(packageName: String, repositoryId: Long, identifier: String) : this() {
arguments = Bundle().apply {
putString(EXTRA_PACKAGE_NAME, packageName)
putLong(EXTRA_REPOSITORY_ID, repositoryId)
putString(EXTRA_IDENTIFIER, identifier)
}
}
if (Android.sdk(28)) {
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
val hideFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE
decorView.systemUiVisibility = decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
val applyHide = Runnable { decorView.systemUiVisibility = decorView.systemUiVisibility or hideFlags }
val handleClick = {
decorView.removeCallbacks(applyHide)
if ((decorView.systemUiVisibility and hideFlags) == hideFlags) {
decorView.systemUiVisibility = decorView.systemUiVisibility and hideFlags.inv()
} else {
decorView.systemUiVisibility = decorView.systemUiVisibility or hideFlags
}
fun show(fragmentManager: FragmentManager) {
show(fragmentManager, this::class.java.name)
}
decorView.postDelayed(applyHide, 2000L)
decorView.setOnClickListener { handleClick() }
val viewPager = ViewPager2(dialog.context)
viewPager.adapter = Adapter(packageName) { handleClick() }
viewPager.setPageTransformer(MarginPageTransformer(resources.sizeScaled(16)))
viewPager.viewTreeObserver.addOnGlobalLayoutListener {
(viewPager.adapter as Adapter).size = Pair(viewPager.width, viewPager.height)
}
dialog.addContentView(viewPager, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT))
this.viewPager = viewPager
private var viewPager: ViewPager2? = null
var restored = false
productDisposable = Observable.just(Unit)
.concatWith(Database.observable(Database.Subject.Products))
.observeOn(Schedulers.io())
.flatMapSingle { RxUtils.querySingle { Database.ProductAdapter.get(packageName, it) } }
.map { Pair(it.find { it.repositoryId == repositoryId }, Database.RepositoryAdapter.get(repositoryId)) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
val (product, repository) = it
val screenshots = product?.screenshots.orEmpty()
(viewPager.adapter as Adapter).update(repository, screenshots)
if (!restored) {
restored = true
val identifier = savedInstanceState?.getString(STATE_IDENTIFIER)
?: requireArguments().getString(STATE_IDENTIFIER)
if (identifier != null) {
val index = screenshots.indexOfFirst { it.identifier == identifier }
if (index >= 0) {
viewPager.setCurrentItem(index, false)
private var productDisposable: Disposable? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val packageName = requireArguments().getString(EXTRA_PACKAGE_NAME)!!
val repositoryId = requireArguments().getLong(EXTRA_REPOSITORY_ID)
val dialog = Dialog(requireContext(), R.style.Theme_Main_Dark)
val window = dialog.window!!
val decorView = window.decorView
val background =
dialog.context.getColorFromAttr(android.R.attr.colorBackground).defaultColor
decorView.setBackgroundColor(background.let {
ColorUtils.blendARGB(
0x00ffffff and it,
it,
0.9f
)
})
decorView.setPadding(0, 0, 0, 0)
background.let { ColorUtils.blendARGB(0x00ffffff and it, it, 0.8f) }.let {
window.statusBarColor = it
window.navigationBarColor = it
}
window.attributes = window.attributes.apply {
title = ScreenshotsFragment::class.java.name
format = PixelFormat.TRANSLUCENT
windowAnimations = run {
val typedArray = dialog.context.obtainStyledAttributes(
null,
intArrayOf(android.R.attr.windowAnimationStyle), android.R.attr.dialogTheme, 0
)
try {
typedArray.getResourceId(0, 0)
} finally {
typedArray.recycle()
}
}
if (Android.sdk(28)) {
layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
}
}
return dialog
}
override fun onDestroyView() {
super.onDestroyView()
viewPager = null
productDisposable?.dispose()
productDisposable = null
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val viewPager = viewPager
if (viewPager != null) {
val identifier = (viewPager.adapter as Adapter).getCurrentIdentifier(viewPager)
identifier?.let { outState.putString(STATE_IDENTIFIER, it) }
}
}
private class Adapter(private val packageName: String, private val onClick: () -> Unit):
StableRecyclerAdapter<Adapter.ViewType, RecyclerView.ViewHolder>() {
enum class ViewType { SCREENSHOT }
private class ViewHolder(context: Context): RecyclerView.ViewHolder(ImageView(context)) {
val image: ImageView
get() = itemView as ImageView
val placeholder: Drawable
init {
itemView.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
RecyclerView.LayoutParams.MATCH_PARENT)
val placeholder = itemView.context.getDrawableCompat(R.drawable.ic_photo_camera).mutate()
placeholder.setTint(itemView.context.getColorFromAttr(android.R.attr.textColorPrimary).defaultColor
.let { ColorUtils.blendARGB(0x00ffffff and it, it, 0.25f) })
this.placeholder = PaddingDrawable(placeholder, 4f)
}
}
private var repository: Repository? = null
private var screenshots = emptyList<Product.Screenshot>()
fun update(repository: Repository?, screenshots: List<Product.Screenshot>) {
this.repository = repository
this.screenshots = screenshots
notifyDataSetChanged()
}
var size = Pair(0, 0)
set(value) {
if (field != value) {
field = value
notifyDataSetChanged()
val hideFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE
decorView.systemUiVisibility =
decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
val applyHide =
Runnable { decorView.systemUiVisibility = decorView.systemUiVisibility or hideFlags }
val handleClick = {
decorView.removeCallbacks(applyHide)
if ((decorView.systemUiVisibility and hideFlags) == hideFlags) {
decorView.systemUiVisibility = decorView.systemUiVisibility and hideFlags.inv()
} else {
decorView.systemUiVisibility = decorView.systemUiVisibility or hideFlags
}
}
}
decorView.postDelayed(applyHide, 2000L)
decorView.setOnClickListener { handleClick() }
fun getCurrentIdentifier(viewPager: ViewPager2): String? {
val position = viewPager.currentItem
return screenshots.getOrNull(position)?.identifier
}
override val viewTypeClass: Class<ViewType>
get() = ViewType::class.java
override fun getItemCount(): Int = screenshots.size
override fun getItemDescriptor(position: Int): String = screenshots[position].identifier
override fun getItemEnumViewType(position: Int): ViewType = ViewType.SCREENSHOT
override fun onCreateViewHolder(parent: ViewGroup, viewType: ViewType): RecyclerView.ViewHolder {
return ViewHolder(parent.context).apply {
itemView.setOnClickListener { onClick() }
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder as ViewHolder
val screenshot = screenshots[position]
val (width, height) = size
if (width > 0 && height > 0) {
holder.image.load(PicassoDownloader.createScreenshotUri(repository!!, packageName, screenshot)) {
placeholder(holder.placeholder)
error(holder.placeholder)
resize(width, height)
centerInside()
val viewPager = ViewPager2(dialog.context)
viewPager.adapter = Adapter(packageName) { handleClick() }
viewPager.setPageTransformer(MarginPageTransformer(resources.sizeScaled(16)))
viewPager.viewTreeObserver.addOnGlobalLayoutListener {
(viewPager.adapter as Adapter).size = Pair(viewPager.width, viewPager.height)
}
dialog.addContentView(
viewPager, ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
this.viewPager = viewPager
var restored = false
productDisposable = Observable.just(Unit)
.concatWith(Database.observable(Database.Subject.Products))
.observeOn(Schedulers.io())
.flatMapSingle { RxUtils.querySingle { Database.ProductAdapter.get(packageName, it) } }
.map { it ->
Pair(
it.find { it.repositoryId == repositoryId },
Database.RepositoryAdapter.get(repositoryId)
)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe { it ->
val (product, repository) = it
val screenshots = product?.screenshots.orEmpty()
(viewPager.adapter as Adapter).update(repository, screenshots)
if (!restored) {
restored = true
val identifier = savedInstanceState?.getString(STATE_IDENTIFIER)
?: requireArguments().getString(STATE_IDENTIFIER)
if (identifier != null) {
val index = screenshots.indexOfFirst { it.identifier == identifier }
if (index >= 0) {
viewPager.setCurrentItem(index, false)
}
}
}
}
return dialog
}
override fun onDestroyView() {
super.onDestroyView()
viewPager = null
productDisposable?.dispose()
productDisposable = null
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val viewPager = viewPager
if (viewPager != null) {
val identifier = (viewPager.adapter as Adapter).getCurrentIdentifier(viewPager)
identifier?.let { outState.putString(STATE_IDENTIFIER, it) }
}
}
private class Adapter(private val packageName: String, private val onClick: () -> Unit) :
StableRecyclerAdapter<Adapter.ViewType, RecyclerView.ViewHolder>() {
enum class ViewType { SCREENSHOT }
private class ViewHolder(context: Context) : RecyclerView.ViewHolder(ImageView(context)) {
val image: ImageView
get() = itemView as ImageView
val placeholder: Drawable
init {
itemView.layoutParams = RecyclerView.LayoutParams(
RecyclerView.LayoutParams.MATCH_PARENT,
RecyclerView.LayoutParams.MATCH_PARENT
)
val placeholder =
itemView.context.getDrawableCompat(R.drawable.ic_photo_camera).mutate()
placeholder.setTint(itemView.context.getColorFromAttr(android.R.attr.textColorPrimary).defaultColor
.let { ColorUtils.blendARGB(0x00ffffff and it, it, 0.25f) })
this.placeholder = PaddingDrawable(placeholder, 4f)
}
}
private var repository: Repository? = null
private var screenshots = emptyList<Product.Screenshot>()
fun update(repository: Repository?, screenshots: List<Product.Screenshot>) {
this.repository = repository
this.screenshots = screenshots
notifyDataSetChanged()
}
var size = Pair(0, 0)
set(value) {
if (field != value) {
field = value
notifyDataSetChanged()
}
}
fun getCurrentIdentifier(viewPager: ViewPager2): String? {
val position = viewPager.currentItem
return screenshots.getOrNull(position)?.identifier
}
override val viewTypeClass: Class<ViewType>
get() = ViewType::class.java
override fun getItemCount(): Int = screenshots.size
override fun getItemDescriptor(position: Int): String = screenshots[position].identifier
override fun getItemEnumViewType(position: Int): ViewType = ViewType.SCREENSHOT
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: ViewType
): RecyclerView.ViewHolder {
return ViewHolder(parent.context).apply {
itemView.setOnClickListener { onClick() }
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder as ViewHolder
val screenshot = screenshots[position]
val (width, height) = size
if (width > 0 && height > 0) {
holder.image.load(
PicassoDownloader.createScreenshotUri(
repository!!,
packageName,
screenshot
)
) {
placeholder(holder.placeholder)
error(holder.placeholder)
resize(width, height)
centerInside()
}
} else {
holder.image.clear()
}
}
} else {
holder.image.clear()
}
}
}
}