diff --git a/app/src/main/java/me/zhanghai/android/files/compat/TypedValueCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/TypedValueCompat.kt new file mode 100644 index 000000000..cd1d3dd81 --- /dev/null +++ b/app/src/main/java/me/zhanghai/android/files/compat/TypedValueCompat.kt @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023 Hai Zhang + * All Rights Reserved. + */ + +package me.zhanghai.android.files.compat + +import android.util.TypedValue +import androidx.core.util.TypedValueCompat + +val TypedValue.complexUnitCompat: Int + get() = TypedValueCompat.getUnitFromComplexDimension(data) diff --git a/app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt b/app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt index fa4b2be8c..e2b29468b 100644 --- a/app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt +++ b/app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt @@ -104,6 +104,7 @@ import me.zhanghai.android.files.util.createViewIntent import me.zhanghai.android.files.util.extraPath import me.zhanghai.android.files.util.extraPathList import me.zhanghai.android.files.util.fadeToVisibilityUnsafe +import me.zhanghai.android.files.util.getDimensionDp import me.zhanghai.android.files.util.getQuantityString import me.zhanghai.android.files.util.hasSw600Dp import me.zhanghai.android.files.util.isOrientationLandscape @@ -115,6 +116,7 @@ import me.zhanghai.android.files.util.valueCompat import me.zhanghai.android.files.util.viewModels import me.zhanghai.android.files.util.withChooser import me.zhanghai.android.files.viewer.image.ImageViewerActivity +import kotlin.math.roundToInt class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter.Listener, OpenApkDialogFragment.Listener, ConfirmDeleteFilesDialogFragment.Listener, @@ -599,14 +601,13 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. layoutManager.spanCount = when (viewModel.viewType) { FileViewType.LIST -> 1 FileViewType.GRID -> { - var width = resources.configuration.screenWidthDp + var widthDp = resources.configuration.screenWidthDp val persistentDrawerLayout = binding.persistentDrawerLayout if (persistentDrawerLayout != null && persistentDrawerLayout.isDrawerOpen(GravityCompat.START)) { - // R.dimen.navigation_max_width - width -= 320 + widthDp -= getDimensionDp(R.dimen.navigation_max_width).roundToInt() } - (width / 180).coerceAtLeast(2) + (widthDp / 180).coerceAtLeast(2) } } } diff --git a/app/src/main/java/me/zhanghai/android/files/util/ContextExtensions.kt b/app/src/main/java/me/zhanghai/android/files/util/ContextExtensions.kt index adc40a718..13a1b8b0b 100644 --- a/app/src/main/java/me/zhanghai/android/files/util/ContextExtensions.kt +++ b/app/src/main/java/me/zhanghai/android/files/util/ContextExtensions.kt @@ -13,6 +13,7 @@ import android.content.ContextWrapper import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration +import android.content.res.Resources import android.graphics.drawable.Drawable import android.os.Bundle import android.os.Looper @@ -36,7 +37,9 @@ import androidx.annotation.PluralsRes import androidx.annotation.StyleRes import androidx.appcompat.view.ContextThemeWrapper import androidx.core.content.res.ResourcesCompat +import androidx.core.util.TypedValueCompat import me.zhanghai.android.files.R +import me.zhanghai.android.files.compat.complexUnitCompat import me.zhanghai.android.files.compat.getFloatCompat import me.zhanghai.android.files.compat.mainExecutorCompat import me.zhanghai.android.files.compat.obtainStyledAttributesCompat @@ -56,17 +59,38 @@ val Context.activity: Activity? fun Context.getAnimation(@AnimRes id: Int): Animation = AnimationUtils.loadAnimation(this, id) -fun Context.getBoolean(@BoolRes id: Int) = resources.getBoolean(id) +fun Context.getBoolean(@BoolRes id: Int): Boolean = resources.getBoolean(id) -fun Context.getDimension(@DimenRes id: Int) = resources.getDimension(id) +@Dimension +fun Context.getDimension(@DimenRes id: Int): Float = resources.getDimension(id) + +@Dimension(unit = Dimension.DP) +fun Context.getDimensionDp(@DimenRes id: Int): Float { + TypedValue::class.useTemp { value -> + resources.getValue(id, value, true) + if (value.type != TypedValue.TYPE_DIMENSION) { + throw Resources.NotFoundException( + "Resource ID #0x${Integer.toHexString(id)} type #0x${ + Integer.toHexString(value.type) + } is not valid" + ) + } + if (value.complexUnitCompat == TypedValue.COMPLEX_UNIT_DIP) { + return TypedValue.complexToFloat(value.data) + } + return dimensionToDp(TypedValue.complexToDimension(value.data, resources.displayMetrics)) + } +} -fun Context.getDimensionPixelOffset(@DimenRes id: Int) = resources.getDimensionPixelOffset(id) +@Dimension +fun Context.getDimensionPixelOffset(@DimenRes id: Int): Int = resources.getDimensionPixelOffset(id) -fun Context.getDimensionPixelSize(@DimenRes id: Int) = resources.getDimensionPixelSize(id) +@Dimension +fun Context.getDimensionPixelSize(@DimenRes id: Int): Int = resources.getDimensionPixelSize(id) -fun Context.getFloat(@DimenRes id: Int) = resources.getFloatCompat(id) +fun Context.getFloat(@DimenRes id: Int): Float = resources.getFloatCompat(id) -fun Context.getInteger(@IntegerRes id: Int) = resources.getInteger(id) +fun Context.getInteger(@IntegerRes id: Int): Int = resources.getInteger(id) fun Context.getInterpolator(@InterpolatorRes id: Int): Interpolator = AnimationUtils.loadInterpolator(this, id) @@ -162,6 +186,13 @@ fun Context.dpToDimensionPixelSize(@Dimension(unit = Dimension.DP) dp: Float): I fun Context.dpToDimensionPixelSize(@Dimension(unit = Dimension.DP) dp: Int) = dpToDimensionPixelSize(dp.toFloat()) +@Dimension(unit = Dimension.DP) +fun Context.dimensionToDp(@Dimension dimension: Float): Float = + TypedValueCompat.pxToDp(dimension, resources.displayMetrics) + +@Dimension(unit = Dimension.DP) +fun Context.dimensionToDp(@Dimension dimension: Int): Float = dimensionToDp(dimension.toFloat()) + fun Context.hasSwDp(@Dimension(unit = Dimension.DP) dp: Int): Boolean = resources.configuration.smallestScreenWidthDp >= dp diff --git a/app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt b/app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt index 0396eec72..869c39180 100644 --- a/app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt +++ b/app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt @@ -7,7 +7,11 @@ package me.zhanghai.android.files.util import android.content.ActivityNotFoundException import android.content.Intent +import android.content.res.ColorStateList +import android.graphics.drawable.Drawable import android.os.Bundle +import android.view.animation.Animation +import android.view.animation.Interpolator import android.widget.Toast import androidx.annotation.AnimRes import androidx.annotation.AnyRes @@ -29,34 +33,45 @@ import me.zhanghai.android.files.compat.getColorCompat import me.zhanghai.android.files.compat.getColorStateListCompat import me.zhanghai.android.files.compat.getDrawableCompat -fun Fragment.checkSelfPermission(permission: String) = +fun Fragment.checkSelfPermission(permission: String): Int = requireContext().checkSelfPermissionCompat(permission) -fun Fragment.finish() = requireActivity().finish() +fun Fragment.finish() { + requireActivity().finish() +} -fun Fragment.getAnimation(@AnimRes id: Int) = requireContext().getAnimation(id) +fun Fragment.getAnimation(@AnimRes id: Int): Animation = requireContext().getAnimation(id) -fun Fragment.getBoolean(@BoolRes id: Int) = requireContext().getBoolean(id) +fun Fragment.getBoolean(@BoolRes id: Int): Boolean = requireContext().getBoolean(id) @ColorInt -fun Fragment.getColor(@ColorRes id: Int) = requireContext().getColorCompat(id) +fun Fragment.getColor(@ColorRes id: Int): Int = requireContext().getColorCompat(id) + +fun Fragment.getColorStateList(@ColorRes id: Int): ColorStateList = + requireContext().getColorStateListCompat(id) -fun Fragment.getColorStateList(@ColorRes id: Int) = requireContext().getColorStateListCompat(id) +@Dimension +fun Fragment.getDimension(@DimenRes id: Int): Float = requireContext().getDimension(id) -fun Fragment.getDimension(@DimenRes id: Int) = requireContext().getDimension(id) +@Dimension(unit = Dimension.DP) +fun Fragment.getDimensionDp(@DimenRes id: Int): Float = requireContext().getDimensionDp(id) -fun Fragment.getDimensionPixelOffset(@DimenRes id: Int) = +@Dimension +fun Fragment.getDimensionPixelOffset(@DimenRes id: Int): Int = requireContext().getDimensionPixelOffset(id) -fun Fragment.getDimensionPixelSize(@DimenRes id: Int) = requireContext().getDimensionPixelSize(id) +@Dimension +fun Fragment.getDimensionPixelSize(@DimenRes id: Int): Int = + requireContext().getDimensionPixelSize(id) -fun Fragment.getDrawable(@DrawableRes id: Int) = requireContext().getDrawableCompat(id) +fun Fragment.getDrawable(@DrawableRes id: Int): Drawable = requireContext().getDrawableCompat(id) -fun Fragment.getFloat(@DimenRes id: Int) = requireContext().getFloat(id) +fun Fragment.getFloat(@DimenRes id: Int): Float = requireContext().getFloat(id) -fun Fragment.getInteger(@IntegerRes id: Int) = requireContext().getInteger(id) +fun Fragment.getInteger(@IntegerRes id: Int): Int = requireContext().getInteger(id) -fun Fragment.getInterpolator(@InterpolatorRes id: Int) = requireContext().getInterpolator(id) +fun Fragment.getInterpolator(@InterpolatorRes id: Int): Interpolator = + requireContext().getInterpolator(id) fun Fragment.getQuantityString(@PluralsRes id: Int, quantity: Int): String = requireContext().getQuantityString(id, quantity) @@ -123,6 +138,14 @@ fun Fragment.dpToDimensionPixelSize(@Dimension(unit = Dimension.DP) dp: Float) = fun Fragment.dpToDimensionPixelSize(@Dimension(unit = Dimension.DP) dp: Int) = requireContext().dpToDimensionPixelSize(dp) +@Dimension(unit = Dimension.DP) +fun Fragment.dimensionToDp(@Dimension dimension: Float): Float = + requireContext().dimensionToDp(dimension) + +@Dimension(unit = Dimension.DP) +fun Fragment.dimensionToDp(@Dimension dimension: Int): Float = + requireContext().dimensionToDp(dimension) + fun Fragment.setResult(resultCode: Int, resultData: Intent? = null) = requireActivity().setResult(resultCode, resultData) diff --git a/app/src/main/java/me/zhanghai/android/files/util/TypedValueExtensions.kt b/app/src/main/java/me/zhanghai/android/files/util/TypedValueExtensions.kt new file mode 100644 index 000000000..9d03fce3e --- /dev/null +++ b/app/src/main/java/me/zhanghai/android/files/util/TypedValueExtensions.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Hai Zhang + * All Rights Reserved. + */ + +package me.zhanghai.android.files.util + +import android.util.TypedValue +import java.util.concurrent.atomic.AtomicReference +import kotlin.reflect.KClass + +inline fun KClass.useTemp(block: (TypedValue) -> T): T { + val temp = TypedValue::class.obtainTemp() + return try { + block(temp) + } finally { + temp.releaseTemp() + } +} + +fun KClass.obtainTemp(): TypedValue = tempTypedValue.getAndSet(null) ?: TypedValue() + +private val tempTypedValue = AtomicReference() + +fun TypedValue.releaseTemp() { + tempTypedValue.compareAndSet(null, this) +}