aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-prod (mdb) <android-build-team-robot@google.com>2019-11-22 12:51:49 +0000
committerandroid-build-prod (mdb) <android-build-team-robot@google.com>2019-11-22 12:51:49 +0000
commitfc517f62dbd3175989aba068cd58445db4cbb391 (patch)
treea9ee7b72baf3a48be9af7dccd2b68ff83b61ae91
parent162f8b1fdf3b5ab651ae8ef2b44f3ab79b008062 (diff)
parent6f8e163bd0cc48e08f3b4f42347719d58c3a780f (diff)
downloadsupport-fc517f62dbd3175989aba068cd58445db4cbb391.tar.gz
Snap for 6026338 from 6f8e163bd0cc48e08f3b4f42347719d58c3a780f to androidx-browser-release
Change-Id: I2896dd1a076c16b9d31b92abbf7c8a18bbbc7e17
-rw-r--r--buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt5
-rw-r--r--buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt9
-rw-r--r--camera/camera-view/build.gradle2
-rw-r--r--camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceTextureReleaseBlockingListenerTest.java89
-rw-r--r--compose/compose-compiler/build.gradle27
-rw-r--r--compose/compose-runtime/compose-runtime-benchmark/build.gradle2
-rw-r--r--compose/compose-runtime/integration-tests/samples/build.gradle2
-rw-r--r--core/core/api/restricted_1.3.0-alpha01.txt10
-rw-r--r--core/core/api/restricted_current.txt10
-rw-r--r--core/core/src/main/java/androidx/core/util/LogWriter.java8
-rw-r--r--fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java1
-rw-r--r--fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java1
-rw-r--r--fragment/fragment/src/main/java/androidx/fragment/app/LogWriter.java60
-rw-r--r--jetifier/jetifier/migration.config4
-rw-r--r--media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java82
-rw-r--r--media2/widget/src/main/java/androidx/media2/widget/VideoView.java139
-rw-r--r--ui/integration-tests/benchmark/build.gradle2
-rw-r--r--ui/integration-tests/test/build.gradle2
-rw-r--r--ui/ui-android-view/build.gradle2
-rw-r--r--ui/ui-animation-core/api/0.1.0-dev03.txt22
-rw-r--r--ui/ui-animation-core/api/api_lint.ignore2
-rw-r--r--ui/ui-animation-core/api/current.txt22
-rw-r--r--ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev03.txt22
-rw-r--r--ui/ui-animation-core/api/public_plus_experimental_current.txt22
-rw-r--r--ui/ui-animation-core/api/restricted_0.1.0-dev03.txt22
-rw-r--r--ui/ui-animation-core/api/restricted_current.txt22
-rw-r--r--ui/ui-animation-core/integration-tests/samples/build.gradle2
-rw-r--r--ui/ui-animation-core/src/main/java/androidx/animation/Animation.kt57
-rw-r--r--ui/ui-animation-core/src/main/java/androidx/animation/AnimationBuilder.kt6
-rw-r--r--ui/ui-animation-core/src/main/java/androidx/animation/SpringSimulation.kt4
-rw-r--r--ui/ui-animation-core/src/main/java/androidx/animation/TransitionAnimation.kt23
-rw-r--r--ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt37
-rw-r--r--ui/ui-animation-core/src/test/java/androidx/animation/TransitionAnimationTest.kt34
-rw-r--r--ui/ui-animation/api/0.1.0-dev03.txt2
-rw-r--r--ui/ui-animation/api/current.txt2
-rw-r--r--ui/ui-animation/api/public_plus_experimental_0.1.0-dev03.txt2
-rw-r--r--ui/ui-animation/api/public_plus_experimental_current.txt2
-rw-r--r--ui/ui-animation/api/restricted_0.1.0-dev03.txt2
-rw-r--r--ui/ui-animation/api/restricted_current.txt2
-rw-r--r--ui/ui-animation/build.gradle2
-rw-r--r--ui/ui-animation/integration-tests/animation-demos/build.gradle2
-rw-r--r--ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloAnimationActivity.kt10
-rw-r--r--ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloGestureBasedAnimationActivity.kt8
-rw-r--r--ui/ui-animation/integration-tests/samples/build.gradle2
-rw-r--r--ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt8
-rw-r--r--ui/ui-core/api/0.1.0-dev03.txt23
-rw-r--r--ui/ui-core/api/current.txt23
-rw-r--r--ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt23
-rw-r--r--ui/ui-core/api/public_plus_experimental_current.txt23
-rw-r--r--ui/ui-core/api/restricted_0.1.0-dev03.txt23
-rw-r--r--ui/ui-core/api/restricted_current.txt23
-rw-r--r--ui/ui-core/integration-tests/samples/build.gradle2
-rw-r--r--ui/ui-core/src/main/java/androidx/ui/focus/FocusState.kt56
-rw-r--r--ui/ui-foundation/build.gradle2
-rw-r--r--ui/ui-foundation/integration-tests/foundation-demos/build.gradle2
-rw-r--r--ui/ui-foundation/integration-tests/samples/build.gradle2
-rw-r--r--ui/ui-framework/api/0.1.0-dev03.txt20
-rw-r--r--ui/ui-framework/api/current.txt20
-rw-r--r--ui/ui-framework/api/public_plus_experimental_0.1.0-dev03.txt20
-rw-r--r--ui/ui-framework/api/public_plus_experimental_current.txt20
-rw-r--r--ui/ui-framework/api/restricted_0.1.0-dev03.txt20
-rw-r--r--ui/ui-framework/api/restricted_current.txt20
-rw-r--r--ui/ui-framework/build.gradle2
-rw-r--r--ui/ui-framework/integration-tests/framework-demos/build.gradle2
-rw-r--r--ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml9
-rw-r--r--ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/focus/FocusableActivity.kt85
-rw-r--r--ui/ui-framework/integration-tests/samples/build.gradle2
-rw-r--r--ui/ui-framework/src/main/java/androidx/ui/focus/Focusable.kt106
-rw-r--r--ui/ui-layout/build.gradle2
-rw-r--r--ui/ui-layout/integration-tests/layout-demos/build.gradle2
-rw-r--r--ui/ui-layout/integration-tests/samples/build.gradle2
-rw-r--r--ui/ui-material/build.gradle2
-rw-r--r--ui/ui-material/integration-tests/material-demos/build.gradle2
-rw-r--r--ui/ui-material/integration-tests/material-studies/build.gradle2
-rw-r--r--ui/ui-material/integration-tests/samples/build.gradle2
-rw-r--r--ui/ui-material/src/main/java/androidx/ui/material/ripple/DefaultRippleEffect.kt1
-rw-r--r--ui/ui-platform/api/0.1.0-dev03.txt28
-rw-r--r--ui/ui-platform/api/current.txt28
-rw-r--r--ui/ui-platform/api/public_plus_experimental_0.1.0-dev03.txt28
-rw-r--r--ui/ui-platform/api/public_plus_experimental_current.txt28
-rw-r--r--ui/ui-platform/api/restricted_0.1.0-dev03.txt28
-rw-r--r--ui/ui-platform/api/restricted_current.txt28
-rw-r--r--ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt313
-rw-r--r--ui/ui-platform/src/main/java/androidx/ui/core/focus/FocusNodeUtils.kt55
-rw-r--r--ui/ui-platform/src/test/java/androidx/ui/core/FocusNodeTest.kt112
-rw-r--r--ui/ui-platform/src/test/java/androidx/ui/core/focus/FindParentFocusNodeTest.kt91
-rw-r--r--ui/ui-platform/src/test/java/androidx/ui/core/focus/RequestFocusTest.kt405
-rw-r--r--ui/ui-test/build.gradle2
-rw-r--r--ui/ui-text/build.gradle2
-rw-r--r--ui/ui-text/integration-tests/samples/build.gradle2
-rw-r--r--ui/ui-text/integration-tests/text-demos/build.gradle2
-rw-r--r--ui/ui-tooling/build.gradle2
-rw-r--r--ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt15
-rw-r--r--ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/SimpleComposablePreview.kt10
-rw-r--r--ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt12
-rw-r--r--ui/ui-vector/build.gradle2
-rw-r--r--work/workmanager-lint/src/test/java/androidx/work/lint/BadConfigurationProviderTest.kt6
-rw-r--r--work/workmanager-lint/src/test/java/androidx/work/lint/Lint.kt33
-rw-r--r--work/workmanager-lint/src/test/java/androidx/work/lint/PeriodicEnqueueIssueDetectorTest.kt10
-rw-r--r--work/workmanager-lint/src/test/java/androidx/work/lint/RemoveWorkManagerInitializerDetectorTest.kt8
100 files changed, 2331 insertions, 260 deletions
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index ff11c68ae82..b3a054b2f5e 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -114,11 +114,6 @@ class AndroidXPlugin : Plugin<Project> {
if (project.isRoot) {
project.configureRootProject()
}
- // Compose-compiler can't have the standard library plugins applied to mark it a library
- // to trigger e.g. build-info generation. See b/142323149.
- if (project.name == "compose-compiler") {
- project.addCreateLibraryBuildInfoFileTask(androidXExtension)
- }
project.configureJacoco()
diff --git a/buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt b/buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt
index e6da539d2d7..676de5786c6 100644
--- a/buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt
@@ -68,7 +68,10 @@ open class CreateLibraryBuildInfoFileTask : DefaultTask() {
return library?.mavenGroup?.requireSameVersion ?: false
}
- private fun getCommitShaAtHead(): String {
+ /* For androidx release notes, the most common use case is to track and publish the last sha
+ * of the build that is released. Thus, we use frameworks/support to get the sha
+ */
+ private fun getFrameworksSupportCommitShaAtHead(): String {
val commitList: List<Commit> = GitClientImpl(project.rootDir).getGitLog(
GitCommitRange(
fromExclusive = "",
@@ -76,7 +79,7 @@ open class CreateLibraryBuildInfoFileTask : DefaultTask() {
n = 1
),
keepMerges = true,
- fullProjectDir = project.projectDir
+ fullProjectDir = getSupportRoot(project)
)
return commitList.first().sha
}
@@ -108,7 +111,7 @@ open class CreateLibraryBuildInfoFileTask : DefaultTask() {
libraryBuildInfoFile.groupId = project.group.toString()
libraryBuildInfoFile.version = project.version.toString()
libraryBuildInfoFile.path = getProjectSpecificDirectory()
- libraryBuildInfoFile.sha = getCommitShaAtHead()
+ libraryBuildInfoFile.sha = getFrameworksSupportCommitShaAtHead()
libraryBuildInfoFile.groupIdRequiresSameVersion = requiresSameVersion()
val libraryDependencies = ArrayList<LibraryBuildInfoFile.Dependency>()
val checks = ArrayList<LibraryBuildInfoFile.Check>()
diff --git a/camera/camera-view/build.gradle b/camera/camera-view/build.gradle
index bef2a184263..1d648232bed 100644
--- a/camera/camera-view/build.gradle
+++ b/camera/camera-view/build.gradle
@@ -40,6 +40,8 @@ dependencies {
androidTestImplementation(ANDROIDX_TEST_RULES)
androidTestImplementation(TRUTH)
androidTestImplementation(project(":camera:camera-camera2"))
+ androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
+ androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it's own MockMaker
}
android {
defaultConfig {
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceTextureReleaseBlockingListenerTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceTextureReleaseBlockingListenerTest.java
index f49fa9eaaf3..2ae3a29797b 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceTextureReleaseBlockingListenerTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/SurfaceTextureReleaseBlockingListenerTest.java
@@ -20,15 +20,19 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.view.TextureView;
-import androidx.annotation.RequiresApi;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import com.google.common.util.concurrent.ListenableFuture;
@@ -59,8 +63,12 @@ public class SurfaceTextureReleaseBlockingListenerTest {
new SurfaceTextureReleaseBlockingListener(mTextureView);
}
- // isReleased() exists only on 23 and above. Is private prior to 26
- @RequiresApi(23)
+ /**
+ * {@link SurfaceTexture#isReleased()} exists only on 23 and above. Is private prior to 26.
+ * @see #surfaceIsValid_ifOnlyReleasedByTextureView22below() for equivalent test on API 22 and
+ * below
+ */
+ @SdkSuppress(minSdkVersion = 23)
@Test
public void surfaceIsValid_ifOnlyReleasedByTextureView() {
SurfaceTexture surfaceTexture = new SurfaceTexture(0);
@@ -74,8 +82,30 @@ public class SurfaceTextureReleaseBlockingListenerTest {
assertFalse(surfaceTexture.isReleased());
}
- // isReleased() exists only on 23 and above. Is private prior to 26
- @RequiresApi(23)
+ /**
+ * {@link SurfaceTexture#isReleased()} exists only on 23 and above. Is private prior to 26.
+ * @see #surfaceIsValid_ifOnlyReleasedByTextureView() for equivalent test on API 23 and above
+ */
+ // See equivalent test for API >= 23 in SurfaceTextureReleaseBlockingListenerAndroidTest
+ @SdkSuppress(maxSdkVersion = 22)
+ @Test
+ public void surfaceIsValid_ifOnlyReleasedByTextureView22below() {
+ SurfaceTexture surfaceTexture = mock(SurfaceTexture.class);
+ mSurfaceTextureReleaseBlockingListener.setSurfaceTextureSafely(surfaceTexture,
+ mSurfaceReleaseFuture);
+
+ // Simulate the TextureView destroying the SurfaceTexture
+ mSurfaceTextureReleaseBlockingListener.onSurfaceTextureDestroyed(surfaceTexture);
+
+ verify(surfaceTexture, never()).release();
+ }
+
+ /**
+ * {@link SurfaceTexture#isReleased()} exists only on 23 and above. Is private prior to 26.
+ * @see #surfaceIsValid_ifOnlyReleasedBySurfaceReleaseFuture22below() for equivalent test on API
+ * 22 and below
+ */
+ @SdkSuppress(minSdkVersion = 23)
@Test
public void surfaceIsValid_ifOnlyReleasedBySurfaceReleaseFuture() {
SurfaceTexture surfaceTexture = new SurfaceTexture(0);
@@ -91,8 +121,33 @@ public class SurfaceTextureReleaseBlockingListenerTest {
mSurfaceTextureReleaseBlockingListener.onSurfaceTextureDestroyed(surfaceTexture);
}
- // isReleased() exists only on 23 and above. Is private prior to 26
- @RequiresApi(23)
+ /**
+ * {@link SurfaceTexture#isReleased()} exists only on 23 and above. Is private prior to 26.
+ * @see #surfaceIsValid_ifOnlyReleasedBySurfaceReleaseFuture() for equivalent test on API 23
+ * and above
+ */
+ @SdkSuppress(maxSdkVersion = 22)
+ @Test
+ public void surfaceIsValid_ifOnlyReleasedBySurfaceReleaseFuture22below() {
+ SurfaceTexture surfaceTexture = mock(SurfaceTexture.class);
+ surfaceTexture.detachFromGLContext();
+ mSurfaceTextureReleaseBlockingListener.setSurfaceTextureSafely(surfaceTexture,
+ mSurfaceReleaseFuture);
+
+ mCompleter.set(null);
+
+ verify(surfaceTexture, never()).release();
+
+ // TODO(b/144878737) Remove when SurfaceTexture null dereference issue fixed
+ mSurfaceTextureReleaseBlockingListener.onSurfaceTextureDestroyed(surfaceTexture);
+ }
+
+ /**
+ * {@link SurfaceTexture#isReleased()} exists only on 23 and above. Is private prior to 26.
+ * @see #surfaceIsInvalid_ifReleasedByBothTextureViewAndSurfaceReleaseFuture22below() for
+ * equivalent test on API 22 and below
+ */
+ @SdkSuppress(minSdkVersion = 23)
@Test
public void surfaceIsInvalid_ifReleasedByBothTextureViewAndSurfaceReleaseFuture() {
SurfaceTexture surfaceTexture = new SurfaceTexture(0);
@@ -107,6 +162,26 @@ public class SurfaceTextureReleaseBlockingListenerTest {
assertTrue(surfaceTexture.isReleased());
}
+ /**
+ * {@link SurfaceTexture#isReleased()} exists only on 23 and above. Is private prior to 26.
+ * @see #surfaceIsInvalid_ifReleasedByBothTextureViewAndSurfaceReleaseFuture() for equivalent
+ * test on API 23 and above
+ */
+ @SdkSuppress(maxSdkVersion = 22)
+ @Test
+ public void surfaceIsInvalid_ifReleasedByBothTextureViewAndSurfaceReleaseFuture22below() {
+ SurfaceTexture surfaceTexture = mock(SurfaceTexture.class);
+ surfaceTexture.detachFromGLContext();
+ mSurfaceTextureReleaseBlockingListener.setSurfaceTextureSafely(surfaceTexture,
+ mSurfaceReleaseFuture);
+
+ // Simulate the TextureView destroying the SurfaceTexture
+ mSurfaceTextureReleaseBlockingListener.onSurfaceTextureDestroyed(surfaceTexture);
+ mCompleter.set(null);
+
+ verify(surfaceTexture, atLeastOnce()).release();
+ }
+
@Test
public void setSurfaceTextureSafely_callsSetSurfaceTextureOnTextureView() {
SurfaceTexture surfaceTexture = new SurfaceTexture(0);
diff --git a/compose/compose-compiler/build.gradle b/compose/compose-compiler/build.gradle
index 5a74087fb6a..d2f1f32f324 100644
--- a/compose/compose-compiler/build.gradle
+++ b/compose/compose-compiler/build.gradle
@@ -27,37 +27,30 @@ buildscript {
}
plugins {
+ id("java")
id("AndroidXPlugin")
}
apply plugin: 'org.anarres.jarjar'
-configurations {
- jarFiles
-}
-
-// This dependency isn't seen by Jetpad because that artifact declares Publish.NONE
-// This dependency doesn't appear in the .pom because the dependency is of type jarFile
dependencies {
- jarFiles(project(":compose:compose-compiler-hosted")) {
- transitive = false
- }
+ compileOnly(project(":compose:compose-compiler-hosted"))
}
jarjar.repackage('embeddedPlugin') {
destinationName "compose-compiler.jar"
- from configurations.jarFiles
+ from configurations.compileClasspath
classRename 'com.intellij.**', 'org.jetbrains.kotlin.com.intellij.@1'
}
configurations {
- embeddablePlugin
-}
-
-artifacts {
- embeddablePlugin(embeddedPlugin.destinationPath) {
- name "compose-compiler"
- type "jar"
+ // replace the standard jar with the one built by 'jarjar.repackage' in both api and runtime variants
+ apiElements.outgoing.artifacts.clear()
+ apiElements.outgoing.artifact(embeddedPlugin.destinationPath) {
+ builtBy embeddedPlugin
+ }
+ runtimeElements.outgoing.artifacts.clear()
+ runtimeElements.outgoing.artifact(embeddedPlugin.destinationPath) {
builtBy embeddedPlugin
}
}
diff --git a/compose/compose-runtime/compose-runtime-benchmark/build.gradle b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
index c04ad252955..44d34fed2d0 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/build.gradle
+++ b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
@@ -45,7 +45,7 @@ android {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
androidTestImplementation(project(":compose:compose-runtime"))
androidTestImplementation(project(":ui:ui-core"))
diff --git a/compose/compose-runtime/integration-tests/samples/build.gradle b/compose/compose-runtime/integration-tests/samples/build.gradle
index 547e956f5d0..dad34379624 100644
--- a/compose/compose-runtime/integration-tests/samples/build.gradle
+++ b/compose/compose-runtime/integration-tests/samples/build.gradle
@@ -24,7 +24,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/core/core/api/restricted_1.3.0-alpha01.txt b/core/core/api/restricted_1.3.0-alpha01.txt
index 9350f1d4833..7296a86d950 100644
--- a/core/core/api/restricted_1.3.0-alpha01.txt
+++ b/core/core/api/restricted_1.3.0-alpha01.txt
@@ -1932,11 +1932,11 @@ package androidx.core.util {
method public static void buildShortClassTag(Object!, StringBuilder!);
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class LogWriter extends java.io.Writer {
- ctor public LogWriter(String!);
- method public void close();
- method public void flush();
- method public void write(char[]!, int, int);
+ @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS) public class LogWriter extends java.io.Writer {
+ ctor @Deprecated public LogWriter(String!);
+ method @Deprecated public void close();
+ method @Deprecated public void flush();
+ method @Deprecated public void write(char[]!, int, int);
}
public class ObjectsCompat {
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 9350f1d4833..7296a86d950 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1932,11 +1932,11 @@ package androidx.core.util {
method public static void buildShortClassTag(Object!, StringBuilder!);
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class LogWriter extends java.io.Writer {
- ctor public LogWriter(String!);
- method public void close();
- method public void flush();
- method public void write(char[]!, int, int);
+ @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS) public class LogWriter extends java.io.Writer {
+ ctor @Deprecated public LogWriter(String!);
+ method @Deprecated public void close();
+ method @Deprecated public void flush();
+ method @Deprecated public void write(char[]!, int, int);
}
public class ObjectsCompat {
diff --git a/core/core/src/main/java/androidx/core/util/LogWriter.java b/core/core/src/main/java/androidx/core/util/LogWriter.java
index 435965948f0..bb3bcd25899 100644
--- a/core/core/src/main/java/androidx/core/util/LogWriter.java
+++ b/core/core/src/main/java/androidx/core/util/LogWriter.java
@@ -16,7 +16,7 @@
package androidx.core.util;
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.TESTS;
import android.util.Log;
@@ -25,11 +25,11 @@ import androidx.annotation.RestrictTo;
import java.io.Writer;
/**
- * Helper for accessing features in {@link android.util.LogWriter}.
- *
* @hide
+ * @deprecated Copied to use sites. Do not use.
*/
-@RestrictTo(LIBRARY_GROUP_PREFIX)
+@RestrictTo(TESTS) // Prevent new users.
+@Deprecated
public class LogWriter extends Writer {
private final String mTag;
private StringBuilder mBuilder = new StringBuilder(128);
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
index 3c35d639510..39c2cd76538 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
@@ -20,7 +20,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.core.util.LogWriter;
import androidx.lifecycle.Lifecycle;
import java.io.PrintWriter;
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 9b29be47bea..6eb46f5e5fe 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -46,7 +46,6 @@ import androidx.annotation.RestrictTo;
import androidx.annotation.StringRes;
import androidx.collection.ArraySet;
import androidx.core.os.CancellationSignal;
-import androidx.core.util.LogWriter;
import androidx.fragment.R;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/LogWriter.java b/fragment/fragment/src/main/java/androidx/fragment/app/LogWriter.java
new file mode 100644
index 00000000000..91418cb3ccc
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/LogWriter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 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 androidx.fragment.app;
+
+import android.util.Log;
+
+import java.io.Writer;
+
+final class LogWriter extends Writer {
+ private final String mTag;
+ private StringBuilder mBuilder = new StringBuilder(128);
+
+ /**
+ * Create a new Writer that sends to the log with the given tag.
+ */
+ LogWriter(String tag) {
+ mTag = tag;
+ }
+
+ @Override public void close() {
+ flushBuilder();
+ }
+
+ @Override public void flush() {
+ flushBuilder();
+ }
+
+ @Override public void write(char[] buf, int offset, int count) {
+ for(int i = 0; i < count; i++) {
+ char c = buf[offset + i];
+ if ( c == '\n') {
+ flushBuilder();
+ }
+ else {
+ mBuilder.append(c);
+ }
+ }
+ }
+
+ private void flushBuilder() {
+ if (mBuilder.length() > 0) {
+ Log.d(mTag, mBuilder.toString());
+ mBuilder.delete(0, mBuilder.length());
+ }
+ }
+}
diff --git a/jetifier/jetifier/migration.config b/jetifier/jetifier/migration.config
index ee4583867e5..6496fe6fc21 100644
--- a/jetifier/jetifier/migration.config
+++ b/jetifier/jetifier/migration.config
@@ -425,6 +425,10 @@
"to": "ignore"
},
{
+ "from": "androidx/fragment/app/LogWriter(.*)",
+ "to": "ignore"
+ },
+ {
"from": "androidx/gridlayout/widget/GridLayout(.*)",
"to": "ignore"
},
diff --git a/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java b/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
index f48ba8ec4b8..46c354dc4cb 100644
--- a/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
+++ b/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
@@ -87,48 +87,66 @@ import java.util.Locale;
* internally create a MediaControlView instance and handle all the commands from buttons inside
* MediaControlView. It is also possible to create a MediaControlView programmatically and add it
* to a custom video view. For more information, refer to {@link VideoView}.
- *
- * By default, the buttons inside MediaControlView will not visible unless the corresponding
+ * <p>
+ * By default, the buttons inside MediaControlView will not be visible unless the corresponding
* {@link SessionCommand} is marked as allowed. For more details, refer to {@link MediaSession}.
* <p>
- * <em> UI transitions : </em>
+ * <h3>UI transitions</h3>
* Currently, MediaControlView animates UI transitions between three different modes: full,
- * progress-bar only, and none. Full mode is where all the views are visible; Progress-bar only mode
- * is where only the progress bar is visible and the title, transport controls and other icons are
- * hidden; None mode is where all views are gone. The default interval between each mode is 2000ms,
- * but it can be customized by using {@link VideoView#setMediaControlView(MediaControlView, long)}.
+ * progress-bar only, and none.
+ * <ul>
+ * <li><b>Full</b> mode is where all the views are visible
+ * <li><b>Progress-bar only</b> mode is where only the progress bar is visible and the title,
+ * transport controls and other icons are hidden
+ * <li><b>None</b> mode is where all views are gone.
+ * </ul>
+ * The default interval between each mode is 2000ms, but it can be customized by using
+ * {@link VideoView#setMediaControlView(MediaControlView, long)}.
+ * <p>
* Transitions occur based on the following logic:
- * 1) In Full mode
- * a) If a touch/trackball event is received, will transition to None mode.
- * b) If a touch/trackball event is not received, will transition to Progress-bar only mode
- * after the interval.
- * 2) In Progress-bar only mode
- * a) If a touch/trackball event is received, will transition to Full mode.
- * b) If a touch/trackball event is not received, will transition to None mode after the
+ * <ol>
+ * <li> In Full mode
+ * <ul>
+ * <li>If a touch/trackball event is received, will transition to None mode.
+ * <li>If a touch/trackball event is not received, will transition to Progress-bar only mode
+ * after the interval.
+ * </ul>
+ * <li> In Progress-bar only mode
+ * <ul>
+ * <li>If a touch/trackball event is received, will transition to Full mode.
+ * <li>If a touch/trackball event is not received, will transition to None mode after the
* interval.
- * 3) In None mode
- * a) If a touch/trackball event is received, will transition to Full mode.
- * 4) While animating, all touch/trackball event will be ignored.
- *
+ * </ul>
+ * <li> In None mode, if a touch/trackball event is received, will transition to Full mode.
+ * </ol>
+ * While animating, all touch/trackball event will be ignored.
* <p>
- * <em> Customization : </em>
+ * <h3>Customization</h3>
* In addition, the following customizations are supported:
- * 1) Set focus to the play/pause button by calling {@link #requestPlayButtonFocus()}.
- * 2) Set full screen behavior by calling {@link #setOnFullScreenListener(OnFullScreenListener)}
+ * <ul>
+ * <li>Set focus to the play/pause button by calling {@link #requestPlayButtonFocus()}.
+ * <li>Set full screen behavior by calling {@link #setOnFullScreenListener(OnFullScreenListener)}.
+ * Calling this method will also show the full screen button.
+ * </ul>
* <p>
- * <em> Displaying metadata : </em>
+ * <h3>Displaying metadata</h3>
* MediaControlView supports displaying metadata by calling
* {@link MediaItem#setMetadata(MediaMetadata)}.
- *
- * Metadata display is different for two different media types: music, and non-music.
- * For music, the following metadata are supported:
- * {@link MediaMetadata#METADATA_KEY_TITLE}, {@link MediaMetadata#METADATA_KEY_ARTIST},
- * and {@link MediaMetadata#METADATA_KEY_ALBUM_ART}.
- * If values for these keys are not set, the following default values will be shown, respectively:
- * {@link androidx.media2.widget.R.string#mcv2_music_title_unknown_text}
- * {@link androidx.media2.widget.R.string#mcv2_music_artist_unknown_text}
- * {@link androidx.media2.widget.R.drawable#media2_widget_ic_default_album_image}
- *
+ * Metadata display is different for two different media types: music (sound only, with no video)
+ * and non-music (having video).
+ * <p>
+ * The following table shows the metadata displayed on VideoView and the default
+ * values assigned if the keys are not set:
+ * <table>
+ * <tr><th>Key</th><th>Default</th></tr>
+ * <tr><td>{@link MediaMetadata#METADATA_KEY_TITLE}</td>
+ * <td>{@link androidx.media2.widget.R.string#mcv2_music_title_unknown_text}</td></tr>
+ * <tr><td>{@link MediaMetadata#METADATA_KEY_ARTIST}</td>
+ * <td>{@link androidx.media2.widget.R.string#mcv2_music_artist_unknown_text}</td></tr>
+ * <tr><td>{@link MediaMetadata#METADATA_KEY_ALBUM_ART}</td>
+ * <td>{@link androidx.media2.widget.R.drawable#media2_widget_ic_default_album_image}</td></tr>
+ * </table>
+ * <p>
* For non-music, only {@link MediaMetadata#METADATA_KEY_TITLE} metadata is supported.
* If the value is not set, the following default value will be shown:
* {@link androidx.media2.widget.R.string#mcv2_non_music_title_unknown_text}
diff --git a/media2/widget/src/main/java/androidx/media2/widget/VideoView.java b/media2/widget/src/main/java/androidx/media2/widget/VideoView.java
index 34585209f0c..cc2bd92104a 100644
--- a/media2/widget/src/main/java/androidx/media2/widget/VideoView.java
+++ b/media2/widget/src/main/java/androidx/media2/widget/VideoView.java
@@ -54,67 +54,108 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
- * A high level view for media playbacks that can be integrated with either a {@link SessionPlayer}
+ * A high level view for media playback that can be integrated with either a {@link SessionPlayer}
* or a {@link MediaController}. Developers can easily implement a video rendering application
- * using this class.
+ * using this class. By default, {@link MediaControlView} is attached so the playback
+ * control buttons are displayed on top of VideoView.
* <p>
- * For simple use cases not requiring communication with {@link MediaSession}, apps need to create
- * a {@link SessionPlayer} (e.g. {@link androidx.media2.player.MediaPlayer}) and set it to this view
- * by calling {@link #setPlayer}.
- * For more advanced use cases that require {@link MediaSession} (e.g. handling media key events,
- * integrating with other MediaSession apps as Assistant), apps need to create
- * a {@link MediaController} attached to the {@link MediaSession} and set it to this view
- * by calling {@link #setMediaController}.
+ * Contents:
+ * <ol>
+ * <li><a href="UseCases">Using VideoView with SessionPlayer or MediaController</a>
+ * <li><a href="UseWithMCV">Using VideoView with MediaControlView</a>
+ * <li><a href="ViewType">Choosing a view type</a>
+ * <li><a href="LegacyVideoView">Comparison with android.widget.VideoView</a>
+ * <li><a href="DisplayMetadata">Displaying Metadata</a>
+ * </ol>
*
- * <p>
- * <em> View type can be selected : </em>
- * VideoView can render videos on top of TextureView as well as
- * SurfaceView selectively. The default is SurfaceView and it can be changed using
- * {@link #setViewType(int)} method. Using SurfaceView is recommended in most cases for saving
- * battery. TextureView might be preferred for supporting various UIs such as animation and
- * translucency.
- *
- * <p>
- * <em> Differences between {@link android.widget.VideoView android.widget.VideoView} class : </em>
- * {@link VideoView} covers and inherits the most of
- * {@link android.widget.VideoView android.widget.VideoView}'s functionality. The main differences
- * are
+ * <h3 id="UseCases">Using VideoView with SessionPlayer or MediaController</h3>
* <ul>
- * <li> {@link VideoView} does not create a {@link android.media.MediaPlayer} instance while
- * {@link android.widget.VideoView android.widget.VideoView} does. Instead, either a
- * {@link SessionPlayer} or a {@link MediaController} instance should be created externally and set
- * to {@link VideoView} using {@link #setPlayer(SessionPlayer)} or
- * {@link #setMediaController(MediaController)}, respectively.
- * <li> {@link VideoView} inherits ViewGroup and renders videos using SurfaceView and TextureView
- * selectively while {@link android.widget.VideoView android.widget.VideoView} inherits SurfaceView
- * class.
- * <li> {@link VideoView} is integrated with {@link MediaControlView} and
- * a default MediaControlView instance is attached to this VideoView by default.
- * <li> If a developer wants to attach a custom {@link MediaControlView},
- * assign the custom media control widget using {@link #setMediaControlView}.
- * <li> If {@link VideoView} communicates with {@link MediaSession} by calling
- * {@link #setMediaController(MediaController)}, it will respond to media key events.
+ * <li> For simple use cases that do not require communication with a {@link MediaSession},
+ * apps need to create a player instance that extends {@link SessionPlayer} (e.g.
+ * {@link androidx.media2.player.MediaPlayer}) and link it to this view by calling
+ * {@link #setPlayer}.
+ * <li> For more advanced use cases that require a {@link MediaSession} (e.g. handling media
+ * key events, integrating with other MediaSession apps as Assistant), apps need to create
+ * a {@link MediaController} that's attached to the {@link MediaSession} and link it to this
+ * view by calling {@link #setMediaController}.
* </ul>
*
+ * <h3 id="UseWithMCV">Using VideoView with MediaControlView</h3>
+ * {@link VideoView} is integrated with {@link MediaControlView} and a MediaControlView
+ * instance is attached to VideoView by default.
* <p>
- * <em> Displaying metadata : </em>
- * VideoView supports displaying metadata for music by calling
- * {@link MediaItem#setMetadata(MediaMetadata)}. Currently supported metadata are
- * {@link MediaMetadata#METADATA_KEY_TITLE}, {@link MediaMetadata#METADATA_KEY_ARTIST},
- * and {@link MediaMetadata#METADATA_KEY_ALBUM_ART}.
+ * If you want to attach a custom {@link MediaControlView}, assign the custom media
+ * control widget using {@link #setMediaControlView}.
+ * <p>
+ * If you don't want to use {@link MediaControlView}, set
+ * the VideoView attribute {@link androidx.media2.widget.R.attr#enableControlView} to false.
+ *
+ * <h3 id="ViewType">Choosing a view type</h3>
+ * VideoView can render videos on a TextureView or SurfaceView. The
+ * default is SurfaceView which can be changed by using the {@link #setViewType(int)} method or
+ * by setting the {@link androidx.media2.widget.R.attr#viewType} attribute in the layout file.
+ * <p> Using SurfaceView is recommended in most cases for saving battery life.
+ * TextureView might be preferred for supporting various UIs such as animation and translucency.
*
- * If values for these keys are not set, the following default values will be shown, respectively:
- * {@link androidx.media2.widget.R.string#mcv2_music_title_unknown_text}
- * {@link androidx.media2.widget.R.string#mcv2_music_artist_unknown_text}
- * {@link androidx.media2.widget.R.drawable#media2_widget_ic_default_album_image}
+ * <h3 id="LegacyVideoView">Comparison with android.widget.VideoView</h3>
+ * These are the main differences between the media2 VideoView widget and the older android widget:
+ * <ul>
+ * <li>
+ * {@link android.widget.VideoView android.widget.VideoView} creates a
+ * {@link android.media.MediaPlayer} instance internally and wraps playback APIs around it.
+ * <p>
+ * {@link VideoView androidx.media2.widget.VideoView} does not create a player instance
+ * internally. Instead, either a {@link SessionPlayer} or a {@link MediaController} instance
+ * should be created externally and link to {@link VideoView} using
+ * {@link #setPlayer(SessionPlayer)} or {@link #setMediaController(MediaController)},
+ * respectively.
+ * <li>
+ * {@link android.widget.VideoView android.widget.VideoView} inherits from the SurfaceView
+ * class.
+ * <p>
+ * {@link VideoView androidx.media2.widget.VideoView} inherits from ViewGroup and can render
+ * videos using SurfaceView or TextureView, depending on your choice.
+ * <li>
+ * A {@link VideoView} can respond to media key events if you call {@link #setMediaController}
+ * to link it to a {@link MediaController} that's connected to an active {@link MediaSession}.
+ * </ul>
*
+ * <h3 id="DisplayMetadata">Displaying Metadata</h3>
+ * When you play music only (sound with no video), VideoView can display album art and other
+ * metadata by calling {@link MediaItem#setMetadata(MediaMetadata)}.
+ * The following table shows the metadata displayed by the VideoView, and the default values
+ * assigned if the keys are not set:
+ * <table>
+ * <tr><th>Key</th><th>Default</th></tr>
+ * <tr><td>{@link MediaMetadata#METADATA_KEY_TITLE}</td>
+ * <td>{@link androidx.media2.widget.R.string#mcv2_music_title_unknown_text}</td></tr>
+ * <tr><td>{@link MediaMetadata#METADATA_KEY_ARTIST}</td>
+ * <td>{@link androidx.media2.widget.R.string#mcv2_music_artist_unknown_text}</td></tr>
+ * <tr><td>{@link MediaMetadata#METADATA_KEY_ALBUM_ART}</td>
+ * <td>{@link androidx.media2.widget.R.drawable#media2_widget_ic_default_album_image}</td></tr>
+ * </table>
* <p>
* Note: VideoView does not retain its full state when going into the background. In particular, it
- * does not restore the current play state, play position, selected tracks. Applications should save
- * and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and
+ * does not save, and does not restore the current play state, play position, selected tracks.
+ * Applications should save and restore these on their own in
+ * {@link android.app.Activity#onSaveInstanceState} and
* {@link android.app.Activity#onRestoreInstanceState}.
- * {@link androidx.media2.widget.R.attr#enableControlView}
- * {@link androidx.media2.widget.R.attr#viewType}
+ * <p> Attributes :
+ * <ul>
+ * <li> {@link androidx.media2.widget.R.attr#enableControlView}
+ * <li> {@link androidx.media2.widget.R.attr#viewType}
+ * </ul>
+ * <p> Example of attributes for a VideoView with TextureView and no attached control view:
+ * <pre> {@code
+ * <androidx.media2.widget.VideoView
+ * android:id="@+id/video_view"
+ * widget:enableControlView="false"
+ * widget:viewType="textureView"
+ * />}</pre>
+ *
+ * @see MediaControlView
+ * @see SessionPlayer
+ * @see MediaController
*/
public class VideoView extends SelectiveLayout {
@IntDef({
diff --git a/ui/integration-tests/benchmark/build.gradle b/ui/integration-tests/benchmark/build.gradle
index 0c0f31d5222..f04b08cb9b5 100644
--- a/ui/integration-tests/benchmark/build.gradle
+++ b/ui/integration-tests/benchmark/build.gradle
@@ -30,7 +30,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation project(":benchmark:benchmark-junit4")
implementation project(":compose:compose-runtime")
diff --git a/ui/integration-tests/test/build.gradle b/ui/integration-tests/test/build.gradle
index aa9a0e2f471..8079c7eb20a 100644
--- a/ui/integration-tests/test/build.gradle
+++ b/ui/integration-tests/test/build.gradle
@@ -30,7 +30,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-android-view/build.gradle b/ui/ui-android-view/build.gradle
index 9888349ee8e..62375be33cd 100644
--- a/ui/ui-android-view/build.gradle
+++ b/ui/ui-android-view/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_COROUTINES_ANDROID)
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-animation-core/api/0.1.0-dev03.txt b/ui/ui-animation-core/api/0.1.0-dev03.txt
index c790e823674..227080eafe8 100644
--- a/ui/ui-animation-core/api/0.1.0-dev03.txt
+++ b/ui/ui-animation-core/api/0.1.0-dev03.txt
@@ -210,6 +210,22 @@ package androidx.animation {
method public androidx.animation.DurationBasedAnimation<T> build$lintWithKotlin();
}
+ public final class Spring {
+ ctor public Spring();
+ field public static final androidx.animation.Spring.Companion! Companion;
+ field public static final float DampingRatioHighBouncy = 0.2f;
+ field public static final float DampingRatioLowBouncy = 0.75f;
+ field public static final float DampingRatioMediumBouncy = 0.5f;
+ field public static final float DampingRatioNoBouncy = 1.0f;
+ field public static final float StiffnessHigh = 10000.0f;
+ field public static final float StiffnessLow = 200.0f;
+ field public static final float StiffnessMedium = 1500.0f;
+ field public static final float StiffnessVeryLow = 50.0f;
+ }
+
+ public static final class Spring.Companion {
+ }
+
public final class SpringSimulationKt {
ctor public SpringSimulationKt();
}
@@ -224,7 +240,7 @@ package androidx.animation {
}
public final class TransitionAnimation<T> implements androidx.animation.TransitionState {
- ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock);
+ ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock, T? initState);
method public operator <T> T! get(androidx.animation.PropKey<T> propKey);
method public kotlin.jvm.functions.Function1<T,kotlin.Unit>? getOnStateChangeFinished();
method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnUpdate();
@@ -239,8 +255,6 @@ package androidx.animation {
public final class TransitionDefinition<T> {
ctor public TransitionDefinition();
- method public androidx.animation.TransitionAnimation<T> createAnimation();
- method public androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.AnimationClockObservable clock);
method public androidx.animation.TransitionState getStateFor(T? name);
method public void snapTransition(kotlin.Pair<? extends T,? extends T>![] fromToPairs, T? nextState = null);
method public void state(T? name, kotlin.jvm.functions.Function1<? super androidx.animation.MutableTransitionState,kotlin.Unit> init);
@@ -250,6 +264,8 @@ package androidx.animation {
public final class TransitionDefinitionKt {
ctor public TransitionDefinitionKt();
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>);
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>, androidx.animation.AnimationClockObservable clock, T? initState = null);
method public static <T> androidx.animation.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.animation.TransitionDefinition<T>,kotlin.Unit> init);
}
diff --git a/ui/ui-animation-core/api/api_lint.ignore b/ui/ui-animation-core/api/api_lint.ignore
index 523c32099d5..413d35162a2 100644
--- a/ui/ui-animation-core/api/api_lint.ignore
+++ b/ui/ui-animation-core/api/api_lint.ignore
@@ -57,3 +57,5 @@ MissingNullability: androidx.animation.FloatPropKey#interpolate(float, float, fl
Missing nullability on method `interpolate` return
MissingNullability: androidx.animation.IntPropKey#interpolate(int, int, float):
Missing nullability on method `interpolate` return
+MissingNullability: androidx.animation.Spring#Companion:
+ Missing nullability on field `Companion` in class `class androidx.animation.Spring`
diff --git a/ui/ui-animation-core/api/current.txt b/ui/ui-animation-core/api/current.txt
index c790e823674..227080eafe8 100644
--- a/ui/ui-animation-core/api/current.txt
+++ b/ui/ui-animation-core/api/current.txt
@@ -210,6 +210,22 @@ package androidx.animation {
method public androidx.animation.DurationBasedAnimation<T> build$lintWithKotlin();
}
+ public final class Spring {
+ ctor public Spring();
+ field public static final androidx.animation.Spring.Companion! Companion;
+ field public static final float DampingRatioHighBouncy = 0.2f;
+ field public static final float DampingRatioLowBouncy = 0.75f;
+ field public static final float DampingRatioMediumBouncy = 0.5f;
+ field public static final float DampingRatioNoBouncy = 1.0f;
+ field public static final float StiffnessHigh = 10000.0f;
+ field public static final float StiffnessLow = 200.0f;
+ field public static final float StiffnessMedium = 1500.0f;
+ field public static final float StiffnessVeryLow = 50.0f;
+ }
+
+ public static final class Spring.Companion {
+ }
+
public final class SpringSimulationKt {
ctor public SpringSimulationKt();
}
@@ -224,7 +240,7 @@ package androidx.animation {
}
public final class TransitionAnimation<T> implements androidx.animation.TransitionState {
- ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock);
+ ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock, T? initState);
method public operator <T> T! get(androidx.animation.PropKey<T> propKey);
method public kotlin.jvm.functions.Function1<T,kotlin.Unit>? getOnStateChangeFinished();
method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnUpdate();
@@ -239,8 +255,6 @@ package androidx.animation {
public final class TransitionDefinition<T> {
ctor public TransitionDefinition();
- method public androidx.animation.TransitionAnimation<T> createAnimation();
- method public androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.AnimationClockObservable clock);
method public androidx.animation.TransitionState getStateFor(T? name);
method public void snapTransition(kotlin.Pair<? extends T,? extends T>![] fromToPairs, T? nextState = null);
method public void state(T? name, kotlin.jvm.functions.Function1<? super androidx.animation.MutableTransitionState,kotlin.Unit> init);
@@ -250,6 +264,8 @@ package androidx.animation {
public final class TransitionDefinitionKt {
ctor public TransitionDefinitionKt();
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>);
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>, androidx.animation.AnimationClockObservable clock, T? initState = null);
method public static <T> androidx.animation.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.animation.TransitionDefinition<T>,kotlin.Unit> init);
}
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev03.txt b/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev03.txt
index c790e823674..227080eafe8 100644
--- a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev03.txt
+++ b/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev03.txt
@@ -210,6 +210,22 @@ package androidx.animation {
method public androidx.animation.DurationBasedAnimation<T> build$lintWithKotlin();
}
+ public final class Spring {
+ ctor public Spring();
+ field public static final androidx.animation.Spring.Companion! Companion;
+ field public static final float DampingRatioHighBouncy = 0.2f;
+ field public static final float DampingRatioLowBouncy = 0.75f;
+ field public static final float DampingRatioMediumBouncy = 0.5f;
+ field public static final float DampingRatioNoBouncy = 1.0f;
+ field public static final float StiffnessHigh = 10000.0f;
+ field public static final float StiffnessLow = 200.0f;
+ field public static final float StiffnessMedium = 1500.0f;
+ field public static final float StiffnessVeryLow = 50.0f;
+ }
+
+ public static final class Spring.Companion {
+ }
+
public final class SpringSimulationKt {
ctor public SpringSimulationKt();
}
@@ -224,7 +240,7 @@ package androidx.animation {
}
public final class TransitionAnimation<T> implements androidx.animation.TransitionState {
- ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock);
+ ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock, T? initState);
method public operator <T> T! get(androidx.animation.PropKey<T> propKey);
method public kotlin.jvm.functions.Function1<T,kotlin.Unit>? getOnStateChangeFinished();
method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnUpdate();
@@ -239,8 +255,6 @@ package androidx.animation {
public final class TransitionDefinition<T> {
ctor public TransitionDefinition();
- method public androidx.animation.TransitionAnimation<T> createAnimation();
- method public androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.AnimationClockObservable clock);
method public androidx.animation.TransitionState getStateFor(T? name);
method public void snapTransition(kotlin.Pair<? extends T,? extends T>![] fromToPairs, T? nextState = null);
method public void state(T? name, kotlin.jvm.functions.Function1<? super androidx.animation.MutableTransitionState,kotlin.Unit> init);
@@ -250,6 +264,8 @@ package androidx.animation {
public final class TransitionDefinitionKt {
ctor public TransitionDefinitionKt();
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>);
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>, androidx.animation.AnimationClockObservable clock, T? initState = null);
method public static <T> androidx.animation.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.animation.TransitionDefinition<T>,kotlin.Unit> init);
}
diff --git a/ui/ui-animation-core/api/public_plus_experimental_current.txt b/ui/ui-animation-core/api/public_plus_experimental_current.txt
index c790e823674..227080eafe8 100644
--- a/ui/ui-animation-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-animation-core/api/public_plus_experimental_current.txt
@@ -210,6 +210,22 @@ package androidx.animation {
method public androidx.animation.DurationBasedAnimation<T> build$lintWithKotlin();
}
+ public final class Spring {
+ ctor public Spring();
+ field public static final androidx.animation.Spring.Companion! Companion;
+ field public static final float DampingRatioHighBouncy = 0.2f;
+ field public static final float DampingRatioLowBouncy = 0.75f;
+ field public static final float DampingRatioMediumBouncy = 0.5f;
+ field public static final float DampingRatioNoBouncy = 1.0f;
+ field public static final float StiffnessHigh = 10000.0f;
+ field public static final float StiffnessLow = 200.0f;
+ field public static final float StiffnessMedium = 1500.0f;
+ field public static final float StiffnessVeryLow = 50.0f;
+ }
+
+ public static final class Spring.Companion {
+ }
+
public final class SpringSimulationKt {
ctor public SpringSimulationKt();
}
@@ -224,7 +240,7 @@ package androidx.animation {
}
public final class TransitionAnimation<T> implements androidx.animation.TransitionState {
- ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock);
+ ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock, T? initState);
method public operator <T> T! get(androidx.animation.PropKey<T> propKey);
method public kotlin.jvm.functions.Function1<T,kotlin.Unit>? getOnStateChangeFinished();
method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnUpdate();
@@ -239,8 +255,6 @@ package androidx.animation {
public final class TransitionDefinition<T> {
ctor public TransitionDefinition();
- method public androidx.animation.TransitionAnimation<T> createAnimation();
- method public androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.AnimationClockObservable clock);
method public androidx.animation.TransitionState getStateFor(T? name);
method public void snapTransition(kotlin.Pair<? extends T,? extends T>![] fromToPairs, T? nextState = null);
method public void state(T? name, kotlin.jvm.functions.Function1<? super androidx.animation.MutableTransitionState,kotlin.Unit> init);
@@ -250,6 +264,8 @@ package androidx.animation {
public final class TransitionDefinitionKt {
ctor public TransitionDefinitionKt();
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>);
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>, androidx.animation.AnimationClockObservable clock, T? initState = null);
method public static <T> androidx.animation.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.animation.TransitionDefinition<T>,kotlin.Unit> init);
}
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev03.txt b/ui/ui-animation-core/api/restricted_0.1.0-dev03.txt
index c790e823674..227080eafe8 100644
--- a/ui/ui-animation-core/api/restricted_0.1.0-dev03.txt
+++ b/ui/ui-animation-core/api/restricted_0.1.0-dev03.txt
@@ -210,6 +210,22 @@ package androidx.animation {
method public androidx.animation.DurationBasedAnimation<T> build$lintWithKotlin();
}
+ public final class Spring {
+ ctor public Spring();
+ field public static final androidx.animation.Spring.Companion! Companion;
+ field public static final float DampingRatioHighBouncy = 0.2f;
+ field public static final float DampingRatioLowBouncy = 0.75f;
+ field public static final float DampingRatioMediumBouncy = 0.5f;
+ field public static final float DampingRatioNoBouncy = 1.0f;
+ field public static final float StiffnessHigh = 10000.0f;
+ field public static final float StiffnessLow = 200.0f;
+ field public static final float StiffnessMedium = 1500.0f;
+ field public static final float StiffnessVeryLow = 50.0f;
+ }
+
+ public static final class Spring.Companion {
+ }
+
public final class SpringSimulationKt {
ctor public SpringSimulationKt();
}
@@ -224,7 +240,7 @@ package androidx.animation {
}
public final class TransitionAnimation<T> implements androidx.animation.TransitionState {
- ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock);
+ ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock, T? initState);
method public operator <T> T! get(androidx.animation.PropKey<T> propKey);
method public kotlin.jvm.functions.Function1<T,kotlin.Unit>? getOnStateChangeFinished();
method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnUpdate();
@@ -239,8 +255,6 @@ package androidx.animation {
public final class TransitionDefinition<T> {
ctor public TransitionDefinition();
- method public androidx.animation.TransitionAnimation<T> createAnimation();
- method public androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.AnimationClockObservable clock);
method public androidx.animation.TransitionState getStateFor(T? name);
method public void snapTransition(kotlin.Pair<? extends T,? extends T>![] fromToPairs, T? nextState = null);
method public void state(T? name, kotlin.jvm.functions.Function1<? super androidx.animation.MutableTransitionState,kotlin.Unit> init);
@@ -250,6 +264,8 @@ package androidx.animation {
public final class TransitionDefinitionKt {
ctor public TransitionDefinitionKt();
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>);
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>, androidx.animation.AnimationClockObservable clock, T? initState = null);
method public static <T> androidx.animation.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.animation.TransitionDefinition<T>,kotlin.Unit> init);
}
diff --git a/ui/ui-animation-core/api/restricted_current.txt b/ui/ui-animation-core/api/restricted_current.txt
index c790e823674..227080eafe8 100644
--- a/ui/ui-animation-core/api/restricted_current.txt
+++ b/ui/ui-animation-core/api/restricted_current.txt
@@ -210,6 +210,22 @@ package androidx.animation {
method public androidx.animation.DurationBasedAnimation<T> build$lintWithKotlin();
}
+ public final class Spring {
+ ctor public Spring();
+ field public static final androidx.animation.Spring.Companion! Companion;
+ field public static final float DampingRatioHighBouncy = 0.2f;
+ field public static final float DampingRatioLowBouncy = 0.75f;
+ field public static final float DampingRatioMediumBouncy = 0.5f;
+ field public static final float DampingRatioNoBouncy = 1.0f;
+ field public static final float StiffnessHigh = 10000.0f;
+ field public static final float StiffnessLow = 200.0f;
+ field public static final float StiffnessMedium = 1500.0f;
+ field public static final float StiffnessVeryLow = 50.0f;
+ }
+
+ public static final class Spring.Companion {
+ }
+
public final class SpringSimulationKt {
ctor public SpringSimulationKt();
}
@@ -224,7 +240,7 @@ package androidx.animation {
}
public final class TransitionAnimation<T> implements androidx.animation.TransitionState {
- ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock);
+ ctor public TransitionAnimation(androidx.animation.TransitionDefinition<T> def, androidx.animation.AnimationClockObservable clock, T? initState);
method public operator <T> T! get(androidx.animation.PropKey<T> propKey);
method public kotlin.jvm.functions.Function1<T,kotlin.Unit>? getOnStateChangeFinished();
method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnUpdate();
@@ -239,8 +255,6 @@ package androidx.animation {
public final class TransitionDefinition<T> {
ctor public TransitionDefinition();
- method public androidx.animation.TransitionAnimation<T> createAnimation();
- method public androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.AnimationClockObservable clock);
method public androidx.animation.TransitionState getStateFor(T? name);
method public void snapTransition(kotlin.Pair<? extends T,? extends T>![] fromToPairs, T? nextState = null);
method public void state(T? name, kotlin.jvm.functions.Function1<? super androidx.animation.MutableTransitionState,kotlin.Unit> init);
@@ -250,6 +264,8 @@ package androidx.animation {
public final class TransitionDefinitionKt {
ctor public TransitionDefinitionKt();
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>);
+ method public static <T> androidx.animation.TransitionAnimation<T> createAnimation(androidx.animation.TransitionDefinition<T>, androidx.animation.AnimationClockObservable clock, T? initState = null);
method public static <T> androidx.animation.TransitionDefinition<T> transitionDefinition(kotlin.jvm.functions.Function1<? super androidx.animation.TransitionDefinition<T>,kotlin.Unit> init);
}
diff --git a/ui/ui-animation-core/integration-tests/samples/build.gradle b/ui/ui-animation-core/integration-tests/samples/build.gradle
index 35576712b2e..cc0bdb850c2 100644
--- a/ui/ui-animation-core/integration-tests/samples/build.gradle
+++ b/ui/ui-animation-core/integration-tests/samples/build.gradle
@@ -26,7 +26,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-animation-core/src/main/java/androidx/animation/Animation.kt b/ui/ui-animation-core/src/main/java/androidx/animation/Animation.kt
index 2bd4c9c1083..df591772f74 100644
--- a/ui/ui-animation-core/src/main/java/androidx/animation/Animation.kt
+++ b/ui/ui-animation-core/src/main/java/androidx/animation/Animation.kt
@@ -16,8 +16,6 @@
package androidx.animation
-import androidx.animation.Physics.Companion.DampingRatioNoBouncy
-import androidx.animation.Physics.Companion.StiffnessVeryLow
import kotlin.math.min
const val DEBUG = false
@@ -212,31 +210,19 @@ internal fun <T> Snap(): DurationBasedAnimation<T> =
Tween(0L, 0L, LinearEasing)
/**
- * [Physics] animation is in its core a spring animation. It is the default animation that the
- * animation system uses to createAnimation from [TransitionState] to [TransitionState] when no
- * animations are specified. Its configuration can be tuned via adjusting the spring parameters,
- * namely [dampingRatio] and [stiffness]. By default, [Physics] animation uses a spring with
- * [dampingRatio] = [DampingRatioNoBouncy] and [stiffness] = [StiffnessVeryLow].
+ * Physics class contains a number of recommended configurations for physics animations.
*/
-internal class Physics<T>(
- /**
- * Damping ratio of the spring. Defaults to [DampingRatioNoBouncy]
- */
- dampingRatio: Float = DampingRatioNoBouncy,
- /**
- * Stiffness of the spring. Defaults to [StiffnessVeryLow]
- */
- stiffness: Float = StiffnessVeryLow
-) : Animation<T> {
-
- // TODO: Make all of these consts public.
+// TODO: Consider making all animations public, and fold this companion object into
+// SpringAnimation.
+class Spring {
companion object {
/**
* Stiffness constant for extremely stiff spring
*/
const val StiffnessHigh = 10_000f
/**
- * Stiffness constant for medium stiff spring. This is the default stiffness for spring force.
+ * Stiffness constant for medium stiff spring. This is the default stiffness for spring
+ * force.
*/
const val StiffnessMedium = 1500f
/**
@@ -254,9 +240,9 @@ internal class Physics<T>(
*/
const val DampingRatioHighBouncy = 0.2f
/**
- * Damping ratio for a medium bouncy spring. This is also the default damping ratio for spring
- * force. Note for under-damped springs (i.e. damping ratio < 1), the lower the damping ratio,
- * the more bouncy the spring.
+ * Damping ratio for a medium bouncy spring. This is also the default damping ratio for
+ * spring force. Note for under-damped springs (i.e. damping ratio < 1), the lower the
+ * damping ratio, the more bouncy the spring.
*/
const val DampingRatioMediumBouncy = 0.5f
/**
@@ -265,12 +251,31 @@ internal class Physics<T>(
*/
const val DampingRatioLowBouncy = 0.75f
/**
- * Damping ratio for a spring with no bounciness. This damping ratio will create a critically
- * damped spring that returns to equilibrium within the shortest amount of time without
- * oscillating.
+ * Damping ratio for a spring with no bounciness. This damping ratio will create a
+ * critically damped spring that returns to equilibrium within the shortest amount of time
+ * without oscillating.
*/
const val DampingRatioNoBouncy = 1f
}
+}
+
+/**
+ * [SpringAnimation] animation is in its core a spring animation. It is the default animation that
+ * the animation system uses to createAnimation from [TransitionState] to [TransitionState] when no
+ * animations are specified. Its configuration can be tuned via adjusting the spring parameters,
+ * namely dampingRatio and stiffness. By default, [SpringAnimation] animation uses a spring with
+ * dampingRatio = [Spring.DampingRatioNoBouncy] and stiffness = [Spring.StiffnessVeryLow].
+ */
+internal class SpringAnimation<T>(
+ /**
+ * Damping ratio of the spring. Defaults to [Spring.DampingRatioNoBouncy]
+ */
+ dampingRatio: Float = Spring.DampingRatioNoBouncy,
+ /**
+ * Stiffness of the spring. Defaults to [Spring.StiffnessVeryLow]
+ */
+ stiffness: Float = Spring.StiffnessMedium
+) : Animation<T> {
private val spring = SpringSimulation(1f).also {
it.dampingRatio = dampingRatio
diff --git a/ui/ui-animation-core/src/main/java/androidx/animation/AnimationBuilder.kt b/ui/ui-animation-core/src/main/java/androidx/animation/AnimationBuilder.kt
index 505c5e046cc..48badce7d27 100644
--- a/ui/ui-animation-core/src/main/java/androidx/animation/AnimationBuilder.kt
+++ b/ui/ui-animation-core/src/main/java/androidx/animation/AnimationBuilder.kt
@@ -16,8 +16,8 @@
package androidx.animation
-import androidx.animation.Physics.Companion.DampingRatioNoBouncy
-import androidx.animation.Physics.Companion.StiffnessVeryLow
+import androidx.animation.Spring.Companion.DampingRatioNoBouncy
+import androidx.animation.Spring.Companion.StiffnessVeryLow
abstract class AnimationBuilder<T> {
internal abstract fun build(): Animation<T>
@@ -180,7 +180,7 @@ open class PhysicsBuilder<T>(
) : AnimationBuilder<T>() {
override fun build(): Animation<T> =
- Physics(dampingRatio, stiffness)
+ SpringAnimation(dampingRatio, stiffness)
}
/**
diff --git a/ui/ui-animation-core/src/main/java/androidx/animation/SpringSimulation.kt b/ui/ui-animation-core/src/main/java/androidx/animation/SpringSimulation.kt
index 8a3f6478f63..91d620f47a2 100644
--- a/ui/ui-animation-core/src/main/java/androidx/animation/SpringSimulation.kt
+++ b/ui/ui-animation-core/src/main/java/androidx/animation/SpringSimulation.kt
@@ -47,7 +47,7 @@ internal val UNSET = Float.MAX_VALUE
internal class SpringSimulation(var finalPosition: Float) {
// Natural frequency
- private var naturalFreq = Math.sqrt(Physics.StiffnessVeryLow.toDouble())
+ private var naturalFreq = Math.sqrt(Spring.StiffnessVeryLow.toDouble())
// Indicates whether the spring has been initialized
private var initialized = false
@@ -83,7 +83,7 @@ internal class SpringSimulation(var finalPosition: Float) {
*
* @return damping ratio of the spring
*/
- var dampingRatio: Float = Physics.DampingRatioNoBouncy
+ var dampingRatio: Float = Spring.DampingRatioNoBouncy
set(value) {
if (value < 0) {
throw IllegalArgumentException("Damping ratio must be non-negative")
diff --git a/ui/ui-animation-core/src/main/java/androidx/animation/TransitionAnimation.kt b/ui/ui-animation-core/src/main/java/androidx/animation/TransitionAnimation.kt
index 199bc9b5dfa..cfa72b506aa 100644
--- a/ui/ui-animation-core/src/main/java/androidx/animation/TransitionAnimation.kt
+++ b/ui/ui-animation-core/src/main/java/androidx/animation/TransitionAnimation.kt
@@ -24,7 +24,7 @@ import androidx.animation.InterruptionHandling.UNINTERRUPTIBLE
* [TransitionState]) to another. More specifically, it reads the property values out of the new
* state that it is going to, as well as the animations defined for the properties, and run these
* animations until all properties have reached their pre-defined values in the new state. When no
- * animation is specified for a property, a default [Physics] animation will be used.
+ * animation is specified for a property, a default [SpringAnimation] animation will be used.
*
* [TransitionAnimation] may be interrupted while the animation is on-going by a request to go
* to another state. [TransitionAnimation] ensures that all the animating properties preserve their
@@ -35,7 +35,8 @@ import androidx.animation.InterruptionHandling.UNINTERRUPTIBLE
*/
class TransitionAnimation<T> (
private val def: TransitionDefinition<T>,
- private val clock: AnimationClockObservable
+ private val clock: AnimationClockObservable,
+ initState: T? = null
) : TransitionState {
var onUpdate: (() -> Unit)? = null
@@ -61,10 +62,17 @@ class TransitionAnimation<T> (
// TODO("Create a more efficient code path for default only transition def")
init {
- currentState = AnimationState(def.defaultState, def.defaultState.name)
+ // If an initial state is specified in the ctor, use that instead of the default state.
+ val defaultState: StateImpl<T>
+ if (initState == null) {
+ defaultState = def.defaultState
+ } else {
+ defaultState = def.states[initState]!!
+ }
+ currentState = AnimationState(defaultState, defaultState.name)
// Need to come up with a better plan to avoid the foot gun of accidentally modifying state
- fromState = def.defaultState
- toState = def.defaultState
+ fromState = defaultState
+ toState = defaultState
}
// Interpolate current state and the new state
@@ -96,13 +104,14 @@ class TransitionAnimation<T> (
// props in each state.
}
- startAnimation()
-
fromState = AnimationState(currentState, toState.name)
toState = newState
if (DEBUG) {
Log.w("TransAnim", "Animating to new state: ${toState.name}")
}
+
+ // Start animation should be called after all the setup has been done
+ startAnimation()
}
private fun getPlayTime(): Long {
diff --git a/ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt b/ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt
index 9a85ff3cfcc..48162a815f4 100644
--- a/ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt
+++ b/ui/ui-animation-core/src/main/java/androidx/animation/TransitionDefinition.kt
@@ -23,7 +23,7 @@ import kotlin.experimental.ExperimentalTypeInference
*
* Each property involved in the states that the transition is from and to can have an animation
* associated with it. When such an animation is defined, the animation system will be using it
- * instead of the default [Physics] animation to createAnimation the value change for that property.
+ * instead of the default [SpringAnimation] animation to createAnimation the value change for that property.
*
* @sample androidx.animation.samples.TransitionSpecWith3Properties
**/
@@ -50,7 +50,7 @@ class TransitionSpec<S> internal constructor(private val fromToPairs: Array<out
/**
* The default animation to use when it wasn't explicitly provided for a property
*/
- internal var defaultAnimation: () -> Animation<Any> = { Physics() }
+ internal var defaultAnimation: () -> Animation<Any> = { SpringAnimation() }
private val propAnimation: MutableMap<PropKey<*>, Animation<*>> = mutableMapOf()
internal fun <T> getAnimationForProp(prop: PropKey<T>): Animation<T> {
@@ -79,9 +79,9 @@ class TransitionSpec<S> internal constructor(private val fromToPairs: Array<out
TweenBuilder<T>().apply(init)
/**
- * Creates a [Physics] animation, initialized with [init]
+ * Creates a [SpringAnimation] animation, initialized with [init]
*
- * @param init Initialization function for the [Physics] animation
+ * @param init Initialization function for the [SpringAnimation] animation
*/
fun <T> physics(init: PhysicsBuilder<T>.() -> Unit): AnimationBuilder<T> =
PhysicsBuilder<T>().apply(init)
@@ -198,18 +198,6 @@ class TransitionDefinition<T> {
}
/**
- * Creates a transition animation using the transition definition.
- */
- fun createAnimation() = TransitionAnimation(this, DefaultAnimationClock())
-
- /**
- * Creates a transition animation using the transition definition and the given clock.
- *
- * @param clock The clock source for animation to get frame time from.
- */
- fun createAnimation(clock: AnimationClockObservable) = TransitionAnimation(this, clock)
-
- /**
* Returns a state holder for the specific state [name]. Useful for the cases
* where we don't need actual animation to be happening like in tests.
*/
@@ -217,6 +205,23 @@ class TransitionDefinition<T> {
}
/**
+ * Creates a transition animation using the transition definition.
+ * // TODO: Ripple impl needs to pass the ambient here clock, then we can remove this function.
+ */
+fun <T> TransitionDefinition<T>.createAnimation() =
+ TransitionAnimation(this, DefaultAnimationClock())
+
+/**
+ * Creates a transition animation using the transition definition and the given clock.
+ *
+ * @param clock The clock source for animation to get frame time from.
+ */
+fun <T> TransitionDefinition<T>.createAnimation(
+ clock: AnimationClockObservable,
+ initState: T? = null
+) = TransitionAnimation(this, clock, initState)
+
+/**
* Creates a [TransitionDefinition] using the [init] function to initialize it.
*
* @param init Initialization function for the [TransitionDefinition]
diff --git a/ui/ui-animation-core/src/test/java/androidx/animation/TransitionAnimationTest.kt b/ui/ui-animation-core/src/test/java/androidx/animation/TransitionAnimationTest.kt
index bdfb631d788..a151099627a 100644
--- a/ui/ui-animation-core/src/test/java/androidx/animation/TransitionAnimationTest.kt
+++ b/ui/ui-animation-core/src/test/java/androidx/animation/TransitionAnimationTest.kt
@@ -28,7 +28,7 @@ class TransitionAnimationTest {
val clock = ManualAnimationClock(0)
val anim = TransitionAnimation(def1, clock)
anim.toState(AnimState.B)
- val physicsAnim = Physics<Float>()
+ val physicsAnim = SpringAnimation<Float>()
var playTime = 0L
do {
// Increment the time stamp until the animation finishes
@@ -43,6 +43,33 @@ class TransitionAnimationTest {
playTime += 20L
} while (anim.isRunning)
}
+
+ @Test
+ fun testInitialState() {
+ val clock = ManualAnimationClock(0)
+ val anim = TransitionAnimation(def1, clock, AnimState.C)
+ assertEquals(anim[prop1], 1000f)
+ assertEquals(anim[prop2], -250f)
+ }
+
+ @Test
+ fun testStateChangedListener() {
+ val clock = ManualAnimationClock(0)
+ val anim = TransitionAnimation(def1, clock, AnimState.C)
+ var lastState: AnimState? = null
+ anim.onStateChangeFinished = {
+ lastState = it
+ }
+ anim.toState(AnimState.A)
+ // Increment the clock by some large amount to guarantee the finish of the animation
+ clock.clockTimeMillis += 100000
+ assertEquals(AnimState.A, lastState)
+
+ anim.toState(AnimState.B)
+ // Increment the clock by some large amount to guarantee the finish of the animation
+ clock.clockTimeMillis += 100000
+ assertEquals(AnimState.B, lastState)
+ }
}
private enum class AnimState {
@@ -62,4 +89,9 @@ private val def1 = transitionDefinition {
this[prop1] = 1f
this[prop2] = -100f
}
+
+ state(AnimState.C) {
+ this[prop1] = 1000f
+ this[prop2] = -250f
+ }
} \ No newline at end of file
diff --git a/ui/ui-animation/api/0.1.0-dev03.txt b/ui/ui-animation/api/0.1.0-dev03.txt
index 113f0be5a55..e1311a33597 100644
--- a/ui/ui-animation/api/0.1.0-dev03.txt
+++ b/ui/ui-animation/api/0.1.0-dev03.txt
@@ -35,7 +35,7 @@ package androidx.ui.animation {
public final class TransitionKt {
ctor public TransitionKt();
- method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
+ method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished = null, kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
method public static boolean getTransitionsEnabled();
method public static void setTransitionsEnabled(boolean p);
}
diff --git a/ui/ui-animation/api/current.txt b/ui/ui-animation/api/current.txt
index 113f0be5a55..e1311a33597 100644
--- a/ui/ui-animation/api/current.txt
+++ b/ui/ui-animation/api/current.txt
@@ -35,7 +35,7 @@ package androidx.ui.animation {
public final class TransitionKt {
ctor public TransitionKt();
- method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
+ method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished = null, kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
method public static boolean getTransitionsEnabled();
method public static void setTransitionsEnabled(boolean p);
}
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev03.txt b/ui/ui-animation/api/public_plus_experimental_0.1.0-dev03.txt
index 113f0be5a55..e1311a33597 100644
--- a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev03.txt
+++ b/ui/ui-animation/api/public_plus_experimental_0.1.0-dev03.txt
@@ -35,7 +35,7 @@ package androidx.ui.animation {
public final class TransitionKt {
ctor public TransitionKt();
- method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
+ method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished = null, kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
method public static boolean getTransitionsEnabled();
method public static void setTransitionsEnabled(boolean p);
}
diff --git a/ui/ui-animation/api/public_plus_experimental_current.txt b/ui/ui-animation/api/public_plus_experimental_current.txt
index 113f0be5a55..e1311a33597 100644
--- a/ui/ui-animation/api/public_plus_experimental_current.txt
+++ b/ui/ui-animation/api/public_plus_experimental_current.txt
@@ -35,7 +35,7 @@ package androidx.ui.animation {
public final class TransitionKt {
ctor public TransitionKt();
- method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
+ method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished = null, kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
method public static boolean getTransitionsEnabled();
method public static void setTransitionsEnabled(boolean p);
}
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev03.txt b/ui/ui-animation/api/restricted_0.1.0-dev03.txt
index 113f0be5a55..e1311a33597 100644
--- a/ui/ui-animation/api/restricted_0.1.0-dev03.txt
+++ b/ui/ui-animation/api/restricted_0.1.0-dev03.txt
@@ -35,7 +35,7 @@ package androidx.ui.animation {
public final class TransitionKt {
ctor public TransitionKt();
- method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
+ method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished = null, kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
method public static boolean getTransitionsEnabled();
method public static void setTransitionsEnabled(boolean p);
}
diff --git a/ui/ui-animation/api/restricted_current.txt b/ui/ui-animation/api/restricted_current.txt
index 113f0be5a55..e1311a33597 100644
--- a/ui/ui-animation/api/restricted_current.txt
+++ b/ui/ui-animation/api/restricted_current.txt
@@ -35,7 +35,7 @@ package androidx.ui.animation {
public final class TransitionKt {
ctor public TransitionKt();
- method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
+ method public static <T> void Transition(androidx.animation.TransitionDefinition<T> definition, T? toState, androidx.animation.AnimationClockObservable clock = +ambient(AnimationClockAmbient), kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onStateChangeFinished = null, kotlin.jvm.functions.Function1<? super androidx.animation.TransitionState,kotlin.Unit> children);
method public static boolean getTransitionsEnabled();
method public static void setTransitionsEnabled(boolean p);
}
diff --git a/ui/ui-animation/build.gradle b/ui/ui-animation/build.gradle
index c5fe45b7c25..4808cff9561 100644
--- a/ui/ui-animation/build.gradle
+++ b/ui/ui-animation/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-animation/integration-tests/animation-demos/build.gradle b/ui/ui-animation/integration-tests/animation-demos/build.gradle
index 9530e43ef6e..9433acf576a 100644
--- a/ui/ui-animation/integration-tests/animation-demos/build.gradle
+++ b/ui/ui-animation/integration-tests/animation-demos/build.gradle
@@ -13,7 +13,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloAnimationActivity.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloAnimationActivity.kt
index 494f1184923..0b3607bf055 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloAnimationActivity.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloAnimationActivity.kt
@@ -67,6 +67,16 @@ private val definition = transitionDefinition {
this[background] = Color(red = 188, green = 222, blue = 145, alpha = 255)
this[y] = 0f // percentage
}
+ // Apply this transition to all state changes (i.e. Open -> Closed and Closed -> Open)
+ transition {
+ background using tween {
+ duration = 800
+ }
+ y using physics {
+ // Extremely low stiffness
+ stiffness = 40f
+ }
+ }
}
val handler = Handler(Looper.getMainLooper())
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloGestureBasedAnimationActivity.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloGestureBasedAnimationActivity.kt
index 23af1aaa4c7..b79eb01e390 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloGestureBasedAnimationActivity.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/HelloGestureBasedAnimationActivity.kt
@@ -60,6 +60,14 @@ private val definition = transitionDefinition {
this[scale] = 3f
this[color] = Color(red = 0, green = 100, blue = 0, alpha = 255)
}
+ transition {
+ scale using physics {
+ stiffness = 50f
+ }
+ color using physics {
+ stiffness = 50f
+ }
+ }
}
@Composable
diff --git a/ui/ui-animation/integration-tests/samples/build.gradle b/ui/ui-animation/integration-tests/samples/build.gradle
index 5a479b79915..f51e1948d8c 100644
--- a/ui/ui-animation/integration-tests/samples/build.gradle
+++ b/ui/ui-animation/integration-tests/samples/build.gradle
@@ -26,7 +26,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt b/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
index 24cc90d8549..fe609ff7fa9 100644
--- a/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
+++ b/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
@@ -21,6 +21,7 @@ import androidx.animation.PropKey
import androidx.animation.TransitionAnimation
import androidx.animation.TransitionDefinition
import androidx.animation.TransitionState
+import androidx.animation.createAnimation
import androidx.compose.Composable
import androidx.compose.Model
import androidx.compose.ambient
@@ -38,11 +39,13 @@ fun <T> Transition(
definition: TransitionDefinition<T>,
toState: T,
clock: AnimationClockObservable = +ambient(AnimationClockAmbient),
+ onStateChangeFinished: ((T) -> Unit)? = null,
children: @Composable() (state: TransitionState) -> Unit
) {
if (transitionsEnabled) {
// TODO: This null is workaround for b/132148894
- val model = +memo(definition, null) { TransitionModel(definition, clock) }
+ val model = +memo(definition, null) { TransitionModel(definition, toState, clock) }
+ model.anim.onStateChangeFinished = onStateChangeFinished
model.anim.toState(toState)
children(model)
} else {
@@ -61,12 +64,13 @@ var transitionsEnabled = true
@Model
private class TransitionModel<T>(
transitionDef: TransitionDefinition<T>,
+ initState: T,
clock: AnimationClockObservable
) : TransitionState {
private var animationPulse = 0L
internal val anim: TransitionAnimation<T> =
- transitionDef.createAnimation(clock).apply {
+ transitionDef.createAnimation(clock, initState).apply {
onUpdate = {
animationPulse++
}
diff --git a/ui/ui-core/api/0.1.0-dev03.txt b/ui/ui-core/api/0.1.0-dev03.txt
index ba2d75d8d08..20c581b10f4 100644
--- a/ui/ui-core/api/0.1.0-dev03.txt
+++ b/ui/ui-core/api/0.1.0-dev03.txt
@@ -1268,6 +1268,29 @@ package androidx.ui.engine.geometry {
}
+package androidx.ui.focus {
+
+ public enum FocusDetailedState {
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Active;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState ActiveParent;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Captured;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Disabled;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Inactive;
+ }
+
+ public enum FocusState {
+ enum_constant public static final androidx.ui.focus.FocusState Focused;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocusable;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocused;
+ }
+
+ public final class FocusStateKt {
+ ctor public FocusStateKt();
+ method public static androidx.ui.focus.FocusState focusState(androidx.ui.focus.FocusDetailedState);
+ }
+
+}
+
package androidx.ui.graphics {
public final class AndroidCanvasKt {
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index ba2d75d8d08..20c581b10f4 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -1268,6 +1268,29 @@ package androidx.ui.engine.geometry {
}
+package androidx.ui.focus {
+
+ public enum FocusDetailedState {
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Active;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState ActiveParent;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Captured;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Disabled;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Inactive;
+ }
+
+ public enum FocusState {
+ enum_constant public static final androidx.ui.focus.FocusState Focused;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocusable;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocused;
+ }
+
+ public final class FocusStateKt {
+ ctor public FocusStateKt();
+ method public static androidx.ui.focus.FocusState focusState(androidx.ui.focus.FocusDetailedState);
+ }
+
+}
+
package androidx.ui.graphics {
public final class AndroidCanvasKt {
diff --git a/ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt b/ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt
index ba2d75d8d08..20c581b10f4 100644
--- a/ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt
+++ b/ui/ui-core/api/public_plus_experimental_0.1.0-dev03.txt
@@ -1268,6 +1268,29 @@ package androidx.ui.engine.geometry {
}
+package androidx.ui.focus {
+
+ public enum FocusDetailedState {
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Active;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState ActiveParent;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Captured;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Disabled;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Inactive;
+ }
+
+ public enum FocusState {
+ enum_constant public static final androidx.ui.focus.FocusState Focused;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocusable;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocused;
+ }
+
+ public final class FocusStateKt {
+ ctor public FocusStateKt();
+ method public static androidx.ui.focus.FocusState focusState(androidx.ui.focus.FocusDetailedState);
+ }
+
+}
+
package androidx.ui.graphics {
public final class AndroidCanvasKt {
diff --git a/ui/ui-core/api/public_plus_experimental_current.txt b/ui/ui-core/api/public_plus_experimental_current.txt
index ba2d75d8d08..20c581b10f4 100644
--- a/ui/ui-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-core/api/public_plus_experimental_current.txt
@@ -1268,6 +1268,29 @@ package androidx.ui.engine.geometry {
}
+package androidx.ui.focus {
+
+ public enum FocusDetailedState {
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Active;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState ActiveParent;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Captured;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Disabled;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Inactive;
+ }
+
+ public enum FocusState {
+ enum_constant public static final androidx.ui.focus.FocusState Focused;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocusable;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocused;
+ }
+
+ public final class FocusStateKt {
+ ctor public FocusStateKt();
+ method public static androidx.ui.focus.FocusState focusState(androidx.ui.focus.FocusDetailedState);
+ }
+
+}
+
package androidx.ui.graphics {
public final class AndroidCanvasKt {
diff --git a/ui/ui-core/api/restricted_0.1.0-dev03.txt b/ui/ui-core/api/restricted_0.1.0-dev03.txt
index ba2d75d8d08..20c581b10f4 100644
--- a/ui/ui-core/api/restricted_0.1.0-dev03.txt
+++ b/ui/ui-core/api/restricted_0.1.0-dev03.txt
@@ -1268,6 +1268,29 @@ package androidx.ui.engine.geometry {
}
+package androidx.ui.focus {
+
+ public enum FocusDetailedState {
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Active;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState ActiveParent;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Captured;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Disabled;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Inactive;
+ }
+
+ public enum FocusState {
+ enum_constant public static final androidx.ui.focus.FocusState Focused;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocusable;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocused;
+ }
+
+ public final class FocusStateKt {
+ ctor public FocusStateKt();
+ method public static androidx.ui.focus.FocusState focusState(androidx.ui.focus.FocusDetailedState);
+ }
+
+}
+
package androidx.ui.graphics {
public final class AndroidCanvasKt {
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index ba2d75d8d08..20c581b10f4 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -1268,6 +1268,29 @@ package androidx.ui.engine.geometry {
}
+package androidx.ui.focus {
+
+ public enum FocusDetailedState {
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Active;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState ActiveParent;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Captured;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Disabled;
+ enum_constant public static final androidx.ui.focus.FocusDetailedState Inactive;
+ }
+
+ public enum FocusState {
+ enum_constant public static final androidx.ui.focus.FocusState Focused;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocusable;
+ enum_constant public static final androidx.ui.focus.FocusState NotFocused;
+ }
+
+ public final class FocusStateKt {
+ ctor public FocusStateKt();
+ method public static androidx.ui.focus.FocusState focusState(androidx.ui.focus.FocusDetailedState);
+ }
+
+}
+
package androidx.ui.graphics {
public final class AndroidCanvasKt {
diff --git a/ui/ui-core/integration-tests/samples/build.gradle b/ui/ui-core/integration-tests/samples/build.gradle
index d8814643426..84ad5db2a52 100644
--- a/ui/ui-core/integration-tests/samples/build.gradle
+++ b/ui/ui-core/integration-tests/samples/build.gradle
@@ -26,7 +26,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-core/src/main/java/androidx/ui/focus/FocusState.kt b/ui/ui-core/src/main/java/androidx/ui/focus/FocusState.kt
new file mode 100644
index 00000000000..e2cae0ca6a6
--- /dev/null
+++ b/ui/ui-core/src/main/java/androidx/ui/focus/FocusState.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 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 androidx.ui.focus
+
+/**
+ * Different states of the focus system.
+ *
+ * These are the most frequently used states. For more detailed states, refer to
+ * [FocusDetailedState].
+ *
+ * [Focused]: A focusable component that is currently in focus.
+ * [NotFocusable]: A focusable component that is currently not focusable. Eg. A disabled button.
+ * [NotFocused]: A focusable component that is not currently focused.
+ */
+enum class FocusState { Focused, NotFocusable, NotFocused }
+
+/**
+ * Different states of the focus system.
+ * These are the detailed states used by the Focus Nodes.
+ * If you need higher level states, eg [Focused][FocusState.Focused] or
+ * [NotFocused][FocusState.NotFocused], use the states in [FocusState].
+ *
+ * [Active]: The focusable component is currently active (i.e. it receives key events).
+ * [ActiveParent] : One of the descendants of the focusable component is [Active].
+ * [Captured]: The focusable component is currently active (has focus), and is in a state where
+ * it does not want to give up focus. (Eg. a text field with an invalid phone number).
+ * [Disabled]: The focusable component is not currently focusable. (eg. A disabled button).
+ * [Inactive]: The focusable component does not receive any key events. (ie it is not active,
+ * nor are any of its descendants active).
+ */
+enum class FocusDetailedState { Active, ActiveParent, Captured, Disabled, Inactive }
+
+/**
+ * Converts a [FocusDetailedState] to a [FocusState].
+ */
+fun FocusDetailedState.focusState() = when (this) {
+ FocusDetailedState.Captured,
+ FocusDetailedState.Active -> FocusState.Focused
+ FocusDetailedState.ActiveParent,
+ FocusDetailedState.Inactive -> FocusState.NotFocused
+ FocusDetailedState.Disabled -> FocusState.NotFocusable
+}
diff --git a/ui/ui-foundation/build.gradle b/ui/ui-foundation/build.gradle
index 8cd68d14a43..c5a01718928 100644
--- a/ui/ui-foundation/build.gradle
+++ b/ui/ui-foundation/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-foundation/integration-tests/foundation-demos/build.gradle b/ui/ui-foundation/integration-tests/foundation-demos/build.gradle
index 5c913868ed2..9ea749979df 100644
--- a/ui/ui-foundation/integration-tests/foundation-demos/build.gradle
+++ b/ui/ui-foundation/integration-tests/foundation-demos/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_COROUTINES_ANDROID)
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-foundation/integration-tests/samples/build.gradle b/ui/ui-foundation/integration-tests/samples/build.gradle
index ba7a9a4d14f..d6a2247ffa9 100644
--- a/ui/ui-foundation/integration-tests/samples/build.gradle
+++ b/ui/ui-foundation/integration-tests/samples/build.gradle
@@ -26,7 +26,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-framework/api/0.1.0-dev03.txt b/ui/ui-framework/api/0.1.0-dev03.txt
index 3344b1c600d..521cfa16686 100644
--- a/ui/ui-framework/api/0.1.0-dev03.txt
+++ b/ui/ui-framework/api/0.1.0-dev03.txt
@@ -350,6 +350,26 @@ package androidx.ui.core.selection {
}
+package androidx.ui.focus {
+
+ public final class FocusOperator {
+ ctor public FocusOperator();
+ method public androidx.ui.focus.FocusDetailedState getFocusDetailedState();
+ method public androidx.ui.focus.FocusState getFocusState();
+ method public void requestFocus();
+ property public final androidx.ui.focus.FocusDetailedState focusDetailedState;
+ property public final androidx.ui.focus.FocusState focusState;
+ }
+
+ public final class FocusableKt {
+ ctor public FocusableKt();
+ method public static void Focusable(androidx.ui.focus.FocusOperator focusOperator = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function1<? super androidx.ui.focus.FocusOperator,kotlin.Unit> children);
+ }
+
+}
+
package androidx.ui.graphics.vector {
public final class VectorAsset {
diff --git a/ui/ui-framework/api/current.txt b/ui/ui-framework/api/current.txt
index 3344b1c600d..521cfa16686 100644
--- a/ui/ui-framework/api/current.txt
+++ b/ui/ui-framework/api/current.txt
@@ -350,6 +350,26 @@ package androidx.ui.core.selection {
}
+package androidx.ui.focus {
+
+ public final class FocusOperator {
+ ctor public FocusOperator();
+ method public androidx.ui.focus.FocusDetailedState getFocusDetailedState();
+ method public androidx.ui.focus.FocusState getFocusState();
+ method public void requestFocus();
+ property public final androidx.ui.focus.FocusDetailedState focusDetailedState;
+ property public final androidx.ui.focus.FocusState focusState;
+ }
+
+ public final class FocusableKt {
+ ctor public FocusableKt();
+ method public static void Focusable(androidx.ui.focus.FocusOperator focusOperator = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function1<? super androidx.ui.focus.FocusOperator,kotlin.Unit> children);
+ }
+
+}
+
package androidx.ui.graphics.vector {
public final class VectorAsset {
diff --git a/ui/ui-framework/api/public_plus_experimental_0.1.0-dev03.txt b/ui/ui-framework/api/public_plus_experimental_0.1.0-dev03.txt
index 3344b1c600d..521cfa16686 100644
--- a/ui/ui-framework/api/public_plus_experimental_0.1.0-dev03.txt
+++ b/ui/ui-framework/api/public_plus_experimental_0.1.0-dev03.txt
@@ -350,6 +350,26 @@ package androidx.ui.core.selection {
}
+package androidx.ui.focus {
+
+ public final class FocusOperator {
+ ctor public FocusOperator();
+ method public androidx.ui.focus.FocusDetailedState getFocusDetailedState();
+ method public androidx.ui.focus.FocusState getFocusState();
+ method public void requestFocus();
+ property public final androidx.ui.focus.FocusDetailedState focusDetailedState;
+ property public final androidx.ui.focus.FocusState focusState;
+ }
+
+ public final class FocusableKt {
+ ctor public FocusableKt();
+ method public static void Focusable(androidx.ui.focus.FocusOperator focusOperator = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function1<? super androidx.ui.focus.FocusOperator,kotlin.Unit> children);
+ }
+
+}
+
package androidx.ui.graphics.vector {
public final class VectorAsset {
diff --git a/ui/ui-framework/api/public_plus_experimental_current.txt b/ui/ui-framework/api/public_plus_experimental_current.txt
index 3344b1c600d..521cfa16686 100644
--- a/ui/ui-framework/api/public_plus_experimental_current.txt
+++ b/ui/ui-framework/api/public_plus_experimental_current.txt
@@ -350,6 +350,26 @@ package androidx.ui.core.selection {
}
+package androidx.ui.focus {
+
+ public final class FocusOperator {
+ ctor public FocusOperator();
+ method public androidx.ui.focus.FocusDetailedState getFocusDetailedState();
+ method public androidx.ui.focus.FocusState getFocusState();
+ method public void requestFocus();
+ property public final androidx.ui.focus.FocusDetailedState focusDetailedState;
+ property public final androidx.ui.focus.FocusState focusState;
+ }
+
+ public final class FocusableKt {
+ ctor public FocusableKt();
+ method public static void Focusable(androidx.ui.focus.FocusOperator focusOperator = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function1<? super androidx.ui.focus.FocusOperator,kotlin.Unit> children);
+ }
+
+}
+
package androidx.ui.graphics.vector {
public final class VectorAsset {
diff --git a/ui/ui-framework/api/restricted_0.1.0-dev03.txt b/ui/ui-framework/api/restricted_0.1.0-dev03.txt
index 3344b1c600d..521cfa16686 100644
--- a/ui/ui-framework/api/restricted_0.1.0-dev03.txt
+++ b/ui/ui-framework/api/restricted_0.1.0-dev03.txt
@@ -350,6 +350,26 @@ package androidx.ui.core.selection {
}
+package androidx.ui.focus {
+
+ public final class FocusOperator {
+ ctor public FocusOperator();
+ method public androidx.ui.focus.FocusDetailedState getFocusDetailedState();
+ method public androidx.ui.focus.FocusState getFocusState();
+ method public void requestFocus();
+ property public final androidx.ui.focus.FocusDetailedState focusDetailedState;
+ property public final androidx.ui.focus.FocusState focusState;
+ }
+
+ public final class FocusableKt {
+ ctor public FocusableKt();
+ method public static void Focusable(androidx.ui.focus.FocusOperator focusOperator = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function1<? super androidx.ui.focus.FocusOperator,kotlin.Unit> children);
+ }
+
+}
+
package androidx.ui.graphics.vector {
public final class VectorAsset {
diff --git a/ui/ui-framework/api/restricted_current.txt b/ui/ui-framework/api/restricted_current.txt
index 3344b1c600d..521cfa16686 100644
--- a/ui/ui-framework/api/restricted_current.txt
+++ b/ui/ui-framework/api/restricted_current.txt
@@ -350,6 +350,26 @@ package androidx.ui.core.selection {
}
+package androidx.ui.focus {
+
+ public final class FocusOperator {
+ ctor public FocusOperator();
+ method public androidx.ui.focus.FocusDetailedState getFocusDetailedState();
+ method public androidx.ui.focus.FocusState getFocusState();
+ method public void requestFocus();
+ property public final androidx.ui.focus.FocusDetailedState focusDetailedState;
+ property public final androidx.ui.focus.FocusState focusState;
+ }
+
+ public final class FocusableKt {
+ ctor public FocusableKt();
+ method public static void Focusable(androidx.ui.focus.FocusOperator focusOperator = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function1<? super androidx.ui.focus.FocusOperator,kotlin.Unit> children);
+ }
+
+}
+
package androidx.ui.graphics.vector {
public final class VectorAsset {
diff --git a/ui/ui-framework/build.gradle b/ui/ui-framework/build.gradle
index 7874e613a03..f956d84173e 100644
--- a/ui/ui-framework/build.gradle
+++ b/ui/ui-framework/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_COROUTINES_ANDROID)
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-framework/integration-tests/framework-demos/build.gradle b/ui/ui-framework/integration-tests/framework-demos/build.gradle
index 6e950b9fe2a..2aa44307434 100644
--- a/ui/ui-framework/integration-tests/framework-demos/build.gradle
+++ b/ui/ui-framework/integration-tests/framework-demos/build.gradle
@@ -12,7 +12,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml b/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
index 178834d4030..e962fd8dfa5 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
@@ -77,7 +77,14 @@
<category android:name="androidx.ui.demos.SAMPLE_CODE" />
</intent-filter>
</activity>
-
+ <activity android:name=".focus.FocusableActivity"
+ android:configChanges="orientation|screenSize"
+ android:label="Focus">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="androidx.ui.demos.SAMPLE_CODE"/>
+ </intent-filter>
+ </activity>
<!-- Simple Movement Based GestureDetector Demos -->
<activity android:name=".gestures.TouchSlopDragGestureDetectorDemo"
android:configChanges="orientation|screenSize"
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/focus/FocusableActivity.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/focus/FocusableActivity.kt
new file mode 100644
index 00000000000..9dd9b411b40
--- /dev/null
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/focus/FocusableActivity.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 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 androidx.ui.framework.demos.focus
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.compose.Composable
+import androidx.ui.core.Text
+import androidx.ui.core.gesture.PressGestureDetector
+import androidx.ui.core.setContent
+import androidx.ui.focus.FocusState.NotFocused
+import androidx.ui.focus.FocusState.NotFocusable
+import androidx.ui.focus.FocusState.Focused
+import androidx.ui.focus.Focusable
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Column
+import androidx.ui.layout.ExpandedWidth
+import androidx.ui.layout.MainAxisAlignment
+import androidx.ui.layout.Row
+import androidx.ui.layout.RowScope
+import androidx.ui.material.MaterialTheme
+import androidx.ui.text.TextStyle
+
+class FocusableActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ Focusable {
+ MaterialTheme {
+ Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ CenteredRow {
+ Text("Click on any focusable to bring it into focus:")
+ }
+ CenteredRow {
+ FocusableText("Focusable 1")
+ }
+ CenteredRow {
+ FocusableText("Focusable 2")
+ }
+ CenteredRow {
+ FocusableText("Focusable 3")
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun FocusableText(text: String) {
+ Focusable { focus ->
+ PressGestureDetector(onPress = { focus.requestFocus() }) {
+ Text(
+ text = text,
+ style = TextStyle(
+ color = when (focus.focusState) {
+ Focused -> Color.Green
+ NotFocused -> Color.Black
+ NotFocusable -> Color.Gray
+ }
+ )
+ )
+ }
+ }
+}
+
+@Composable
+private fun CenteredRow(children: @Composable() RowScope.() -> Unit) {
+ Row(modifier = ExpandedWidth, mainAxisAlignment = MainAxisAlignment.Center, children = children)
+} \ No newline at end of file
diff --git a/ui/ui-framework/integration-tests/samples/build.gradle b/ui/ui-framework/integration-tests/samples/build.gradle
index f83e20c840a..e322408b6d5 100644
--- a/ui/ui-framework/integration-tests/samples/build.gradle
+++ b/ui/ui-framework/integration-tests/samples/build.gradle
@@ -26,7 +26,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-framework/src/main/java/androidx/ui/focus/Focusable.kt b/ui/ui-framework/src/main/java/androidx/ui/focus/Focusable.kt
new file mode 100644
index 00000000000..c1c75510ffb
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/focus/Focusable.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2019 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 androidx.ui.focus
+
+import androidx.compose.Composable
+import androidx.compose.Recompose
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.FocusNode
+import androidx.ui.core.OnChildPositioned
+import androidx.ui.core.Ref
+import androidx.ui.core.focus.initializeFocusState
+import androidx.ui.focus.FocusDetailedState.Inactive
+
+private val focusNotCreated = "Focus node could not be created."
+
+/**
+ * This composable can be used to create components that are Focusable. A component that is focused
+ * receives any invoked actions. Some examples of actions are 'paste' (receiving the
+ * contents of the clipboard), or receiving text from the keyboard.
+ *
+ * [Focusable] components have access to the current focus state. The children of a [Focusable]
+ * have access to this focus state during composition.
+ *
+ * [focusOperator] : This object is returned in the receiver scope of the components
+ * passed as [children]. You should not specify this parameter unless you want to hoist the
+ * focusOperator so that you can control the focusable from outside the scope of its children.
+ *
+ * [children]: This is a composable block called with [focusOperator] in its receiver scope.
+ * Children can use [FocusOperator.focusState] for conditional composition.
+ *
+ */
+@Composable
+fun Focusable(
+ focusOperator: FocusOperator = +memo { FocusOperator() },
+ children: @Composable() (FocusOperator) -> Unit
+) {
+ // TODO (b/144897112): Remove manual recomposition.
+ Recompose { recompose ->
+
+ val focusNodeRef = Ref<FocusNode>()
+ FocusNode(recompose = recompose, ref = focusNodeRef) {
+
+ val focusNode = (focusNodeRef.value ?: error(focusNotCreated))
+
+ focusOperator.focusNode = focusNode
+
+ // Set the focusNode coordinates when the composable is positioned. Also, if this is
+ // the focus root and the host view is in focus, request focus for this node.
+ OnChildPositioned(
+ onPositioned = {
+ focusNode.layoutCoordinates = it
+ if (focusNode.focusState == Inactive) {
+ focusNode.initializeFocusState()
+ }
+ }, children = {
+ children(focusOperator)
+ })
+ }
+ }
+}
+
+/**
+ * The [FocusOperator] is returned in the receiver scope of the children of a [Focusable]. It
+ * access to focus APIs pertaining to the [Focusable].
+ */
+class FocusOperator {
+ /**
+ * The [FocusNode] associated with this [FocusOperator].
+ *
+ * @throws UninitializedPropertyAccessException if this [FocusOperator] has no associated
+ * [FocusNode].
+ */
+ internal lateinit var focusNode: FocusNode
+
+ /**
+ * A more detailed focus state of the [Focusable] associated with this [FocusOperator]. For a
+ * smaller subset of states, use [focusState].
+ */
+ val focusDetailedState: FocusDetailedState get() = focusNode.focusState
+
+ /**
+ * The current focus state of the [Focusable] associated with this [FocusOperator]. For more
+ * detailed focus state information, use [focusDetailedState].
+ */
+ val focusState: FocusState get() = focusDetailedState.focusState()
+
+ /**
+ * Request focus for the [Focusable] associated with this [FocusOperator].
+ */
+ fun requestFocus() = focusNode.requestFocus()
+} \ No newline at end of file
diff --git a/ui/ui-layout/build.gradle b/ui/ui-layout/build.gradle
index 6805851fa58..9beabcf072b 100644
--- a/ui/ui-layout/build.gradle
+++ b/ui/ui-layout/build.gradle
@@ -30,7 +30,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-layout/integration-tests/layout-demos/build.gradle b/ui/ui-layout/integration-tests/layout-demos/build.gradle
index de2f8818e55..7fff9e3489b 100644
--- a/ui/ui-layout/integration-tests/layout-demos/build.gradle
+++ b/ui/ui-layout/integration-tests/layout-demos/build.gradle
@@ -27,7 +27,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_COROUTINES_ANDROID)
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-layout/integration-tests/samples/build.gradle b/ui/ui-layout/integration-tests/samples/build.gradle
index 2f85730d21f..c9bab7aba58 100644
--- a/ui/ui-layout/integration-tests/samples/build.gradle
+++ b/ui/ui-layout/integration-tests/samples/build.gradle
@@ -26,7 +26,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-material/build.gradle b/ui/ui-material/build.gradle
index 0ceb2c18478..0883fe8359a 100644
--- a/ui/ui-material/build.gradle
+++ b/ui/ui-material/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-material/integration-tests/material-demos/build.gradle b/ui/ui-material/integration-tests/material-demos/build.gradle
index 674c142932b..35fea8bd31b 100644
--- a/ui/ui-material/integration-tests/material-demos/build.gradle
+++ b/ui/ui-material/integration-tests/material-demos/build.gradle
@@ -13,7 +13,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_COROUTINES_ANDROID)
implementation(KOTLIN_REFLECT)
diff --git a/ui/ui-material/integration-tests/material-studies/build.gradle b/ui/ui-material/integration-tests/material-studies/build.gradle
index 56efbbf9911..7667548ad47 100644
--- a/ui/ui-material/integration-tests/material-studies/build.gradle
+++ b/ui/ui-material/integration-tests/material-studies/build.gradle
@@ -27,7 +27,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-material/integration-tests/samples/build.gradle b/ui/ui-material/integration-tests/samples/build.gradle
index 3762ffd427d..0cef6f5dbb3 100644
--- a/ui/ui-material/integration-tests/samples/build.gradle
+++ b/ui/ui-material/integration-tests/samples/build.gradle
@@ -26,7 +26,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/ripple/DefaultRippleEffect.kt b/ui/ui-material/src/main/java/androidx/ui/material/ripple/DefaultRippleEffect.kt
index d93a99da3c2..9a766e68bd5 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/ripple/DefaultRippleEffect.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/ripple/DefaultRippleEffect.kt
@@ -21,6 +21,7 @@ import androidx.animation.FloatPropKey
import androidx.animation.InterruptionHandling
import androidx.animation.LinearEasing
import androidx.animation.TransitionAnimation
+import androidx.animation.createAnimation
import androidx.animation.transitionDefinition
import androidx.ui.animation.PxPositionPropKey
import androidx.ui.animation.PxPropKey
diff --git a/ui/ui-platform/api/0.1.0-dev03.txt b/ui/ui-platform/api/0.1.0-dev03.txt
index 7cdc7590cea..f288fffcdb4 100644
--- a/ui/ui-platform/api/0.1.0-dev03.txt
+++ b/ui/ui-platform/api/0.1.0-dev03.txt
@@ -140,6 +140,25 @@ package androidx.ui.core {
property public final kotlin.jvm.functions.Function3<androidx.ui.core.DrawReceiver,androidx.ui.graphics.Canvas,androidx.ui.core.PxSize,kotlin.Unit>? onPaintWithChildren;
}
+ public final class FocusNode extends androidx.ui.core.ComponentNode {
+ ctor public FocusNode();
+ method public boolean captureFocus();
+ method public boolean freeFocus();
+ method public androidx.ui.focus.FocusDetailedState getFocusState();
+ method public androidx.ui.core.LayoutCoordinates? getLayoutCoordinates();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getRecompose();
+ method public androidx.ui.core.Ref<androidx.ui.core.FocusNode>? getRef();
+ method public void requestFocus(boolean propagateFocus = true);
+ method public void setFocusState$lintWithKotlin(androidx.ui.focus.FocusDetailedState p);
+ method public void setLayoutCoordinates(androidx.ui.core.LayoutCoordinates? p);
+ method public void setRecompose(kotlin.jvm.functions.Function0<kotlin.Unit> value);
+ method public void setRef(androidx.ui.core.Ref<androidx.ui.core.FocusNode>? value);
+ property public final androidx.ui.focus.FocusDetailedState focusState;
+ property public final androidx.ui.core.LayoutCoordinates? layoutCoordinates;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> recompose;
+ property public final androidx.ui.core.Ref<androidx.ui.core.FocusNode>? ref;
+ }
+
public final class LayoutNode extends androidx.ui.core.ComponentNode implements androidx.ui.core.Measurable {
ctor public LayoutNode();
method public boolean getAffectsParentSize();
@@ -324,6 +343,15 @@ package androidx.ui.core {
}
+package androidx.ui.core.focus {
+
+ public final class FocusNodeUtilsKt {
+ ctor public FocusNodeUtilsKt();
+ method public static void initializeFocusState(androidx.ui.core.FocusNode);
+ }
+
+}
+
package androidx.ui.core.pointerinput {
public final class MotionEventAdapterKt {
diff --git a/ui/ui-platform/api/current.txt b/ui/ui-platform/api/current.txt
index 7cdc7590cea..f288fffcdb4 100644
--- a/ui/ui-platform/api/current.txt
+++ b/ui/ui-platform/api/current.txt
@@ -140,6 +140,25 @@ package androidx.ui.core {
property public final kotlin.jvm.functions.Function3<androidx.ui.core.DrawReceiver,androidx.ui.graphics.Canvas,androidx.ui.core.PxSize,kotlin.Unit>? onPaintWithChildren;
}
+ public final class FocusNode extends androidx.ui.core.ComponentNode {
+ ctor public FocusNode();
+ method public boolean captureFocus();
+ method public boolean freeFocus();
+ method public androidx.ui.focus.FocusDetailedState getFocusState();
+ method public androidx.ui.core.LayoutCoordinates? getLayoutCoordinates();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getRecompose();
+ method public androidx.ui.core.Ref<androidx.ui.core.FocusNode>? getRef();
+ method public void requestFocus(boolean propagateFocus = true);
+ method public void setFocusState$lintWithKotlin(androidx.ui.focus.FocusDetailedState p);
+ method public void setLayoutCoordinates(androidx.ui.core.LayoutCoordinates? p);
+ method public void setRecompose(kotlin.jvm.functions.Function0<kotlin.Unit> value);
+ method public void setRef(androidx.ui.core.Ref<androidx.ui.core.FocusNode>? value);
+ property public final androidx.ui.focus.FocusDetailedState focusState;
+ property public final androidx.ui.core.LayoutCoordinates? layoutCoordinates;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> recompose;
+ property public final androidx.ui.core.Ref<androidx.ui.core.FocusNode>? ref;
+ }
+
public final class LayoutNode extends androidx.ui.core.ComponentNode implements androidx.ui.core.Measurable {
ctor public LayoutNode();
method public boolean getAffectsParentSize();
@@ -324,6 +343,15 @@ package androidx.ui.core {
}
+package androidx.ui.core.focus {
+
+ public final class FocusNodeUtilsKt {
+ ctor public FocusNodeUtilsKt();
+ method public static void initializeFocusState(androidx.ui.core.FocusNode);
+ }
+
+}
+
package androidx.ui.core.pointerinput {
public final class MotionEventAdapterKt {
diff --git a/ui/ui-platform/api/public_plus_experimental_0.1.0-dev03.txt b/ui/ui-platform/api/public_plus_experimental_0.1.0-dev03.txt
index 7cdc7590cea..f288fffcdb4 100644
--- a/ui/ui-platform/api/public_plus_experimental_0.1.0-dev03.txt
+++ b/ui/ui-platform/api/public_plus_experimental_0.1.0-dev03.txt
@@ -140,6 +140,25 @@ package androidx.ui.core {
property public final kotlin.jvm.functions.Function3<androidx.ui.core.DrawReceiver,androidx.ui.graphics.Canvas,androidx.ui.core.PxSize,kotlin.Unit>? onPaintWithChildren;
}
+ public final class FocusNode extends androidx.ui.core.ComponentNode {
+ ctor public FocusNode();
+ method public boolean captureFocus();
+ method public boolean freeFocus();
+ method public androidx.ui.focus.FocusDetailedState getFocusState();
+ method public androidx.ui.core.LayoutCoordinates? getLayoutCoordinates();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getRecompose();
+ method public androidx.ui.core.Ref<androidx.ui.core.FocusNode>? getRef();
+ method public void requestFocus(boolean propagateFocus = true);
+ method public void setFocusState$lintWithKotlin(androidx.ui.focus.FocusDetailedState p);
+ method public void setLayoutCoordinates(androidx.ui.core.LayoutCoordinates? p);
+ method public void setRecompose(kotlin.jvm.functions.Function0<kotlin.Unit> value);
+ method public void setRef(androidx.ui.core.Ref<androidx.ui.core.FocusNode>? value);
+ property public final androidx.ui.focus.FocusDetailedState focusState;
+ property public final androidx.ui.core.LayoutCoordinates? layoutCoordinates;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> recompose;
+ property public final androidx.ui.core.Ref<androidx.ui.core.FocusNode>? ref;
+ }
+
public final class LayoutNode extends androidx.ui.core.ComponentNode implements androidx.ui.core.Measurable {
ctor public LayoutNode();
method public boolean getAffectsParentSize();
@@ -324,6 +343,15 @@ package androidx.ui.core {
}
+package androidx.ui.core.focus {
+
+ public final class FocusNodeUtilsKt {
+ ctor public FocusNodeUtilsKt();
+ method public static void initializeFocusState(androidx.ui.core.FocusNode);
+ }
+
+}
+
package androidx.ui.core.pointerinput {
public final class MotionEventAdapterKt {
diff --git a/ui/ui-platform/api/public_plus_experimental_current.txt b/ui/ui-platform/api/public_plus_experimental_current.txt
index 7cdc7590cea..f288fffcdb4 100644
--- a/ui/ui-platform/api/public_plus_experimental_current.txt
+++ b/ui/ui-platform/api/public_plus_experimental_current.txt
@@ -140,6 +140,25 @@ package androidx.ui.core {
property public final kotlin.jvm.functions.Function3<androidx.ui.core.DrawReceiver,androidx.ui.graphics.Canvas,androidx.ui.core.PxSize,kotlin.Unit>? onPaintWithChildren;
}
+ public final class FocusNode extends androidx.ui.core.ComponentNode {
+ ctor public FocusNode();
+ method public boolean captureFocus();
+ method public boolean freeFocus();
+ method public androidx.ui.focus.FocusDetailedState getFocusState();
+ method public androidx.ui.core.LayoutCoordinates? getLayoutCoordinates();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getRecompose();
+ method public androidx.ui.core.Ref<androidx.ui.core.FocusNode>? getRef();
+ method public void requestFocus(boolean propagateFocus = true);
+ method public void setFocusState$lintWithKotlin(androidx.ui.focus.FocusDetailedState p);
+ method public void setLayoutCoordinates(androidx.ui.core.LayoutCoordinates? p);
+ method public void setRecompose(kotlin.jvm.functions.Function0<kotlin.Unit> value);
+ method public void setRef(androidx.ui.core.Ref<androidx.ui.core.FocusNode>? value);
+ property public final androidx.ui.focus.FocusDetailedState focusState;
+ property public final androidx.ui.core.LayoutCoordinates? layoutCoordinates;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> recompose;
+ property public final androidx.ui.core.Ref<androidx.ui.core.FocusNode>? ref;
+ }
+
public final class LayoutNode extends androidx.ui.core.ComponentNode implements androidx.ui.core.Measurable {
ctor public LayoutNode();
method public boolean getAffectsParentSize();
@@ -324,6 +343,15 @@ package androidx.ui.core {
}
+package androidx.ui.core.focus {
+
+ public final class FocusNodeUtilsKt {
+ ctor public FocusNodeUtilsKt();
+ method public static void initializeFocusState(androidx.ui.core.FocusNode);
+ }
+
+}
+
package androidx.ui.core.pointerinput {
public final class MotionEventAdapterKt {
diff --git a/ui/ui-platform/api/restricted_0.1.0-dev03.txt b/ui/ui-platform/api/restricted_0.1.0-dev03.txt
index 7cdc7590cea..f288fffcdb4 100644
--- a/ui/ui-platform/api/restricted_0.1.0-dev03.txt
+++ b/ui/ui-platform/api/restricted_0.1.0-dev03.txt
@@ -140,6 +140,25 @@ package androidx.ui.core {
property public final kotlin.jvm.functions.Function3<androidx.ui.core.DrawReceiver,androidx.ui.graphics.Canvas,androidx.ui.core.PxSize,kotlin.Unit>? onPaintWithChildren;
}
+ public final class FocusNode extends androidx.ui.core.ComponentNode {
+ ctor public FocusNode();
+ method public boolean captureFocus();
+ method public boolean freeFocus();
+ method public androidx.ui.focus.FocusDetailedState getFocusState();
+ method public androidx.ui.core.LayoutCoordinates? getLayoutCoordinates();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getRecompose();
+ method public androidx.ui.core.Ref<androidx.ui.core.FocusNode>? getRef();
+ method public void requestFocus(boolean propagateFocus = true);
+ method public void setFocusState$lintWithKotlin(androidx.ui.focus.FocusDetailedState p);
+ method public void setLayoutCoordinates(androidx.ui.core.LayoutCoordinates? p);
+ method public void setRecompose(kotlin.jvm.functions.Function0<kotlin.Unit> value);
+ method public void setRef(androidx.ui.core.Ref<androidx.ui.core.FocusNode>? value);
+ property public final androidx.ui.focus.FocusDetailedState focusState;
+ property public final androidx.ui.core.LayoutCoordinates? layoutCoordinates;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> recompose;
+ property public final androidx.ui.core.Ref<androidx.ui.core.FocusNode>? ref;
+ }
+
public final class LayoutNode extends androidx.ui.core.ComponentNode implements androidx.ui.core.Measurable {
ctor public LayoutNode();
method public boolean getAffectsParentSize();
@@ -324,6 +343,15 @@ package androidx.ui.core {
}
+package androidx.ui.core.focus {
+
+ public final class FocusNodeUtilsKt {
+ ctor public FocusNodeUtilsKt();
+ method public static void initializeFocusState(androidx.ui.core.FocusNode);
+ }
+
+}
+
package androidx.ui.core.pointerinput {
public final class MotionEventAdapterKt {
diff --git a/ui/ui-platform/api/restricted_current.txt b/ui/ui-platform/api/restricted_current.txt
index 7cdc7590cea..f288fffcdb4 100644
--- a/ui/ui-platform/api/restricted_current.txt
+++ b/ui/ui-platform/api/restricted_current.txt
@@ -140,6 +140,25 @@ package androidx.ui.core {
property public final kotlin.jvm.functions.Function3<androidx.ui.core.DrawReceiver,androidx.ui.graphics.Canvas,androidx.ui.core.PxSize,kotlin.Unit>? onPaintWithChildren;
}
+ public final class FocusNode extends androidx.ui.core.ComponentNode {
+ ctor public FocusNode();
+ method public boolean captureFocus();
+ method public boolean freeFocus();
+ method public androidx.ui.focus.FocusDetailedState getFocusState();
+ method public androidx.ui.core.LayoutCoordinates? getLayoutCoordinates();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getRecompose();
+ method public androidx.ui.core.Ref<androidx.ui.core.FocusNode>? getRef();
+ method public void requestFocus(boolean propagateFocus = true);
+ method public void setFocusState$lintWithKotlin(androidx.ui.focus.FocusDetailedState p);
+ method public void setLayoutCoordinates(androidx.ui.core.LayoutCoordinates? p);
+ method public void setRecompose(kotlin.jvm.functions.Function0<kotlin.Unit> value);
+ method public void setRef(androidx.ui.core.Ref<androidx.ui.core.FocusNode>? value);
+ property public final androidx.ui.focus.FocusDetailedState focusState;
+ property public final androidx.ui.core.LayoutCoordinates? layoutCoordinates;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> recompose;
+ property public final androidx.ui.core.Ref<androidx.ui.core.FocusNode>? ref;
+ }
+
public final class LayoutNode extends androidx.ui.core.ComponentNode implements androidx.ui.core.Measurable {
ctor public LayoutNode();
method public boolean getAffectsParentSize();
@@ -324,6 +343,15 @@ package androidx.ui.core {
}
+package androidx.ui.core.focus {
+
+ public final class FocusNodeUtilsKt {
+ ctor public FocusNodeUtilsKt();
+ method public static void initializeFocusState(androidx.ui.core.FocusNode);
+ }
+
+}
+
package androidx.ui.core.pointerinput {
public final class MotionEventAdapterKt {
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
index ded520d0e2a..2b28c5cb9ba 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
@@ -17,8 +17,17 @@ package androidx.ui.core
import androidx.compose.Emittable
import androidx.ui.core.semantics.SemanticsConfiguration
+import androidx.ui.focus.FocusDetailedState.Active
+import androidx.ui.focus.FocusDetailedState.Inactive
+import androidx.ui.focus.FocusDetailedState.Disabled
+import androidx.ui.focus.FocusDetailedState.Captured
+import androidx.ui.focus.FocusDetailedState.ActiveParent
import androidx.ui.graphics.Canvas
import androidx.ui.engine.geometry.Shape
+import androidx.ui.core.focus.findParentFocusNode
+import androidx.ui.core.focus.ownerHasFocus
+import androidx.ui.core.focus.requestFocusForOwner
+import androidx.ui.focus.FocusDetailedState
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@@ -346,6 +355,289 @@ class PointerInputNode : ComponentNode() {
}
/**
+ * Backing node that implements focus.
+ */
+class FocusNode : ComponentNode() {
+ /**
+ * Implementation oddity around composition; used to capture a reference to this
+ * [FocusNode] when composed. This is a reverse property that mutates its right-hand side.
+ *
+ * TODO: Once we finalize the API consider removing this and replace this with an
+ * interface that sets the value as a property on the object that needs it.
+ */
+ var ref: Ref<FocusNode>?
+ get() = null
+ set(value) {
+ value?.value = this
+ }
+
+ /**
+ * The recompose function of the Recompose component this [FocusNode] is hosted in.
+ *
+ * We need to trigger re-composition manually because we determine focus during composition, and
+ * editing an @Model object during composition does not trigger a re-composition.
+ *
+ * TODO (b/144897112): Remove manual recomposition.
+ */
+ private lateinit var _recompose: () -> Unit
+ var recompose: () -> Unit
+ get() = _recompose
+ set(value) { _recompose = value }
+
+ /**
+ * The focus state for the current component. When the component is in the [Active] state, it
+ * receives key events and other actions. We use [FocusDetailedState]s internally and
+ * developers have the option to build their components using [FocusDetailedState], or a
+ * subset of states defined in [FocusState][androidx.ui.focus.FocusState].
+ */
+ var focusState: FocusDetailedState = Inactive
+ internal set
+
+ /**
+ * The [LayoutCoordinates] of the [OnChildPositioned][androidx.ui.core.OnChildPositioned]
+ * component that hosts the child components of this [FocusNode].
+ */
+ @Suppress("KDocUnresolvedReference")
+ var layoutCoordinates: LayoutCoordinates? = null
+
+ /**
+ * The list of focusable children of this [FocusNode]. The [ComponentNode] base class defines
+ * [children] of this node, but the [focusableChildren] set includes all the [FocusNode]s
+ * that are directly reachable from this [FocusNode].
+ */
+ private val focusableChildren = mutableSetOf<FocusNode>()
+
+ /**
+ * The [FocusNode] from the set of [focusableChildren] that is currently [Active].
+ */
+ private var focusedChild: FocusNode? = null
+
+ /**
+ * Add this focusable child to the parent's focusable children list.
+ */
+ override fun attach(owner: Owner) {
+ findParentFocusNode()?.focusableChildren?.add(this)
+ super.attach(owner)
+ }
+
+ /**
+ * Remove this focusable child from the parent's focusable children list.
+ */
+ override fun detach() {
+ // TODO (b/144119129): If this node is focused, let the parent know that it needs to
+ // grant focus to another focus node.
+ super.detach()
+ findParentFocusNode()?.focusableChildren?.remove(this)
+ }
+
+ /**
+ * Request focus for this node.
+ *
+ * @param propagateFocus Whether the focus should be propagated to the node's children.
+ *
+ * In Compose, the parent [FocusNode] controls focus for its focusable children.Calling this
+ * function will send a focus request to this [FocusNode]'s parent [FocusNode].
+ */
+ fun requestFocus(propagateFocus: Boolean = true) {
+
+ when (focusState) {
+ Active, Captured, Disabled -> return
+ ActiveParent -> {
+ /** We don't need to do anything if [propagateFocus] is true,
+ since this subtree already has focus.*/
+ if (!propagateFocus && focusedChild?.clearFocus() ?: true) {
+ grantFocus(propagateFocus)
+ }
+ }
+ Inactive -> {
+ val focusParent = findParentFocusNode()
+ if (focusParent == null) {
+ // TODO (b/144116848) : Find out if the view hosting this composable is in focus.
+ // The top most focusable is [Active] only if the view hosting this composable is
+ // in focus. For now, we are making the assumption that our activity has only one
+ // view, and it is always in focus.
+ // Also, if the host AndroidComposeView does not have focus, request focus.
+ // Proceed to grant focus to this node only if the host view gains focus.
+ grantFocus(propagateFocus)
+ recompose()
+ } else {
+ focusParent.requestFocusForChild(this, propagateFocus)
+ }
+ }
+ }
+ }
+
+ /**
+ * Deny requests to clear focus.
+ *
+ * This is used when a component wants to hold onto focus (eg. A phone number field with an
+ * invalid number.
+ *
+ * @return true if the focus was successfully captured. False otherwise.
+ */
+ fun captureFocus(): Boolean {
+ if (focusState == Active) {
+ focusState = Captured
+ return true
+ } else {
+ return false
+ }
+ }
+
+ /**
+ * When the node is in the [Captured] state, it rejects all requests to clear focus. Calling
+ * [freeFocus] puts the node in the [Active] state, where it is no longer preventing other
+ * nodes from requesting focus.
+ *
+ * @return true if the captured focus was released. If the node is not in the [Captured]
+ * state. this function returns false to indicate that this operation was a no-op.
+ */
+ fun freeFocus(): Boolean {
+ if (focusState == Captured) {
+ focusState = Active
+ return true
+ } else {
+ return false
+ }
+ }
+
+ /**
+ * This function grants focus to this node.
+ *
+ * @param propagateFocus Whether the focus should be propagated to the node's children.
+ *
+ * Note: This function is private, and should only be called by a parent [FocusNode] to grant
+ * focus to one of its child [FocusNode]s.
+ */
+ private fun grantFocus(propagateFocus: Boolean) {
+
+ // TODO (b/144126570) use ChildFocusablility.
+ // For now we assume children get focus before parent).
+
+ // TODO (b/144126759): Design a system to decide which child get's focus.
+ // for now we grant focus to the first child.
+ val focusedCandidate = focusableChildren.firstOrNull()
+
+ if (focusedCandidate == null || !propagateFocus) {
+ // No Focused Children, or we don't want to propagate focus to children.
+ focusState = Active
+ } else {
+ focusState = ActiveParent
+ focusedChild = focusedCandidate
+ focusedCandidate.grantFocus(propagateFocus)
+ focusedCandidate.recompose()
+ }
+ }
+
+ /**
+ * This function clears focus from this node.
+ *
+ * Note: This function is private, and should only be called by a parent [FocusNode] to clear
+ * focus from one of its child [FocusNode]s.
+ */
+ private fun clearFocus(): Boolean {
+ return when (focusState) {
+
+ Active -> {
+ focusState = Inactive
+ true
+ }
+ /**
+ * If the node is [ActiveParent], we need to clear focus from the [Active] descendant
+ * first, before clearing focus of this node.
+ */
+ ActiveParent -> focusedChild?.clearFocus() ?: error("No Focused Child")
+ /**
+ * If the node is [Captured], deny requests to clear focus.
+ */
+ Captured -> false
+ /**
+ * Nothing to do if the node is not focused. Even though the node ends up in a
+ * cleared state, we return false to indicate that we didn't change any state (This
+ * return value is used to trigger a recomposition, so returning false will not
+ * trigger any recomposition).
+ */
+ Inactive, Disabled -> false
+ }
+ }
+
+ /**
+ * Focusable children of this [FocusNode] can use this function to request focus.
+ *
+ * @param childNode: The node that is requesting focus.
+ * @param propagateFocus Whether the focus should be propagated to the node's children.
+ * @return true if focus was granted, false otherwise.
+ */
+ private fun requestFocusForChild(childNode: FocusNode, propagateFocus: Boolean): Boolean {
+
+ // Only this node's children can ask for focus.
+ if (!focusableChildren.contains(childNode)) {
+ error("Non child node cannot request focus.")
+ }
+
+ return when (focusState) {
+ /**
+ * If this node is [Active], it can give focus to the requesting child.
+ */
+ Active -> {
+ focusState = ActiveParent
+ focusedChild = childNode
+ childNode.grantFocus(propagateFocus)
+ recompose()
+ true
+ }
+ /**
+ * If this node is [ActiveParent] ie, one of the parent's descendants is [Active],
+ * remove focus from the currently focused child and grant it to the requesting child.
+ */
+ ActiveParent -> {
+ val previouslyFocusedNode = focusedChild ?: error("no focusedChild found")
+ if (previouslyFocusedNode.clearFocus()) {
+ focusedChild = childNode
+ childNode.grantFocus(propagateFocus)
+ previouslyFocusedNode.recompose()
+ childNode.recompose()
+ true
+ } else {
+ // Currently focused component does not want to give up focus.
+ false
+ }
+ }
+ /**
+ * If this node is not [Active], we must gain focus first before granting it
+ * to the requesting child.
+ */
+ Inactive -> {
+ val focusParent = findParentFocusNode()
+ if (focusParent == null) {
+ requestFocusForOwner()
+ // If the owner successfully gains focus, proceed otherwise return false.
+ if (ownerHasFocus()) {
+ focusState = Active
+ requestFocusForChild(childNode, propagateFocus)
+ } else {
+ false
+ }
+ } else if (focusParent.requestFocusForChild(this, propagateFocus = false)) {
+ requestFocusForChild(childNode, propagateFocus)
+ } else {
+ // Could not gain focus, so have no focus to give.
+ false
+ }
+ }
+ /**
+ * If this node is [Captured], decline requests from the children.
+ */
+ Captured -> false
+ /**
+ * Children of a [Disabled] parent should also be [Disabled].
+ */
+ Disabled -> error("non root FocusNode needs a focusable parent")
+ }
+ }
+}
+
+/**
* Backing node for the Draw component.
*/
class DrawNode : ComponentNode() {
@@ -404,6 +696,7 @@ class LayoutNode : ComponentNode(), Measurable {
measurables: List<Measurable>,
constraints: Constraints
): MeasureScope.LayoutResult
+
/**
* The function used to calculate [IntrinsicMeasurable.minIntrinsicWidth].
*/
@@ -412,6 +705,7 @@ class LayoutNode : ComponentNode(), Measurable {
measurables: List<IntrinsicMeasurable>,
h: IntPx
): IntPx
+
/**
* The lambda used to calculate [IntrinsicMeasurable.minIntrinsicHeight].
*/
@@ -420,6 +714,7 @@ class LayoutNode : ComponentNode(), Measurable {
measurables: List<IntrinsicMeasurable>,
w: IntPx
): IntPx
+
/**
* The function used to calculate [IntrinsicMeasurable.maxIntrinsicWidth].
*/
@@ -428,6 +723,7 @@ class LayoutNode : ComponentNode(), Measurable {
measurables: List<IntrinsicMeasurable>,
h: IntPx
): IntPx
+
/**
* The lambda used to calculate [IntrinsicMeasurable.maxIntrinsicHeight].
*/
@@ -1097,28 +1393,31 @@ class SemanticsComponentNode(
* merged with the semantics of any ancestors (if the ancestor allows that).
*
* Whether descendants of this composable can add their semantic information to the
- * [SemanticsNode] introduced by this configuration is controlled by
- * [explicitChildNodes].
+ * [SemanticsNode][androidx.ui.core.semantics.SemanticNode] introduced by this configuration is
+ * controlled by [explicitChildNodes].
*/
+ @Suppress("KDocUnresolvedReference")
container: Boolean = false,
/**
* Whether descendants of this composable are allowed to add semantic information
- * to the [SemanticsNode] annotated by this composable.
+ * to the [SemanticsNode][androidx.ui.core.semantics.SemanticNode] annotated by this composable.
*
- * When set to false descendants are allowed to annotate [SemanticNode]s of
+ * When set to false descendants are allowed to annotate [SemanticNodes][androidx.ui.core
+ * .semantics.SemanticNode] of
* their parent with the semantic information they want to contribute to the
* semantic tree.
* When set to true the only way for descendants to contribute semantic
* information to the semantic tree is to introduce new explicit
- * [SemanticNode]s to the tree.
+ * [SemanticNodes][androidx.ui.core.semantics.SemanticNode] to the tree.
*
* If the semantics properties of this node include
- * [SemanticsProperties.scopesRoute] set to true, then [explicitChildNodes]
- * must be true also.
+ * [scopesRoute][androidx.ui.semantics.SemanticsProperties.scopesRoute] set to
+ * true, then [explicitChildNodes] must be true also.
*
* This setting is often used in combination with [SemanticsConfiguration.isSemanticBoundary]
* to create semantic boundaries that are either writable or not for children.
*/
+ @Suppress("KDocUnresolvedReference")
explicitChildNodes: Boolean = false
) : ComponentNode() {
private var needsSemanticsUpdate = true
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/focus/FocusNodeUtils.kt b/ui/ui-platform/src/main/java/androidx/ui/core/focus/FocusNodeUtils.kt
new file mode 100644
index 00000000000..795de8fb7be
--- /dev/null
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/focus/FocusNodeUtils.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 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 androidx.ui.core.focus
+
+import androidx.ui.core.FocusNode
+
+/**
+ * Find the first ancestor that is a [FocusNode].
+ */
+internal fun FocusNode.findParentFocusNode(): FocusNode? {
+ var focusableParent = parent
+ while (focusableParent != null) {
+ if (focusableParent is FocusNode) {
+ return focusableParent
+ } else {
+ focusableParent = focusableParent.parent
+ }
+ }
+ return null
+}
+
+internal fun FocusNode.ownerHasFocus(): Boolean {
+ // TODO(b/144895515): Read the focus state from the owner.
+ return true
+}
+
+internal fun FocusNode.requestFocusForOwner() {
+ // TODO(b/144893832): Ask the owner to request focus.
+}
+
+/**
+ * Checks the focus state of the [Owner][androidx.ui.core.Owner] and Initializes the focus state of
+ * the node.
+ *
+ * Note: This function acts only on the root node. It is a no-op for other nodes.
+ */
+fun FocusNode.initializeFocusState() {
+ if (findParentFocusNode() == null && ownerHasFocus()) {
+ requestFocus()
+ }
+} \ No newline at end of file
diff --git a/ui/ui-platform/src/test/java/androidx/ui/core/FocusNodeTest.kt b/ui/ui-platform/src/test/java/androidx/ui/core/FocusNodeTest.kt
new file mode 100644
index 00000000000..3a6b3614f02
--- /dev/null
+++ b/ui/ui-platform/src/test/java/androidx/ui/core/FocusNodeTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019 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 androidx.ui.core
+
+import androidx.test.filters.SmallTest
+import androidx.ui.focus.FocusDetailedState.Active
+import androidx.ui.focus.FocusDetailedState.Captured
+import androidx.ui.focus.FocusDetailedState.Inactive
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class FocusNodeTest {
+
+ @Test
+ fun captureReferenceToFocusNode() {
+ // Arrange.
+ val focusNode = FocusNode()
+ val focusNodeRef = Ref<FocusNode>()
+
+ // Act.
+ focusNode.ref = focusNodeRef
+
+ // Assert.
+ assertThat(focusNodeRef.value).isEqualTo(focusNode)
+ }
+
+ @Test
+ fun defaultFocusState() {
+ // Arrange.
+ val focusNode = FocusNode()
+
+ // Assert.
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+ }
+
+ @Test
+ fun defaultLayoutCoordinates() {
+ // Arrange.
+ val focusNode = FocusNode()
+
+ // Assert.
+ assertThat(focusNode.layoutCoordinates).isNull()
+ }
+
+ @Test
+ fun captureFocusfromActiveState() {
+ // Arrange.
+ val focusNode = FocusNode().apply { focusState = Active }
+
+ // Act.
+ focusNode.captureFocus()
+
+ // Assert.
+ assertThat(focusNode.focusState).isEqualTo(Captured)
+ }
+
+ @Test
+ fun captureFocusfromNonActiveState() {
+ // Arrange.
+ val focusNode = FocusNode().apply { focusState = Inactive }
+
+ // Act.
+ focusNode.captureFocus()
+
+ // Assert.
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+ }
+
+ @Test
+ fun freeFocusfromCapturedState() {
+ // Arrange.
+ val focusNode = FocusNode().apply { focusState = Captured }
+
+ // Act.
+ focusNode.freeFocus()
+
+ // Assert.
+ assertThat(focusNode.focusState).isEqualTo(
+ Active
+ )
+ }
+
+ @Test
+ fun freeFocusfromNonActiveState() {
+ // Arrange.
+ val focusNode = FocusNode().apply { focusState = Inactive }
+
+ // Act.
+ focusNode.freeFocus()
+
+ // Assert.
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+ }
+} \ No newline at end of file
diff --git a/ui/ui-platform/src/test/java/androidx/ui/core/focus/FindParentFocusNodeTest.kt b/ui/ui-platform/src/test/java/androidx/ui/core/focus/FindParentFocusNodeTest.kt
new file mode 100644
index 00000000000..d29c95fac0b
--- /dev/null
+++ b/ui/ui-platform/src/test/java/androidx/ui/core/focus/FindParentFocusNodeTest.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2019 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 androidx.ui.core.focus
+
+import androidx.test.filters.SmallTest
+import androidx.ui.core.FocusNode
+import androidx.ui.core.LayoutNode
+import androidx.ui.core.PointerInputNode
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class FindParentFocusNodeTest {
+
+ @Test
+ fun noParentReturnsNull() {
+ // Arrange.
+ val focusNode = FocusNode()
+
+ // Act.
+ val parentFousNode = focusNode.findParentFocusNode()
+
+ // Assert.
+ assertThat(parentFousNode).isNull()
+ }
+
+ @Test
+ fun returnsParent() {
+ // Arrange.
+ val focusNode = FocusNode()
+ val parentFocusNode = FocusNode()
+ parentFocusNode.emitInsertAt(0, focusNode)
+
+ // Act.
+ val parent = focusNode.findParentFocusNode()
+
+ // Assert.
+ assertThat(parent).isEqualTo(parentFocusNode)
+ }
+
+ @Test
+ fun returnsImmediateParent() {
+ // Arrange.
+ val focusNode = FocusNode()
+ val parentFocusNode = FocusNode()
+ val grandparentFocusNode = FocusNode()
+ grandparentFocusNode.emitInsertAt(0, parentFocusNode)
+ parentFocusNode.emitInsertAt(0, focusNode)
+
+ // Act.
+ val parent = focusNode.findParentFocusNode()
+
+ // Assert.
+ assertThat(parent).isEqualTo(parentFocusNode)
+ }
+
+ @Test
+ fun ignoresIntermediateComponentNodes() {
+ // Arrange.
+ val focusNode = FocusNode()
+ val intermediatePointerInputNode = PointerInputNode()
+ val intermediateLayoutNode = LayoutNode()
+ val parentFocusNode = FocusNode()
+ parentFocusNode.emitInsertAt(0, intermediatePointerInputNode)
+ intermediatePointerInputNode.emitInsertAt(0, intermediateLayoutNode)
+ intermediateLayoutNode.emitInsertAt(0, focusNode)
+
+ // Act.
+ val parent = focusNode.findParentFocusNode()
+
+ // Assert.
+ assertThat(parent).isEqualTo(parentFocusNode)
+ }
+} \ No newline at end of file
diff --git a/ui/ui-platform/src/test/java/androidx/ui/core/focus/RequestFocusTest.kt b/ui/ui-platform/src/test/java/androidx/ui/core/focus/RequestFocusTest.kt
new file mode 100644
index 00000000000..2c271dbc8f5
--- /dev/null
+++ b/ui/ui-platform/src/test/java/androidx/ui/core/focus/RequestFocusTest.kt
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2019 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 androidx.ui.core.focus
+
+import androidx.test.filters.SmallTest
+import androidx.ui.core.FocusNode
+import androidx.ui.core.Owner
+import androidx.ui.focus.FocusDetailedState.Active
+import androidx.ui.focus.FocusDetailedState.ActiveParent
+import androidx.ui.focus.FocusDetailedState.Captured
+import androidx.ui.focus.FocusDetailedState.Disabled
+import androidx.ui.focus.FocusDetailedState.Inactive
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito.mock
+
+@SmallTest
+@RunWith(Parameterized::class)
+class RequestFocusTest(val propagateFocus: Boolean) {
+ lateinit var host: Owner
+
+ @Before
+ fun setup() {
+ host = mock(Owner::class.java)
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "propagateFocus = {0}")
+ fun initParameters() = listOf(true, false)
+ }
+
+ @Test
+ fun activeComponent() {
+ // Arrange.
+ val focusNode = FocusNode().apply {
+ focusState = Active
+ recompose = {}
+ }
+
+ // Act.
+ focusNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(focusNode.focusState).isEqualTo(Active)
+ }
+
+ @Test
+ fun capturedComponent() {
+ // Arrange.
+ val focusNode = FocusNode().apply {
+ focusState = Captured
+ recompose = {}
+ }
+
+ // Act.
+ focusNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(focusNode.focusState).isEqualTo(Captured)
+ }
+
+ @Test
+ fun disabledComponent() {
+ // Arrange.
+ val focusNode = FocusNode().apply {
+ focusState = Disabled
+ recompose = {}
+ }
+
+ // Act.
+ focusNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(focusNode.focusState).isEqualTo(Disabled)
+ }
+
+ @Test
+ fun rootNode() {
+ // Arrange.
+ val rootNode = FocusNode().apply { recompose = {} }
+
+ // Act.
+ rootNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(rootNode.focusState).isEqualTo(Active)
+ }
+
+ @Test
+ fun rootNodeWithChildren() {
+ // Arrange.
+ val childNode = FocusNode().apply { recompose = {} }
+ val rootNode = FocusNode().apply {
+ recompose = {}
+ attach(host)
+ emitInsertAt(0, childNode)
+ }
+
+ // Act.
+ rootNode.requestFocus(propagateFocus)
+
+ // Assert.
+ when (propagateFocus) {
+ true -> assertThat(rootNode.focusState).isEqualTo(ActiveParent)
+ false -> assertThat(rootNode.focusState).isEqualTo(Active)
+ }
+ }
+
+ @Test
+ fun parentNodeWithNoFocusedAncestor() {
+ // Arrange.
+ val childNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply { recompose = {} }
+ val grandparentNode = FocusNode().apply {
+ recompose = {}
+ attach(host)
+ emitInsertAt(0, parentNode)
+ }
+ parentNode.emitInsertAt(0, childNode)
+
+ // Act.
+ parentNode.requestFocus(propagateFocus)
+
+ // Assert.
+ when (propagateFocus) {
+ true -> assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ false -> assertThat(parentNode.focusState).isEqualTo(Active)
+ }
+ }
+
+ @Test
+ fun parentNodeWithNoFocusedAncestor_childRequestsFocus() {
+ // Arrange.
+ val childNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply { recompose = {} }
+ val grandparentNode = FocusNode().apply {
+ recompose = {}
+ attach(host)
+ emitInsertAt(0, parentNode)
+ }
+ parentNode.emitInsertAt(0, childNode)
+
+ // Act.
+ childNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ }
+
+ @Test
+ fun childNodeWithNoFocusedAncestor() {
+ // Arrange.
+ val childNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply { recompose = {} }
+ val grandparentNode = FocusNode().apply {
+ recompose = {}
+ attach(host)
+ emitInsertAt(0, parentNode)
+ }
+ parentNode.emitInsertAt(0, childNode)
+
+ // Act.
+ childNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(childNode.focusState).isEqualTo(Active)
+ }
+
+ @Test
+ fun requestFocus_parentIsFocused() {
+ // Arrange.
+ val focusNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply {
+ attach(host)
+ focusState = Active
+ recompose = {}
+ emitInsertAt(0, focusNode)
+ }
+
+ // Verify Setup.
+ assertThat(parentNode.focusState).isEqualTo(Active)
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+
+ // After executing requestFocus, siblingNode will be 'Active'.
+ focusNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Active)
+ }
+
+ @Test
+ fun requestFocus_childIsFocused() {
+ // Arrange.
+ val focusNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply {
+ attach(host)
+ recompose = {}
+ emitInsertAt(0, focusNode)
+ }
+ focusNode.requestFocus(propagateFocus)
+
+ // Verify Setup.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Active)
+
+ // Act.
+ parentNode.requestFocus(propagateFocus)
+
+ // Assert.
+ when (propagateFocus) {
+ true -> {
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Active)
+ }
+ false -> {
+ assertThat(parentNode.focusState).isEqualTo(Active)
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+ }
+ }
+ }
+
+ @Test
+ fun requestFocus_childHasCapturedFocus() {
+ // Arrange.
+ val focusNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply {
+ attach(host)
+ recompose = {}
+ emitInsertAt(0, focusNode)
+ }
+ focusNode.apply {
+ requestFocus(propagateFocus)
+ captureFocus()
+ }
+
+ // Verify Setup.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Captured)
+
+ // Act.
+ parentNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Captured)
+ }
+
+ @Test
+ fun requestFocus_siblingIsFocused() {
+ // Arrange.
+ val focusNode = FocusNode().apply { recompose = {} }
+ val siblingNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply {
+ recompose = {}
+ focusState = Active
+ attach(host)
+ emitInsertAt(0, focusNode)
+ emitInsertAt(1, siblingNode)
+ }
+ // After executing requestFocus, siblingNode will be 'Active'.
+ siblingNode.requestFocus(propagateFocus)
+
+ // Verify Setup.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+ assertThat(siblingNode.focusState).isEqualTo(Active)
+
+ // Act.
+ focusNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Active)
+ assertThat(siblingNode.focusState).isEqualTo(Inactive)
+ }
+
+ @Test
+ fun requestFocus_siblingHasCapturedFocused() {
+ // Arrange.
+ val focusNode = FocusNode().apply { recompose = {} }
+ val siblingNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply {
+ recompose = {}
+ focusState = Active
+ attach(host)
+ emitInsertAt(0, focusNode)
+ emitInsertAt(1, siblingNode)
+ }
+ // After executing requestFocus, siblingNode will be 'Active'.
+ siblingNode.apply {
+ requestFocus(propagateFocus)
+ captureFocus()
+ }
+
+ // Verify Setup.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+ assertThat(siblingNode.focusState).isEqualTo(Captured)
+
+ // Act.
+ focusNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+ assertThat(siblingNode.focusState).isEqualTo(Captured)
+ }
+
+ @Test
+ fun requestFocus_cousinIsFocused() {
+ // Arrange.
+ val focusNode = FocusNode().apply {
+ focusState = Inactive
+ recompose = {}
+ }
+
+ val cousinNode = FocusNode().apply {
+ focusState = Inactive
+ recompose = {}
+ }
+
+ val parentNode = FocusNode().apply {
+ focusState = Inactive
+ recompose = {}
+ }
+ val auntNode = FocusNode().apply {
+ focusState = Inactive
+ recompose = {}
+ }
+
+ val grandparentNode = FocusNode().apply {
+ focusState = Active
+ recompose = {}
+ }
+
+ grandparentNode.apply {
+ attach(host)
+ emitInsertAt(0, parentNode)
+ emitInsertAt(1, auntNode)
+ }
+
+ parentNode.emitInsertAt(0, focusNode)
+ auntNode.emitInsertAt(0, cousinNode)
+ cousinNode.requestFocus(propagateFocus)
+
+ // Verify Setup.
+ assertThat(cousinNode.focusState).isEqualTo(Active)
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+
+ // Act.
+ focusNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(cousinNode.focusState).isEqualTo(Inactive)
+ assertThat(focusNode.focusState).isEqualTo(Active)
+ }
+
+ @Test
+ fun requestFocus_grandParentIsFocused() {
+ // Arrange.
+ val focusNode = FocusNode().apply { recompose = {} }
+ val parentNode = FocusNode().apply { recompose = {} }
+ val grandparentNode = FocusNode().apply { recompose = {} }
+
+ grandparentNode.apply {
+ attach(host)
+ emitInsertAt(0, parentNode)
+ focusState = Active
+ }
+ parentNode.emitInsertAt(0, focusNode)
+
+ // Verify Setup.
+ assertThat(grandparentNode.focusState).isEqualTo(Active)
+ assertThat(parentNode.focusState).isEqualTo(Inactive)
+ assertThat(focusNode.focusState).isEqualTo(Inactive)
+
+ // Act.
+ focusNode.requestFocus(propagateFocus)
+
+ // Assert.
+ assertThat(grandparentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(parentNode.focusState).isEqualTo(ActiveParent)
+ assertThat(focusNode.focusState).isEqualTo(Active)
+ }
+} \ No newline at end of file
diff --git a/ui/ui-test/build.gradle b/ui/ui-test/build.gradle
index b955db81aab..78872d5c478 100644
--- a/ui/ui-test/build.gradle
+++ b/ui/ui-test/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
implementation(ANDROIDX_TEST_RULES)
diff --git a/ui/ui-text/build.gradle b/ui/ui-text/build.gradle
index 9305e319091..185fbee2e09 100644
--- a/ui/ui-text/build.gradle
+++ b/ui/ui-text/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
// TODO: Non-Kotlin dependency, move to Android-specific code
diff --git a/ui/ui-text/integration-tests/samples/build.gradle b/ui/ui-text/integration-tests/samples/build.gradle
index d6194ef8553..b58854bb61b 100644
--- a/ui/ui-text/integration-tests/samples/build.gradle
+++ b/ui/ui-text/integration-tests/samples/build.gradle
@@ -26,7 +26,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-text/integration-tests/text-demos/build.gradle b/ui/ui-text/integration-tests/text-demos/build.gradle
index b71916523e6..5b72f84fb1d 100644
--- a/ui/ui-text/integration-tests/text-demos/build.gradle
+++ b/ui/ui-text/integration-tests/text-demos/build.gradle
@@ -10,7 +10,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-tooling/build.gradle b/ui/ui-tooling/build.gradle
index 209822315cd..bd5b8a652b1 100644
--- a/ui/ui-tooling/build.gradle
+++ b/ui/ui-tooling/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
implementation(KOTLIN_STDLIB)
diff --git a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt
index 7d6f77e75ca..3fa2bcfa6e0 100644
--- a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt
+++ b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt
@@ -76,6 +76,21 @@ class ComposeViewAdapterTest {
}
}
+ @Test
+ fun previewInClass() {
+ activityTestRule.runOnUiThread {
+ composeViewAdapter.init(
+ "androidx.ui.tooling.TestGroup",
+ "InClassPreview",
+ debugViewInfos = true
+ )
+ }
+
+ activityTestRule.runOnUiThread {
+ assertTrue(composeViewAdapter.viewInfos.isNotEmpty())
+ }
+ }
+
companion object {
class TestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/SimpleComposablePreview.kt b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/SimpleComposablePreview.kt
index 6f61093b283..f3a214318f1 100644
--- a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/SimpleComposablePreview.kt
+++ b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/SimpleComposablePreview.kt
@@ -36,4 +36,14 @@ private fun PrivateSimpleComposablePreview() {
Surface(color = Color.Red) {
Text("Private Hello world")
}
+}
+
+class TestGroup {
+ @Preview
+ @Composable
+ fun InClassPreview() {
+ Surface(color = Color.Red) {
+ Text("In class")
+ }
+ }
} \ No newline at end of file
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
index a4080293b1f..652a903fd8a 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
@@ -38,6 +38,7 @@ import androidx.ui.tooling.Group
import androidx.ui.tooling.Inspectable
import androidx.ui.tooling.asTree
import androidx.ui.tooling.tables
+import java.lang.reflect.Modifier
const val TOOLS_NS_URI = "http://schemas.android.com/tools"
@@ -215,7 +216,16 @@ internal class ComposeViewAdapter : FrameLayout {
val composableClass = Class.forName(className)
val method = composableClass.getDeclaredMethod(methodName)
method.isAccessible = true
- method.invoke(null)
+
+ if (Modifier.isStatic(method.modifiers)) {
+ // This is a top level or static method
+ method.invoke(null)
+ } else {
+ // The method is part of a class. We try to instantiate the class with an
+ // empty constructor.
+ val instance = composableClass.getConstructor().newInstance()
+ method.invoke(instance)
+ }
} catch (e: ReflectiveOperationException) {
throw ClassNotFoundException("Composable Method not found", e)
}
diff --git a/ui/ui-vector/build.gradle b/ui/ui-vector/build.gradle
index d994acf054d..7a109d3ba13 100644
--- a/ui/ui-vector/build.gradle
+++ b/ui/ui-vector/build.gradle
@@ -29,7 +29,7 @@ plugins {
}
dependencies {
- kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+ kotlinPlugin project(path: ":compose:compose-compiler")
api project(':ui:ui-core')
implementation "androidx.collection:collection:1.0.0"
diff --git a/work/workmanager-lint/src/test/java/androidx/work/lint/BadConfigurationProviderTest.kt b/work/workmanager-lint/src/test/java/androidx/work/lint/BadConfigurationProviderTest.kt
index ba4af629f99..771a3f40552 100644
--- a/work/workmanager-lint/src/test/java/androidx/work/lint/BadConfigurationProviderTest.kt
+++ b/work/workmanager-lint/src/test/java/androidx/work/lint/BadConfigurationProviderTest.kt
@@ -45,7 +45,7 @@ class BadConfigurationProviderTest {
WORK_MANAGER_CONFIGURATION_PROVIDER,
customApplication
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(BadConfigurationProviderIssueDetector.ISSUE)
.run()
.expectClean()
@@ -88,7 +88,7 @@ class BadConfigurationProviderTest {
customApplication,
invalidProvider
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(BadConfigurationProviderIssueDetector.ISSUE)
.run()
.expect("""
@@ -123,7 +123,7 @@ class BadConfigurationProviderTest {
WORK_MANAGER_CONFIGURATION_PROVIDER,
customApplication
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(BadConfigurationProviderIssueDetector.ISSUE)
.run()
.expectClean()
diff --git a/work/workmanager-lint/src/test/java/androidx/work/lint/Lint.kt b/work/workmanager-lint/src/test/java/androidx/work/lint/Lint.kt
new file mode 100644
index 00000000000..915c853040c
--- /dev/null
+++ b/work/workmanager-lint/src/test/java/androidx/work/lint/Lint.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 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 androidx.work.lint
+
+import java.io.File
+import java.io.InputStream
+import java.util.Properties
+
+fun sdkDirectory(): Lazy<File> = lazy {
+ var stream: InputStream? = null
+ try {
+ stream = Stubs::class.java.classLoader.getResourceAsStream("sdk.prop")
+ val properties = Properties()
+ properties.load(stream)
+ File(properties["sdk.dir"] as String)
+ } finally {
+ stream?.close()
+ }
+}
diff --git a/work/workmanager-lint/src/test/java/androidx/work/lint/PeriodicEnqueueIssueDetectorTest.kt b/work/workmanager-lint/src/test/java/androidx/work/lint/PeriodicEnqueueIssueDetectorTest.kt
index ab0dd04ae02..10395fe4fe7 100644
--- a/work/workmanager-lint/src/test/java/androidx/work/lint/PeriodicEnqueueIssueDetectorTest.kt
+++ b/work/workmanager-lint/src/test/java/androidx/work/lint/PeriodicEnqueueIssueDetectorTest.kt
@@ -54,7 +54,7 @@ class PeriodicEnqueueIssueDetectorTest {
PERIODIC_WORK_REQUEST,
snippet
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(PeriodicEnqueueIssueDetector.ISSUE)
.run()
.expect("""
@@ -94,7 +94,7 @@ class PeriodicEnqueueIssueDetectorTest {
PERIODIC_WORK_REQUEST,
snippet
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(PeriodicEnqueueIssueDetector.ISSUE)
.run()
.expect("""
@@ -136,7 +136,7 @@ class PeriodicEnqueueIssueDetectorTest {
PERIODIC_WORK_REQUEST,
snippet
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(PeriodicEnqueueIssueDetector.ISSUE)
.run()
.expect("""
@@ -175,7 +175,7 @@ class PeriodicEnqueueIssueDetectorTest {
PERIODIC_WORK_REQUEST,
snippet
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(PeriodicEnqueueIssueDetector.ISSUE)
.run()
.expectClean()
@@ -208,7 +208,7 @@ class PeriodicEnqueueIssueDetectorTest {
PERIODIC_WORK_REQUEST,
snippet
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(PeriodicEnqueueIssueDetector.ISSUE)
.run()
.expectClean()
diff --git a/work/workmanager-lint/src/test/java/androidx/work/lint/RemoveWorkManagerInitializerDetectorTest.kt b/work/workmanager-lint/src/test/java/androidx/work/lint/RemoveWorkManagerInitializerDetectorTest.kt
index 2a472bf414f..d6dcce2dd93 100644
--- a/work/workmanager-lint/src/test/java/androidx/work/lint/RemoveWorkManagerInitializerDetectorTest.kt
+++ b/work/workmanager-lint/src/test/java/androidx/work/lint/RemoveWorkManagerInitializerDetectorTest.kt
@@ -68,7 +68,7 @@ class RemoveWorkManagerInitializerDetectorTest {
WORK_MANAGER_CONFIGURATION_PROVIDER,
customApplication
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(RemoveWorkManagerInitializerDetector.ISSUE)
.run()
.expectClean()
@@ -115,7 +115,7 @@ class RemoveWorkManagerInitializerDetectorTest {
WORK_MANAGER_CONFIGURATION_PROVIDER,
customApplication
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(RemoveWorkManagerInitializerDetector.ISSUE)
.run()
.expectClean()
@@ -160,7 +160,7 @@ class RemoveWorkManagerInitializerDetectorTest {
WORK_MANAGER_CONFIGURATION_PROVIDER,
customApplication
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(RemoveWorkManagerInitializerDetector.ISSUE)
.run()
.expect("""
@@ -214,7 +214,7 @@ class RemoveWorkManagerInitializerDetectorTest {
WORK_MANAGER_CONFIGURATION_PROVIDER,
customApplication
)
- .allowMissingSdk()
+ .sdkHome(sdkDirectory().value)
.issues(RemoveWorkManagerInitializerDetector.ISSUE)
.run()
.expect("""