From 7f964bdf028c49d562bee7d5375bde4c485f46a2 Mon Sep 17 00:00:00 2001 From: Eugen Pechanec Date: Tue, 23 Oct 2018 17:04:18 +0200 Subject: [PATCH] Initial commit, v1.0.0 Support from Jellybean to Pie (including Android P Developer Preview) Root layouts: Frame-, Linear-, Constraint-, CoordinatorLayout Inset widgets: CardView TODO: Add Readme Better sample app - runtime switching of different configurations --- .gitignore | 11 + .idea/codeStyles/Project.xml | 377 ++++++++++++++++++ .idea/gradle.xml | 26 ++ .idea/misc.xml | 21 + .idea/runConfigurations.xml | 12 + android-tasks.gradle | 4 + build.gradle | 42 ++ gradle.properties | 13 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 172 ++++++++ gradlew.bat | 84 ++++ insets/build.gradle | 60 +++ insets/consumer-proguard-rules.pro | 4 + .../insets/ExampleInstrumentedTest.java | 26 ++ insets/src/main/AndroidManifest.xml | 1 + .../xpece/androidx/optical/InsetsCompat.java | 103 +++++ .../optical/insets/ExampleUnitTest.java | 17 + java-tasks.gradle | 4 + platform-hidden/build.gradle | 9 + .../src/main/java/android/graphics/Rect.java | 22 + .../src/main/java/android/os/Parcelable.java | 20 + platform-insets/build.gradle | 28 ++ platform-insets/src/main/AndroidManifest.xml | 1 + .../main/java/android/graphics/Insets.java | 93 +++++ root-constraint-layout/build.gradle | 55 +++ .../src/main/AndroidManifest.xml | 1 + .../optical/RootConstraintLayout.java | 34 ++ .../optical/RootConstraintLayoutTest.kt | 22 + root-coordinator-layout/build.gradle | 54 +++ .../src/main/AndroidManifest.xml | 1 + .../optical/RootCoordinatorLayout.java | 34 ++ .../optical/RootCoordinatorLayoutTest.kt | 22 + root-frame-layout/build.gradle | 52 +++ .../src/main/AndroidManifest.xml | 1 + .../androidx/optical/RootFrameLayout.java | 41 ++ .../androidx/optical/RootFrameLayoutTest.kt | 22 + root-linear-layout/build.gradle | 52 +++ .../src/main/AndroidManifest.xml | 1 + .../androidx/optical/RootLinearLayout.java | 41 ++ .../androidx/optical/RootLinearLayoutTest.kt | 22 + sample/build.gradle | 48 +++ sample/proguard-rules.pro | 2 + sample/src/main/AndroidManifest.xml | 40 ++ sample/src/main/ic_launcher-web.png | Bin 0 -> 9466 bytes .../optical/sample/LinearLayoutActivity.kt | 11 + .../sample/RecyclerViewInsetsActivity.kt | 60 +++ .../sample/RecyclerViewOffsetsActivity.kt | 67 ++++ .../optical/sample/RootRecyclerView.java | 36 ++ .../src/main/res/layout/activity_linear.xml | 45 +++ .../res/layout/activity_recycler_insets.xml | 12 + .../res/layout/activity_recycler_offsets.xml | 11 + sample/src/main/res/layout/item_card.xml | 17 + sample/src/main/res/values/colors.xml | 7 + sample/src/main/res/values/strings.xml | 4 + sample/src/main/res/values/styles.xml | 12 + settings.gradle | 5 + widget-cardview/build.gradle | 54 +++ widget-cardview/src/main/AndroidManifest.xml | 1 + .../xpece/androidx/optical/InsetCardView.java | 67 ++++ .../androidx/optical/InsetCardViewTest.kt | 61 +++ 61 files changed, 2170 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 android-tasks.gradle create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 insets/build.gradle create mode 100644 insets/consumer-proguard-rules.pro create mode 100644 insets/src/androidTest/java/net/xpece/androidx/optical/insets/ExampleInstrumentedTest.java create mode 100644 insets/src/main/AndroidManifest.xml create mode 100644 insets/src/main/java/net/xpece/androidx/optical/InsetsCompat.java create mode 100644 insets/src/test/java/net/xpece/androidx/optical/insets/ExampleUnitTest.java create mode 100644 java-tasks.gradle create mode 100644 platform-hidden/build.gradle create mode 100644 platform-hidden/src/main/java/android/graphics/Rect.java create mode 100644 platform-hidden/src/main/java/android/os/Parcelable.java create mode 100644 platform-insets/build.gradle create mode 100644 platform-insets/src/main/AndroidManifest.xml create mode 100644 platform-insets/src/main/java/android/graphics/Insets.java create mode 100644 root-constraint-layout/build.gradle create mode 100644 root-constraint-layout/src/main/AndroidManifest.xml create mode 100644 root-constraint-layout/src/main/java/net/xpece/androidx/optical/RootConstraintLayout.java create mode 100644 root-constraint-layout/src/test/java/net/xpece/androidx/optical/RootConstraintLayoutTest.kt create mode 100644 root-coordinator-layout/build.gradle create mode 100644 root-coordinator-layout/src/main/AndroidManifest.xml create mode 100644 root-coordinator-layout/src/main/java/net/xpece/androidx/optical/RootCoordinatorLayout.java create mode 100644 root-coordinator-layout/src/test/java/net/xpece/androidx/optical/RootCoordinatorLayoutTest.kt create mode 100644 root-frame-layout/build.gradle create mode 100644 root-frame-layout/src/main/AndroidManifest.xml create mode 100644 root-frame-layout/src/main/java/net/xpece/androidx/optical/RootFrameLayout.java create mode 100644 root-frame-layout/src/test/java/net/xpece/androidx/optical/RootFrameLayoutTest.kt create mode 100644 root-linear-layout/build.gradle create mode 100644 root-linear-layout/src/main/AndroidManifest.xml create mode 100644 root-linear-layout/src/main/java/net/xpece/androidx/optical/RootLinearLayout.java create mode 100644 root-linear-layout/src/test/java/net/xpece/androidx/optical/RootLinearLayoutTest.kt create mode 100644 sample/build.gradle create mode 100644 sample/proguard-rules.pro create mode 100644 sample/src/main/AndroidManifest.xml create mode 100644 sample/src/main/ic_launcher-web.png create mode 100644 sample/src/main/java/net/xpece/androidx/optical/sample/LinearLayoutActivity.kt create mode 100644 sample/src/main/java/net/xpece/androidx/optical/sample/RecyclerViewInsetsActivity.kt create mode 100644 sample/src/main/java/net/xpece/androidx/optical/sample/RecyclerViewOffsetsActivity.kt create mode 100644 sample/src/main/java/net/xpece/androidx/optical/sample/RootRecyclerView.java create mode 100644 sample/src/main/res/layout/activity_linear.xml create mode 100644 sample/src/main/res/layout/activity_recycler_insets.xml create mode 100644 sample/src/main/res/layout/activity_recycler_offsets.xml create mode 100644 sample/src/main/res/layout/item_card.xml create mode 100644 sample/src/main/res/values/colors.xml create mode 100644 sample/src/main/res/values/strings.xml create mode 100644 sample/src/main/res/values/styles.xml create mode 100644 settings.gradle create mode 100644 widget-cardview/build.gradle create mode 100644 widget-cardview/src/main/AndroidManifest.xml create mode 100644 widget-cardview/src/main/java/net/xpece/androidx/optical/InsetCardView.java create mode 100644 widget-cardview/src/test/java/net/xpece/androidx/optical/InsetCardViewTest.kt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2bf90ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +/local.properties +/.idea/caches/build_file_checksum.ser +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..c3896bd --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,377 @@ + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..cbc8539 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b8ca955 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/android-tasks.gradle b/android-tasks.gradle new file mode 100644 index 0000000..529e66a --- /dev/null +++ b/android-tasks.gradle @@ -0,0 +1,4 @@ +task androidSourceJar(type: Jar) { + classifier = 'sources' + from android.sourceSets.main.java.srcDirs +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..c4a139f --- /dev/null +++ b/build.gradle @@ -0,0 +1,42 @@ +import com.android.build.gradle.LibraryExtension +import com.android.build.gradle.LibraryPlugin + +buildscript { + ext.kotlin_version = '1.3.0-rc-190' + + repositories { + google() + jcenter() + maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.0-beta01' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'digital.wup:android-maven-publish:3.6.2' + } +} + +allprojects { + repositories { + google() + jcenter() + maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } + } + + group = "net.xpece.androidx.optical" +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + +// Disable BuildConfig generation for Android library modules so javac can be skipped altogether. +plugins.whenPluginAdded { + if (it instanceof LibraryPlugin) { + extensions.getByType(LibraryExtension).libraryVariants.all { + // TODO replace with https://issuetracker.google.com/issues/72050365 once released. + it.generateBuildConfig.enabled = false + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..743d692 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,13 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718 GIT binary patch literal 54329 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

<5G2M1WZP5 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 F{|7XC?P&l2 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d76b502 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/insets/build.gradle b/insets/build.gradle new file mode 100644 index 0000000..3ae670d --- /dev/null +++ b/insets/build.gradle @@ -0,0 +1,60 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'digital.wup.android-maven-publish' + +android { + compileSdkVersion 28 + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + + consumerProguardFiles 'consumer-proguard-rules.pro' + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + } + } + +} + +/* + * Having platform-insets as implementation dependency would solve these issues: + * - Separate consumer proguard rules + * - Insets class included for consumers by default + * - platform-insets could be an Android library module and could reference classes from the SDK + * + * platform-insets will have to be included manually by consumers wishing to extend this library. + * All that for the simple fact that compileOnly dependencies don't get bundled into resulting APK. + */ + +dependencies { + compileOnly project(':platform-insets') + testCompileOnly project(':platform-insets') + + implementation "com.android.support:support-annotations:28.0.0" + + testImplementation 'junit:junit:4.12' + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} + +apply from: rootProject.file('android-tasks.gradle') + +version = '1.0.0' + +publishing { + publications { + mavenAar(MavenPublication) { + from components.android + artifact androidSourceJar + } + } +} diff --git a/insets/consumer-proguard-rules.pro b/insets/consumer-proguard-rules.pro new file mode 100644 index 0000000..2ce1ac1 --- /dev/null +++ b/insets/consumer-proguard-rules.pro @@ -0,0 +1,4 @@ +-dontwarn android.graphics.Insets +-dontnote android.graphics.Insets + +-keepnames class android.graphics.Insets { *; } diff --git a/insets/src/androidTest/java/net/xpece/androidx/optical/insets/ExampleInstrumentedTest.java b/insets/src/androidTest/java/net/xpece/androidx/optical/insets/ExampleInstrumentedTest.java new file mode 100644 index 0000000..1f7398f --- /dev/null +++ b/insets/src/androidTest/java/net/xpece/androidx/optical/insets/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package net.xpece.androidx.optical.insets; + +import static org.junit.Assert.assertEquals; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("net.xpece.androidx.optical.insets.test", appContext.getPackageName()); + } +} diff --git a/insets/src/main/AndroidManifest.xml b/insets/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0098394 --- /dev/null +++ b/insets/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/insets/src/main/java/net/xpece/androidx/optical/InsetsCompat.java b/insets/src/main/java/net/xpece/androidx/optical/InsetsCompat.java new file mode 100644 index 0000000..e7732a5 --- /dev/null +++ b/insets/src/main/java/net/xpece/androidx/optical/InsetsCompat.java @@ -0,0 +1,103 @@ +package net.xpece.androidx.optical; + +import android.annotation.SuppressLint; +import android.graphics.Insets; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; + +import java.lang.reflect.Method; + +/** + * An Insets instance holds four integer offsets which describe changes to the four + * edges of a Rectangle. By convention, positive values move edges towards the + * centre of the rectangle. + *

+ * Insets are immutable so may be treated as values. + */ +@RequiresApi(16) +@SuppressLint("PrivateApi") +@SuppressWarnings("unused") +public final class InsetsCompat { + public static final Insets NONE; + // Insets were on the dark greylist during Android Pie Developer Preview. + // It looks they're on the light greylist in public release. + private static boolean sSafe = !"P".equals(Build.VERSION.CODENAME); + + static { + Insets none; + try { + none = of(0, 0, 0, 0); + } catch (NoSuchMethodError ex) { + sSafe = false; + none = of(0, 0, 0, 0); + } + NONE = none; + } + + private InsetsCompat() { + } + + /** + * Return an Insets instance with the appropriate values. + * + * @param left the left inset + * @param top the top inset + * @param right the right inset + * @param bottom the bottom inset + * @return Insets instance with the appropriate values + */ + public static Insets of(int left, int top, int right, int bottom) { + if (sSafe) { + return Insets.of(left, top, right, bottom); + } else { + final InsetDrawable insets = new InsetDrawable(null, left, top, right, bottom); + return Reflection.getOpticalBounds(insets); + } + } + + /** + * Return an Insets instance with the appropriate values. + * + * @param r the rectangle from which to take the values + * @return an Insets instance with the appropriate values + */ + public static Insets of(@Nullable Rect r) { + if (r == null) { + return NONE; + } else { + return of(r.left, r.top, r.right, r.bottom); + } + } + + // Having separate class prevents reflective method lookup when it's not needed. + @SuppressWarnings("JavaReflectionMemberAccess") + private static class Reflection { + + private static Method METHOD_GET_OPTICAL_INSETS; + + static { + try { + METHOD_GET_OPTICAL_INSETS = Drawable.class.getDeclaredMethod("getOpticalInsets"); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private Reflection() { + } + + @NonNull + static Insets getOpticalBounds(final @NonNull Drawable drawable) { + try { + return (Insets) METHOD_GET_OPTICAL_INSETS.invoke(drawable); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/insets/src/test/java/net/xpece/androidx/optical/insets/ExampleUnitTest.java b/insets/src/test/java/net/xpece/androidx/optical/insets/ExampleUnitTest.java new file mode 100644 index 0000000..c2d9df2 --- /dev/null +++ b/insets/src/test/java/net/xpece/androidx/optical/insets/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package net.xpece.androidx.optical.insets; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} diff --git a/java-tasks.gradle b/java-tasks.gradle new file mode 100644 index 0000000..9b20fe3 --- /dev/null +++ b/java-tasks.gradle @@ -0,0 +1,4 @@ +task sourceJar(type: Jar) { + from sourceSets.main.allJava + classifier "sources" +} diff --git a/platform-hidden/build.gradle b/platform-hidden/build.gradle new file mode 100644 index 0000000..e12de59 --- /dev/null +++ b/platform-hidden/build.gradle @@ -0,0 +1,9 @@ +apply plugin: 'java-library' + +/* + * This module contains stubs of classes from Android SDK + * so they're available to Java libraries that need them at compile time. + */ + +sourceCompatibility = "6" +targetCompatibility = "6" diff --git a/platform-hidden/src/main/java/android/graphics/Rect.java b/platform-hidden/src/main/java/android/graphics/Rect.java new file mode 100644 index 0000000..1f6abbf --- /dev/null +++ b/platform-hidden/src/main/java/android/graphics/Rect.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import android.os.Parcelable; + +final class Rect implements Parcelable { +} diff --git a/platform-hidden/src/main/java/android/os/Parcelable.java b/platform-hidden/src/main/java/android/os/Parcelable.java new file mode 100644 index 0000000..548350f --- /dev/null +++ b/platform-hidden/src/main/java/android/os/Parcelable.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +public interface Parcelable { +} diff --git a/platform-insets/build.gradle b/platform-insets/build.gradle new file mode 100644 index 0000000..6872e28 --- /dev/null +++ b/platform-insets/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'java-library' +apply plugin: 'maven-publish' + +/* + * This library can only be compileOnly dependency of other modules if it's not an Android library. + * At the same time we only want to expose the Insets class so we need to hide any other classes + * from the Android SDK in another compileOnly dependency of this module. + */ + +dependencies { + compileOnly project(':platform-hidden') + + implementation "com.android.support:support-annotations:28.0.0" +} + +sourceCompatibility = "6" +targetCompatibility = "6" + +version = '1.0.0' + +publishing { + publications { + mavenJar(MavenPublication) { + from components.java + // Javadoc or sources are not necessary. See Android SDK. + } + } +} diff --git a/platform-insets/src/main/AndroidManifest.xml b/platform-insets/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4c7abf7 --- /dev/null +++ b/platform-insets/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/platform-insets/src/main/java/android/graphics/Insets.java b/platform-insets/src/main/java/android/graphics/Insets.java new file mode 100644 index 0000000..87d791d --- /dev/null +++ b/platform-insets/src/main/java/android/graphics/Insets.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RestrictTo; + +/** + * An Insets instance holds four integer offsets which describe changes to the four + * edges of a Rectangle. By convention, positive values move edges towards the + * centre of the rectangle. + *

+ * Insets are immutable so may be treated as values. + */ +@SuppressWarnings("unused") +public class Insets { + @NonNull + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public static Insets NONE = placeholder(); + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public final int left; + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public final int top; + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public final int right; + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public final int bottom; + + private Insets(int left, int top, int right, int bottom) { + throw new UnsupportedOperationException(); + } + + @NonNull + private static Insets placeholder() { + throw new UnsupportedOperationException("Did you include Optical library proguard rules?"); + } + + // Factory methods + + /** + * Return an Insets instance with the appropriate values. + * + * @param left the left inset + * @param top the top inset + * @param right the right inset + * @param bottom the bottom inset + * @return Insets instance with the appropriate values + */ + @NonNull + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public static Insets of(int left, int top, int right, int bottom) { + throw new UnsupportedOperationException(); + } + + /** + * Return an Insets instance with the appropriate values. + * + * @param r the rectangle from which to take the values + * @return an Insets instance with the appropriate values + */ + @NonNull + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + public static Insets of(@Nullable Rect r) { + throw new UnsupportedOperationException(); + } + + /** + * Two Insets instances are equal if they belong to the same class and their fields are + * pairwise equal. + * + * @param o the object to compare this instance with. + * @return true if this object is equal {@code o} + */ + @Override + public boolean equals(@Nullable Object o) { + throw new UnsupportedOperationException(); + } +} diff --git a/root-constraint-layout/build.gradle b/root-constraint-layout/build.gradle new file mode 100644 index 0000000..19ff14e --- /dev/null +++ b/root-constraint-layout/build.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'digital.wup.android-maven-publish' + +android { + compileSdkVersion 28 + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + buildTypes { + release { + minifyEnabled false + } + } + +} + +dependencies { + implementation project(':insets') + + compileOnly project(':platform-insets') + testCompileOnly project(':platform-insets') + + implementation "com.android.support:support-annotations:28.0.0" + + //noinspection GradleDependency + api 'com.android.support.constraint:constraint-layout:1.0.2' + + testImplementation 'junit:junit:4.12' + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + testImplementation "org.robolectric:robolectric:3.8" +} + +apply from: rootProject.file('android-tasks.gradle') + +version = '1.0.0' + +publishing { + publications { + mavenAar(MavenPublication) { + from components.android + artifact androidSourceJar + } + } +} diff --git a/root-constraint-layout/src/main/AndroidManifest.xml b/root-constraint-layout/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0a6a8bc --- /dev/null +++ b/root-constraint-layout/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/root-constraint-layout/src/main/java/net/xpece/androidx/optical/RootConstraintLayout.java b/root-constraint-layout/src/main/java/net/xpece/androidx/optical/RootConstraintLayout.java new file mode 100644 index 0000000..dd07d63 --- /dev/null +++ b/root-constraint-layout/src/main/java/net/xpece/androidx/optical/RootConstraintLayout.java @@ -0,0 +1,34 @@ +package net.xpece.androidx.optical; + +import android.content.Context; +import android.graphics.Insets; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.annotation.RestrictTo; +import android.support.constraint.ConstraintLayout; +import android.util.AttributeSet; + +public class RootConstraintLayout extends ConstraintLayout { + public RootConstraintLayout(@NonNull Context context) { + super(context); + } + + public RootConstraintLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public RootConstraintLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + //@Override + @NonNull + @RequiresApi(16) + @RestrictTo(RestrictTo.Scope.LIBRARY) + Insets getOpticalInsets() { + return InsetsCompat.NONE; + } +} diff --git a/root-constraint-layout/src/test/java/net/xpece/androidx/optical/RootConstraintLayoutTest.kt b/root-constraint-layout/src/test/java/net/xpece/androidx/optical/RootConstraintLayoutTest.kt new file mode 100644 index 0000000..a114ae7 --- /dev/null +++ b/root-constraint-layout/src/test/java/net/xpece/androidx/optical/RootConstraintLayoutTest.kt @@ -0,0 +1,22 @@ +package net.xpece.androidx.optical + +import android.app.Activity +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import kotlin.test.assertEquals + +// SDK 15 and older is not supported in Robolectric. +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [16, 18]) +class RootConstraintLayoutTest { + + @Test + fun getOpticalInsets_returnsNone() { + val activity = Robolectric.setupActivity(Activity::class.java) + val layout = RootConstraintLayout(activity) + assertEquals(InsetsCompat.NONE, layout.opticalInsets) + } +} diff --git a/root-coordinator-layout/build.gradle b/root-coordinator-layout/build.gradle new file mode 100644 index 0000000..87be686 --- /dev/null +++ b/root-coordinator-layout/build.gradle @@ -0,0 +1,54 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'digital.wup.android-maven-publish' + +android { + compileSdkVersion 28 + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + buildTypes { + release { + minifyEnabled false + } + } + +} + +dependencies { + implementation project(':insets') + + compileOnly project(':platform-insets') + testCompileOnly project(':platform-insets') + + implementation "com.android.support:support-annotations:28.0.0" + + api 'com.android.support:coordinatorlayout:28.0.0' + + testImplementation 'junit:junit:4.12' + testImplementation "org.robolectric:robolectric:3.8" + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" +} + +version = '1.0.0' + +apply from: rootProject.file('android-tasks.gradle') + +publishing { + publications { + mavenAar(MavenPublication) { + from components.android + artifact androidSourceJar + } + } +} diff --git a/root-coordinator-layout/src/main/AndroidManifest.xml b/root-coordinator-layout/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e59f66c --- /dev/null +++ b/root-coordinator-layout/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/root-coordinator-layout/src/main/java/net/xpece/androidx/optical/RootCoordinatorLayout.java b/root-coordinator-layout/src/main/java/net/xpece/androidx/optical/RootCoordinatorLayout.java new file mode 100644 index 0000000..4bd50b5 --- /dev/null +++ b/root-coordinator-layout/src/main/java/net/xpece/androidx/optical/RootCoordinatorLayout.java @@ -0,0 +1,34 @@ +package net.xpece.androidx.optical; + +import android.content.Context; +import android.graphics.Insets; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.annotation.RestrictTo; +import android.support.design.widget.CoordinatorLayout; +import android.util.AttributeSet; + +public class RootCoordinatorLayout extends CoordinatorLayout { + public RootCoordinatorLayout(@NonNull Context context) { + super(context); + } + + public RootCoordinatorLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public RootCoordinatorLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + //@Override + @NonNull + @RequiresApi(16) + @RestrictTo(RestrictTo.Scope.LIBRARY) + Insets getOpticalInsets() { + return InsetsCompat.NONE; + } +} diff --git a/root-coordinator-layout/src/test/java/net/xpece/androidx/optical/RootCoordinatorLayoutTest.kt b/root-coordinator-layout/src/test/java/net/xpece/androidx/optical/RootCoordinatorLayoutTest.kt new file mode 100644 index 0000000..0e56878 --- /dev/null +++ b/root-coordinator-layout/src/test/java/net/xpece/androidx/optical/RootCoordinatorLayoutTest.kt @@ -0,0 +1,22 @@ +package net.xpece.androidx.optical + +import android.app.Activity +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import kotlin.test.assertEquals + +// SDK 15 and older is not supported in Robolectric. +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [16, 18]) +class RootCoordinatorLayoutTest { + + @Test + fun getOpticalInsets_returnsNone() { + val activity = Robolectric.setupActivity(Activity::class.java) + val layout = RootCoordinatorLayout(activity) + assertEquals(InsetsCompat.NONE, layout.opticalInsets) + } +} diff --git a/root-frame-layout/build.gradle b/root-frame-layout/build.gradle new file mode 100644 index 0000000..6ea715a --- /dev/null +++ b/root-frame-layout/build.gradle @@ -0,0 +1,52 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'digital.wup.android-maven-publish' + +android { + compileSdkVersion 28 + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + buildTypes { + release { + minifyEnabled false + } + } + +} + +dependencies { + implementation project(':insets') + + compileOnly project(':platform-insets') + testCompileOnly project(':platform-insets') + + implementation "com.android.support:support-annotations:28.0.0" + + testImplementation 'junit:junit:4.12' + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + testImplementation "org.robolectric:robolectric:3.8" +} + +version = '1.0.0' + +apply from: rootProject.file('android-tasks.gradle') + +publishing { + publications { + mavenAar(MavenPublication) { + from components.android + artifact androidSourceJar + } + } +} diff --git a/root-frame-layout/src/main/AndroidManifest.xml b/root-frame-layout/src/main/AndroidManifest.xml new file mode 100644 index 0000000..227cf24 --- /dev/null +++ b/root-frame-layout/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/root-frame-layout/src/main/java/net/xpece/androidx/optical/RootFrameLayout.java b/root-frame-layout/src/main/java/net/xpece/androidx/optical/RootFrameLayout.java new file mode 100644 index 0000000..ed23942 --- /dev/null +++ b/root-frame-layout/src/main/java/net/xpece/androidx/optical/RootFrameLayout.java @@ -0,0 +1,41 @@ +package net.xpece.androidx.optical; + +import android.content.Context; +import android.graphics.Insets; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.annotation.RestrictTo; +import android.support.annotation.StyleRes; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +public class RootFrameLayout extends FrameLayout { + public RootFrameLayout(@NonNull Context context) { + super(context); + } + + public RootFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public RootFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @RequiresApi(21) + public RootFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + //@Override + @NonNull + @RequiresApi(16) + @RestrictTo(RestrictTo.Scope.LIBRARY) + Insets getOpticalInsets() { + return InsetsCompat.NONE; + } +} diff --git a/root-frame-layout/src/test/java/net/xpece/androidx/optical/RootFrameLayoutTest.kt b/root-frame-layout/src/test/java/net/xpece/androidx/optical/RootFrameLayoutTest.kt new file mode 100644 index 0000000..31acd60 --- /dev/null +++ b/root-frame-layout/src/test/java/net/xpece/androidx/optical/RootFrameLayoutTest.kt @@ -0,0 +1,22 @@ +package net.xpece.androidx.optical + +import android.app.Activity +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import kotlin.test.assertEquals + +// SDK 15 and older is not supported in Robolectric. +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [16, 18]) +class RootFrameLayoutTest { + + @Test + fun getOpticalInsets_returnsNone() { + val activity = Robolectric.setupActivity(Activity::class.java) + val layout = RootFrameLayout(activity) + assertEquals(InsetsCompat.NONE, layout.opticalInsets) + } +} diff --git a/root-linear-layout/build.gradle b/root-linear-layout/build.gradle new file mode 100644 index 0000000..6ea715a --- /dev/null +++ b/root-linear-layout/build.gradle @@ -0,0 +1,52 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'digital.wup.android-maven-publish' + +android { + compileSdkVersion 28 + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + buildTypes { + release { + minifyEnabled false + } + } + +} + +dependencies { + implementation project(':insets') + + compileOnly project(':platform-insets') + testCompileOnly project(':platform-insets') + + implementation "com.android.support:support-annotations:28.0.0" + + testImplementation 'junit:junit:4.12' + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + testImplementation "org.robolectric:robolectric:3.8" +} + +version = '1.0.0' + +apply from: rootProject.file('android-tasks.gradle') + +publishing { + publications { + mavenAar(MavenPublication) { + from components.android + artifact androidSourceJar + } + } +} diff --git a/root-linear-layout/src/main/AndroidManifest.xml b/root-linear-layout/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ce5d0c7 --- /dev/null +++ b/root-linear-layout/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/root-linear-layout/src/main/java/net/xpece/androidx/optical/RootLinearLayout.java b/root-linear-layout/src/main/java/net/xpece/androidx/optical/RootLinearLayout.java new file mode 100644 index 0000000..8e41792 --- /dev/null +++ b/root-linear-layout/src/main/java/net/xpece/androidx/optical/RootLinearLayout.java @@ -0,0 +1,41 @@ +package net.xpece.androidx.optical; + +import android.content.Context; +import android.graphics.Insets; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.annotation.RestrictTo; +import android.support.annotation.StyleRes; +import android.util.AttributeSet; +import android.widget.LinearLayout; + +public class RootLinearLayout extends LinearLayout { + public RootLinearLayout(@NonNull Context context) { + super(context); + } + + public RootLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public RootLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @RequiresApi(21) + public RootLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + //@Override + @NonNull + @RequiresApi(16) + @RestrictTo(RestrictTo.Scope.LIBRARY) + Insets getOpticalInsets() { + return InsetsCompat.NONE; + } +} diff --git a/root-linear-layout/src/test/java/net/xpece/androidx/optical/RootLinearLayoutTest.kt b/root-linear-layout/src/test/java/net/xpece/androidx/optical/RootLinearLayoutTest.kt new file mode 100644 index 0000000..7b6d824 --- /dev/null +++ b/root-linear-layout/src/test/java/net/xpece/androidx/optical/RootLinearLayoutTest.kt @@ -0,0 +1,22 @@ +package net.xpece.androidx.optical + +import android.app.Activity +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import kotlin.test.assertEquals + +// SDK 15 and older is not supported in Robolectric. +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [16, 18]) +class RootLinearLayoutTest { + + @Test + fun getOpticalInsets_returnsNone() { + val activity = Robolectric.setupActivity(Activity::class.java) + val layout = RootLinearLayout(activity) + assertEquals(InsetsCompat.NONE, layout.opticalInsets) + } +} diff --git a/sample/build.gradle b/sample/build.gradle new file mode 100644 index 0000000..a369a04 --- /dev/null +++ b/sample/build.gradle @@ -0,0 +1,48 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 28 + + defaultConfig { + applicationId "net.xpece.androidx.optical" + minSdkVersion 14 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.debug + } + } +} + +dependencies { + implementation project(':widget-cardview') + implementation project(':root-frame-layout') + implementation project(':root-linear-layout') + implementation project(':root-constraint-layout') + implementation project(':root-coordinator-layout') + + implementation project(':insets') + compileOnly project(':platform-insets') +// testCompileOnly project(':platform-insets') + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:recyclerview-v7:28.0.0' + + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.android.support:coordinatorlayout:28.0.0' +} + +repositories { + google() + jcenter() +} diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro new file mode 100644 index 0000000..c5d6610 --- /dev/null +++ b/sample/proguard-rules.pro @@ -0,0 +1,2 @@ +#-keepnames class ** { *; } +-keepattributes SourceFile,LineNumberTable diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6f782d4 --- /dev/null +++ b/sample/src/main/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/src/main/ic_launcher-web.png b/sample/src/main/ic_launcher-web.png new file mode 100644 index 0000000000000000000000000000000000000000..6bb18963872d7364ea08d14b91d0d71e4c73d7a9 GIT binary patch literal 9466 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 literal 0 HcmV?d00001 diff --git a/sample/src/main/java/net/xpece/androidx/optical/sample/LinearLayoutActivity.kt b/sample/src/main/java/net/xpece/androidx/optical/sample/LinearLayoutActivity.kt new file mode 100644 index 0000000..085b8d4 --- /dev/null +++ b/sample/src/main/java/net/xpece/androidx/optical/sample/LinearLayoutActivity.kt @@ -0,0 +1,11 @@ +package net.xpece.androidx.optical.sample + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity + +class LinearLayoutActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_linear) + } +} diff --git a/sample/src/main/java/net/xpece/androidx/optical/sample/RecyclerViewInsetsActivity.kt b/sample/src/main/java/net/xpece/androidx/optical/sample/RecyclerViewInsetsActivity.kt new file mode 100644 index 0000000..9c4cb56 --- /dev/null +++ b/sample/src/main/java/net/xpece/androidx/optical/sample/RecyclerViewInsetsActivity.kt @@ -0,0 +1,60 @@ +package net.xpece.androidx.optical.sample + +import android.graphics.Rect +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.RecyclerView +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import kotlinx.android.synthetic.main.activity_recycler_offsets.* + +class RecyclerViewInsetsActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_recycler_insets) + + val insets = + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics) + .toInt() + list.addItemDecoration(MyDecoration(insets)) + + list.adapter = MyAdapter() + } + + class MyDecoration(private val insets: Int) : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + outRect.left = insets + outRect.right = insets + outRect.bottom = insets + outRect.top = if (parent.getChildAdapterPosition(view) == 0) { + insets + } else { + 0 + } + } + } + + class MyAdapter : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { + val view = + LayoutInflater.from(parent.context).inflate(R.layout.item_card, parent, false) + return MyViewHolder(view) + } + + override fun getItemCount(): Int { + return 5 + } + + override fun onBindViewHolder(holder: MyViewHolder, position: Int) { + } + } + + class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) +} diff --git a/sample/src/main/java/net/xpece/androidx/optical/sample/RecyclerViewOffsetsActivity.kt b/sample/src/main/java/net/xpece/androidx/optical/sample/RecyclerViewOffsetsActivity.kt new file mode 100644 index 0000000..909379f --- /dev/null +++ b/sample/src/main/java/net/xpece/androidx/optical/sample/RecyclerViewOffsetsActivity.kt @@ -0,0 +1,67 @@ +package net.xpece.androidx.optical.sample + +import android.graphics.Rect +import android.os.Build +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.CardView +import android.support.v7.widget.RecyclerView +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import kotlinx.android.synthetic.main.activity_recycler_offsets.* + +class RecyclerViewOffsetsActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_recycler_offsets) + + val insets = + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics) + list.addItemDecoration(MyDecoration(insets)) + + list.adapter = MyAdapter() + } + + class MyDecoration(private val insets: Float) : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + val card = view as CardView + val maxCardElevation = if (Build.VERSION.SDK_INT < 21 || card.useCompatPadding) { + card.maxCardElevation + } else { + 0f + } + outRect.left = (insets - maxCardElevation).toInt() + outRect.right = (insets - maxCardElevation).toInt() + outRect.bottom = (insets - maxCardElevation * 1.5f).toInt() + outRect.top = if (parent.getChildAdapterPosition(view) == 0) { + (insets - maxCardElevation * 1.5f).toInt() + } else { + (0 - maxCardElevation * 1.5f).toInt() + } + } + } + + class MyAdapter : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { + val view = + LayoutInflater.from(parent.context).inflate(R.layout.item_card, parent, false) + return MyViewHolder(view) + } + + override fun getItemCount(): Int { + return 5 + } + + override fun onBindViewHolder(holder: MyViewHolder, position: Int) { + } + } + + class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) +} diff --git a/sample/src/main/java/net/xpece/androidx/optical/sample/RootRecyclerView.java b/sample/src/main/java/net/xpece/androidx/optical/sample/RootRecyclerView.java new file mode 100644 index 0000000..f24fd85 --- /dev/null +++ b/sample/src/main/java/net/xpece/androidx/optical/sample/RootRecyclerView.java @@ -0,0 +1,36 @@ +package net.xpece.androidx.optical.sample; + +import android.content.Context; +import android.graphics.Insets; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.annotation.RestrictTo; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; + +import net.xpece.androidx.optical.InsetsCompat; + +public class RootRecyclerView extends RecyclerView { + public RootRecyclerView(@NonNull Context context) { + super(context); + } + + public RootRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public RootRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyle) { + super(context, attrs, defStyle); + } + + //@Override + @NonNull + @RequiresApi(16) + @RestrictTo(RestrictTo.Scope.LIBRARY) + Insets getOpticalInsets() { + return InsetsCompat.NONE; + } +} diff --git a/sample/src/main/res/layout/activity_linear.xml b/sample/src/main/res/layout/activity_linear.xml new file mode 100644 index 0000000..ecd3438 --- /dev/null +++ b/sample/src/main/res/layout/activity_linear.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + diff --git a/sample/src/main/res/layout/activity_recycler_insets.xml b/sample/src/main/res/layout/activity_recycler_insets.xml new file mode 100644 index 0000000..8646a55 --- /dev/null +++ b/sample/src/main/res/layout/activity_recycler_insets.xml @@ -0,0 +1,12 @@ + + diff --git a/sample/src/main/res/layout/activity_recycler_offsets.xml b/sample/src/main/res/layout/activity_recycler_offsets.xml new file mode 100644 index 0000000..9de90e0 --- /dev/null +++ b/sample/src/main/res/layout/activity_recycler_offsets.xml @@ -0,0 +1,11 @@ + + diff --git a/sample/src/main/res/layout/item_card.xml b/sample/src/main/res/layout/item_card.xml new file mode 100644 index 0000000..377b5f7 --- /dev/null +++ b/sample/src/main/res/layout/item_card.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml new file mode 100644 index 0000000..82680c2 --- /dev/null +++ b/sample/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #008577 + #00574B + #D81B60 + #FAFAFA + diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml new file mode 100644 index 0000000..a08c457 --- /dev/null +++ b/sample/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + Android Optical + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in libero ornare, cursus neque a, imperdiet risus. Etiam dignissim massa egestas orci congue, vitae fringilla odio eleifend. Proin lacus augue, aliquet ac libero et, tempor sollicitudin sem. Ut commodo erat vitae gravida vulputate. In maximus convallis enim nec congue. Nulla hendrerit id quam at ornare. Vivamus gravida sem vel tellus porttitor volutpat. Morbi iaculis mauris erat, eu dignissim tortor porttitor sit amet. In lobortis mauris sit amet arcu blandit imperdiet. Sed neque ligula, semper ac nibh non, consectetur ultrices dolor. Nunc in felis eget elit tincidunt dignissim et in purus. Nulla tincidunt malesuada augue, sed blandit odio malesuada in. Aliquam leo nisl, facilisis a porttitor a, mollis non mi. + diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml new file mode 100644 index 0000000..6afcb1b --- /dev/null +++ b/sample/src/main/res/values/styles.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..c1e5df0 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +include ':sample' +include ':platform-insets', ':platform-hidden' +include ':insets' +include ':root-linear-layout', ':root-frame-layout', ':root-constraint-layout', ':root-coordinator-layout' +include ':widget-cardview' diff --git a/widget-cardview/build.gradle b/widget-cardview/build.gradle new file mode 100644 index 0000000..7337232 --- /dev/null +++ b/widget-cardview/build.gradle @@ -0,0 +1,54 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'digital.wup.android-maven-publish' + +android { + compileSdkVersion 28 + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + buildTypes { + release { + minifyEnabled false + } + } + +} + +dependencies { + implementation project(':insets') + + compileOnly project(':platform-insets') + testCompileOnly project(':platform-insets') + + implementation "com.android.support:support-annotations:28.0.0" + + api "com.android.support:cardview-v7:28.0.0" + + testImplementation 'junit:junit:4.12' + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + testImplementation "org.robolectric:robolectric:3.8" +} + +version = '1.0.0' + +apply from: rootProject.file('android-tasks.gradle') + +publishing { + publications { + mavenAar(MavenPublication) { + from components.android + artifact androidSourceJar + } + } +} diff --git a/widget-cardview/src/main/AndroidManifest.xml b/widget-cardview/src/main/AndroidManifest.xml new file mode 100644 index 0000000..012078b --- /dev/null +++ b/widget-cardview/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/widget-cardview/src/main/java/net/xpece/androidx/optical/InsetCardView.java b/widget-cardview/src/main/java/net/xpece/androidx/optical/InsetCardView.java new file mode 100644 index 0000000..354d72d --- /dev/null +++ b/widget-cardview/src/main/java/net/xpece/androidx/optical/InsetCardView.java @@ -0,0 +1,67 @@ +package net.xpece.androidx.optical; + +import android.content.Context; +import android.graphics.Insets; +import android.os.Build; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.annotation.RestrictTo; +import android.support.v7.widget.CardView; +import android.util.AttributeSet; + +public class InsetCardView extends CardView { + + private Insets mOpticalInsets = null; + + public InsetCardView(@NonNull Context context) { + super(context); + } + + public InsetCardView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public InsetCardView(@NonNull Context context, @Nullable AttributeSet attrs, + @AttrRes int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void setUseCompatPadding(boolean useCompatPadding) { + mOpticalInsets = null; + super.setUseCompatPadding(useCompatPadding); + } + + @Override + public void setMaxCardElevation(float maxElevation) { + mOpticalInsets = null; + super.setMaxCardElevation(maxElevation); + } + + //@Override + @NonNull + @RequiresApi(16) + @RestrictTo(RestrictTo.Scope.LIBRARY) + Insets getOpticalInsets() { + if (mOpticalInsets == null) { + if (Build.VERSION.SDK_INT < 21 || getUseCompatPadding()) { + final float maxCardElevation = getMaxCardElevation(); + if (maxCardElevation != 0) { + mOpticalInsets = InsetsCompat.of( + (int) maxCardElevation, + (int) (maxCardElevation * 1.5f), + (int) maxCardElevation, + (int) (maxCardElevation * 1.5f) + ); + } else { + mOpticalInsets = InsetsCompat.NONE; + } + } else { + mOpticalInsets = InsetsCompat.NONE; + } + } + return mOpticalInsets; + } +} diff --git a/widget-cardview/src/test/java/net/xpece/androidx/optical/InsetCardViewTest.kt b/widget-cardview/src/test/java/net/xpece/androidx/optical/InsetCardViewTest.kt new file mode 100644 index 0000000..40b9599 --- /dev/null +++ b/widget-cardview/src/test/java/net/xpece/androidx/optical/InsetCardViewTest.kt @@ -0,0 +1,61 @@ +package net.xpece.androidx.optical + +import android.app.Activity +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import kotlin.test.assertEquals + +// SDK 15 and older is not supported in Robolectric. +@RunWith(RobolectricTestRunner::class) +class InsetCardViewTest { + + @Config(sdk = [17, 19]) + @Test + fun getOpticalInsets_returnsMaxCardElevation_onAndroid4() { + val activity = Robolectric.setupActivity(Activity::class.java) + val card = InsetCardView(activity) + card.layout(0, 0, 100, 100) + // padding - contentPadding is not good enough. It's compound of two rounded numbers. + val expected = with(card) { + InsetsCompat.of( + maxCardElevation.toInt(), + (maxCardElevation * 1.5f).toInt(), + maxCardElevation.toInt(), + (maxCardElevation * 1.5f).toInt() + ) + } + assertEquals(expected, card.opticalInsets) + } + + @Config(sdk = [21]) + @Test + fun getOpticalInsets_returnsMaxCardElevation_onLollipopWithCompatPaddingEnabled() { + val activity = Robolectric.setupActivity(Activity::class.java) + val card = InsetCardView(activity) + card.useCompatPadding = true + card.layout(0, 0, 100, 100) + // padding - contentPadding is not good enough. It's compound of two rounded numbers. + val expected = with(card) { + InsetsCompat.of( + maxCardElevation.toInt(), + (maxCardElevation * 1.5f).toInt(), + maxCardElevation.toInt(), + (maxCardElevation * 1.5f).toInt() + ) + } + assertEquals(expected, card.opticalInsets) + } + + @Config(sdk = [21]) + @Test + fun getOpticalInsets_returnsNone_onLollipopWithCompatPaddingDisabled() { + val activity = Robolectric.setupActivity(Activity::class.java) + val card = InsetCardView(activity) + card.useCompatPadding = false + card.layout(0, 0, 100, 100) + assertEquals(InsetsCompat.NONE, card.opticalInsets) + } +}