Merge "Move sharetarget module from alpha02 to beta01" into androidx-master-dev
diff --git a/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java b/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
index 6dc60c9..44810bf 100644
--- a/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
+++ b/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
@@ -103,6 +103,11 @@
CustomTabsSessionToken(@Nullable ICustomTabsCallback callbackBinder,
@Nullable PendingIntent sessionId) {
+ if (callbackBinder == null && sessionId == null) {
+ throw new IllegalStateException("CustomTabsSessionToken must have either a session id "
+ + "or a callback (or both).");
+ }
+
mCallbackBinder = callbackBinder;
mSessionId = sessionId;
@@ -193,10 +198,16 @@
public boolean equals(Object o) {
if (!(o instanceof CustomTabsSessionToken)) return false;
CustomTabsSessionToken other = (CustomTabsSessionToken) o;
- if (mSessionId != null && other.getId() != null) return mSessionId.equals(other.getId());
- return other.getCallbackBinder() != null
- && other.getCallbackBinder().equals(mCallbackBinder.asBinder());
+ PendingIntent otherSessionId = other.getId();
+ // If one object has a session id and the other one doesn't, they're not equal.
+ if ((mSessionId == null) != (otherSessionId == null)) return false;
+
+ // If both objects have an id, check that they are equal.
+ if (mSessionId != null) return mSessionId.equals(otherSessionId);
+
+ // Otherwise check for binder equality.
+ return getCallbackBinder().equals(other.getCallbackBinder());
}
/**
diff --git a/browser/src/test/java/androidx/browser/customtabs/CustomTabsSessionTokenTest.java b/browser/src/test/java/androidx/browser/customtabs/CustomTabsSessionTokenTest.java
new file mode 100644
index 0000000..b64cd51
--- /dev/null
+++ b/browser/src/test/java/androidx/browser/customtabs/CustomTabsSessionTokenTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.browser.customtabs;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.support.customtabs.ICustomTabsCallback;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+/**
+ * Tests for {@link CustomTabsSessionToken}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+@SmallTest
+public class CustomTabsSessionTokenTest {
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ mContext = ApplicationProvider.getApplicationContext();
+ }
+
+ @Test
+ public void testEquality_withId() {
+ CustomTabsSessionToken token1 = new CustomTabsSessionToken(
+ new CustomTabsSessionToken.MockCallback(),
+ createSessionId(27)
+ );
+
+ CustomTabsSessionToken token2 = new CustomTabsSessionToken(
+ new CustomTabsSessionToken.MockCallback(),
+ createSessionId(27)
+ );
+
+ assertEquals(token1, token2);
+ }
+
+ @Test
+ public void testNonEquality_withId() {
+ // Using the same binder to ensure only the id matters.
+ ICustomTabsCallback.Stub binder = new CustomTabsSessionToken.MockCallback();
+
+ CustomTabsSessionToken token1 = new CustomTabsSessionToken(binder, createSessionId(10));
+ CustomTabsSessionToken token2 = new CustomTabsSessionToken(binder, createSessionId(20));
+
+ assertNotEquals(token1, token2);
+ }
+
+ @Test
+ public void testEquality_withBinder() {
+ ICustomTabsCallback.Stub binder = new CustomTabsSessionToken.MockCallback();
+
+ CustomTabsSessionToken token1 = new CustomTabsSessionToken(binder, null);
+ CustomTabsSessionToken token2 = new CustomTabsSessionToken(binder, null);
+
+ assertEquals(token1, token2);
+ }
+
+ @Test
+ public void testNonEquality_withBinder() {
+ ICustomTabsCallback.Stub binder1 = new CustomTabsSessionToken.MockCallback();
+ ICustomTabsCallback.Stub binder2 = new CustomTabsSessionToken.MockCallback();
+
+ CustomTabsSessionToken token1 = new CustomTabsSessionToken(binder1, null);
+ CustomTabsSessionToken token2 = new CustomTabsSessionToken(binder2, null);
+
+ assertNotEquals(token1, token2);
+ }
+
+ @Test
+ public void testNonEquality_mixedIdAndBinder() {
+ // Using the same binder to ensure only the id matters.
+ ICustomTabsCallback.Stub binder = new CustomTabsSessionToken.MockCallback();
+
+ CustomTabsSessionToken token1 = new CustomTabsSessionToken(binder, createSessionId(10));
+ // Tokens cannot be mixed if only one has an id even if the binder is the same.
+ CustomTabsSessionToken token2 = new CustomTabsSessionToken(binder, null);
+
+ assertNotEquals(token1, token2);
+ }
+
+ // This code does the same as CustomTabsClient#createSessionId but that is not necessary for the
+ // test, we just need to create a PendingIntent that uses sessionId as the requestCode.
+ private PendingIntent createSessionId(int sessionId) {
+ return PendingIntent.getActivity(mContext, sessionId, new Intent(), 0);
+ }
+
+ private void assertEquals(CustomTabsSessionToken token1, CustomTabsSessionToken token2) {
+ Assert.assertEquals(token1, token2);
+ Assert.assertEquals(token2, token1);
+
+ Assert.assertEquals(token1.hashCode(), token2.hashCode());
+ }
+
+ private void assertNotEquals(CustomTabsSessionToken token1, CustomTabsSessionToken token2) {
+ Assert.assertNotEquals(token1, token2);
+ Assert.assertNotEquals(token2, token1);
+
+ // I guess technically this could be flaky, but let's hope not...
+ Assert.assertNotEquals(token1.hashCode(), token2.hashCode());
+ }
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 3b2fa99..612ef2c 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -55,7 +55,7 @@
val DYNAMICANIMATION = Version("1.1.0-alpha03")
val DYNAMICANIMATION_KTX = Version("1.0.0-alpha03")
val EMOJI = Version("1.1.0-alpha01")
- val ENTERPRISE = Version("1.0.0-alpha04")
+ val ENTERPRISE = Version("1.0.0-beta01")
val EXIFINTERFACE = Version("1.1.0-beta02")
val FRAGMENT = Version("1.2.0-alpha03")
val FUTURES = Version("1.0.0-rc01")
diff --git a/core/core/api/1.2.0-alpha04.txt b/core/core/api/1.2.0-alpha04.txt
index ca0e30a..fb12c76 100644
--- a/core/core/api/1.2.0-alpha04.txt
+++ b/core/core/api/1.2.0-alpha04.txt
@@ -932,7 +932,8 @@
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
- method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+ method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index ca0e30a..fb12c76 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -932,7 +932,8 @@
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
- method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+ method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
diff --git a/core/core/api/restricted_1.2.0-alpha04.txt b/core/core/api/restricted_1.2.0-alpha04.txt
index ea4ca3f..50fe0b6 100644
--- a/core/core/api/restricted_1.2.0-alpha04.txt
+++ b/core/core/api/restricted_1.2.0-alpha04.txt
@@ -1035,7 +1035,8 @@
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
- method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+ method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index ea4ca3f..50fe0b6 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1035,7 +1035,8 @@
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
- method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+ method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
diff --git a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
index adcfc35..6d00a7c 100644
--- a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
@@ -259,7 +259,7 @@
ShortcutInfoCompat compat = mBuilder
.setPersons(persons)
- .setLongLived()
+ .setLongLived(true)
.build();
ShortcutInfo shortcut = compat.toShortcutInfo();
diff --git a/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java b/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
index 3631ef4..a8a8963 100644
--- a/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
@@ -494,13 +494,23 @@
}
/**
+ * @deprecated Use {@ink #setLongLived(boolean)) instead.
+ */
+ @Deprecated
+ @NonNull
+ public Builder setLongLived() {
+ mInfo.mIsLongLived = true;
+ return this;
+ }
+
+ /**
* Sets if a shortcut would be valid even if it has been unpublished/invisible by the app
* (as a dynamic or pinned shortcut). If it is long lived, it can be cached by various
* system services even after it has been unpublished as a dynamic shortcut.
*/
@NonNull
- public Builder setLongLived() {
- mInfo.mIsLongLived = true;
+ public Builder setLongLived(boolean longLived) {
+ mInfo.mIsLongLived = longLived;
return this;
}
diff --git a/enterprise/feedback/api/1.0.0-beta01.txt b/enterprise/feedback/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..040814c
--- /dev/null
+++ b/enterprise/feedback/api/1.0.0-beta01.txt
@@ -0,0 +1,59 @@
+// Signature format: 3.0
+package androidx.enterprise.feedback {
+
+ public abstract class KeyedAppState {
+ method public static androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder builder();
+ method public abstract String? getData();
+ method public abstract String getKey();
+ method public abstract String? getMessage();
+ method public abstract int getSeverity();
+ field public static final int MAX_DATA_LENGTH = 1000; // 0x3e8
+ field public static final int MAX_KEY_LENGTH = 100; // 0x64
+ field public static final int MAX_MESSAGE_LENGTH = 1000; // 0x3e8
+ field public static final int SEVERITY_ERROR = 2; // 0x2
+ field public static final int SEVERITY_INFO = 1; // 0x1
+ }
+
+ public abstract static class KeyedAppState.KeyedAppStateBuilder {
+ method public androidx.enterprise.feedback.KeyedAppState build();
+ method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setData(String?);
+ method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setKey(String);
+ method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setMessage(String?);
+ method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setSeverity(int);
+ }
+
+ public abstract class KeyedAppStatesReporter {
+ method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context);
+ method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context, java.util.concurrent.Executor);
+ method public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+ method public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+ }
+
+ public abstract class KeyedAppStatesService extends android.app.Service {
+ ctor public KeyedAppStatesService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onReceive(java.util.Collection<androidx.enterprise.feedback.ReceivedKeyedAppState!>, boolean);
+ }
+
+ public abstract class ReceivedKeyedAppState {
+ method public static androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder builder();
+ method public abstract String? getData();
+ method public abstract String getKey();
+ method public abstract String? getMessage();
+ method public abstract String getPackageName();
+ method public abstract int getSeverity();
+ method public abstract long getTimestamp();
+ }
+
+ public abstract static class ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder {
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState build();
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setData(String?);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setKey(String);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setMessage(String?);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setPackageName(String);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setSeverity(int);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setTimestamp(long);
+ }
+
+}
+
diff --git a/enterprise/feedback/api/res-1.0.0-beta01.txt b/enterprise/feedback/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/enterprise/feedback/api/res-1.0.0-beta01.txt
diff --git a/enterprise/feedback/api/restricted_1.0.0-beta01.txt b/enterprise/feedback/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..040814c
--- /dev/null
+++ b/enterprise/feedback/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,59 @@
+// Signature format: 3.0
+package androidx.enterprise.feedback {
+
+ public abstract class KeyedAppState {
+ method public static androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder builder();
+ method public abstract String? getData();
+ method public abstract String getKey();
+ method public abstract String? getMessage();
+ method public abstract int getSeverity();
+ field public static final int MAX_DATA_LENGTH = 1000; // 0x3e8
+ field public static final int MAX_KEY_LENGTH = 100; // 0x64
+ field public static final int MAX_MESSAGE_LENGTH = 1000; // 0x3e8
+ field public static final int SEVERITY_ERROR = 2; // 0x2
+ field public static final int SEVERITY_INFO = 1; // 0x1
+ }
+
+ public abstract static class KeyedAppState.KeyedAppStateBuilder {
+ method public androidx.enterprise.feedback.KeyedAppState build();
+ method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setData(String?);
+ method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setKey(String);
+ method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setMessage(String?);
+ method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setSeverity(int);
+ }
+
+ public abstract class KeyedAppStatesReporter {
+ method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context);
+ method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context, java.util.concurrent.Executor);
+ method public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+ method public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+ }
+
+ public abstract class KeyedAppStatesService extends android.app.Service {
+ ctor public KeyedAppStatesService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onReceive(java.util.Collection<androidx.enterprise.feedback.ReceivedKeyedAppState!>, boolean);
+ }
+
+ public abstract class ReceivedKeyedAppState {
+ method public static androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder builder();
+ method public abstract String? getData();
+ method public abstract String getKey();
+ method public abstract String? getMessage();
+ method public abstract String getPackageName();
+ method public abstract int getSeverity();
+ method public abstract long getTimestamp();
+ }
+
+ public abstract static class ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder {
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState build();
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setData(String?);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setKey(String);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setMessage(String?);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setPackageName(String);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setSeverity(int);
+ method public abstract androidx.enterprise.feedback.ReceivedKeyedAppState.ReceivedKeyedAppStateBuilder setTimestamp(long);
+ }
+
+}
+
diff --git a/enterprise/feedback/testing/api/1.0.0-beta01.txt b/enterprise/feedback/testing/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..9c90bc0
--- /dev/null
+++ b/enterprise/feedback/testing/api/1.0.0-beta01.txt
@@ -0,0 +1,17 @@
+// Signature format: 3.0
+package androidx.enterprise.feedback {
+
+ public class FakeKeyedAppStatesReporter extends androidx.enterprise.feedback.KeyedAppStatesReporter {
+ method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getKeyedAppStates();
+ method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getKeyedAppStatesByKey();
+ method public int getNumberOfUploads();
+ method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStates();
+ method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStatesByKey();
+ method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStates();
+ method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStatesByKey();
+ method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+ method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+ }
+
+}
+
diff --git a/enterprise/feedback/testing/api/res-1.0.0-beta01.txt b/enterprise/feedback/testing/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/enterprise/feedback/testing/api/res-1.0.0-beta01.txt
diff --git a/enterprise/feedback/testing/api/restricted_1.0.0-beta01.txt b/enterprise/feedback/testing/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..9c90bc0
--- /dev/null
+++ b/enterprise/feedback/testing/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,17 @@
+// Signature format: 3.0
+package androidx.enterprise.feedback {
+
+ public class FakeKeyedAppStatesReporter extends androidx.enterprise.feedback.KeyedAppStatesReporter {
+ method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getKeyedAppStates();
+ method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getKeyedAppStatesByKey();
+ method public int getNumberOfUploads();
+ method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStates();
+ method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStatesByKey();
+ method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStates();
+ method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStatesByKey();
+ method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+ method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata-ktx/api/2.2.0-alpha04.txt b/lifecycle/lifecycle-livedata-ktx/api/2.2.0-alpha04.txt
index 26f3e49..c8887ad 100644
--- a/lifecycle/lifecycle-livedata-ktx/api/2.2.0-alpha04.txt
+++ b/lifecycle/lifecycle-livedata-ktx/api/2.2.0-alpha04.txt
@@ -1,25 +1,19 @@
// Signature format: 3.0
package androidx.lifecycle {
- public final class CoroutineLiveDataApi26Kt {
- ctor public CoroutineLiveDataApi26Kt();
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- }
-
public final class CoroutineLiveDataKt {
ctor public CoroutineLiveDataKt();
method public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, long timeoutInMs = 5000L, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
}
- public final class FlowLiveDataApi26Kt {
- ctor public FlowLiveDataApi26Kt();
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout);
- }
-
- public final class FlowLiveDataKt {
- ctor public FlowLiveDataKt();
+ public final class FlowLiveDataConversions {
+ ctor public FlowLiveDataConversions();
method public static <T> kotlinx.coroutines.flow.Flow<T> asFlow(androidx.lifecycle.LiveData<T>);
method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, long timeoutInMs = 5000L);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout);
}
public interface LiveDataScope<T> {
diff --git a/lifecycle/lifecycle-livedata-ktx/api/current.txt b/lifecycle/lifecycle-livedata-ktx/api/current.txt
index 26f3e49..c8887ad 100644
--- a/lifecycle/lifecycle-livedata-ktx/api/current.txt
+++ b/lifecycle/lifecycle-livedata-ktx/api/current.txt
@@ -1,25 +1,19 @@
// Signature format: 3.0
package androidx.lifecycle {
- public final class CoroutineLiveDataApi26Kt {
- ctor public CoroutineLiveDataApi26Kt();
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- }
-
public final class CoroutineLiveDataKt {
ctor public CoroutineLiveDataKt();
method public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, long timeoutInMs = 5000L, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
}
- public final class FlowLiveDataApi26Kt {
- ctor public FlowLiveDataApi26Kt();
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout);
- }
-
- public final class FlowLiveDataKt {
- ctor public FlowLiveDataKt();
+ public final class FlowLiveDataConversions {
+ ctor public FlowLiveDataConversions();
method public static <T> kotlinx.coroutines.flow.Flow<T> asFlow(androidx.lifecycle.LiveData<T>);
method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, long timeoutInMs = 5000L);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout);
}
public interface LiveDataScope<T> {
diff --git a/lifecycle/lifecycle-livedata-ktx/api/restricted_2.2.0-alpha04.txt b/lifecycle/lifecycle-livedata-ktx/api/restricted_2.2.0-alpha04.txt
index 26f3e49..c8887ad 100644
--- a/lifecycle/lifecycle-livedata-ktx/api/restricted_2.2.0-alpha04.txt
+++ b/lifecycle/lifecycle-livedata-ktx/api/restricted_2.2.0-alpha04.txt
@@ -1,25 +1,19 @@
// Signature format: 3.0
package androidx.lifecycle {
- public final class CoroutineLiveDataApi26Kt {
- ctor public CoroutineLiveDataApi26Kt();
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- }
-
public final class CoroutineLiveDataKt {
ctor public CoroutineLiveDataKt();
method public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, long timeoutInMs = 5000L, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
}
- public final class FlowLiveDataApi26Kt {
- ctor public FlowLiveDataApi26Kt();
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout);
- }
-
- public final class FlowLiveDataKt {
- ctor public FlowLiveDataKt();
+ public final class FlowLiveDataConversions {
+ ctor public FlowLiveDataConversions();
method public static <T> kotlinx.coroutines.flow.Flow<T> asFlow(androidx.lifecycle.LiveData<T>);
method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, long timeoutInMs = 5000L);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout);
}
public interface LiveDataScope<T> {
diff --git a/lifecycle/lifecycle-livedata-ktx/api/restricted_current.txt b/lifecycle/lifecycle-livedata-ktx/api/restricted_current.txt
index 26f3e49..c8887ad 100644
--- a/lifecycle/lifecycle-livedata-ktx/api/restricted_current.txt
+++ b/lifecycle/lifecycle-livedata-ktx/api/restricted_current.txt
@@ -1,25 +1,19 @@
// Signature format: 3.0
package androidx.lifecycle {
- public final class CoroutineLiveDataApi26Kt {
- ctor public CoroutineLiveDataApi26Kt();
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- }
-
public final class CoroutineLiveDataKt {
ctor public CoroutineLiveDataKt();
method public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, long timeoutInMs = 5000L, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
}
- public final class FlowLiveDataApi26Kt {
- ctor public FlowLiveDataApi26Kt();
- method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout);
- }
-
- public final class FlowLiveDataKt {
- ctor public FlowLiveDataKt();
+ public final class FlowLiveDataConversions {
+ ctor public FlowLiveDataConversions();
method public static <T> kotlinx.coroutines.flow.Flow<T> asFlow(androidx.lifecycle.LiveData<T>);
method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, long timeoutInMs = 5000L);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, kotlin.coroutines.CoroutineContext context = EmptyCoroutineContext, java.time.Duration timeout);
}
public interface LiveDataScope<T> {
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
index f6e7ea8..c0841ac 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
@@ -16,7 +16,9 @@
package androidx.lifecycle
+import android.os.Build
import androidx.annotation.MainThread
+import androidx.annotation.RequiresApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.DisposableHandle
@@ -25,6 +27,7 @@
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import java.time.Duration
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@@ -324,4 +327,109 @@
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT,
@BuilderInference block: suspend LiveDataScope<T>.() -> Unit
-): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block)
\ No newline at end of file
+): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block)
+
+/**
+ * Builds a LiveData that has values yielded from the given [block] that executes on a
+ * [LiveDataScope].
+ *
+ * The [block] starts executing when the returned [LiveData] becomes active ([LiveData.onActive]).
+ * If the [LiveData] becomes inactive ([LiveData.onInactive]) while the [block] is executing, it
+ * will be cancelled after the [timeout] duration unless the [LiveData] becomes active again
+ * before that timeout (to gracefully handle cases like Activity rotation). Any value
+ * [LiveDataScope.emit]ed from a cancelled [block] will be ignored.
+ *
+ * After a cancellation, if the [LiveData] becomes active again, the [block] will be re-executed
+ * from the beginning. If you would like to continue the operations based on where it was stopped
+ * last, you can use the [LiveDataScope.latestValue] function to get the last
+ * [LiveDataScope.emit]ed value.
+
+ * If the [block] completes successfully *or* is cancelled due to reasons other than [LiveData]
+ * becoming inactive, it *will not* be re-executed even after [LiveData] goes through active
+ * inactive cycle.
+ *
+ * As a best practice, it is important for the [block] to cooperate in cancellation. See kotlin
+ * coroutines documentation for details
+ * https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html.
+ *
+ * ```
+ * // a simple LiveData that receives value 3, 3 seconds after being observed for the first time.
+ * val data : LiveData<Int> = liveData {
+ * delay(3000)
+ * emit(3)
+ * }
+ *
+ *
+ * // a LiveData that fetches a `User` object based on a `userId` and refreshes it every 30 seconds
+ * // as long as it is observed
+ * val userId : LiveData<String> = ...
+ * val user = userId.switchMap { id ->
+ * liveData {
+ * while(true) {
+ * // note that `while(true)` is fine because the `delay(30_000)` below will cooperate in
+ * // cancellation if LiveData is not actively observed anymore
+ * val data = api.fetch(id) // errors are ignored for brevity
+ * emit(data)
+ * delay(30_000)
+ * }
+ * }
+ * }
+ *
+ * // A retrying data fetcher with doubling back-off
+ * val user = liveData {
+ * var backOffTime = 1_000
+ * var succeeded = false
+ * while(!succeeded) {
+ * try {
+ * emit(api.fetch(id))
+ * succeeded = true
+ * } catch(ioError : IOException) {
+ * delay(backOffTime)
+ * backOffTime *= minOf(backOffTime * 2, 60_000)
+ * }
+ * }
+ * }
+ *
+ * // a LiveData that tries to load the `User` from local cache first and then tries to fetch
+ * // from the server and also yields the updated value
+ * val user = liveData {
+ * // dispatch loading first
+ * emit(LOADING(id))
+ * // check local storage
+ * val cached = cache.loadUser(id)
+ * if (cached != null) {
+ * emit(cached)
+ * }
+ * if (cached == null || cached.isStale()) {
+ * val fresh = api.fetch(id) // errors are ignored for brevity
+ * cache.save(fresh)
+ * emit(fresh)
+ * }
+ * }
+ *
+ * // a LiveData that immediately receives a LiveData<User> from the database and yields it as a
+ * // source but also tries to back-fill the database from the server
+ * val user = liveData {
+ * val fromDb: LiveData<User> = roomDatabase.loadUser(id)
+ * emitSource(fromDb)
+ * val updated = api.fetch(id) // errors are ignored for brevity
+ * // Since we are using Room here, updating the database will update the `fromDb` LiveData
+ * // that was obtained above. See Room's documentation for more details.
+ * // https://developer.android.com/training/data-storage/room/accessing-data#query-observable
+ * roomDatabase.insert(updated)
+ * }
+ * ```
+ *
+ * * @param context The CoroutineContext to run the given block in. Defaults to
+ * [EmptyCoroutineContext] combined with [Dispatchers.Main].
+ * @param timeout The timeout duration before cancelling the block if there are no active observers
+ * ([LiveData.hasActiveObservers].
+ * @param block The block to run when the [LiveData] has active observers.
+ */
+@RequiresApi(Build.VERSION_CODES.O)
+@UseExperimental(ExperimentalTypeInference::class)
+fun <T> liveData(
+ context: CoroutineContext = EmptyCoroutineContext,
+ timeout: Duration,
+ @BuilderInference block: suspend LiveDataScope<T>.() -> Unit
+): LiveData<T> = CoroutineLiveData(context, timeout.toMillis(), block)
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveDataApi26.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveDataApi26.kt
deleted file mode 100644
index 57b7756..0000000
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveDataApi26.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.lifecycle
-
-import android.os.Build
-import androidx.annotation.RequiresApi
-import kotlinx.coroutines.Dispatchers
-import java.time.Duration
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.experimental.ExperimentalTypeInference
-
-// this function resides in a separate file to avoid parsing API 26 parameter (Duration) in older
-// versions of the platform
-/**
- * Builds a LiveData that has values yielded from the given [block] that executes on a
- * [LiveDataScope].
- *
- * The [block] starts executing when the returned [LiveData] becomes active ([LiveData.onActive]).
- * If the [LiveData] becomes inactive ([LiveData.onInactive]) while the [block] is executing, it
- * will be cancelled after the [timeout] duration unless the [LiveData] becomes active again
- * before that timeout (to gracefully handle cases like Activity rotation). Any value
- * [LiveDataScope.emit]ed from a cancelled [block] will be ignored.
- *
- * After a cancellation, if the [LiveData] becomes active again, the [block] will be re-executed
- * from the beginning. If you would like to continue the operations based on where it was stopped
- * last, you can use the [LiveDataScope.latestValue] function to get the last
- * [LiveDataScope.emit]ed value.
-
- * If the [block] completes successfully *or* is cancelled due to reasons other than [LiveData]
- * becoming inactive, it *will not* be re-executed even after [LiveData] goes through active
- * inactive cycle.
- *
- * As a best practice, it is important for the [block] to cooperate in cancellation. See kotlin
- * coroutines documentation for details
- * https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html.
- *
- * ```
- * // a simple LiveData that receives value 3, 3 seconds after being observed for the first time.
- * val data : LiveData<Int> = liveData {
- * delay(3000)
- * emit(3)
- * }
- *
- *
- * // a LiveData that fetches a `User` object based on a `userId` and refreshes it every 30 seconds
- * // as long as it is observed
- * val userId : LiveData<String> = ...
- * val user = userId.switchMap { id ->
- * liveData {
- * while(true) {
- * // note that `while(true)` is fine because the `delay(30_000)` below will cooperate in
- * // cancellation if LiveData is not actively observed anymore
- * val data = api.fetch(id) // errors are ignored for brevity
- * emit(data)
- * delay(30_000)
- * }
- * }
- * }
- *
- * // A retrying data fetcher with doubling back-off
- * val user = liveData {
- * var backOffTime = 1_000
- * var succeeded = false
- * while(!succeeded) {
- * try {
- * emit(api.fetch(id))
- * succeeded = true
- * } catch(ioError : IOException) {
- * delay(backOffTime)
- * backOffTime *= minOf(backOffTime * 2, 60_000)
- * }
- * }
- * }
- *
- * // a LiveData that tries to load the `User` from local cache first and then tries to fetch
- * // from the server and also yields the updated value
- * val user = liveData {
- * // dispatch loading first
- * emit(LOADING(id))
- * // check local storage
- * val cached = cache.loadUser(id)
- * if (cached != null) {
- * emit(cached)
- * }
- * if (cached == null || cached.isStale()) {
- * val fresh = api.fetch(id) // errors are ignored for brevity
- * cache.save(fresh)
- * emit(fresh)
- * }
- * }
- *
- * // a LiveData that immediately receives a LiveData<User> from the database and yields it as a
- * // source but also tries to back-fill the database from the server
- * val user = liveData {
- * val fromDb: LiveData<User> = roomDatabase.loadUser(id)
- * emitSource(fromDb)
- * val updated = api.fetch(id) // errors are ignored for brevity
- * // Since we are using Room here, updating the database will update the `fromDb` LiveData
- * // that was obtained above. See Room's documentation for more details.
- * // https://developer.android.com/training/data-storage/room/accessing-data#query-observable
- * roomDatabase.insert(updated)
- * }
- * ```
- *
- * * @param context The CoroutineContext to run the given block in. Defaults to
- * [EmptyCoroutineContext] combined with [Dispatchers.Main].
- * @param timeout The timeout duration before cancelling the block if there are no active observers
- * ([LiveData.hasActiveObservers].
- * @param block The block to run when the [LiveData] has active observers.
- */
-@RequiresApi(Build.VERSION_CODES.O)
-@UseExperimental(ExperimentalTypeInference::class)
-fun <T> liveData(
- context: CoroutineContext = EmptyCoroutineContext,
- timeout: Duration,
- @BuilderInference block: suspend LiveDataScope<T>.() -> Unit
-): LiveData<T> = CoroutineLiveData(context, timeout.toMillis(), block)
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
index 3b2c2f25..f3d3207 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
@@ -14,8 +14,12 @@
* limitations under the License.
*/
+@file:JvmName("FlowLiveDataConversions")
+
package androidx.lifecycle
+import android.os.Build
+import androidx.annotation.RequiresApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -25,6 +29,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
+import java.time.Duration
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@@ -56,6 +61,7 @@
* @param timeoutInMs The timeout in ms before cancelling the block if there are no active observers
* ([LiveData.hasActiveObservers]. Defaults to [DEFAULT_TIMEOUT].
*/
+@JvmOverloads
fun <T> Flow<T>.asLiveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT
@@ -93,4 +99,38 @@
removeObserver(observer)
}
}
-}
\ No newline at end of file
+}
+
+/**
+ * Creates a LiveData that has values collected from the origin [Flow].
+ *
+ * The upstream flow collection starts when the returned [LiveData] becomes active
+ * ([LiveData.onActive]).
+ * If the [LiveData] becomes inactive ([LiveData.onInactive]) while the flow has not completed,
+ * the flow collection will be cancelled after [timeout] unless the [LiveData]
+ * becomes active again before that timeout (to gracefully handle cases like Activity rotation).
+ *
+ * After a cancellation, if the [LiveData] becomes active again, the upstream flow collection will
+ * be re-executed.
+ *
+ * If the upstream flow completes successfully *or* is cancelled due to reasons other than
+ * [LiveData] becoming inactive, it *will not* be re-collected even after [LiveData] goes through
+ * active inactive cycle.
+ *
+ * If flow completes with an exception, then exception will be delivered to the
+ * [CoroutineExceptionHandler][kotlinx.coroutines.CoroutineExceptionHandler] of provided [context].
+ * By default [EmptyCoroutineContext] is used to so an exception will be delivered to main's
+ * thread [UncaughtExceptionHandler][Thread.UncaughtExceptionHandler]. If your flow upstream is
+ * expected to throw, you can use [catch operator][kotlinx.coroutines.flow.catch] on upstream flow
+ * to emit a helpful error object.
+ *
+ * @param context The CoroutineContext to collect the upstream flow in. Defaults to
+ * [EmptyCoroutineContext] combined with [Dispatchers.Main]
+ * @param timeout The timeout in ms before cancelling the block if there are no active observers
+ * ([LiveData.hasActiveObservers]. Defaults to [DEFAULT_TIMEOUT].
+ */
+@RequiresApi(Build.VERSION_CODES.O)
+fun <T> Flow<T>.asLiveData(
+ context: CoroutineContext = EmptyCoroutineContext,
+ timeout: Duration
+): LiveData<T> = asLiveData(context, timeout.toMillis())
\ No newline at end of file
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveDataApi26.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveDataApi26.kt
deleted file mode 100644
index ac2643d..0000000
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveDataApi26.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.lifecycle
-
-import android.os.Build
-import androidx.annotation.RequiresApi
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.Flow
-import java.time.Duration
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-
-/**
- * Creates a LiveData that has values collected from the origin [Flow].
- *
- * The upstream flow collection starts when the returned [LiveData] becomes active
- * ([LiveData.onActive]).
- * If the [LiveData] becomes inactive ([LiveData.onInactive]) while the flow has not completed,
- * the flow collection will be cancelled after [timeout] unless the [LiveData]
- * becomes active again before that timeout (to gracefully handle cases like Activity rotation).
- *
- * After a cancellation, if the [LiveData] becomes active again, the upstream flow collection will
- * be re-executed.
- *
- * If the upstream flow completes successfully *or* is cancelled due to reasons other than
- * [LiveData] becoming inactive, it *will not* be re-collected even after [LiveData] goes through
- * active inactive cycle.
- *
- * If flow completes with an exception, then exception will be delivered to the
- * [CoroutineExceptionHandler][kotlinx.coroutines.CoroutineExceptionHandler] of provided [context].
- * By default [EmptyCoroutineContext] is used to so an exception will be delivered to main's
- * thread [UncaughtExceptionHandler][Thread.UncaughtExceptionHandler]. If your flow upstream is
- * expected to throw, you can use [catch operator][kotlinx.coroutines.flow.catch] on upstream flow
- * to emit a helpful error object.
- *
- * @param context The CoroutineContext to collect the upstream flow in. Defaults to
- * [EmptyCoroutineContext] combined with [Dispatchers.Main]
- * @param timeout The timeout in ms before cancelling the block if there are no active observers
- * ([LiveData.hasActiveObservers]. Defaults to [DEFAULT_TIMEOUT].
- */
-@RequiresApi(Build.VERSION_CODES.O)
-fun <T> Flow<T>.asLiveData(
- context: CoroutineContext = EmptyCoroutineContext,
- timeout: Duration
-): LiveData<T> = asLiveData(context, timeout.toMillis())
\ No newline at end of file
diff --git a/lifecycle/lifecycle-livedata-ktx/src/test/java/androidx/lifecycle/LiveDataFlowJavaTest.java b/lifecycle/lifecycle-livedata-ktx/src/test/java/androidx/lifecycle/LiveDataFlowJavaTest.java
new file mode 100644
index 0000000..c44f948
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-ktx/src/test/java/androidx/lifecycle/LiveDataFlowJavaTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.lifecycle;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import kotlinx.coroutines.flow.Flow;
+
+@RunWith(JUnit4.class)
+public class LiveDataFlowJavaTest {
+
+ /**
+ * A purpose of this function only to show case java interop.
+ * Real tests are in {@link FlowAsLiveDataTest} and {@link LiveDataAsFlowTest}
+ */
+ @Test
+ public void noOp() {
+ LiveData<String> liveData = new MutableLiveData<>("no-op");
+ Flow<String> flow = FlowLiveDataConversions.asFlow(liveData);
+ FlowLiveDataConversions.asLiveData(flow);
+ }
+}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
index e542705..3df4b6d 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
@@ -44,7 +44,6 @@
import androidx.media2.common.SessionPlayer;
import androidx.media2.common.SessionPlayer.TrackInfo;
import androidx.media2.session.MediaController;
-import androidx.media2.widget.test.R;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -90,10 +89,11 @@
@Before
public void setup() throws Throwable {
mActivity = mActivityRule.getActivity();
- mMediaControlView = mActivity.findViewById(R.id.mediacontrolview);
+ mMediaControlView = mActivity.findViewById(
+ androidx.media2.widget.test.R.id.mediacontrolview);
Uri fileSchemeUri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
- + R.raw.test_file_scheme_video);
+ + androidx.media2.widget.test.R.raw.test_file_scheme_video);
mFileSchemeMediaItem = createTestMediaItem(fileSchemeUri);
setKeepScreenOn(mActivityRule);
@@ -355,7 +355,7 @@
@Test
public void testButtonVisibilityForMusicFile() throws Throwable {
Uri uri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
- + R.raw.test_music);
+ + androidx.media2.widget.test.R.raw.test_music);
final MediaItem uriMediaItem = createTestMediaItem(uri);
final CountDownLatch latch = new CountDownLatch(1);
@@ -379,7 +379,7 @@
@Test
public void testUpdateAndSelectSubtitleTrack() throws Throwable {
Uri uri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
- + R.raw.testvideo_with_2_subtitle_tracks);
+ + androidx.media2.widget.test.R.raw.testvideo_with_2_subtitle_tracks);
final String subtitleTrackOffText = mContext.getResources().getString(
R.string.MediaControlView_subtitle_off_text);
@@ -471,7 +471,8 @@
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
- ViewGroup layout = mActivity.findViewById(R.id.framelayout);
+ ViewGroup layout = mActivity.findViewById(
+ androidx.media2.widget.test.R.id.framelayout);
layout.removeView(mMediaControlView);
mMediaControlView = new MediaControlView(mActivity);
if (playerWrapper.mPlayer != null) {
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithoutPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithoutPlayerTest.java
index fed585d..9f2642a 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithoutPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithoutPlayerTest.java
@@ -29,7 +29,6 @@
import android.view.View;
import androidx.annotation.NonNull;
-import androidx.media2.widget.test.R;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
@@ -60,7 +59,8 @@
@Before
public void setup() throws Throwable {
mActivity = mActivityRule.getActivity();
- mMediaControlView = mActivity.findViewById(R.id.mediacontrolview);
+ mMediaControlView = mActivity.findViewById(
+ androidx.media2.widget.test.R.id.mediacontrolview);
setKeepScreenOn(mActivityRule);
checkAttachedToWindow(mMediaControlView);
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
index e005a01..19b36f11 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
@@ -141,6 +141,8 @@
return list;
}
+ // TODO(b/138091975) Do not ignore returned Futures if feasible.
+ @SuppressWarnings("FutureReturnValueIgnored")
PlayerWrapper createPlayerWrapperOfController(@NonNull PlayerWrapper.PlayerCallback callback,
@Nullable MediaItem item, @Nullable List<MediaItem> playlist) {
prepareLooper();
@@ -169,6 +171,8 @@
return wrapper;
}
+ // TODO(b/138091975) Do not ignore returned Futures if feasible.
+ @SuppressWarnings("FutureReturnValueIgnored")
PlayerWrapper createPlayerWrapperOfPlayer(@NonNull PlayerWrapper.PlayerCallback callback,
@Nullable MediaItem item, @Nullable List<MediaItem> playlist) {
SessionPlayer player = new MediaPlayer(mContext);
diff --git a/paging/common/api/3.0.0-alpha01.txt b/paging/common/api/3.0.0-alpha01.txt
index ac81ae2..4cb7b1d 100644
--- a/paging/common/api/3.0.0-alpha01.txt
+++ b/paging/common/api/3.0.0-alpha01.txt
@@ -150,7 +150,6 @@
method @Deprecated public final androidx.paging.DataSource<?,T> getDataSource();
method public abstract Object? getLastKey();
method public int getLoadedCount();
- method public final androidx.paging.PagedSource<?,T> getPagedSource();
method public int getPositionOffset();
method public int getSize();
method public abstract boolean isDetached();
@@ -166,7 +165,6 @@
property public boolean isImmutable;
property public abstract Object? lastKey;
property public int loadedCount;
- property public final androidx.paging.PagedSource<?,T> pagedSource;
property public int positionOffset;
property public int size;
}
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index ac81ae2..4cb7b1d 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -150,7 +150,6 @@
method @Deprecated public final androidx.paging.DataSource<?,T> getDataSource();
method public abstract Object? getLastKey();
method public int getLoadedCount();
- method public final androidx.paging.PagedSource<?,T> getPagedSource();
method public int getPositionOffset();
method public int getSize();
method public abstract boolean isDetached();
@@ -166,7 +165,6 @@
property public boolean isImmutable;
property public abstract Object? lastKey;
property public int loadedCount;
- property public final androidx.paging.PagedSource<?,T> pagedSource;
property public int positionOffset;
property public int size;
}
diff --git a/paging/common/api/restricted_3.0.0-alpha01.txt b/paging/common/api/restricted_3.0.0-alpha01.txt
index 259dc82..976e9a3 100644
--- a/paging/common/api/restricted_3.0.0-alpha01.txt
+++ b/paging/common/api/restricted_3.0.0-alpha01.txt
@@ -155,7 +155,6 @@
method @Deprecated public final androidx.paging.DataSource<?,T> getDataSource();
method public abstract Object? getLastKey();
method public int getLoadedCount();
- method public final androidx.paging.PagedSource<?,T> getPagedSource();
method public int getPositionOffset();
method public int getSize();
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final androidx.paging.PagedStorage<T> getStorage();
@@ -173,7 +172,6 @@
property public boolean isImmutable;
property public abstract Object? lastKey;
property public int loadedCount;
- property public final androidx.paging.PagedSource<?,T> pagedSource;
property public int positionOffset;
property public int size;
}
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index 259dc82..976e9a3 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -155,7 +155,6 @@
method @Deprecated public final androidx.paging.DataSource<?,T> getDataSource();
method public abstract Object? getLastKey();
method public int getLoadedCount();
- method public final androidx.paging.PagedSource<?,T> getPagedSource();
method public int getPositionOffset();
method public int getSize();
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final androidx.paging.PagedStorage<T> getStorage();
@@ -173,7 +172,6 @@
property public boolean isImmutable;
property public abstract Object? lastKey;
property public int loadedCount;
- property public final androidx.paging.PagedSource<?,T> pagedSource;
property public int positionOffset;
property public int size;
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
index 9d429b1..f880cb6 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
@@ -997,16 +997,20 @@
/**
* The [PagedSource] that provides data to this [PagedList].
+ *
+ * @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
val pagedSource: PagedSource<*, T>
/**
* @throws IllegalStateException if this [PagedList] was instantiated without a
- * [PagedSourceWrapper] wrapping a backing [DataSource]
+ * wrapping a backing [DataSource]
*/
@Deprecated(
- message = "DataSource is deprecated and has been replaced by PagedSource",
- replaceWith = ReplaceWith("pagedSource")
+ message = "DataSource is deprecated and has been replaced by PagedSource. PagedList " +
+ "offers indirect ways of controlling fetch ('loadAround()', 'retry()') so that " +
+ "you should not need to access the DataSource/PagedSource."
)
val dataSource: DataSource<*, T>
get() {
diff --git a/room/guava/build.gradle b/room/guava/build.gradle
index 4de80a2..a167763 100644
--- a/room/guava/build.gradle
+++ b/room/guava/build.gradle
@@ -33,7 +33,7 @@
implementation(ARCH_CORE_RUNTIME)
api(SUPPORT_ANNOTATIONS)
- api(GUAVA_ANDROID)
+ implementation(GUAVA_ANDROID)
androidTestImplementation(ANDROIDX_TEST_RUNNER)
androidTestImplementation(TRUTH)
}
diff --git a/sharetarget/integration-tests/testapp/build.gradle b/sharetarget/integration-tests/testapp/build.gradle
index 4cf7a1f..fd2075d 100644
--- a/sharetarget/integration-tests/testapp/build.gradle
+++ b/sharetarget/integration-tests/testapp/build.gradle
@@ -22,8 +22,8 @@
}
dependencies {
- api(project(":core:core"))
- api(project(":sharetarget"))
+ api("androidx.core:core:1.1.0")
+ api("androidx.sharetarget:sharetarget:1.0.0-alpha02")
api(project(":appcompat"))
api(CONSTRAINT_LAYOUT, { transitive = true })
}
diff --git a/slices/core/api/1.1.0-alpha02.txt b/slices/core/api/1.1.0-alpha02.txt
index bada082..728a150 100644
--- a/slices/core/api/1.1.0-alpha02.txt
+++ b/slices/core/api/1.1.0-alpha02.txt
@@ -64,7 +64,7 @@
public abstract class SliceProviderWithCallbacks<T extends androidx.slice.SliceProviderWithCallbacks> extends androidx.slice.SliceProvider implements androidx.remotecallback.CallbackBase<T> androidx.remotecallback.CallbackReceiver<T> {
ctor public SliceProviderWithCallbacks();
method public T createRemoteCallback(android.content.Context);
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.remotecallback.RemoteCallback toRemoteCallback(Class<T!>, android.content.Context, String?, android.os.Bundle, String?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.remotecallback.RemoteCallback toRemoteCallback(Class<T!>, android.content.Context, String, android.os.Bundle, String?);
}
}
diff --git a/slices/core/api/current.txt b/slices/core/api/current.txt
index bada082..728a150 100644
--- a/slices/core/api/current.txt
+++ b/slices/core/api/current.txt
@@ -64,7 +64,7 @@
public abstract class SliceProviderWithCallbacks<T extends androidx.slice.SliceProviderWithCallbacks> extends androidx.slice.SliceProvider implements androidx.remotecallback.CallbackBase<T> androidx.remotecallback.CallbackReceiver<T> {
ctor public SliceProviderWithCallbacks();
method public T createRemoteCallback(android.content.Context);
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.remotecallback.RemoteCallback toRemoteCallback(Class<T!>, android.content.Context, String?, android.os.Bundle, String?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.remotecallback.RemoteCallback toRemoteCallback(Class<T!>, android.content.Context, String, android.os.Bundle, String?);
}
}
diff --git a/slices/core/api/restricted_1.1.0-alpha02.txt b/slices/core/api/restricted_1.1.0-alpha02.txt
index 3cf1dca..878a544 100644
--- a/slices/core/api/restricted_1.1.0-alpha02.txt
+++ b/slices/core/api/restricted_1.1.0-alpha02.txt
@@ -126,7 +126,7 @@
public abstract class SliceProviderWithCallbacks<T extends androidx.slice.SliceProviderWithCallbacks> extends androidx.slice.SliceProvider implements androidx.remotecallback.CallbackBase<T> androidx.remotecallback.CallbackReceiver<T> {
ctor public SliceProviderWithCallbacks();
method public T createRemoteCallback(android.content.Context);
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.remotecallback.RemoteCallback toRemoteCallback(Class<T!>, android.content.Context, String?, android.os.Bundle, String?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.remotecallback.RemoteCallback toRemoteCallback(Class<T!>, android.content.Context, String, android.os.Bundle, String?);
}
@RequiresApi(19) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class SliceSpec implements androidx.versionedparcelable.VersionedParcelable {
diff --git a/slices/core/api/restricted_current.txt b/slices/core/api/restricted_current.txt
index 3cf1dca..878a544 100644
--- a/slices/core/api/restricted_current.txt
+++ b/slices/core/api/restricted_current.txt
@@ -126,7 +126,7 @@
public abstract class SliceProviderWithCallbacks<T extends androidx.slice.SliceProviderWithCallbacks> extends androidx.slice.SliceProvider implements androidx.remotecallback.CallbackBase<T> androidx.remotecallback.CallbackReceiver<T> {
ctor public SliceProviderWithCallbacks();
method public T createRemoteCallback(android.content.Context);
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.remotecallback.RemoteCallback toRemoteCallback(Class<T!>, android.content.Context, String?, android.os.Bundle, String?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.remotecallback.RemoteCallback toRemoteCallback(Class<T!>, android.content.Context, String, android.os.Bundle, String?);
}
@RequiresApi(19) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class SliceSpec implements androidx.versionedparcelable.VersionedParcelable {
diff --git a/slices/core/src/main/java/androidx/slice/SliceProvider.java b/slices/core/src/main/java/androidx/slice/SliceProvider.java
index a4b57aa..6164547 100644
--- a/slices/core/src/main/java/androidx/slice/SliceProvider.java
+++ b/slices/core/src/main/java/androidx/slice/SliceProvider.java
@@ -235,6 +235,7 @@
@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
if (Build.VERSION.SDK_INT < 19) return null;
+ if (extras == null) return null;
return mCompat != null ? mCompat.call(method, arg, extras) : null;
}
diff --git a/slices/core/src/main/java/androidx/slice/SliceProviderWithCallbacks.java b/slices/core/src/main/java/androidx/slice/SliceProviderWithCallbacks.java
index 5404f79..fffb2d2 100644
--- a/slices/core/src/main/java/androidx/slice/SliceProviderWithCallbacks.java
+++ b/slices/core/src/main/java/androidx/slice/SliceProviderWithCallbacks.java
@@ -59,6 +59,7 @@
@Nullable
@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+ if (extras == null) return null;
if (ProviderRelayReceiver.METHOD_PROVIDER_CALLBACK.equals(method)) {
CallbackHandlerRegistry.sInstance.invokeCallback(getContext(), this, extras);
return null;
@@ -81,7 +82,7 @@
@Override
@RestrictTo(LIBRARY_GROUP_PREFIX)
public RemoteCallback toRemoteCallback(@NonNull Class<T> cls, @NonNull Context context,
- @Nullable String authority, @NonNull Bundle args, @Nullable String method) {
+ @NonNull String authority, @NonNull Bundle args, @Nullable String method) {
if (authority == null) {
throw new IllegalStateException(
"ContentProvider must be attached before creating callbacks");
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
index 97af4e8..a975728 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
@@ -31,10 +31,10 @@
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.FlexColumn
-import androidx.ui.layout.HorizontalScroller
+import androidx.ui.foundation.HorizontalScroller
import androidx.ui.layout.Row
-import androidx.ui.layout.ScrollerPosition
-import androidx.ui.layout.VerticalScroller
+import androidx.ui.foundation.ScrollerPosition
+import androidx.ui.foundation.VerticalScroller
import androidx.ui.material.MaterialTheme
import androidx.ui.material.surface.Surface
import androidx.ui.test.ComposeTestCase
@@ -65,7 +65,7 @@
}!!
override fun toggleState() {
- scrollerPosition.position = if (scrollerPosition.position == 0.px) 10.px else 0.px
+ scrollerPosition.value = if (scrollerPosition.value == 0.px) 10.px else 0.px
FrameManager.nextFrame()
}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
index 40d97a8..65ac2d2 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
@@ -32,8 +32,8 @@
import androidx.ui.layout.Column
import androidx.ui.layout.Container
import androidx.ui.layout.CrossAxisAlignment
-import androidx.ui.layout.ScrollerPosition
-import androidx.ui.layout.VerticalScroller
+import androidx.ui.foundation.ScrollerPosition
+import androidx.ui.foundation.VerticalScroller
import androidx.ui.painting.Paint
import androidx.ui.painting.PaintingStyle
import androidx.ui.test.ComposeTestCase
@@ -75,7 +75,7 @@
}!!
override fun toggleState() {
- scrollerPosition.position = if (scrollerPosition.position == 0.px) 10.px else 0.px
+ scrollerPosition.value = if (scrollerPosition.value == 0.px) 10.px else 0.px
FrameManager.nextFrame()
}
diff --git a/ui/ui-foundation/api/1.0.0-alpha01.txt b/ui/ui-foundation/api/1.0.0-alpha01.txt
index 782f0c2..522a82c 100644
--- a/ui/ui-foundation/api/1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/1.0.0-alpha01.txt
@@ -42,6 +42,27 @@
method public void vertical(int column, kotlin.ranges.IntRange rows = 0 <other> rowCount, androidx.ui.foundation.shape.border.Border border = defaultBorder);
}
+ public final class ScrollerKt {
+ ctor public ScrollerKt();
+ method public static void HorizontalScroller(androidx.ui.foundation.ScrollerPosition scrollerPosition = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollPositionChanged = { position, _ -> scrollerPosition.value = position }, boolean isScrollable = true, kotlin.jvm.functions.Function0<kotlin.Unit> child);
+ method public static void VerticalScroller(androidx.ui.foundation.ScrollerPosition scrollerPosition = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollPositionChanged = { position, _ -> scrollerPosition.value = position }, boolean isScrollable = true, kotlin.jvm.functions.Function0<kotlin.Unit> child);
+ }
+
+ public final class ScrollerPosition {
+ ctor public ScrollerPosition();
+ method public androidx.ui.core.Px getValue();
+ method public void scrollBy(androidx.ui.core.Px value);
+ method public void scrollTo(androidx.ui.core.Px value);
+ method public void setValue(androidx.ui.core.Px p);
+ method public void smoothScrollBy(androidx.ui.core.Px value, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onFinished = {});
+ method public void smoothScrollTo(androidx.ui.core.Px value, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onFinished = {});
+ property public final androidx.ui.core.Px value;
+ }
+
public final class SimpleImageKt {
ctor public SimpleImageKt();
method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
@@ -71,13 +92,13 @@
package androidx.ui.foundation.animation {
public final class AnimatedFloatDragController implements androidx.ui.foundation.gestures.DragValueController {
+ ctor public AnimatedFloatDragController(androidx.animation.AnimatedFloat animatedFloat, androidx.ui.foundation.animation.FlingConfig? flingConfig);
ctor public AnimatedFloatDragController(float initialValue, androidx.ui.foundation.animation.FlingConfig? flingConfig);
method public androidx.animation.AnimatedFloat getAnimatedFloat();
method public float getCurrentValue();
method public void onDrag(float target);
method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
method public void setBounds(float min, float max);
- property public final androidx.animation.AnimatedFloat animatedFloat;
property public float currentValue;
}
diff --git a/ui/ui-foundation/api/current.txt b/ui/ui-foundation/api/current.txt
index 782f0c2..522a82c 100644
--- a/ui/ui-foundation/api/current.txt
+++ b/ui/ui-foundation/api/current.txt
@@ -42,6 +42,27 @@
method public void vertical(int column, kotlin.ranges.IntRange rows = 0 <other> rowCount, androidx.ui.foundation.shape.border.Border border = defaultBorder);
}
+ public final class ScrollerKt {
+ ctor public ScrollerKt();
+ method public static void HorizontalScroller(androidx.ui.foundation.ScrollerPosition scrollerPosition = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollPositionChanged = { position, _ -> scrollerPosition.value = position }, boolean isScrollable = true, kotlin.jvm.functions.Function0<kotlin.Unit> child);
+ method public static void VerticalScroller(androidx.ui.foundation.ScrollerPosition scrollerPosition = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollPositionChanged = { position, _ -> scrollerPosition.value = position }, boolean isScrollable = true, kotlin.jvm.functions.Function0<kotlin.Unit> child);
+ }
+
+ public final class ScrollerPosition {
+ ctor public ScrollerPosition();
+ method public androidx.ui.core.Px getValue();
+ method public void scrollBy(androidx.ui.core.Px value);
+ method public void scrollTo(androidx.ui.core.Px value);
+ method public void setValue(androidx.ui.core.Px p);
+ method public void smoothScrollBy(androidx.ui.core.Px value, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onFinished = {});
+ method public void smoothScrollTo(androidx.ui.core.Px value, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onFinished = {});
+ property public final androidx.ui.core.Px value;
+ }
+
public final class SimpleImageKt {
ctor public SimpleImageKt();
method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
@@ -71,13 +92,13 @@
package androidx.ui.foundation.animation {
public final class AnimatedFloatDragController implements androidx.ui.foundation.gestures.DragValueController {
+ ctor public AnimatedFloatDragController(androidx.animation.AnimatedFloat animatedFloat, androidx.ui.foundation.animation.FlingConfig? flingConfig);
ctor public AnimatedFloatDragController(float initialValue, androidx.ui.foundation.animation.FlingConfig? flingConfig);
method public androidx.animation.AnimatedFloat getAnimatedFloat();
method public float getCurrentValue();
method public void onDrag(float target);
method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
method public void setBounds(float min, float max);
- property public final androidx.animation.AnimatedFloat animatedFloat;
property public float currentValue;
}
diff --git a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
index 782f0c2..522a82c 100644
--- a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
@@ -42,6 +42,27 @@
method public void vertical(int column, kotlin.ranges.IntRange rows = 0 <other> rowCount, androidx.ui.foundation.shape.border.Border border = defaultBorder);
}
+ public final class ScrollerKt {
+ ctor public ScrollerKt();
+ method public static void HorizontalScroller(androidx.ui.foundation.ScrollerPosition scrollerPosition = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollPositionChanged = { position, _ -> scrollerPosition.value = position }, boolean isScrollable = true, kotlin.jvm.functions.Function0<kotlin.Unit> child);
+ method public static void VerticalScroller(androidx.ui.foundation.ScrollerPosition scrollerPosition = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollPositionChanged = { position, _ -> scrollerPosition.value = position }, boolean isScrollable = true, kotlin.jvm.functions.Function0<kotlin.Unit> child);
+ }
+
+ public final class ScrollerPosition {
+ ctor public ScrollerPosition();
+ method public androidx.ui.core.Px getValue();
+ method public void scrollBy(androidx.ui.core.Px value);
+ method public void scrollTo(androidx.ui.core.Px value);
+ method public void setValue(androidx.ui.core.Px p);
+ method public void smoothScrollBy(androidx.ui.core.Px value, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onFinished = {});
+ method public void smoothScrollTo(androidx.ui.core.Px value, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onFinished = {});
+ property public final androidx.ui.core.Px value;
+ }
+
public final class SimpleImageKt {
ctor public SimpleImageKt();
method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
@@ -71,13 +92,13 @@
package androidx.ui.foundation.animation {
public final class AnimatedFloatDragController implements androidx.ui.foundation.gestures.DragValueController {
+ ctor public AnimatedFloatDragController(androidx.animation.AnimatedFloat animatedFloat, androidx.ui.foundation.animation.FlingConfig? flingConfig);
ctor public AnimatedFloatDragController(float initialValue, androidx.ui.foundation.animation.FlingConfig? flingConfig);
method public androidx.animation.AnimatedFloat getAnimatedFloat();
method public float getCurrentValue();
method public void onDrag(float target);
method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
method public void setBounds(float min, float max);
- property public final androidx.animation.AnimatedFloat animatedFloat;
property public float currentValue;
}
diff --git a/ui/ui-foundation/api/restricted_current.txt b/ui/ui-foundation/api/restricted_current.txt
index 782f0c2..522a82c 100644
--- a/ui/ui-foundation/api/restricted_current.txt
+++ b/ui/ui-foundation/api/restricted_current.txt
@@ -42,6 +42,27 @@
method public void vertical(int column, kotlin.ranges.IntRange rows = 0 <other> rowCount, androidx.ui.foundation.shape.border.Border border = defaultBorder);
}
+ public final class ScrollerKt {
+ ctor public ScrollerKt();
+ method public static void HorizontalScroller(androidx.ui.foundation.ScrollerPosition scrollerPosition = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollPositionChanged = { position, _ -> scrollerPosition.value = position }, boolean isScrollable = true, kotlin.jvm.functions.Function0<kotlin.Unit> child);
+ method public static void VerticalScroller(androidx.ui.foundation.ScrollerPosition scrollerPosition = +memo({
+ <init>()
+}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollPositionChanged = { position, _ -> scrollerPosition.value = position }, boolean isScrollable = true, kotlin.jvm.functions.Function0<kotlin.Unit> child);
+ }
+
+ public final class ScrollerPosition {
+ ctor public ScrollerPosition();
+ method public androidx.ui.core.Px getValue();
+ method public void scrollBy(androidx.ui.core.Px value);
+ method public void scrollTo(androidx.ui.core.Px value);
+ method public void setValue(androidx.ui.core.Px p);
+ method public void smoothScrollBy(androidx.ui.core.Px value, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onFinished = {});
+ method public void smoothScrollTo(androidx.ui.core.Px value, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onFinished = {});
+ property public final androidx.ui.core.Px value;
+ }
+
public final class SimpleImageKt {
ctor public SimpleImageKt();
method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
@@ -71,13 +92,13 @@
package androidx.ui.foundation.animation {
public final class AnimatedFloatDragController implements androidx.ui.foundation.gestures.DragValueController {
+ ctor public AnimatedFloatDragController(androidx.animation.AnimatedFloat animatedFloat, androidx.ui.foundation.animation.FlingConfig? flingConfig);
ctor public AnimatedFloatDragController(float initialValue, androidx.ui.foundation.animation.FlingConfig? flingConfig);
method public androidx.animation.AnimatedFloat getAnimatedFloat();
method public float getCurrentValue();
method public void onDrag(float target);
method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
method public void setBounds(float min, float max);
- property public final androidx.animation.AnimatedFloat animatedFloat;
property public float currentValue;
}
diff --git a/ui/ui-foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml b/ui/ui-foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml
index 4609982..2bdca6b 100644
--- a/ui/ui-foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml
+++ b/ui/ui-foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml
@@ -21,11 +21,27 @@
<application>
<activity android:name=".AnimatedDraggableActivity"
- android:configChanges="orientation|screenSize"
- android:label="Foundation/AnimatedDraggable">
+ android:configChanges="orientation|screenSize"
+ android:label="Foundation/AnimatedDraggable">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="androidx.ui.demos.SAMPLE_CODE" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="androidx.ui.demos.SAMPLE_CODE"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".VerticalScrollerActivity"
+ android:configChanges="orientation|screenSize"
+ android:label="Foundation/VerticalScroller">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="androidx.ui.demos.SAMPLE_CODE"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".HorizontalScrollerActivity"
+ android:configChanges="orientation|screenSize"
+ android:label="Foundation/HorizontalScroller with controls">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="androidx.ui.demos.SAMPLE_CODE"/>
</intent-filter>
</activity>
diff --git a/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/HorizontalScrollerActivity.kt b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/HorizontalScrollerActivity.kt
new file mode 100644
index 0000000..6b4d4b9
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/HorizontalScrollerActivity.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.foundation.demos
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.compose.composer
+import androidx.ui.core.setContent
+import androidx.ui.foundation.samples.ControlledHorizontalScrollerSample
+import androidx.ui.layout.Wrap
+
+class HorizontalScrollerActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ Wrap {
+ ControlledHorizontalScrollerSample()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/VerticalScrollerActivity.kt b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/VerticalScrollerActivity.kt
new file mode 100644
index 0000000..defa1f8
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/VerticalScrollerActivity.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.foundation.demos
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.compose.composer
+import androidx.ui.core.setContent
+import androidx.ui.foundation.samples.VerticalScrollerSample
+import androidx.ui.layout.Wrap
+
+class VerticalScrollerActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ Wrap {
+ VerticalScrollerSample()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt
new file mode 100644
index 0000000..30d8e63
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.foundation.samples
+
+import android.util.Log
+import androidx.annotation.Sampled
+import androidx.compose.Composable
+import androidx.compose.State
+import androidx.compose.composer
+import androidx.compose.memo
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.Draw
+import androidx.ui.core.PxSize
+import androidx.ui.core.Text
+import androidx.ui.core.dp
+import androidx.ui.core.px
+import androidx.ui.core.setContent
+import androidx.ui.core.sp
+import androidx.ui.core.toRect
+import androidx.ui.foundation.Clickable
+import androidx.ui.foundation.ColoredRect
+import androidx.ui.foundation.HorizontalScroller
+import androidx.ui.foundation.VerticalScroller
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Column
+import androidx.ui.layout.Padding
+import androidx.ui.layout.Row
+import androidx.ui.foundation.ScrollerPosition
+import androidx.ui.foundation.shape.DrawShape
+import androidx.ui.foundation.shape.RectangleShape
+import androidx.ui.layout.Alignment
+import androidx.ui.layout.Container
+import androidx.ui.layout.EdgeInsets
+import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.Stack
+import androidx.ui.layout.Table
+import androidx.ui.layout.Wrap
+import androidx.ui.painting.Canvas
+import androidx.ui.painting.Paint
+import androidx.ui.text.ParagraphStyle
+import androidx.ui.text.TextStyle
+
+private val colors = listOf(
+ Color(0xFFffd7d7.toInt()),
+ Color(0xFFffe9d6.toInt()),
+ Color(0xFFfffbd0.toInt()),
+ Color(0xFFe3ffd9.toInt()),
+ Color(0xFFd0fff8.toInt())
+)
+
+private val phrases = listOf(
+ "Easy As Pie",
+ "Wouldn't Harm a Fly",
+ "No-Brainer",
+ "Keep On Truckin'",
+ "An Arm and a Leg",
+ "Down To Earth",
+ "Under the Weather",
+ "Up In Arms",
+ "Cup Of Joe",
+ "Not the Sharpest Tool in the Shed",
+ "Ring Any Bells?",
+ "Son of a Gun",
+ "Hard Pill to Swallow",
+ "Close But No Cigar",
+ "Beating a Dead Horse",
+ "If You Can't Stand the Heat, Get Out of the Kitchen",
+ "Cut To The Chase",
+ "Heads Up",
+ "Goody Two-Shoes",
+ "Fish Out Of Water",
+ "Cry Over Spilt Milk",
+ "Elephant in the Room",
+ "There's No I in Team",
+ "Poke Fun At",
+ "Talk the Talk",
+ "Know the Ropes",
+ "Fool's Gold",
+ "It's Not Brain Surgery",
+ "Fight Fire With Fire",
+ "Go For Broke"
+)
+
+@Sampled
+@Composable
+fun VerticalScrollerSample() {
+ val style = TextStyle(fontSize = 30.sp)
+ // Scroller will be clipped to this padding
+ Padding(padding = 10.dp) {
+ VerticalScroller {
+ Column {
+ phrases.forEach { phrase ->
+ Text(text = phrase, style = style)
+ }
+ }
+ }
+ }
+}
+
+@Sampled
+@Composable
+fun SimpleHorizontalScrollerSample() {
+ HorizontalScroller {
+ Row {
+ repeat(100) { index ->
+ Square(index)
+ }
+ }
+ }
+}
+
+@Sampled
+@Composable
+fun ControlledHorizontalScrollerSample() {
+ // Create and own ScrollerPosition to call `smoothScrollTo` later
+ val position = +memo { ScrollerPosition() }
+ val scrollable = +state { true }
+ Column {
+ HorizontalScroller(scrollerPosition = position, isScrollable = scrollable.value) {
+ Row {
+ repeat(1000) { index ->
+ Square(index)
+ }
+ }
+ }
+ // Controls that will call `smoothScrollTo`, `scrollTo` or toggle `scrollable` state
+ ScrollControl(position, scrollable)
+ }
+}
+
+@Composable
+private fun Square(index: Int) {
+ Container(width = 75.dp, height = 200.dp) {
+ DrawShape(RectangleShape, colors[index % colors.size])
+ Text(index.toString())
+ }
+}
+
+@Composable
+private fun ScrollControl(position: ScrollerPosition, scrollable: State<Boolean>) {
+ Padding(top = 20.dp) {
+ Table(3, childAlignment = Alignment.Center) {
+ tableRow {
+ Text("Scroll")
+ SquareButton("< -", Color.Red) {
+ position.scrollTo(position.value - 1000.px)
+ }
+ SquareButton("--- >", Color.Green) {
+ position.scrollBy(10000.px)
+ }
+ }
+ tableRow {
+ Text("Smooth Scroll")
+ SquareButton("< -", Color.Red) {
+ position.smoothScrollTo(position.value - 1000.px)
+ }
+ SquareButton("--- >", Color.Green) {
+ position.smoothScrollBy(10000.px)
+ }
+ }
+ tableRow {
+ SquareButton("Scroll: ${scrollable.value}") {
+ scrollable.value = !scrollable.value
+ }
+ // empty container to fill table
+ Container { }
+ Container { }
+ }
+ }
+ }
+}
+
+@Composable
+private fun SquareButton(text: String, color: Color = Color.LightGray, onClick: () -> Unit) {
+ Clickable(onClick = onClick) {
+ Padding(5.dp) {
+ Container(height = 60.dp, width = 120.dp) {
+ DrawShape(RectangleShape, color)
+ Text(text, style = TextStyle(fontSize = 20.sp))
+ }
+ }
+ }
+}
diff --git a/ui/ui-foundation/src/androidTest/AndroidManifest.xml b/ui/ui-foundation/src/androidTest/AndroidManifest.xml
index 8c0fd1c..bacf210 100644
--- a/ui/ui-foundation/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-foundation/src/androidTest/AndroidManifest.xml
@@ -14,8 +14,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<manifest package="androidx.ui.foundation" xmlns:android="http://schemas.android.com/apk/res/android">
- <application>
+<manifest package="androidx.ui.foundation"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <application
+ android:requestLegacyExternalStorage="true"
+ android:debuggable="false"
+ tools:ignore="HardcodedDebugMode"
+ tools:replace="android:debuggable">
<activity
android:name="androidx.ui.test.android.DefaultTestActivity"
android:theme="@style/TestTheme"/>
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ScrollerTest.kt b/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/ScrollerTest.kt
similarity index 65%
rename from ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ScrollerTest.kt
rename to ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/ScrollerTest.kt
index b15be36..9f78d2c 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ScrollerTest.kt
+++ b/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/ScrollerTest.kt
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package androidx.ui.layout.test
+package androidx.ui.foundation
+import android.app.Activity
import android.graphics.Bitmap
import android.os.Build
+import android.os.Handler
import android.view.PixelCopy
+import android.view.View
+import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.test.filters.SmallTest
import androidx.ui.core.Draw
@@ -34,20 +38,21 @@
import androidx.ui.layout.Container
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.DpConstraints
-import androidx.ui.layout.ScrollerPosition
-import androidx.ui.layout.VerticalScroller
import androidx.ui.graphics.Color
import androidx.ui.painting.Paint
import androidx.ui.painting.PaintingStyle
import androidx.compose.composer
import androidx.test.filters.SdkSuppress
+import androidx.ui.core.AndroidCraneView
import androidx.ui.core.setContent
-import androidx.ui.layout.HorizontalScroller
import androidx.ui.layout.Row
+import androidx.ui.test.android.AndroidComposeTestRule
+import androidx.ui.test.createComposeRule
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -56,7 +61,16 @@
@SmallTest
@RunWith(JUnit4::class)
-class ScrollerTest : LayoutTest() {
+class ScrollerTest {
+
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ // TODO(malkov/pavlis) : some tests here require activity access as we need
+ // to take screen's bitmap, abstract it better
+ val activity
+ get() = (composeTestRule as AndroidComposeTestRule).activityTestRule.activity
+
val colors = listOf(
Color(alpha = 0xFF, red = 0xFF, green = 0, blue = 0),
Color(alpha = 0xFF, red = 0xFF, green = 0xA5, blue = 0),
@@ -69,10 +83,14 @@
)
var drawLatch = CountDownLatch(1)
+ lateinit var handler: Handler
@Before
fun setupDrawLatch() {
drawLatch = CountDownLatch(1)
+ composeTestRule.runOnUiThread {
+ handler = Handler()
+ }
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@@ -99,8 +117,6 @@
val changeListener = ScrollerChangeListener(scrollerPosition)
composeVerticalScroller(scrollerPosition, changeListener, height = 30.ipx)
- changeListener.waitForChange()
-
validateVerticalScroller(0, 30)
// The 'draw' method will no longer be called because only the position
@@ -112,13 +128,13 @@
latch.countDown()
}
}
- runOnUiThread {
+ composeTestRule.runOnUiThread {
activity.window.decorView.viewTreeObserver.addOnDrawListener(onDrawListener)
assertEquals(10.px, changeListener.maxPosition)
- scrollerPosition.position = 10.px
+ scrollerPosition.value = 10.px
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
- runOnUiThread {
+ composeTestRule.runOnUiThread {
activity.window.decorView.viewTreeObserver.removeOnDrawListener(onDrawListener)
}
validateVerticalScroller(10, 30)
@@ -148,8 +164,6 @@
val changeListener = ScrollerChangeListener(scrollerPosition)
composeHorizontalScroller(scrollerPosition, changeListener, width = 30.ipx)
- changeListener.waitForChange()
-
validateHorizontalScroller(0, 30)
// The 'draw' method will no longer be called because only the position
@@ -161,13 +175,13 @@
latch.countDown()
}
}
- runOnUiThread {
+ composeTestRule.runOnUiThread {
activity.window.decorView.viewTreeObserver.addOnDrawListener(onDrawListener)
assertEquals(10.px, changeListener.maxPosition)
- scrollerPosition.position = 10.px
+ scrollerPosition.value = 10.px
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
- runOnUiThread {
+ composeTestRule.runOnUiThread {
activity.window.decorView.viewTreeObserver.removeOnDrawListener(onDrawListener)
}
validateHorizontalScroller(10, 30)
@@ -175,95 +189,89 @@
private fun composeVerticalScroller(
scrollerPosition: ScrollerPosition = ScrollerPosition(),
- onScrollChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
- scrollerPosition.position = position
+ onScrollChanged: (position: Px, max: Px) -> Unit = { position, _ ->
+ scrollerPosition.value = position
},
height: IntPx = 40.ipx
) {
// We assume that the height of the device is more than 45 px
- withDensity(density) {
+ withDensity(composeTestRule.density) {
val constraints = DpConstraints.tightConstraints(45.px.toDp(), height.toDp())
- val runnable: Runnable = object : Runnable {
- override fun run() {
- activity.setContent {
- Align(alignment = Alignment.TopLeft) {
- ConstrainedBox(constraints = constraints) {
- VerticalScroller(
- scrollerPosition = scrollerPosition,
- onScrollChanged = onScrollChanged
- ) {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
- colors.forEach { color ->
- Container(
- height = 5.px.toDp(),
- width = 45.px.toDp()
- ) {
- Draw { canvas, parentSize ->
- val paint = Paint()
- paint.color = color
- paint.style = PaintingStyle.fill
- canvas.drawRect(parentSize.toRect(), paint)
- }
+ composeTestRule.runOnUiThread {
+ activity.setContent {
+ Align(alignment = Alignment.TopLeft) {
+ ConstrainedBox(constraints = constraints) {
+ VerticalScroller(
+ scrollerPosition = scrollerPosition,
+ onScrollPositionChanged = onScrollChanged
+ ) {
+ Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ colors.forEach { color ->
+ Container(
+ height = 5.px.toDp(),
+ width = 45.px.toDp()
+ ) {
+ Draw { canvas, parentSize ->
+ val paint = Paint()
+ paint.color = color
+ paint.style = PaintingStyle.fill
+ canvas.drawRect(parentSize.toRect(), paint)
}
}
}
}
- Draw { _, _ ->
- drawLatch.countDown()
- }
+ }
+ Draw { _, _ ->
+ drawLatch.countDown()
}
}
}
}
}
- activityTestRule.runOnUiThread(runnable)
}
}
private fun composeHorizontalScroller(
scrollerPosition: ScrollerPosition = ScrollerPosition(),
- onScrollChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
- scrollerPosition.position = position
+ onScrollChanged: (position: Px, max: Px) -> Unit = { position, _ ->
+ scrollerPosition.value = position
},
width: IntPx = 40.ipx
) {
// We assume that the height of the device is more than 45 px
- withDensity(density) {
+ withDensity(composeTestRule.density) {
val constraints = DpConstraints.tightConstraints(width.toDp(), 45.px.toDp())
- val runnable: Runnable = object : Runnable {
- override fun run() {
- activity.setContent {
- Align(alignment = Alignment.TopLeft) {
- ConstrainedBox(constraints = constraints) {
- HorizontalScroller(
- scrollerPosition = scrollerPosition,
- onScrollChanged = onScrollChanged
- ) {
- Row(crossAxisAlignment = CrossAxisAlignment.Start) {
- colors.forEach { color ->
- Container(
- width = 5.px.toDp(),
- height = 45.px.toDp()
- ) {
- Draw { canvas, parentSize ->
- val paint = Paint()
- paint.color = color
- paint.style = PaintingStyle.fill
- canvas.drawRect(parentSize.toRect(), paint)
- }
+ composeTestRule.runOnUiThread {
+ activity.setContent {
+ Align(alignment = Alignment.TopLeft) {
+ ConstrainedBox(constraints = constraints) {
+ HorizontalScroller(
+ scrollerPosition = scrollerPosition,
+ onScrollPositionChanged = onScrollChanged
+ ) {
+ Row(crossAxisAlignment = CrossAxisAlignment.Start) {
+ colors.forEach { color ->
+ Container(
+ width = 5.px.toDp(),
+ height = 45.px.toDp()
+ ) {
+ Draw { canvas, parentSize ->
+ val paint = Paint()
+ paint.color = color
+ paint.style = PaintingStyle.fill
+ canvas.drawRect(parentSize.toRect(), paint)
}
}
}
}
- Draw { _, _ ->
- drawLatch.countDown()
- }
+ }
+ Draw { _, _ ->
+ drawLatch.countDown()
}
}
}
}
}
- activityTestRule.runOnUiThread(runnable)
}
}
@@ -315,16 +323,6 @@
}
}
- // We only need this because IR compiler doesn't like converting lambdas to Runnables
- private fun runOnUiThread(block: () -> Unit) {
- @Suppress("ObjectLiteralToLambda") val runnable: Runnable = object : Runnable {
- override fun run() {
- block()
- }
- }
- activityTestRule.runOnUiThread(runnable)
- }
-
private fun waitAndScreenShot(): Bitmap {
val view = findAndroidCraneView()
waitForDraw(view)
@@ -365,7 +363,7 @@
changeCalls++
lock.notify()
}
- scrollerPosition.position = position
+ scrollerPosition.value = position
this.maxPosition = maxPosition
}
@@ -379,4 +377,42 @@
}
}
}
+
+ // TODO(malkov): ALL below is copypaste from LayoutTest as this test in ui-foundation now
+
+ internal fun findAndroidCraneView(): AndroidCraneView {
+ val contentViewGroup = activity.findViewById<ViewGroup>(android.R.id.content)
+ return findAndroidCraneView(contentViewGroup)!!
+ }
+
+ internal fun findAndroidCraneView(parent: ViewGroup): AndroidCraneView? {
+ for (index in 0 until parent.childCount) {
+ val child = parent.getChildAt(index)
+ if (child is AndroidCraneView) {
+ return child
+ } else if (child is ViewGroup) {
+ val craneView = findAndroidCraneView(child)
+ if (craneView != null) {
+ return craneView
+ }
+ }
+ }
+ return null
+ }
+
+ internal fun waitForDraw(view: View) {
+ val viewDrawLatch = CountDownLatch(1)
+ val listener = object : ViewTreeObserver.OnDrawListener {
+ override fun onDraw() {
+ viewDrawLatch.countDown()
+ }
+ }
+ view.post(object : Runnable {
+ override fun run() {
+ view.viewTreeObserver.addOnDrawListener(listener)
+ view.invalidate()
+ }
+ })
+ assertTrue(viewDrawLatch.await(1, TimeUnit.SECONDS))
+ }
}
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Scroller.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Scroller.kt
new file mode 100644
index 0000000..3849680
--- /dev/null
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Scroller.kt
@@ -0,0 +1,310 @@
+/*
+ * 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.foundation
+
+import androidx.animation.AnimatedFloat
+import androidx.animation.ExponentialDecay
+import androidx.animation.ValueHolder
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.compose.Model
+import androidx.compose.memo
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.Clip
+import androidx.ui.core.Constraints
+import androidx.ui.core.IntPx
+import androidx.ui.core.Layout
+import androidx.ui.core.Px
+import androidx.ui.core.RepaintBoundary
+import androidx.ui.core.gesture.PressGestureDetector
+import androidx.ui.core.ipx
+import androidx.ui.core.min
+import androidx.ui.core.px
+import androidx.ui.core.round
+import androidx.ui.core.toPx
+import androidx.ui.foundation.animation.AnimatedFloatDragController
+import androidx.ui.foundation.animation.FlingConfig
+import androidx.ui.foundation.gestures.DragDirection
+import androidx.ui.foundation.gestures.DragValueController
+import androidx.ui.foundation.gestures.Draggable
+import androidx.ui.foundation.shape.RectangleShape
+import androidx.ui.layout.Constraints
+import androidx.ui.layout.Container
+import androidx.ui.lerp
+
+/**
+ * This is the state of a [VerticalScroller] and [HorizontalScroller] that
+ * allows the developer to change the scroll position.
+ * [value] must be between `0` and `maxPosition` in `onScrollPositionChanged`'s `maxPosition`
+ * parameter.
+ */
+@Model
+class ScrollerPosition {
+
+ /**
+ * The amount of scrolling, between `0` and `maxPosition` in `onScrollPositionChanged`'s
+ * `maxPosition` parameter.
+ */
+ var value: Px = 0.px
+
+ /**
+ * Smooth scroll to position in pixels
+ *
+ * @param value target value to smooth scroll to
+ */
+ // TODO (malkov/tianliu) : think about allowing to scroll with custom animation timings/curves
+ fun smoothScrollTo(value: Px, onFinished: (isCancelled: Boolean) -> Unit = {}) {
+ controller.animatedFloat.animateTo(-value.value, onFinished)
+ }
+
+ /**
+ * Smooth scroll by some amount of pixels
+ *
+ * @param value delta to scroll by
+ */
+ fun smoothScrollBy(value: Px, onFinished: (isCancelled: Boolean) -> Unit = {}) {
+ smoothScrollTo(this.value + value, onFinished)
+ }
+
+ /**
+ * Instantly jump to position in pixels
+ *
+ * @param value target value to jump to
+ */
+ fun scrollTo(value: Px) {
+ controller.onDrag(-value.value)
+ }
+
+ /**
+ * Instantly jump by some amount of pixels
+ *
+ * @param value delta to jump by
+ */
+ fun scrollBy(value: Px) {
+ scrollTo(this.value + value)
+ }
+
+ // TODO (malkov/tianliu): Open this for customization
+ private val flingConfig = FlingConfig(
+ decayAnimation = ExponentialDecay(
+ frictionMultiplier = ScrollerDefaultFriction,
+ absVelocityThreshold = ScrollerVelocityThreshold
+ )
+ )
+
+ internal val controller =
+ ScrollerDragValueController({ lh.lambda.invoke(-it) }, flingConfig)
+
+ // This is needed to take instant value we're currently dragging
+ // and avoid reading @Model var field
+ internal val instantValue
+ get() = -controller.currentValue
+
+ // This is needed to avoid var (read of which will cause unnecessary recompose in Scroller)
+ internal val lh = LambdaHolder { value = it.px }
+}
+
+/**
+ * A container that composes all of its contents and lays it out, fitting the width of the child.
+ * If the child's height is less than the [Constraints.maxHeight], the child's height is used,
+ * or the [Constraints.maxHeight] otherwise. If the contents don't fit the height, the drag gesture
+ * allows scrolling its content vertically. The contents of the VerticalScroller are clipped to
+ * the VerticalScroller's bounds.
+ *
+ * @sample androidx.ui.foundation.samples.VerticalScrollerSample
+ *
+ * @param scrollerPosition state of this Scroller that holds current scroll position and provides
+ * user with useful methods like smooth scrolling
+ * @param onScrollPositionChanged callback to be invoked when scroll position is about to be
+ * changed or max bound of scrolling has changed
+ * @param isScrollable param to enabled or disable touch input scrolling, default is true
+ */
+@Composable
+fun VerticalScroller(
+ scrollerPosition: ScrollerPosition = +memo { ScrollerPosition() },
+ onScrollPositionChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
+ scrollerPosition.value = position
+ },
+ isScrollable: Boolean = true,
+ @Children child: @Composable() () -> Unit
+) {
+ Scroller(scrollerPosition, onScrollPositionChanged, true, isScrollable, child)
+}
+
+/**
+ * A container that composes all of its contents and lays it out, fitting the height of the child.
+ * If the child's width is less than the [Constraints.maxWidth], the child's width is used,
+ * or the [Constraints.maxWidth] otherwise. If the contents don't fit the width, the drag gesture
+ * allows scrolling its content horizontally. The contents of the HorizontalScroller are clipped to
+ * the HorizontalScroller's bounds.
+ *
+ * @sample androidx.ui.foundation.samples.SimpleHorizontalScrollerSample
+ *
+ * If you want to control scrolling position from the code, e.g smooth scroll to position,
+ * you must own memorized instance of [ScrollerPosition] and then use it to call `scrollTo...`
+ * functions on it. Same tactic can be applied to the [VerticalScroller]
+ *
+ * @sample androidx.ui.foundation.samples.ControlledHorizontalScrollerSample
+ *
+ * @param scrollerPosition state of this Scroller that holds current scroll position and provides
+ * user with useful methods like smooth scrolling
+ * @param onScrollPositionChanged callback to be invoked when scroll position is about to be
+ * changed or max bound of scrolling has changed
+ * @param isScrollable param to enabled or disable touch input scrolling, default is true
+ */
+@Composable
+fun HorizontalScroller(
+ scrollerPosition: ScrollerPosition = +memo { ScrollerPosition() },
+ onScrollPositionChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
+ scrollerPosition.value = position
+ },
+ isScrollable: Boolean = true,
+ @Children child: @Composable() () -> Unit
+) {
+ Scroller(scrollerPosition, onScrollPositionChanged, false, isScrollable, child)
+}
+
+@Composable
+private fun Scroller(
+ scrollerPosition: ScrollerPosition,
+ onScrollPositionChanged: (position: Px, maxPosition: Px) -> Unit,
+ isVertical: Boolean,
+ isScrollable: Boolean,
+ @Children child: @Composable() () -> Unit
+) {
+ val maxPosition = +state { Px.Infinity }
+ val direction = if (isVertical) DragDirection.Vertical else DragDirection.Horizontal
+ val controller = +memo(isScrollable) {
+ if (isScrollable) {
+ scrollerPosition.controller
+ } else {
+ StubController(scrollerPosition.instantValue).also {
+ scrollerPosition.scrollTo(scrollerPosition.instantValue.px)
+ }
+ }
+ }
+ scrollerPosition.lh.lambda = { onScrollPositionChanged(it.px, maxPosition.value) }
+ PressGestureDetector(onPress = { scrollerPosition.scrollTo(scrollerPosition.value) }) {
+ Draggable(
+ dragDirection = direction,
+ minValue = -maxPosition.value.value,
+ maxValue = 0f,
+ valueController = controller
+ ) {
+ ScrollerLayout(
+ scrollerPosition = scrollerPosition,
+ maxPosition = maxPosition.value,
+ onMaxPositionChanged = {
+ maxPosition.value = it
+ onScrollPositionChanged(scrollerPosition.value, it)
+ },
+ isVertical = isVertical,
+ child = child
+ )
+ }
+ }
+}
+
+@Composable
+private fun ScrollerLayout(
+ scrollerPosition: ScrollerPosition,
+ maxPosition: Px,
+ onMaxPositionChanged: (Px) -> Unit,
+ isVertical: Boolean,
+ child: @Composable() () -> Unit
+) {
+ Layout(children = {
+ Clip(RectangleShape) {
+ Container {
+ RepaintBoundary {
+ child()
+ }
+ }
+ }
+ }) { measurables, constraints ->
+ if (measurables.size > 1) {
+ throw IllegalStateException("Only one child is allowed in a VerticalScroller")
+ }
+ val childConstraints = constraints.copy(
+ maxHeight = if (isVertical) IntPx.Infinity else constraints.maxHeight,
+ maxWidth = if (isVertical) constraints.maxWidth else IntPx.Infinity
+ )
+ val childMeasurable = measurables.firstOrNull()
+ val placeable = childMeasurable?.measure(childConstraints)
+ val width: IntPx
+ val height: IntPx
+ if (placeable == null) {
+ width = constraints.minWidth
+ height = constraints.minHeight
+ } else {
+ width = min(placeable.width, constraints.maxWidth)
+ height = min(placeable.height, constraints.maxHeight)
+ }
+ layout(width, height) {
+ val childHeight = placeable?.height?.toPx() ?: 0.px
+ val childWidth = placeable?.width?.toPx() ?: 0.px
+ val scrollHeight = childHeight - height.toPx()
+ val scrollWidth = childWidth - width.toPx()
+ val side = if (isVertical) scrollHeight else scrollWidth
+ if (side != 0.px && side != maxPosition) {
+ onMaxPositionChanged(side)
+ }
+ val xOffset = if (isVertical) 0.ipx else -scrollerPosition.value.round()
+ val yOffset = if (isVertical) -scrollerPosition.value.round() else 0.ipx
+ placeable?.place(xOffset, yOffset)
+ }
+ }
+}
+
+private fun ScrollerDragValueController(
+ onValueChanged: (Float) -> Unit,
+ flingConfig: FlingConfig? = null
+) = AnimatedFloatDragController(
+ AnimatedFloat(ScrollPositionValueHolder(0f, onValueChanged)),
+ flingConfig
+)
+
+private class ScrollPositionValueHolder(
+ var current: Float,
+ val onValueChanged: (Float) -> Unit
+) : ValueHolder<Float> {
+ override val interpolator: (start: Float, end: Float, fraction: Float) -> Float = ::lerp
+ override var value: Float
+ get() = current
+ set(value) {
+ current = value
+ onValueChanged(value)
+ }
+}
+
+private fun StubController(value: Float) = object : DragValueController {
+ override val currentValue: Float
+ get() = value
+
+ override fun onDrag(target: Float) {}
+
+ override fun onDragEnd(velocity: Float, onValueSettled: (Float) -> Unit) {}
+
+ override fun setBounds(min: Float, max: Float) {}
+}
+
+internal data class LambdaHolder(var lambda: (Float) -> Unit)
+
+private val ScrollerDefaultFriction = 0.35f
+private val ScrollerVelocityThreshold = 1000f
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/AnimatedFloatDragController.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/AnimatedFloatDragController.kt
index c47f27e..8198e44 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/AnimatedFloatDragController.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/AnimatedFloatDragController.kt
@@ -29,16 +29,24 @@
* It makes it possible to have animation support for [Draggable] composable
* as well as to have fling animation after drag has ended, which is defined by [FlingConfig]
*
- * @param initialValue initial value for AnimatedFloat to set it up
+ * @param animatedFloat instance of AnimatedFloat to control
* @param flingConfig sets behavior of the fling after drag has ended.
* Default is null, which means no fling will occur no matter the velocity
*/
class AnimatedFloatDragController(
- initialValue: Float,
+ val animatedFloat: AnimatedFloat,
private val flingConfig: FlingConfig? = null
) : DragValueController {
- val animatedFloat = AnimatedFloat(AnimValueHolder(initialValue, ::lerp))
+ /**
+ * Construct controller that creates and owns AnimatedFloat instance
+ *
+ * @param initialValue initial value for AnimatedFloat to set it up
+ * @param flingConfig sets behavior of the fling after drag has ended.
+ * Default is null, which means no fling will occur no matter the velocity
+ */
+ constructor(initialValue: Float, flingConfig: FlingConfig?)
+ : this(AnimatedFloat(AnimValueHolder(initialValue, ::lerp)), flingConfig)
override val currentValue
get() = animatedFloat.value
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
index 4ce2cf1..bf850804 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
@@ -205,7 +205,7 @@
accessibilityLabel = text.text
}
) {
- val textPainter = +memo(
+ val textDelegate = +memo(
text,
mergedStyle,
paragraphStyle,
@@ -232,17 +232,17 @@
OnPositioned(onPositioned = { layoutCoordinates.value = it })
Draw { canvas, _ ->
internalSelection.value?.let {
- textPainter.paintBackground(
+ textDelegate.paintBackground(
it.start, it.end, selectionColor, canvas
)
}
- textPainter.paint(canvas)
+ textDelegate.paint(canvas)
}
}
ComplexLayout(children) {
measure { _, constraints ->
- textPainter.layout(constraints)
- layout(textPainter.width.px.round(), textPainter.height.px.round()) {}
+ textDelegate.layout(constraints)
+ layout(textDelegate.width.px.round(), textDelegate.height.px.round()) {}
}
minIntrinsicWidth { _, _ ->
// TODO(popam): discuss with the Text team about this
@@ -251,16 +251,16 @@
// textDelegate.minIntrinsicWidth.px.round()
}
minIntrinsicHeight { _, w ->
- textPainter.layout(Constraints(0.ipx, w, 0.ipx, IntPx.Infinity))
- textPainter.height.px.round()
+ textDelegate.layout(Constraints(0.ipx, w, 0.ipx, IntPx.Infinity))
+ textDelegate.height.px.round()
}
maxIntrinsicWidth { _, h ->
- textPainter.layout(Constraints(0.ipx, IntPx.Infinity, 0.ipx, h))
- textPainter.maxIntrinsicWidth.px.round()
+ textDelegate.layout(Constraints(0.ipx, IntPx.Infinity, 0.ipx, h))
+ textDelegate.maxIntrinsicWidth.px.round()
}
maxIntrinsicHeight { _, w ->
- textPainter.layout(Constraints(0.ipx, w, 0.ipx, IntPx.Infinity))
- textPainter.height.px.round()
+ textDelegate.layout(Constraints(0.ipx, w, 0.ipx, IntPx.Infinity))
+ textDelegate.height.px.round()
}
}
@@ -290,7 +290,7 @@
selectionCoordinates = Pair(startPx, endPx),
mode = mode,
onSelectionChange = { internalSelection.value = it },
- textDelegate = textPainter
+ textDelegate = textDelegate
)
if (!textSelectionProcessor.isSelected) return null
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
index 9dfb3d3..cf28b6e 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
@@ -100,7 +100,7 @@
val (visualText, offsetMap) = +memo(value, visualTransformation) {
TextFieldDelegate.applyVisualFilter(value, visualTransformation)
}
- val textPainter = +memo(visualText, mergedStyle, density, resourceLoader) {
+ val textDelegate = +memo(visualText, mergedStyle, density, resourceLoader) {
// TODO(nona): Add parameter for text direction, softwrap, etc.
TextDelegate(
text = visualText,
@@ -131,7 +131,7 @@
textInputService?.let { textInputService ->
TextFieldDelegate.notifyFocusedRect(
value,
- textPainter,
+ textDelegate,
coords,
textInputService,
hasFocus.value,
@@ -153,7 +153,7 @@
onRelease = {
TextFieldDelegate.onRelease(
it,
- textPainter,
+ textDelegate,
processor,
offsetMap,
onValueChange,
@@ -170,7 +170,7 @@
coords.value = it
TextFieldDelegate.notifyFocusedRect(
value,
- textPainter,
+ textDelegate,
it,
textInputService,
hasFocus.value,
@@ -182,12 +182,12 @@
canvas,
value,
offsetMap,
- textPainter,
+ textDelegate,
hasFocus.value,
editorStyle) }
},
measureBlock = { _, constraints ->
- TextFieldDelegate.layout(textPainter, constraints).let {
+ TextFieldDelegate.layout(textDelegate, constraints).let {
layout(it.first, it.second) {}
}
}
diff --git a/ui/ui-layout/api/1.0.0-alpha01.txt b/ui/ui-layout/api/1.0.0-alpha01.txt
index 76dd60f..6a517d6 100644
--- a/ui/ui-layout/api/1.0.0-alpha01.txt
+++ b/ui/ui-layout/api/1.0.0-alpha01.txt
@@ -142,23 +142,6 @@
method public static void Padding(androidx.ui.core.Dp padding, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
- public final class ScrollerKt {
- ctor public ScrollerKt();
- method public static void HorizontalScroller(androidx.ui.layout.ScrollerPosition scrollerPosition = +memo({
- <init>()
-}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollChanged = { position, _ -> scrollerPosition.position = position }, kotlin.jvm.functions.Function0<kotlin.Unit> child);
- method public static void VerticalScroller(androidx.ui.layout.ScrollerPosition scrollerPosition = +memo({
- <init>()
-}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollChanged = { position, _ -> scrollerPosition.position = position }, kotlin.jvm.functions.Function0<kotlin.Unit> child);
- }
-
- public final class ScrollerPosition {
- ctor public ScrollerPosition();
- method public androidx.ui.core.Px getPosition();
- method public void setPosition(androidx.ui.core.Px p);
- property public final androidx.ui.core.Px position;
- }
-
public final class SpacerKt {
ctor public SpacerKt();
method public static void FixedSpacer(androidx.ui.core.Dp width, androidx.ui.core.Dp height);
diff --git a/ui/ui-layout/api/current.txt b/ui/ui-layout/api/current.txt
index 76dd60f..6a517d6 100644
--- a/ui/ui-layout/api/current.txt
+++ b/ui/ui-layout/api/current.txt
@@ -142,23 +142,6 @@
method public static void Padding(androidx.ui.core.Dp padding, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
- public final class ScrollerKt {
- ctor public ScrollerKt();
- method public static void HorizontalScroller(androidx.ui.layout.ScrollerPosition scrollerPosition = +memo({
- <init>()
-}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollChanged = { position, _ -> scrollerPosition.position = position }, kotlin.jvm.functions.Function0<kotlin.Unit> child);
- method public static void VerticalScroller(androidx.ui.layout.ScrollerPosition scrollerPosition = +memo({
- <init>()
-}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollChanged = { position, _ -> scrollerPosition.position = position }, kotlin.jvm.functions.Function0<kotlin.Unit> child);
- }
-
- public final class ScrollerPosition {
- ctor public ScrollerPosition();
- method public androidx.ui.core.Px getPosition();
- method public void setPosition(androidx.ui.core.Px p);
- property public final androidx.ui.core.Px position;
- }
-
public final class SpacerKt {
ctor public SpacerKt();
method public static void FixedSpacer(androidx.ui.core.Dp width, androidx.ui.core.Dp height);
diff --git a/ui/ui-layout/api/restricted_1.0.0-alpha01.txt b/ui/ui-layout/api/restricted_1.0.0-alpha01.txt
index 76dd60f..6a517d6 100644
--- a/ui/ui-layout/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-layout/api/restricted_1.0.0-alpha01.txt
@@ -142,23 +142,6 @@
method public static void Padding(androidx.ui.core.Dp padding, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
- public final class ScrollerKt {
- ctor public ScrollerKt();
- method public static void HorizontalScroller(androidx.ui.layout.ScrollerPosition scrollerPosition = +memo({
- <init>()
-}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollChanged = { position, _ -> scrollerPosition.position = position }, kotlin.jvm.functions.Function0<kotlin.Unit> child);
- method public static void VerticalScroller(androidx.ui.layout.ScrollerPosition scrollerPosition = +memo({
- <init>()
-}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollChanged = { position, _ -> scrollerPosition.position = position }, kotlin.jvm.functions.Function0<kotlin.Unit> child);
- }
-
- public final class ScrollerPosition {
- ctor public ScrollerPosition();
- method public androidx.ui.core.Px getPosition();
- method public void setPosition(androidx.ui.core.Px p);
- property public final androidx.ui.core.Px position;
- }
-
public final class SpacerKt {
ctor public SpacerKt();
method public static void FixedSpacer(androidx.ui.core.Dp width, androidx.ui.core.Dp height);
diff --git a/ui/ui-layout/api/restricted_current.txt b/ui/ui-layout/api/restricted_current.txt
index 76dd60f..6a517d6 100644
--- a/ui/ui-layout/api/restricted_current.txt
+++ b/ui/ui-layout/api/restricted_current.txt
@@ -142,23 +142,6 @@
method public static void Padding(androidx.ui.core.Dp padding, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
- public final class ScrollerKt {
- ctor public ScrollerKt();
- method public static void HorizontalScroller(androidx.ui.layout.ScrollerPosition scrollerPosition = +memo({
- <init>()
-}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollChanged = { position, _ -> scrollerPosition.position = position }, kotlin.jvm.functions.Function0<kotlin.Unit> child);
- method public static void VerticalScroller(androidx.ui.layout.ScrollerPosition scrollerPosition = +memo({
- <init>()
-}), kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit> onScrollChanged = { position, _ -> scrollerPosition.position = position }, kotlin.jvm.functions.Function0<kotlin.Unit> child);
- }
-
- public final class ScrollerPosition {
- ctor public ScrollerPosition();
- method public androidx.ui.core.Px getPosition();
- method public void setPosition(androidx.ui.core.Px p);
- property public final androidx.ui.core.Px position;
- }
-
public final class SpacerKt {
ctor public SpacerKt();
method public static void FixedSpacer(androidx.ui.core.Dp width, androidx.ui.core.Dp height);
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/AndroidManifest.xml b/ui/ui-layout/integration-tests/layout-demos/src/main/AndroidManifest.xml
index 59cf792..0bbe95f 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/AndroidManifest.xml
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/AndroidManifest.xml
@@ -37,15 +37,6 @@
</intent-filter>
</activity>
- <activity android:name=".ScrollerActivity"
- android:configChanges="orientation|screenSize"
- android:label="Scroller">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="androidx.ui.demos.SAMPLE_CODE" />
- </intent-filter>
- </activity>
-
<activity android:name=".TableActivity"
android:configChanges="orientation|screenSize"
android:label="Layout/Table">
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ScrollerActivity.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ScrollerActivity.kt
deleted file mode 100644
index e59ce03..0000000
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ScrollerActivity.kt
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.layout.demos
-
-import android.app.Activity
-import android.os.Bundle
-import androidx.ui.core.Density
-import androidx.ui.core.Text
-import androidx.ui.core.dp
-import androidx.ui.core.withDensity
-import androidx.ui.layout.Column
-import androidx.ui.layout.Padding
-import androidx.ui.layout.VerticalScroller
-import androidx.compose.composer
-import androidx.ui.text.TextStyle
-import androidx.ui.core.setContent
-import androidx.ui.core.sp
-
-class ScrollerActivity : Activity() {
- private val phrases = listOf(
- "Easy As Pie",
- "Wouldn't Harm a Fly",
- "No-Brainer",
- "Keep On Truckin'",
- "An Arm and a Leg",
- "Down To Earth",
- "Under the Weather",
- "Up In Arms",
- "Cup Of Joe",
- "Not the Sharpest Tool in the Shed",
- "Ring Any Bells?",
- "Son of a Gun",
- "Hard Pill to Swallow",
- "Close But No Cigar",
- "Beating a Dead Horse",
- "If You Can't Stand the Heat, Get Out of the Kitchen",
- "Cut To The Chase",
- "Heads Up",
- "Goody Two-Shoes",
- "Fish Out Of Water",
- "Cry Over Spilt Milk",
- "Elephant in the Room",
- "There's No I in Team",
- "Poke Fun At",
- "Talk the Talk",
- "Know the Ropes",
- "Fool's Gold",
- "It's Not Brain Surgery",
- "Fight Fire With Fire",
- "Go For Broke"
- )
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val density = Density(this)
- withDensity(density) {
- val style = TextStyle(fontSize = 30.sp)
- setContent {
- Padding(padding = 10.dp) {
- VerticalScroller {
- Column {
- phrases.forEach { phrase ->
- Text(text = phrase, style = style)
- }
- }
- }
- }
- }
- }
- }
-}
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt
deleted file mode 100644
index 7686861..0000000
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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.layout
-
-import androidx.ui.core.coerceIn
-import androidx.ui.core.Constraints
-import androidx.ui.core.Direction
-import androidx.ui.core.IntPx
-import androidx.ui.core.Layout
-import androidx.ui.core.Px
-import androidx.ui.core.PxPosition
-import androidx.ui.core.gesture.TouchSlopDragGestureDetector
-import androidx.ui.core.gesture.DragObserver
-import androidx.ui.core.min
-import androidx.ui.core.px
-import androidx.ui.core.round
-import androidx.ui.core.toPx
-import androidx.ui.core.toRect
-import androidx.compose.Composable
-import androidx.compose.Model
-import androidx.compose.composer
-import androidx.compose.memo
-import androidx.compose.state
-import androidx.compose.unaryPlus
-import androidx.ui.core.Clip
-import androidx.ui.core.Density
-import androidx.ui.core.PxSize
-import androidx.ui.core.RepaintBoundary
-import androidx.ui.engine.geometry.Outline
-import androidx.ui.engine.geometry.Shape
-
-/**
- * Tracks the vertical drag gesture offset, allowing a range between `0.px` and [max].
- * When the offset changes, [offsetChange] is called with the new offset.
- */
-@Composable
-private fun DirectionalDragGestureDetector(
- vertical: Boolean,
- max: Px = Px.Infinity,
- offsetChange: (Px) -> Unit,
- children: @Composable() () -> Unit
-) {
- val offset = +state { 0.px }
- TouchSlopDragGestureDetector(
- dragObserver = object : DragObserver {
- override fun onDrag(dragDistance: PxPosition): PxPosition {
- val draggedAmount = if (vertical) dragDistance.y else dragDistance.x
- val dragPosition = -offset.value + draggedAmount
- val targetPosition = dragPosition.coerceIn(-max, 0.px)
- if (targetPosition != -offset.value) {
- offset.value = -targetPosition
- offsetChange(offset.value)
- }
- val consumed = draggedAmount + (targetPosition - dragPosition)
- return if (vertical) PxPosition(0.px, consumed) else PxPosition(consumed, 0.px)
- }
- },
- canDrag = { direction ->
- when (direction) {
- Direction.DOWN -> if (vertical) offset.value > 0.px else false
- Direction.UP -> if (vertical) offset.value < max else false
- Direction.RIGHT -> if (vertical) false else offset.value > 0.px
- Direction.LEFT -> if (vertical) false else offset.value < max
- }
- }) { children() }
-}
-
-/**
- * This is the state of a [VerticalScroller] that allows the developer to change the scroll
- * position. [position] must be between `0` and `maxPosition` in `onScrollChanged`'s `maxPosition`
- * parameter.
- */
-@Model
-class ScrollerPosition {
- /**
- * The amount of scrolling, between `0` and `maxPosition` in `onScrollChanged`'s `maxPosition`
- * parameter.
- */
- var position: Px = 0.px
-}
-
-/**
- * A container that composes all of its contents and lays it out, fitting the width of the child.
- * If the child's height is less than the [Constraints.maxHeight], the child's height is used,
- * or the [Constraints.maxHeight] otherwise. If the contents don't fit the height, the drag gesture
- * allows scrolling its content vertically. The contents of the VerticalScroller are clipped to
- * the VerticalScroller's bounds.
- */
-// TODO(mount): Add fling support
-@Composable
-fun VerticalScroller(
- scrollerPosition: ScrollerPosition = +memo { ScrollerPosition() },
- onScrollChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
- scrollerPosition.position = position
- },
- child: @Composable() () -> Unit
-) {
- Scroller(
- vertical = true,
- scrollerPosition = scrollerPosition,
- onScrollChanged = onScrollChanged,
- child = child
- )
-}
-
-/**
- * A container that composes all of its contents and lays it out, fitting the height of the child.
- * If the child's width is less than the [Constraints.maxWidth], the child's width is used,
- * or the [Constraints.maxWidth] otherwise. If the contents don't fit the width, the drag gesture
- * allows scrolling its content horizontally. The contents of the HorizontalScroller are clipped to
- * the HorizontalScroller's bounds.
- */
-// TODO(mount): Add fling support
-@Composable
-fun HorizontalScroller(
- scrollerPosition: ScrollerPosition = +memo { ScrollerPosition() },
- onScrollChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
- scrollerPosition.position = position
- },
- child: @Composable() () -> Unit
-) {
- Scroller(
- vertical = false,
- scrollerPosition = scrollerPosition,
- onScrollChanged = onScrollChanged,
- child = child
- )
-}
-
-@Composable
-private fun Scroller(
- vertical: Boolean,
- scrollerPosition: ScrollerPosition = +memo { ScrollerPosition() },
- onScrollChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
- scrollerPosition.position = position
- },
- child: @Composable() () -> Unit
-) {
- val maxPosition = +state { 0.px }
- Layout(children = {
- Clip(RectangleShape) {
- DirectionalDragGestureDetector(
- vertical = vertical,
- max = maxPosition.value,
- offsetChange = { newOffset -> onScrollChanged(newOffset, maxPosition.value) }) {
- Container {
- RepaintBoundary {
- child()
- }
- }
- }
- }
- }) { measurables, constraints ->
- val childConstraints = if (vertical) {
- constraints.copy(maxHeight = IntPx.Infinity)
- } else {
- constraints.copy(maxWidth = IntPx.Infinity)
- }
- val childMeasurable = measurables.firstOrNull()
- val placeable = childMeasurable?.measure(childConstraints)
- val width: IntPx
- val height: IntPx
- if (placeable == null) {
- width = constraints.minWidth
- height = constraints.minHeight
- } else {
- width = min(placeable.width, constraints.maxWidth)
- height = min(placeable.height, constraints.maxHeight)
- }
- layout(width, height) {
- if (placeable != null) {
- val childSize = if (vertical) {
- placeable.height.toPx()
- } else {
- placeable.width.toPx()
- }
- val newMaxPosition = childSize - if (vertical) height.toPx() else width.toPx()
- if (maxPosition.value != newMaxPosition) {
- maxPosition.value = newMaxPosition
- onScrollChanged(scrollerPosition.position, maxPosition.value)
- }
- val x: IntPx
- val y: IntPx
- if (vertical) {
- x = IntPx.Zero
- y = -scrollerPosition.position.round()
- } else {
- x = -scrollerPosition.position.round()
- y = IntPx.Zero
- }
- placeable.place(x, y)
- }
- }
- }
-}
-
-// TODO(andreykulikov): make RectangleShape from ui-foundation accessible to this class
-private val RectangleShape: Shape = object : Shape {
- override fun createOutline(size: PxSize, density: Density) =
- Outline.Rectangle(size.toRect())
-}
diff --git a/ui/ui-text/integration-tests/text-demos/build.gradle b/ui/ui-text/integration-tests/text-demos/build.gradle
index 389c24b..8254c7e2 100644
--- a/ui/ui-text/integration-tests/text-demos/build.gradle
+++ b/ui/ui-text/integration-tests/text-demos/build.gradle
@@ -20,6 +20,7 @@
implementation project(":compose:compose-runtime")
implementation project(":ui:ui-core")
implementation project(':ui:ui-framework')
+ implementation project(':ui:ui-foundation')
implementation project(':ui:ui-layout')
implementation project(':ui:ui-text')
}
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneInputField.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneInputField.kt
index a775fd1..f590b27 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneInputField.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneInputField.kt
@@ -27,7 +27,7 @@
import androidx.ui.input.KeyboardType
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
-import androidx.ui.layout.VerticalScroller
+import androidx.ui.foundation.VerticalScroller
import androidx.ui.text.TextStyle
val KEYBOARD_TYPES = listOf(
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
index e9af896..a37f2c0 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
@@ -38,7 +38,7 @@
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.Row
-import androidx.ui.layout.VerticalScroller
+import androidx.ui.foundation.VerticalScroller
import androidx.ui.text.ParagraphStyle
import androidx.ui.painting.Shadow
import androidx.compose.composer
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt
index 05cfca9..2614a77 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt
@@ -35,7 +35,7 @@
import androidx.ui.input.KeyboardType
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
-import androidx.ui.layout.VerticalScroller
+import androidx.ui.foundation.VerticalScroller
import androidx.ui.text.AnnotatedString
import androidx.ui.text.TextStyle
import java.util.Locale
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/TextDelegateIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/TextDelegateIntegrationTest.kt
index 4ada2e9..2c0ffe6 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/TextDelegateIntegrationTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/TextDelegateIntegrationTest.kt
@@ -56,9 +56,9 @@
// @Test
// fun preferredLineHeight_style_not_set() {
// val defaultTextStyle = TextStyle(fontFamily = fontFamily)
-// val textPainter = TextDelegate(style = defaultTextStyle)
+// val textDelegate = TextDelegate(style = defaultTextStyle)
//
-// val prefferedHeight = textPainter.preferredLineHeight
+// val prefferedHeight = textDelegate.preferredLineHeight
//
// assertThat(prefferedHeight).isEqualTo(14.0)
// }
@@ -72,16 +72,16 @@
text = text,
textStyles = listOf(AnnotatedString.Item(textStyle, 0, text.length))
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Rtl),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
- assertThat(textPainter.minIntrinsicWidth).isEqualTo(0.0f)
+ assertThat(textDelegate.minIntrinsicWidth).isEqualTo(0.0f)
}
@Test
@@ -100,16 +100,18 @@
)
)
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Rtl),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
- assertThat(textPainter.maxIntrinsicWidth).isEqualTo(fontSize.toPx().value * text.length)
+ assertThat(
+ textDelegate.maxIntrinsicWidth).isEqualTo(fontSize.toPx().value * text.length
+ )
}
}
@@ -129,16 +131,16 @@
)
)
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Rtl),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints(0.ipx, 200.ipx))
+ textDelegate.layout(Constraints(0.ipx, 200.ipx))
- assertThat(textPainter.width).isEqualTo(fontSize.toPx().value * text.length)
+ assertThat(textDelegate.width).isEqualTo(fontSize.toPx().value * text.length)
}
}
@@ -151,16 +153,16 @@
text = text,
textStyles = listOf(AnnotatedString.Item(textStyle, 0, text.length))
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Rtl),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints(maxWidth = width))
+ textDelegate.layout(Constraints(maxWidth = width))
- assertThat(textPainter.width).isEqualTo(width.value.toFloat())
+ assertThat(textDelegate.width).isEqualTo(width.value.toFloat())
}
@Test
@@ -179,31 +181,31 @@
)
)
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Rtl),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
- assertThat(textPainter.height).isEqualTo(fontSize.toPx().value)
+ assertThat(textDelegate.height).isEqualTo(fontSize.toPx().value)
}
}
@Test
fun layout_build_layoutResult() {
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = AnnotatedString(text = "Hello"),
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints(0.ipx, 20.ipx))
+ textDelegate.layout(Constraints(0.ipx, 20.ipx))
- assertThat(textPainter.layoutResult).isNotNull()
+ assertThat(textDelegate.layoutResult).isNotNull()
}
@Test
@@ -219,15 +221,15 @@
)
)
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
- val selection = textPainter.getOffsetForPosition(PxPosition.Origin)
+ val selection = textDelegate.getOffsetForPosition(PxPosition.Origin)
assertThat(selection).isEqualTo(0)
}
@@ -248,15 +250,15 @@
)
)
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
- val selection = textPainter.getOffsetForPosition(
+ val selection = textDelegate.getOffsetForPosition(
position = PxPosition((fontSize.toPx().value * characterIndex + 1).px, 0.px)
)
@@ -272,16 +274,16 @@
text = text,
textStyles = listOf(AnnotatedString.Item(textStyle, 0, text.length))
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
- assertThat(textPainter.hasVisualOverflow).isFalse()
+ assertThat(textDelegate.hasVisualOverflow).isFalse()
}
@Test
@@ -295,7 +297,7 @@
text = text,
textStyles = listOf(AnnotatedString.Item(textStyle, 0, text.length))
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
overflow = TextOverflow.Fade,
@@ -305,9 +307,9 @@
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints(maxWidth = 100.ipx))
+ textDelegate.layout(Constraints(maxWidth = 100.ipx))
- assertThat(textPainter.hasVisualOverflow).isTrue()
+ assertThat(textDelegate.hasVisualOverflow).isTrue()
}
@Test
@@ -321,7 +323,7 @@
text = text,
textStyles = listOf(AnnotatedString.Item(textStyle, 0, text.length))
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
overflow = TextOverflow.Fade,
@@ -330,9 +332,9 @@
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints(maxWidth = 100.ipx))
+ textDelegate.layout(Constraints(maxWidth = 100.ipx))
- assertThat(textPainter.hasVisualOverflow).isTrue()
+ assertThat(textDelegate.hasVisualOverflow).isTrue()
}
@Test
@@ -353,17 +355,17 @@
)
)
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints(maxWidth = 120.ipx))
+ textDelegate.layout(Constraints(maxWidth = 120.ipx))
val expectedBitmap = Bitmap.createBitmap(
- ceil(textPainter.width).toInt(),
- ceil(textPainter.height).toInt(),
+ ceil(textDelegate.width).toInt(),
+ ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val expectedCanvas =
@@ -372,10 +374,10 @@
val defaultSelectionColor = Color(0x6633B5E5)
expectedPaint.color = defaultSelectionColor
- val firstLineLeft = textPainter.layoutResult?.multiParagraph?.getLineLeft(0)
- val secondLineLeft = textPainter.layoutResult?.multiParagraph?.getLineLeft(1)
- val firstLineRight = textPainter.layoutResult?.multiParagraph?.getLineRight(0)
- val secondLineRight = textPainter.layoutResult?.multiParagraph?.getLineRight(1)
+ val firstLineLeft = textDelegate.layoutResult?.multiParagraph?.getLineLeft(0)
+ val secondLineLeft = textDelegate.layoutResult?.multiParagraph?.getLineLeft(1)
+ val firstLineRight = textDelegate.layoutResult?.multiParagraph?.getLineRight(0)
+ val secondLineRight = textDelegate.layoutResult?.multiParagraph?.getLineRight(1)
expectedCanvas.drawRect(
Rect(firstLineLeft!!, 0f, firstLineRight!!, fontSizeInPx),
expectedPaint
@@ -385,21 +387,21 @@
secondLineLeft!!,
fontSizeInPx,
secondLineRight!!,
- textPainter.layoutResult!!.multiParagraph.height
+ textDelegate.layoutResult!!.multiParagraph.height
),
expectedPaint
)
val actualBitmap = Bitmap.createBitmap(
- ceil(textPainter.width).toInt(),
- ceil(textPainter.height).toInt(),
+ ceil(textDelegate.width).toInt(),
+ ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
// Run.
// Select all.
- textPainter.paintBackground(
+ textDelegate.paintBackground(
start = 0,
end = text.length,
color = defaultSelectionColor,
@@ -431,17 +433,17 @@
)
)
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
val expectedBitmap = Bitmap.createBitmap(
- ceil(textPainter.width).toInt(),
- ceil(textPainter.height).toInt(),
+ ceil(textDelegate.width).toInt(),
+ ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val expectedCanvas =
@@ -460,14 +462,14 @@
)
val actualBitmap = Bitmap.createBitmap(
- ceil(textPainter.width).toInt(),
- ceil(textPainter.height).toInt(),
+ ceil(textDelegate.width).toInt(),
+ ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
// Run.
- textPainter.paintBackground(
+ textDelegate.paintBackground(
start = selectionStart,
end = selectionEnd,
color = defaultSelectionColor,
@@ -502,17 +504,17 @@
)
)
)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
val expectedBitmap = Bitmap.createBitmap(
- ceil(textPainter.width).toInt(),
- ceil(textPainter.height).toInt(),
+ ceil(textDelegate.width).toInt(),
+ ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val expectedCanvas =
@@ -543,14 +545,14 @@
)
val actualBitmap = Bitmap.createBitmap(
- ceil(textPainter.width).toInt(),
- ceil(textPainter.height).toInt(),
+ ceil(textDelegate.width).toInt(),
+ ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
// Run.
- textPainter.paintBackground(
+ textDelegate.paintBackground(
start = selectionLTRStart,
end = textLTR.length + selectionRTLEnd,
color = defaultSelectionColor,
@@ -583,17 +585,17 @@
)
)
val selectionColor = Color(0x66AABB33)
- val textPainter = TextDelegate(
+ val textDelegate = TextDelegate(
text = annotatedString,
paragraphStyle = ParagraphStyle(textDirection = TextDirection.Ltr),
density = density,
resourceLoader = resourceLoader
)
- textPainter.layout(Constraints())
+ textDelegate.layout(Constraints())
val expectedBitmap = Bitmap.createBitmap(
- ceil(textPainter.width).toInt(),
- ceil(textPainter.height).toInt(),
+ ceil(textDelegate.width).toInt(),
+ ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val expectedCanvas =
@@ -611,14 +613,14 @@
)
val actualBitmap = Bitmap.createBitmap(
- ceil(textPainter.width).toInt(),
- ceil(textPainter.height).toInt(),
+ ceil(textDelegate.width).toInt(),
+ ceil(textDelegate.height).toInt(),
Bitmap.Config.ARGB_8888
)
val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
// Run.
- textPainter.paintBackground(
+ textDelegate.paintBackground(
start = selectionStart,
end = selectionEnd,
color = selectionColor,
diff --git a/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index f71c46d..485fc03 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -407,8 +407,8 @@
/**
* Feature for {@link #isFeatureSupported(String)}.
* This feature covers
- * {@link WebViewCompat#setForceDark(WebSettings, int)} and
- * {@link WebViewCompat#getForceDark(WebSettings)}.
+ * {@link WebSettingsCompat#setForceDark(WebSettings, int)} and
+ * {@link WebSettingsCompat#getForceDark(WebSettings)}.
*
* TODO(amalova): unhide
* @hide