This commit is contained in:
machiav3lli 2022-10-10 01:49:17 +02:00
parent 7c759cd937
commit 3b4bb5d869
5 changed files with 0 additions and 372 deletions

View File

@ -1,56 +0,0 @@
package com.machiav3lli.fdroid.graphics
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Rect
import android.graphics.drawable.Drawable
open class DrawableWrapper(val drawable: Drawable) : Drawable() {
init {
drawable.callback = object : Callback {
override fun invalidateDrawable(who: Drawable) {
callback?.invalidateDrawable(who)
}
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
callback?.scheduleDrawable(who, what, `when`)
}
override fun unscheduleDrawable(who: Drawable, what: Runnable) {
callback?.unscheduleDrawable(who, what)
}
}
}
override fun onBoundsChange(bounds: Rect) {
drawable.bounds = bounds
}
override fun getIntrinsicWidth(): Int = drawable.intrinsicWidth
override fun getIntrinsicHeight(): Int = drawable.intrinsicHeight
override fun getMinimumWidth(): Int = drawable.minimumWidth
override fun getMinimumHeight(): Int = drawable.minimumHeight
override fun draw(canvas: Canvas) {
drawable.draw(canvas)
}
override fun getAlpha(): Int {
return drawable.alpha
}
override fun setAlpha(alpha: Int) {
drawable.alpha = alpha
}
override fun getColorFilter(): ColorFilter? {
return drawable.colorFilter
}
override fun setColorFilter(colorFilter: ColorFilter?) {
drawable.colorFilter = colorFilter
}
@Deprecated("Deprecated in Java", ReplaceWith("drawable.opacity"))
override fun getOpacity(): Int = drawable.opacity
}

View File

@ -1,21 +0,0 @@
package com.machiav3lli.fdroid.graphics
import android.graphics.Rect
import android.graphics.drawable.Drawable
import kotlin.math.roundToInt
class PaddingDrawable(drawable: Drawable, private val factor: Float) : DrawableWrapper(drawable) {
override fun getIntrinsicWidth(): Int = (factor * super.getIntrinsicWidth()).roundToInt()
override fun getIntrinsicHeight(): Int = (factor * super.getIntrinsicHeight()).roundToInt()
override fun onBoundsChange(bounds: Rect) {
val width = (bounds.width() / factor).roundToInt()
val height = (bounds.height() / factor).roundToInt()
val left = (bounds.width() - width) / 2
val top = (bounds.height() - height) / 2
drawable.setBounds(
bounds.left + left, bounds.top + top,
bounds.left + left + width, bounds.top + top + height
)
}
}

View File

@ -1,272 +0,0 @@
package com.machiav3lli.fdroid.screen
import android.app.Dialog
import android.content.Context
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.ViewGroup
import android.view.WindowManager
import androidx.core.graphics.ColorUtils
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.MarginPageTransformer
import androidx.viewpager2.widget.ViewPager2
import coil.load
import com.google.android.material.imageview.ShapeableImageView
import com.machiav3lli.fdroid.R
import com.machiav3lli.fdroid.database.DatabaseX
import com.machiav3lli.fdroid.database.entity.Repository
import com.machiav3lli.fdroid.entity.Screenshot
import com.machiav3lli.fdroid.graphics.PaddingDrawable
import com.machiav3lli.fdroid.network.CoilDownloader
import com.machiav3lli.fdroid.utility.RxUtils
import com.machiav3lli.fdroid.utility.extension.android.Android
import com.machiav3lli.fdroid.utility.extension.resources.getColorFromAttr
import com.machiav3lli.fdroid.utility.extension.resources.getDrawableCompat
import com.machiav3lli.fdroid.utility.extension.resources.sizeScaled
import com.machiav3lli.fdroid.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"
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)
}
}
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_Amoled)
val window = dialog.window
val decorView = window?.decorView
val db = DatabaseX.getInstance(requireContext())
if (window != null) {
WindowCompat.setDecorFitsSystemWindows(window, false)
}
val background = dialog.context.getColorFromAttr(R.attr.colorSurface).defaultColor
decorView?.setBackgroundColor(
ColorUtils.blendARGB(
0x00FFFFFF and background,
background,
0.9f
)
)
decorView?.setPadding(0, 0, 0, 0)
if (window != null) {
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
}
}
}
val toggleSystemUi = {
if (window != null && decorView != null) {
WindowInsetsControllerCompat(window, decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.statusBars())
controller.hide(WindowInsetsCompat.Type.navigationBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
}
val viewPager = ViewPager2(dialog.context)
viewPager.adapter = Adapter(packageName) { toggleSystemUi() }
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)) // TODO have to be replaced like whole rxJava
.observeOn(Schedulers.io())
.flatMapSingle {
RxUtils.querySingle {
db.productDao.get(packageName).filterNotNull()
}
}
.map { it ->
Pair(
it.find { it.repositoryId == repositoryId },
db.repositoryDao.get(repositoryId)
)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe { it ->
val (product, repository) = it
val screenshots = product?.screenshots.orEmpty()
(viewPager.adapter as Adapter).update(viewPager, 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(ShapeableImageView(context)) {
val image: ShapeableImageView
get() = itemView as ShapeableImageView
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(R.attr.colorSurface).defaultColor)
this.placeholder = PaddingDrawable(placeholder, 4f)
}
}
private var repository: Repository? = null
private var screenshots = emptyList<Screenshot>()
fun update(
viewPager: ViewPager2,
repository: Repository?,
screenshots: List<Screenshot>,
) {
this.repository = repository
this.screenshots = screenshots
notifyItemChanged(viewPager.currentItem)
}
var size = Pair(0, 0)
set(value) {
if (field != value) {
field = value
}
}
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
repository?.let {
holder.image.load(
CoilDownloader.createScreenshotUri(
it,
packageName,
screenshot
)
) {
placeholder(holder.placeholder)
error(holder.placeholder)
size(width, height)
}
}
}
}
}

View File

@ -3,12 +3,10 @@ package com.machiav3lli.fdroid.ui.compose.utils
import android.net.Uri
import com.machiav3lli.fdroid.database.entity.Release
import com.machiav3lli.fdroid.entity.ActionState
import com.machiav3lli.fdroid.entity.Screenshot
interface Callbacks {
fun onActionClick(action: ActionState?)
fun onPermissionsClick(group: String?, permissions: List<String>)
fun onScreenshotClick(screenshot: Screenshot)
fun onReleaseClick(release: Release)
fun onUriClick(uri: Uri, shouldConfirm: Boolean): Boolean
}

View File

@ -63,11 +63,9 @@ import com.machiav3lli.fdroid.entity.ActionState
import com.machiav3lli.fdroid.entity.AntiFeature
import com.machiav3lli.fdroid.entity.DonateType
import com.machiav3lli.fdroid.entity.DownloadState
import com.machiav3lli.fdroid.entity.Screenshot
import com.machiav3lli.fdroid.installer.AppInstaller
import com.machiav3lli.fdroid.network.CoilDownloader
import com.machiav3lli.fdroid.screen.MessageDialog
import com.machiav3lli.fdroid.screen.ScreenshotsFragment
import com.machiav3lli.fdroid.service.Connection
import com.machiav3lli.fdroid.service.DownloadService
import com.machiav3lli.fdroid.ui.activities.MainActivityX
@ -273,25 +271,6 @@ class AppSheetX() : FullscreenBottomSheetDialogFragment(), Callbacks {
)
}
override fun onScreenshotClick(screenshot: Screenshot) {
val pair = viewModel.productRepos.asSequence()
.map { it ->
Pair(
it.second,
it.first.screenshots.find { it === screenshot }?.identifier
)
}
.filter { it.second != null }.firstOrNull()
if (pair != null) {
val (repository, identifier) = pair
if (identifier != null) {
ScreenshotsFragment(packageName, repository.id, identifier).show(
childFragmentManager
)
}
}
}
override fun onReleaseClick(release: Release) {
val installedItem = viewModel.installedItem.value
when {