diff --git a/insets-extensions/build.gradle b/insets-extensions/build.gradle index 77ccd60..cd06ab5 100644 --- a/insets-extensions/build.gradle +++ b/insets-extensions/build.gradle @@ -23,7 +23,7 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation project(':insets') + api project(':insets') implementation 'androidx.annotation:annotation:1.0.2' diff --git a/insets-extensions/src/main/java/net/xpece/androidx/optical/OpticalInsets.kt b/insets-extensions/src/main/java/net/xpece/androidx/optical/OpticalInsets.kt index 8ee129f..bf34af8 100644 --- a/insets-extensions/src/main/java/net/xpece/androidx/optical/OpticalInsets.kt +++ b/insets-extensions/src/main/java/net/xpece/androidx/optical/OpticalInsets.kt @@ -4,11 +4,13 @@ package net.xpece.androidx.optical import android.annotation.SuppressLint +import android.annotation.TargetApi import android.graphics.Insets import android.graphics.drawable.Drawable import android.graphics.drawable.InsetDrawable import android.graphics.drawable.LayerDrawable import android.os.Build +import android.os.Build.VERSION_CODES import androidx.annotation.RequiresApi import android.util.Log import android.view.View @@ -16,12 +18,15 @@ import java.util.concurrent.atomic.AtomicBoolean import kotlin.LazyThreadSafetyMode.NONE private val viewInsetsGetter by lazy(NONE) { + //noinspection SoonBlockedPrivateApi View::class.java.getDeclaredMethod("getOpticalInsets") } /** * Returns the insets that will be used during optical bounds layout mode. */ +@SuppressLint("NewApi") +@Deprecated("Shadows platform call. Doesn't work on API 29+.") @RequiresApi(16) fun View.getOpticalInsets(): Insets = viewInsetsGetter.invoke(this) as Insets @@ -34,9 +39,12 @@ private val drawableInsetsGetter by lazy(NONE) { } } -@Suppress("NOTHING_TO_INLINE") -private inline fun Drawable.getActualOpticalInsets(): Insets = +@TargetApi(29) +private fun Drawable.getActualOpticalInsets(): Insets = if (Build.VERSION.SDK_INT < 18) { drawableInsetsGetter.invoke(this) as Insets +} else { + opticalInsets +} private val isLoggedInsetDrawableReflectionError = AtomicBoolean(false) private val isLoggedLayerDrawableReflectionError = AtomicBoolean(false) @@ -45,39 +53,52 @@ private val isLoggedLayerDrawableReflectionError = AtomicBoolean(false) * Returns the layout insets suggested by this Drawable for use with alignment * operations during layout. */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +@Deprecated("Shadows platform call.", replaceWith = ReplaceWith("getOpticalInsetsCompat")) +@RequiresApi(16) +fun Drawable.getOpticalInsets(): Insets = getOpticalInsetsCompat() + +/** + * Returns the layout insets suggested by this Drawable for use with alignment + * operations during layout. + */ +@Suppress("LiftReturnOrAssignment") @RequiresApi(16) -fun Drawable.getOpticalInsets(): Insets = if (Build.VERSION.SDK_INT < 21 && this is InsetDrawable) { - val actual = getActualOpticalInsets() - if (actual == InsetsCompat.NONE) { - try { - InsetDrawableReflection.getOpticalInsets(this) - } catch (ex: Throwable) { - if (!isLoggedInsetDrawableReflectionError.getAndSet(true)) { - Log.w( - "OpticalInsets", - "Couldn't access InsetDrawable data using reflection. Oh well...", - ex - ) +fun Drawable.getOpticalInsetsCompat(): Insets { + if (Build.VERSION.SDK_INT < 21 && this is InsetDrawable) { + val actual = getActualOpticalInsets() + if (actual == InsetsCompat.NONE) { + try { + return InsetDrawableReflection.getOpticalInsets(this) + } catch (ex: Throwable) { + if (!isLoggedInsetDrawableReflectionError.getAndSet(true)) { + Log.w( + "OpticalInsets", + "Couldn't access InsetDrawable data using reflection. Oh well...", + ex + ) + } + return actual } - actual + } else { + return actual + } + } else if (this is LayerDrawable) { + val actual = getActualOpticalInsets() + if (actual == InsetsCompat.NONE) { + val allInsets = Array(numberOfLayers, this::getTotalLayerInsets) + return InsetsCompat.union(*allInsets) + } else { + return actual } } else { - actual - } -} else if (this is LayerDrawable) { - val actual = getActualOpticalInsets() - if (actual == InsetsCompat.NONE) { - val allInsets = Array(numberOfLayers, this::getTotalLayerInsets) - InsetsCompat.union(*allInsets) - } else { - actual + return getActualOpticalInsets() } -} else { - getActualOpticalInsets() } +@TargetApi(29) private fun LayerDrawable.getTotalLayerInsets(i: Int): Insets = if (Build.VERSION.SDK_INT >= 23) { - val drawableInsets = getDrawable(i).getOpticalInsets() + val drawableInsets = getDrawable(i).getOpticalInsetsCompat() InsetsCompat.of( getLayerInsetLeft(i) + drawableInsets.left, getLayerInsetTop(i) + drawableInsets.top, @@ -127,8 +148,10 @@ private object LayerDrawableReflection { .apply { isAccessible = true } @RequiresApi(16) + @TargetApi(29) + @SuppressLint("NewApi") internal fun getTotalLayerInsets(drawable: LayerDrawable, i: Int): Insets { - val drawableInsets = drawable.getDrawable(i).getOpticalInsets() + val drawableInsets = drawable.getDrawable(i).getOpticalInsetsCompat() val state = fieldLayerState.get(drawable) val children = fieldChildren.get(state) as Array<*> val layer = children[i] diff --git a/insets-helper/build.gradle b/insets-helper/build.gradle index 50d0348..773ba1b 100644 --- a/insets-helper/build.gradle +++ b/insets-helper/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' apply plugin: 'digital.wup.android-maven-publish' android { @@ -21,9 +20,6 @@ android { } dependencies { -// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - - implementation project(':insets') implementation project(':insets-extensions') implementation 'androidx.annotation:annotation:1.0.2' diff --git a/insets-helper/src/main/java/net/xpece/androidx/optical/OpticalInsetsHelper.java b/insets-helper/src/main/java/net/xpece/androidx/optical/OpticalInsetsHelper.java new file mode 100644 index 0000000..68ad51c --- /dev/null +++ b/insets-helper/src/main/java/net/xpece/androidx/optical/OpticalInsetsHelper.java @@ -0,0 +1,50 @@ +package net.xpece.androidx.optical; + +import android.annotation.TargetApi; +import android.graphics.Insets; +import android.graphics.drawable.Drawable; +import android.view.View; + +import androidx.annotation.RequiresApi; + +/** + * Helper class for making views aware of optical insets + * on {@link android.graphics.drawable.InsetDrawable InsetDrawable} below API 21, + * and {@link android.graphics.drawable.LayerDrawable LayerDrawable} (including children + * such as {@link android.graphics.drawable.RippleDrawable RippleDrawable}) on all API levels. + */ +@RequiresApi(16) +public final class OpticalInsetsHelper { + + private final View view; + + private Insets opticalInsets = null; + + public OpticalInsetsHelper(final View view) { + this.view = view; + } + + /** + * Return this from {@code View#getOpticalInsets()}. + */ + @TargetApi(29) + public Insets onGetOpticalInsets() { + if (opticalInsets == null) { + // Same as platform, we don't support changing insets once resolved. + final Drawable background = view.getBackground(); + opticalInsets = background != null ? background.getOpticalInsets() : InsetsCompat.NONE; + // If background.getOpticalInsets() returns null this will blow up. + } + return opticalInsets; + } + + /** + * Call this from {@code View#setOpticalInsets(Insets)}. + */ + public void onSetOpticalInsets(final Insets insets) { + if (opticalInsets != insets) { + opticalInsets = insets; + view.requestLayout(); + } + } +} diff --git a/insets-helper/src/main/java/net/xpece/androidx/optical/OpticalInsetsHelper.kt b/insets-helper/src/main/java/net/xpece/androidx/optical/OpticalInsetsHelper.kt deleted file mode 100644 index 8d60ea4..0000000 --- a/insets-helper/src/main/java/net/xpece/androidx/optical/OpticalInsetsHelper.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.xpece.androidx.optical - -import android.graphics.Insets -import android.graphics.drawable.InsetDrawable -import android.graphics.drawable.LayerDrawable -import android.graphics.drawable.RippleDrawable -import androidx.annotation.RequiresApi -import android.view.View - -/** - * Helper class for making views aware of optical insets on [InsetDrawable] below API 21, - * and [LayerDrawable] (including children such as [RippleDrawable]) on all API levels. - */ -@RequiresApi(16) -class OpticalInsetsHelper(private val view: View) { - - private var opticalInsets: Insets? = null - - /** - * Return this from `View.getOpticalInsets()`. - */ - fun onGetOpticalInsets(): Insets { - if (opticalInsets == null) { - // Same as platform, we don't support changing insets once resolved. - opticalInsets = view.background?.getOpticalInsets() ?: InsetsCompat.NONE - } - return opticalInsets!! - } - - /** - * Call this from `View.setOpticalInsets(Insets)`. - */ - fun onSetOpticalInsets(insets: Insets) { - if (opticalInsets != insets) { - opticalInsets = insets - view.requestLayout() - } - } -} diff --git a/insets/src/main/java/net/xpece/androidx/optical/InsetsCompat.java b/insets/src/main/java/net/xpece/androidx/optical/InsetsCompat.java index 41e2065..80cd827 100644 --- a/insets/src/main/java/net/xpece/androidx/optical/InsetsCompat.java +++ b/insets/src/main/java/net/xpece/androidx/optical/InsetsCompat.java @@ -1,5 +1,7 @@ package net.xpece.androidx.optical; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.drawable.InsetDrawable; @@ -18,6 +20,7 @@ * Insets are immutable so may be treated as values. */ @RequiresApi(16) +@TargetApi(29) @SuppressWarnings("unused") public final class InsetsCompat { @@ -51,6 +54,7 @@ private InsetsCompat() { * @param bottom the bottom inset * @return Insets instance with the appropriate values */ + @SuppressLint("NewApi") public static Insets of(int left, int top, int right, int bottom) { if (sSafe) { return Insets.of(left, top, right, bottom); @@ -74,6 +78,7 @@ public static Insets of(@Nullable Rect r) { } } + @SuppressLint("NewApi") @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) static Insets union(final @NonNull Insets... insets) { int left = 0, top = 0, right = 0, bottom = 0; diff --git a/sample/src/main/java/net/xpece/androidx/optical/sample/MainActivity.kt b/sample/src/main/java/net/xpece/androidx/optical/sample/MainActivity.kt index 386f487..0ea4ac6 100644 --- a/sample/src/main/java/net/xpece/androidx/optical/sample/MainActivity.kt +++ b/sample/src/main/java/net/xpece/androidx/optical/sample/MainActivity.kt @@ -6,7 +6,7 @@ import android.os.Bundle import com.google.android.material.bottomnavigation.BottomNavigationView import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_main.* -import net.xpece.androidx.optical.getOpticalInsets +import net.xpece.androidx.optical.getOpticalInsetsCompat class MainActivity : AppCompatActivity() { @@ -48,6 +48,6 @@ class MainActivity : AppCompatActivity() { navigation.selectedItemId = R.id.navigation_wrong } - LayerDrawable(arrayOf(ColorDrawable(0))).getOpticalInsets() + LayerDrawable(arrayOf(ColorDrawable(0))).getOpticalInsetsCompat() } }