Merge "Fix crash when Text and WithConstraints are siblings" into androidx-master-dev
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index 5030956..2e9c83f 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -83,6 +83,7 @@
val SLICE = LibraryGroup("androidx.slice", false)
val SLIDINGPANELAYOUT = LibraryGroup("androidx.slidingpanelayout")
val SQLITE = LibraryGroup("androidx.sqlite")
+ val STARTUP = LibraryGroup("androidx.startup")
val SWIPEREFRESHLAYOUT = LibraryGroup("androidx.swiperefreshlayout")
val TESTSCREENSHOT = LibraryGroup("androidx.test.screenshot")
val TEXTCLASSIFIER = LibraryGroup("androidx.textclassifier")
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 09102e9..24def6d 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -102,6 +102,7 @@
val SLICE_BUILDERS_KTX = Version("1.0.0-alpha08")
val SLICE_REMOTECALLBACK = Version("1.0.0-alpha01")
val SLIDINGPANELAYOUT = Version("1.1.0-alpha01")
+ val STARTUP = Version("1.0.0-alpha01")
val SQLITE = Version("2.1.0-rc01")
val SQLITE_INSPECTOR = Version("2.1.0-alpha01")
val SWIPE_REFRESH_LAYOUT = Version("1.1.0-alpha04")
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index c813b7b..2256427 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -29,7 +29,7 @@
*/
val RELEASE_RULE = docsRules("public", false) {
prebuilts(LibraryGroups.ACTIVITY, "1.1.0")
- prebuilts(LibraryGroups.ADS, "1.0.0-alpha03")
+ prebuilts(LibraryGroups.ADS, "1.0.0-alpha04")
prebuilts(LibraryGroups.ANNOTATION, "annotation", "1.1.0")
prebuilts(LibraryGroups.ANNOTATION, "annotation-experimental", "1.0.0")
prebuilts(LibraryGroups.ANNOTATION, "annotation-experimental-lint", "1.0.0")
@@ -45,11 +45,11 @@
ignore(LibraryGroups.CAMERA.group, "camera-testing")
ignore(LibraryGroups.CAMERA.group, "camera-extensions-stub")
ignore(LibraryGroups.CAMERA.group, "camera-testlib-extensions")
- prebuilts(LibraryGroups.CAMERA, "camera-view", "1.0.0-alpha05")
- prebuilts(LibraryGroups.CAMERA, "camera-extensions", "1.0.0-alpha05")
+ prebuilts(LibraryGroups.CAMERA, "camera-view", "1.0.0-alpha06")
+ prebuilts(LibraryGroups.CAMERA, "camera-extensions", "1.0.0-alpha06")
.addStubs("camera/camera-extensions-stub/camera-extensions-stub.jar")
- prebuilts(LibraryGroups.CAMERA, "camera-lifecycle", "1.0.0-alpha02")
- prebuilts(LibraryGroups.CAMERA, "1.0.0-alpha08")
+ prebuilts(LibraryGroups.CAMERA, "camera-lifecycle", "1.0.0-alpha03")
+ prebuilts(LibraryGroups.CAMERA, "1.0.0-alpha09")
ignore(LibraryGroups.CAR.group, "car-moderator")
prebuilts(LibraryGroups.CAR, "car", "1.0.0-alpha7")
.addStubs("car/stubs/android.car.jar")
@@ -75,7 +75,7 @@
ignore(LibraryGroups.FRAGMENT.group, "fragment-truth")
prebuilts(LibraryGroups.FRAGMENT, "1.2.0")
prebuilts(LibraryGroups.GRIDLAYOUT, "1.0.0")
- prebuilts(LibraryGroups.HEIFWRITER, "1.0.0")
+ prebuilts(LibraryGroups.HEIFWRITER, "1.1.0-alpha01")
prebuilts(LibraryGroups.INTERPOLATOR, "1.0.0")
prebuilts(LibraryGroups.LEANBACK, "1.1.0-alpha03")
prebuilts(LibraryGroups.LEGACY, "1.0.0")
@@ -84,7 +84,7 @@
ignore(LibraryGroups.LIFECYCLE.group, "lifecycle-livedata-core-truth")
ignore(LibraryGroups.LIFECYCLE.group, "lifecycle-runtime-ktx-lint")
prebuilts(LibraryGroups.LIFECYCLE, "lifecycle-viewmodel-savedstate", "1.0.0")
- prebuilts(LibraryGroups.LIFECYCLE, "2.2.0-rc03")
+ prebuilts(LibraryGroups.LIFECYCLE, "2.2.0")
ignore(LibraryGroups.LOADER.group, "loader-ktx")
prebuilts(LibraryGroups.LOADER, "1.1.0")
prebuilts(LibraryGroups.LOCALBROADCASTMANAGER, "1.1.0-alpha01")
@@ -100,7 +100,7 @@
ignore(LibraryGroups.NAVIGATION.group, "navigation-dynamic-features-fragment")
ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-generator")
ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-gradle-plugin")
- prebuilts(LibraryGroups.NAVIGATION, "2.2.0-rc04")
+ prebuilts(LibraryGroups.NAVIGATION, "2.2.0")
ignore(LibraryGroups.PAGING.group, "paging-guava")
ignore(LibraryGroups.PAGING.group, "paging-testutils")
prebuilts(LibraryGroups.PAGING, "2.1.0")
@@ -129,11 +129,11 @@
prebuilts(LibraryGroups.SLICE, "slice-view", "1.1.0-alpha01")
prebuilts(LibraryGroups.SLIDINGPANELAYOUT, "1.0.0")
ignore(LibraryGroups.INSPECTION_EXTENSIONS.group, "sqlite-inspection")
- prebuilts(LibraryGroups.SQLITE, "2.1.0-rc01")
+ prebuilts(LibraryGroups.SQLITE, "2.1.0")
prebuilts(LibraryGroups.SWIPEREFRESHLAYOUT, "1.1.0-alpha03")
- prebuilts(LibraryGroups.TEXTCLASSIFIER, "1.0.0-alpha02")
+ prebuilts(LibraryGroups.TEXTCLASSIFIER, "1.0.0-alpha03")
ignore(LibraryGroups.TRANSITION.group, "transition-ktx")
- prebuilts(LibraryGroups.TRANSITION, "1.3.0-rc02")
+ prebuilts(LibraryGroups.TRANSITION, "1.3.0")
prebuilts(LibraryGroups.TVPROVIDER, "1.0.0")
prebuilts(LibraryGroups.VECTORDRAWABLE, "1.1.0")
prebuilts(LibraryGroups.VECTORDRAWABLE, "vectordrawable-animated", "1.1.0")
@@ -145,7 +145,7 @@
prebuilts(LibraryGroups.WEBKIT, "1.2.0-alpha01")
ignore(LibraryGroups.WORK.group, "work-gcm")
ignore(LibraryGroups.WORK.group, "work-runtime-lint")
- prebuilts(LibraryGroups.WORK, "2.3.0-rc01")
+ prebuilts(LibraryGroups.WORK, "2.3.0")
default(Ignore)
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 5aa136f..1e5ae65 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -83,7 +83,7 @@
const val OKHTTP_MOCKWEBSERVER = "com.squareup.okhttp3:mockwebserver:3.11.0"
const val REACTIVE_STREAMS = "org.reactivestreams:reactive-streams:1.0.0"
const val RX_JAVA = "io.reactivex.rxjava2:rxjava:2.2.9"
-const val TRUTH = "com.google.truth:truth:1.0"
+const val TRUTH = "com.google.truth:truth:1.0.1"
const val XERIAL = "org.xerial:sqlite-jdbc:3.25.2"
const val XPP3 = "xpp3:xpp3:1.1.4c"
const val XMLPULL = "xmlpull:xmlpull:1.1.3.1"
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
index 3fdf342..291ef62 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
@@ -112,7 +112,6 @@
"ExecutorRegistration",
"NotCloseable",
"UseIcu",
- "NoByteOrShort",
"SamShouldBeLast",
"MissingJvmStatic",
@@ -134,6 +133,7 @@
"AbstractInner",
"ArrayReturn",
"MethodNameTense",
+ "NoByteOrShort",
"CommonArgsFirst"
).joinToString()
)
diff --git a/busytown/androidx_host_tests.sh b/busytown/androidx_host_tests.sh
index 27e8074..d1c6564 100755
--- a/busytown/androidx_host_tests.sh
+++ b/busytown/androidx_host_tests.sh
@@ -4,3 +4,5 @@
cd $(dirname $0)
./build.sh --no-daemon test jacocoTestReport zipEcFiles --info --offline -Pandroidx.enableAffectedModuleDetection
+
+python3 ./merge_outputs.py mergeExecutionData mergeSourceJars DIST_DIR="$DIST_DIR"
diff --git a/busytown/build.sh b/busytown/build.sh
index 7ff0be2..f487b02 100755
--- a/busytown/build.sh
+++ b/busytown/build.sh
@@ -28,4 +28,4 @@
# run gradle
echoAndDo OUT_DIR=out DIST_DIR=$DIST_DIR ANDROID_HOME=./prebuilts/fullsdk-linux frameworks/support/gradlew -p frameworks/support "$@"
-echoAndDo OUT_DIR=out/ui DIST_DIR=$DIST_DIR/ui ANDROID_HOME=./preubilts/fullsdk-linux frameworks/support/ui/gradlew -p frameworks/support/ui "$@"
+echoAndDo OUT_DIR=out/ui DIST_DIR=$DIST_DIR/ui ANDROID_HOME=./prebuilts/fullsdk-linux frameworks/support/ui/gradlew -p frameworks/support/ui "$@"
diff --git a/busytown/merge_outputs.py b/busytown/merge_outputs.py
index 47c0faf..6e2e2c4 100644
--- a/busytown/merge_outputs.py
+++ b/busytown/merge_outputs.py
@@ -22,7 +22,7 @@
Merges certain results of the gradle build.
This script recognizes the following arguments in any order:
mergeBuildInfo (merge the buildInfo files (used by jetpad) produced by the most recent (compose and androidx) builds)
- mergeCoverageExecution (merge the coverage execution data (recorded by jacoco) produced by the most recent builds)
+ mergeExecutionData (merge the coverage execution data (recorded by jacoco) produced by the most recent builds)
mergeSourceJars (merge the source jars (used by jacoco/coverage ui) produced by the most recent builds)
(these args are case sensitive)
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
index 77e1c6a..9599fb8 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
@@ -72,6 +72,7 @@
import androidx.camera.testing.fakes.FakeCaptureStage;
import androidx.camera.testing.fakes.FakeLifecycleOwner;
import androidx.camera.testing.fakes.FakeUseCaseConfig;
+import androidx.concurrent.futures.ResolvableFuture;
import androidx.core.content.ContextCompat;
import androidx.core.util.Preconditions;
import androidx.test.core.app.ApplicationProvider;
@@ -100,7 +101,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
@LargeTest
@RunWith(AndroidJUnit4.class)
@@ -189,7 +189,7 @@
}
@Test
- public void capturedImageHasCorrectSize() {
+ public void capturedImageHasCorrectSize() throws ExecutionException, InterruptedException {
ImageCapture useCase = new ImageCapture.Builder().setTargetResolution(
DEFAULT_RESOLUTION).setTargetRotation(Surface.ROTATION_0).build();
@@ -199,7 +199,7 @@
mLifecycleOwner.startAndResume();
});
- AtomicReference<ImageProperties> imageProperties = new AtomicReference<>(null);
+ ResolvableFuture<ImageProperties> imageProperties = ResolvableFuture.create();
OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imageProperties);
useCase.takePicture(mMainExecutor, callback);
// Wait for the signal that the image has been captured.
@@ -224,7 +224,8 @@
}
@Test
- public void canSupportGuaranteedSize() throws CameraInfoUnavailableException {
+ public void canSupportGuaranteedSize()
+ throws CameraInfoUnavailableException, ExecutionException, InterruptedException {
// CameraSelector.LENS_FACING_FRONT/LENS_FACING_BACK are defined as constant int 0 and 1.
// Using for-loop to check both front and back device cameras can support the guaranteed
// 640x480 size.
@@ -251,7 +252,7 @@
mLifecycleOwner.startAndResume();
});
- AtomicReference<ImageProperties> imageProperties = new AtomicReference<>(null);
+ ResolvableFuture<ImageProperties> imageProperties = ResolvableFuture.create();
OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imageProperties);
useCase.takePicture(mMainExecutor, callback);
// Wait for the signal that the image has been captured.
@@ -544,7 +545,8 @@
}
@Test
- public void takePicture_withBufferFormatRaw10() throws CameraAccessException {
+ public void takePicture_withBufferFormatRaw10()
+ throws CameraAccessException, ExecutionException, InterruptedException {
CameraCharacteristics cameraCharacteristics =
CameraUtil.getCameraManager().getCameraCharacteristics(mCameraId);
StreamConfigurationMap map =
@@ -564,7 +566,7 @@
mLifecycleOwner.startAndResume();
});
- AtomicReference<ImageProperties> imageProperties = new AtomicReference<>();
+ ResolvableFuture<ImageProperties> imageProperties = ResolvableFuture.create();
OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imageProperties);
useCase.takePicture(mMainExecutor, callback);
// Wait for the signal that the image has been captured.
@@ -753,7 +755,7 @@
}
private OnImageCapturedCallback createMockOnImageCapturedCallback(
- @Nullable AtomicReference<ImageProperties> resultProperties) {
+ @Nullable ResolvableFuture<ImageProperties> resultProperties) {
OnImageCapturedCallback callback = mock(OnImageCapturedCallback.class);
doAnswer(
i -> {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
index 95b0fa6..5bd7a30 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
@@ -119,20 +119,29 @@
mInstrumentation.runOnMainSync(() -> {
mPreview.setPreviewSurfaceProvider(createSurfaceTextureProvider(
new SurfaceTextureProvider.SurfaceTextureCallback() {
+ boolean mIsSurfaceTextureReleased = false;
+ Object mIsSurfaceTextureReleasedLock = new Object();
@Override
public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture,
@NonNull Size resolution) {
surfaceTexture.attachToGLContext(GLUtil.getTexIdFromGLContext());
surfaceTexture.setOnFrameAvailableListener(
surfaceTexture1 -> {
- surfaceTexture.updateTexImage();
+ synchronized (mIsSurfaceTextureReleasedLock) {
+ if (!mIsSurfaceTextureReleased) {
+ surfaceTexture.updateTexImage();
+ }
+ }
mSemaphore.release();
});
}
@Override
public void onSafeToRelease(@NonNull SurfaceTexture surfaceTexture) {
- surfaceTexture.release();
+ synchronized (mIsSurfaceTextureReleasedLock) {
+ mIsSurfaceTextureReleased = true;
+ surfaceTexture.release();
+ }
}
}));
CameraX.bindToLifecycle(mLifecycle, DEFAULT_SELECTOR, mPreview, mImageCapture);
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/CoreAppTestUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/CoreAppTestUtil.java
index d712f7c..18d6cda 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/CoreAppTestUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/CoreAppTestUtil.java
@@ -29,6 +29,7 @@
public final class CoreAppTestUtil {
private static final int DISMISS_LOCK_SCREEN_CODE = 82;
+ private static final int MAX_TIMEOUT_MS = 3000;
private CoreAppTestUtil() {
}
@@ -67,7 +68,9 @@
UiDevice device = UiDevice.getInstance(instrumentation);
// In case the lock screen on top, the action to dismiss it.
device.pressKeyCode(DISMISS_LOCK_SCREEN_CODE);
+
device.pressHome();
+ device.waitForIdle(MAX_TIMEOUT_MS);
// Close system dialogs first to avoid interrupt.
instrumentation.getTargetContext().sendBroadcast(
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.java b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.java
index a6f7213..ae17968 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.java
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/BasicUITest.java
@@ -139,6 +139,7 @@
// Returns to Home to restart next test.
mDevice.pressHome();
+ mDevice.waitForIdle(3000);
}
}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.java b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.java
index bd742a8..85e3aca 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.java
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ExistingActivityLifecycleTest.java
@@ -41,9 +41,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.rule.GrantPermissionRule;
-import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.Until;
import org.junit.After;
import org.junit.Assert;
@@ -202,12 +200,12 @@
private void pressHomeButton() {
mDevice.pressHome();
- mDevice.wait(Until.hasObject(By.pkg(mLauncherPackageName).depth(0)), LAUNCH_TIMEOUT_MS);
+ mDevice.waitForIdle(LAUNCH_TIMEOUT_MS);
}
private void pressBackButton() {
mDevice.pressBack();
- mDevice.wait(Until.hasObject(By.pkg(mLauncherPackageName).depth(0)), LAUNCH_TIMEOUT_MS);
+ mDevice.waitForIdle(LAUNCH_TIMEOUT_MS);
}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/NewActivityLifecycleTest.java b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/NewActivityLifecycleTest.java
index 1d2a122..4a8ea1e 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/NewActivityLifecycleTest.java
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/NewActivityLifecycleTest.java
@@ -38,9 +38,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.rule.GrantPermissionRule;
-import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.Until;
import org.junit.After;
import org.junit.Before;
@@ -129,7 +127,7 @@
private void pressHomeButton() {
mDevice.pressHome();
- mDevice.wait(Until.hasObject(By.pkg(mLauncherPackageName).depth(0)), LAUNCH_TIMEOUT_MS);
+ mDevice.waitForIdle(LAUNCH_TIMEOUT_MS);
}
}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/TakePictureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/TakePictureTest.kt
index 2dc09d7..5729097 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/TakePictureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/TakePictureTest.kt
@@ -102,6 +102,7 @@
// Returns to Home to restart next test.
mDevice.pressHome()
+ mDevice.waitForIdle(3000)
}
private fun checkPreviewReady() {
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ToggleButtonUITest.java b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ToggleButtonUITest.java
index 5389e96..6b84139 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ToggleButtonUITest.java
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ToggleButtonUITest.java
@@ -48,9 +48,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.rule.GrantPermissionRule;
-import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.Until;
import junit.framework.AssertionFailedError;
@@ -70,7 +68,6 @@
private final UiDevice mDevice =
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- private final String mLauncherPackageName = mDevice.getLauncherPackageName();
private final Intent mIntent = ApplicationProvider.getApplicationContext().getPackageManager()
.getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
@@ -109,8 +106,14 @@
@After
public void tearDown() {
- pressBackAndReturnHome();
+ // Idles Espresso thread and make activity complete each action.
+ waitFor(new ElapsedTimeIdlingResource(IDLE_TIMEOUT_MS));
+
mActivityRule.finishActivity();
+
+ // Returns to Home to restart next test.
+ mDevice.pressHome();
+ mDevice.waitForIdle(IDLE_TIMEOUT_MS);
}
@Test
@@ -136,8 +139,6 @@
// The mode3 should be different from first and second time.
assertNotEquals(mode3, mode2);
assertNotEquals(mode3, mode1);
-
- waitForIdlingRegistryAndPressBackAndHomeButton();
}
@Test
@@ -155,8 +156,6 @@
// By pressing the torch toggle button two times, it should switch back to original state.
onView(withId(R.id.torch_toggle)).perform(click());
assertEquals(isTorchOn(cameraInfo), isTorchOn);
-
- waitForIdlingRegistryAndPressBackAndHomeButton();
}
@Test
@@ -185,32 +184,12 @@
assertNotNull(mActivityRule.getActivity().getPreview());
}
}
-
- waitForIdlingRegistryAndPressBackAndHomeButton();
- }
-
- private void waitForIdlingRegistryAndPressBackAndHomeButton() {
- // Idles Espresso thread and make activity complete each action.
- waitFor(new ElapsedTimeIdlingResource(IDLE_TIMEOUT_MS));
-
- mDevice.pressBack();
-
- // Returns to Home to restart next test.
- mDevice.pressHome();
- mDevice.wait(Until.hasObject(By.pkg(mLauncherPackageName).depth(0)), IDLE_TIMEOUT_MS);
}
private boolean isTorchOn(CameraInfo cameraInfo) {
return cameraInfo.getTorchState().getValue() == TorchState.ON;
}
- private void pressBackAndReturnHome() {
- mDevice.pressBack();
-
- // Returns to Home to restart next test.
- mDevice.pressHome();
- }
-
private boolean detectButtonVisibility(int resource) {
try {
onView(withId(resource)).check(matches(isDisplayed()));
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewAppPreviewUpdateTest.java b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewAppPreviewUpdateTest.java
index 7c0169b..5a6749f 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewAppPreviewUpdateTest.java
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/PreviewViewAppPreviewUpdateTest.java
@@ -47,6 +47,7 @@
@LargeTest
public final class PreviewViewAppPreviewUpdateTest {
private static final int WAIT_TIMEOUT = 10000;
+ private static final int MAX_TIMEOUT_MS = 3000;
private final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
private Intent mIntent =
new Intent("androidx.camera.integration.view.action.PREVIEWVIEWAPP");
@@ -137,9 +138,11 @@
private void pressHomeButton() {
mDevice.pressHome();
+ mDevice.waitForIdle(MAX_TIMEOUT_MS);
}
private void pressBackButton() {
mDevice.pressBack();
+ mDevice.waitForIdle(MAX_TIMEOUT_MS);
}
}
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/AbstractCodegenSignatureTest.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/AbstractCodegenSignatureTest.kt
index 1504c1f..204fe30 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/AbstractCodegenSignatureTest.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/AbstractCodegenSignatureTest.kt
@@ -16,7 +16,13 @@
package androidx.compose.plugins.kotlin
+import android.app.Activity
+import android.content.Context
+import android.os.Bundle
+import android.widget.LinearLayout
import org.jetbrains.kotlin.backend.common.output.OutputFile
+import org.robolectric.Robolectric
+import java.net.URLClassLoader
abstract class AbstractCodegenSignatureTest : AbstractCodegenTest() {
@@ -92,50 +98,88 @@
assertEquals(expectedApiString, apiString)
}
- fun checkComposerParam(src: String, dumpClasses: Boolean = false) = ensureSetup {
- testFile(
+ fun checkComposerParam(src: String, dumpClasses: Boolean = false): Unit = ensureSetup {
+ val className = "Test_REPLACEME_${uniqueNumber++}"
+ val compiledClasses = classLoader(
"""
import androidx.compose.*
+ import android.widget.LinearLayout
+ import android.content.Context
- class FakeNode
- object FakeNodeApplierAdapter :
- ApplyAdapter<FakeNode> {
- override fun FakeNode.start(instance: FakeNode) {}
- override fun FakeNode.insertAt(index: Int, instance: FakeNode) {}
- override fun FakeNode.removeAt(index: Int, count: Int) {}
- override fun FakeNode.move(from: Int, to: Int, count: Int) {}
- override fun FakeNode.end(instance: FakeNode, parent: FakeNode) {}
- }
+ $src
- class FakeComposer(
- val root: FakeNode
- ) : Composer<FakeNode>(
- SlotTable(),
- Applier(root, FakeNodeApplierAdapter),
- Recomposer.current()
- )
-
- fun makeComposer(): Composer<*> {
- return FakeComposer(FakeNode())
- }
-
- fun invokeComposable(composer: Composer<*>, fn: @Composable() () -> Unit) {
- val realFn = fn as Function1<Composer<*>, Unit>
- realFn(composer)
- }
-
- @Composable fun assertComposer(expected: Composer<*>) {
+ @Composable fun assertComposer(expected: Composer<*>?) {
val actual = currentComposerIntrinsic
assert(expected === actual)
}
- class Test {
- fun test() { run() }
+ private var __context: Context? = null
+
+ fun makeComposer(): Composer<*> {
+ val container = LinearLayout(__context!!)
+ return ViewComposer(
+ container,
+ __context!!,
+ Recomposer.current()
+ )
}
- $src
+
+ fun invokeComposable(composer: Composer<*>?, fn: @Composable() () -> Unit) {
+ if (composer == null) error("Composer was null")
+ val realFn = fn as Function1<Composer<*>, Unit>
+ composer.runWithComposing {
+ composer.startRoot()
+ realFn(composer)
+ composer.endRoot()
+ }
+ }
+
+ class Test {
+ fun test(context: Context) {
+ __context = context
+ run()
+ __context = null
+ }
+ }
""",
- dumpClasses
+ fileName = className,
+ dumpClasses = dumpClasses
)
+
+ val allClassFiles = compiledClasses.allGeneratedFiles.filter {
+ it.relativePath.endsWith(".class")
+ }
+
+ val loader = URLClassLoader(emptyArray(), this.javaClass.classLoader)
+
+ val instanceClass = run {
+ var instanceClass: Class<*>? = null
+ var loadedOne = false
+ for (outFile in allClassFiles) {
+ val bytes = outFile.asByteArray()
+ val loadedClass = loadClass(loader, null, bytes)
+ if (loadedClass.name == "Test") instanceClass = loadedClass
+ loadedOne = true
+ }
+ if (!loadedOne) error("No classes loaded")
+ instanceClass ?: error("Could not find class $className in loaded classes")
+ }
+
+ val instanceOfClass = instanceClass.newInstance()
+ val testMethod = instanceClass.getMethod("test", Context::class.java)
+
+
+
+ val controller = Robolectric.buildActivity(TestActivity::class.java)
+ val activity = controller.create().get()
+ testMethod.invoke(instanceOfClass, activity)
+ }
+
+ private class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(LinearLayout(this))
+ }
}
fun codegen(text: String, dumpClasses: Boolean = false): Unit = ensureSetup {
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposerParamSignatureTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposerParamSignatureTests.kt
index dd4a36e..bdf4804 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposerParamSignatureTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposerParamSignatureTests.kt
@@ -67,8 +67,9 @@
@Test
fun testCorrectComposerPassed1(): Unit = checkComposerParam(
"""
- val a = makeComposer()
+ var a: Composer<*>? = null
fun run() {
+ a = makeComposer()
invokeComposable(a) {
assertComposer(a)
}
@@ -77,13 +78,34 @@
)
@Test
+ fun testSimpleEmits(): Unit = checkApi(
+ """
+ import android.widget.LinearLayout
+ import android.widget.TextView
+
+ @Composable fun Example() {
+ LinearLayout(id=123) {
+ TextView(text="Hello World")
+ }
+ }
+ """,
+ """
+ public final class TestKt {
+ public final static Example(Landroidx/compose/Composer;)V
+ public final static synthetic Example()V
+ }
+ """
+ )
+
+ @Test
fun testCorrectComposerPassed2(): Unit = checkComposerParam(
"""
- val a = makeComposer()
+ var a: Composer<*>? = null
@Composable fun Foo() {
assertComposer(a)
}
fun run() {
+ a = makeComposer()
invokeComposable(a) {
Foo()
}
@@ -94,12 +116,14 @@
@Test
fun testCorrectComposerPassed3(): Unit = checkComposerParam(
"""
- val a = makeComposer()
- val b = makeComposer()
+ var a: Composer<*>? = null
+ var b: Composer<*>? = null
@Composable fun Callback(fn: () -> Unit) {
fn()
}
fun run() {
+ a = makeComposer()
+ b = makeComposer()
invokeComposable(a) {
assertComposer(a)
Callback {
@@ -115,14 +139,16 @@
@Test
fun testCorrectComposerPassed4(): Unit = checkComposerParam(
"""
- val a = makeComposer()
- val b = makeComposer()
+ var a: Composer<*>? = null
+ var b: Composer<*>? = null
@Composable fun makeInt(): Int {
assertComposer(a)
return 10
}
@Composable fun WithDefault(x: Int = makeInt()) {}
fun run() {
+ a = makeComposer()
+ b = makeComposer()
invokeComposable(a) {
assertComposer(a)
WithDefault()
@@ -139,11 +165,12 @@
@Test
fun testCorrectComposerPassed5(): Unit = checkComposerParam(
"""
- val a = makeComposer()
+ var a: Composer<*>? = null
@Composable fun Wrap(children: @Composable() () -> Unit) {
children()
}
fun run() {
+ a = makeComposer()
invokeComposable(a) {
assertComposer(a)
Wrap {
@@ -161,6 +188,21 @@
)
@Test
+ fun testCorrectComposerPassed6(): Unit = checkComposerParam(
+ """
+ import android.widget.TextView
+
+ fun run() {
+ invokeComposable(makeComposer()) {
+ LinearLayout(id=123) {
+ TextView(text="Hello World")
+ }
+ }
+ }
+ """
+ )
+
+ @Test
fun testDefaultParameters(): Unit = checkApi(
"""
@Composable fun Foo(x: Int = 0) {
@@ -328,17 +370,17 @@
public final invoke(ILandroidx/compose/Composer;)V
private final synthetic I %x
public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- final static INNERCLASS TestKt%App%1%1 null null
+ final static INNERCLASS TestKt%App%1%invoke%1 null null
final static INNERCLASS TestKt%App%1 null null
OUTERCLASS TestKt App (ILandroidx/compose/Composer;)V
}
- final class TestKt%App%1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ final class TestKt%App%1%invoke%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
synthetic <init>(II)V
public final invoke(ILandroidx/compose/Composer;)V
private final synthetic I %x
private final synthetic I %a
public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- final static INNERCLASS TestKt%App%1%1 null null
+ final static INNERCLASS TestKt%App%1%invoke%1 null null
final static INNERCLASS TestKt%App%1 null null
OUTERCLASS TestKt%App%1 invoke (ILandroidx/compose/Composer;)V
}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableEmitDescriptor.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableEmitDescriptor.kt
index f9301e9..d3c25cf 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableEmitDescriptor.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableEmitDescriptor.kt
@@ -35,21 +35,32 @@
import org.jetbrains.kotlin.types.replace
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
+interface ComposableEmitMetadata {
+ val composerMetadata: ComposerMetadata
+ val emitCall: ResolvedCall<*>
+ val hasChildren: Boolean
+ val pivotals: List<String>
+ val ctorCall: ResolvedCall<*>
+ val ctorParams: List<String>
+ val validations: List<ValidatedAssignment>
+}
+
class ComposableEmitDescriptor(
val composer: ResolvedCall<*>,
- val emitCall: ResolvedCall<*>,
- val hasChildren: Boolean,
- val pivotals: List<String>,
- val ctorCall: ResolvedCall<*>,
- val ctorParams: List<String>,
- val validations: List<ValidatedAssignment>,
+ override val composerMetadata: ComposerMetadata,
+ override val emitCall: ResolvedCall<*>,
+ override val hasChildren: Boolean,
+ override val pivotals: List<String>,
+ override val ctorCall: ResolvedCall<*>,
+ override val ctorParams: List<String>,
+ override val validations: List<ValidatedAssignment>,
containingDeclaration: DeclarationDescriptor,
original: SimpleFunctionDescriptor?,
annotations: Annotations,
name: Name,
kind: CallableMemberDescriptor.Kind,
source: SourceElement
-) : SimpleFunctionDescriptorImpl(
+) : ComposableEmitMetadata, SimpleFunctionDescriptorImpl(
containingDeclaration,
original,
annotations,
@@ -67,6 +78,7 @@
ctorParams: List<String>,
validations: List<ValidatedAssignment>,
composerCall: ResolvedCall<*>,
+ composerMetadata: ComposerMetadata,
name: Name
): ComposableEmitDescriptor {
@@ -78,6 +90,7 @@
val descriptor = ComposableEmitDescriptor(
composerCall,
+ composerMetadata,
emitCall,
hasChildren,
pivotals,
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeEmitResolver.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeEmitResolver.kt
index 5b5a1d4..32a45c0 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeEmitResolver.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeEmitResolver.kt
@@ -400,7 +400,8 @@
ctorParams = resolvedCall.buildParamsFromAttributes(attributes),
composerCall = getComposerCall,
emitCall = emitCall,
- name = name
+ name = name,
+ composerMetadata = composer
)
}
}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeIrLoweringExtension.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeIrLoweringExtension.kt
index 8713ad9..2efff3d 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeIrLoweringExtension.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeIrLoweringExtension.kt
@@ -65,6 +65,7 @@
if (ComposeFlags.COMPOSER_PARAM) {
return ComposerParameterPhase then
ComposerIntrinsicPhase then
+ ComposeCallPhase then
phases
}
return FrameClassGenPhase then
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
index 92fc9fe..056fc59 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
@@ -16,9 +16,12 @@
package androidx.compose.plugins.kotlin
+import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices
import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices.COMPOSABLE_EMIT_DESCRIPTOR
+import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices.COMPOSABLE_EMIT_METADATA
import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices.COMPOSABLE_FUNCTION_DESCRIPTOR
import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices.COMPOSABLE_PROPERTY_DESCRIPTOR
+import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices.COMPOSER_IR_METADATA
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
@@ -84,7 +87,40 @@
statementGenerator: StatementGenerator,
element: KtCallExpression
): IrExpression? {
- if (ComposeFlags.COMPOSER_PARAM) return null
+ if (ComposeFlags.COMPOSER_PARAM) {
+ // TODO(lmr): Ideally, we can get rid of this logic here by figuring out an
+ // alternative method to getting the composer metadata for each composable
+ // invocation from inside the IR transforms. For now, this unblocks us, but it would
+ // be nice to get rid of this extension point.
+ val resolvedCall = statementGenerator.getResolvedCall(element) ?: return null
+
+ return when (val descriptor = resolvedCall.candidateDescriptor) {
+ is ComposableFunctionDescriptor -> statementGenerator
+ .visitCallExpressionWithoutInterception(element)
+ .also { call ->
+ statementGenerator.context.irTrace.record(
+ COMPOSER_IR_METADATA,
+ call,
+ descriptor.composerMetadata
+ )
+ }
+ is ComposableEmitDescriptor -> statementGenerator
+ .visitCallExpressionWithoutInterception(element)
+ .also { call ->
+ statementGenerator.context.irTrace.record(
+ COMPOSER_IR_METADATA,
+ call,
+ descriptor.composerMetadata
+ )
+ statementGenerator.context.irTrace.record(
+ COMPOSABLE_EMIT_METADATA,
+ call,
+ descriptor
+ )
+ }
+ else -> null
+ }
+ }
val resolvedCall = statementGenerator.getResolvedCall(element)
?: return ErrorExpressionGenerator(statementGenerator).generateErrorCall(element)
@@ -114,7 +150,31 @@
statementGenerator: StatementGenerator,
element: KtSimpleNameExpression
): IrExpression? {
- if (ComposeFlags.COMPOSER_PARAM) return null
+ if (ComposeFlags.COMPOSER_PARAM) {
+ // TODO(lmr): Ideally, we can get rid of this logic here by figuring out an
+ // alternative method to getting the composer metadata for each composable
+ // invocation from inside the IR transforms. For now, this unblocks us, but it would
+ // be nice to get rid of this extension point.
+ val resolvedCall = statementGenerator.getResolvedCall(element) ?: return null
+ val descriptor = resolvedCall.candidateDescriptor
+ return when (descriptor) {
+ is ComposablePropertyDescriptor ->
+ CallGenerator(statementGenerator).generateValueReference(
+ element.startOffsetSkippingComments,
+ element.endOffset,
+ descriptor,
+ resolvedCall,
+ null
+ ).also { call ->
+ statementGenerator.context.irTrace.record(
+ COMPOSER_IR_METADATA,
+ call,
+ descriptor.composerMetadata
+ )
+ }
+ else -> null
+ }
+ }
val resolvedCall = statementGenerator.getResolvedCall(element)
?: return super.visitSimpleNameExpression(statementGenerator, element)
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeWritableSlices.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeWritableSlices.kt
index 1810652..320dfee 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeWritableSlices.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeWritableSlices.kt
@@ -2,6 +2,7 @@
import androidx.compose.plugins.kotlin.ComposableAnnotationChecker
import androidx.compose.plugins.kotlin.ComposableEmitDescriptor
+import androidx.compose.plugins.kotlin.ComposableEmitMetadata
import androidx.compose.plugins.kotlin.ComposableFunctionDescriptor
import androidx.compose.plugins.kotlin.ComposablePropertyDescriptor
import androidx.compose.plugins.kotlin.ComposerMetadata
@@ -10,6 +11,7 @@
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer
+import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@@ -48,6 +50,12 @@
val COMPOSABLE_PROPERTY_DESCRIPTOR: WritableSlice<IrAttributeContainer,
ComposablePropertyDescriptor> =
BasicWritableSlice(RewritePolicy.DO_NOTHING)
+ val COMPOSER_IR_METADATA: WritableSlice<IrAttributeContainer, ComposerMetadata> =
+ BasicWritableSlice(RewritePolicy.DO_NOTHING)
+ val COMPOSABLE_EMIT_METADATA: WritableSlice<IrAttributeContainer, ComposableEmitMetadata> =
+ BasicWritableSlice(RewritePolicy.DO_NOTHING)
+ val IS_COMPOSABLE_CALL: WritableSlice<IrAttributeContainer, Boolean> =
+ BasicWritableSlice(RewritePolicy.DO_NOTHING)
}
private val REWRITES_ALLOWED = object : RewritePolicy {
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableCallTransformer.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableCallTransformer.kt
index 88865c7..69a50f3 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableCallTransformer.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableCallTransformer.kt
@@ -4,8 +4,11 @@
import androidx.compose.plugins.kotlin.ComposableAnnotationChecker
import androidx.compose.plugins.kotlin.ComposableCallableDescriptor
import androidx.compose.plugins.kotlin.ComposableEmitDescriptor
+import androidx.compose.plugins.kotlin.ComposableEmitMetadata
import androidx.compose.plugins.kotlin.ComposableFunctionDescriptor
+import androidx.compose.plugins.kotlin.ComposeFlags
import androidx.compose.plugins.kotlin.ComposeFqNames
+import androidx.compose.plugins.kotlin.ComposerMetadata
import androidx.compose.plugins.kotlin.EmitChildrenValueParameterDescriptor
import androidx.compose.plugins.kotlin.KtxNameConventions
import androidx.compose.plugins.kotlin.ValidatedAssignment
@@ -19,6 +22,7 @@
import androidx.compose.plugins.kotlin.isMarkedStable
import androidx.compose.plugins.kotlin.isSpecialType
import org.jetbrains.kotlin.backend.common.FileLoweringPass
+import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
@@ -77,6 +81,8 @@
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
+import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.declarations.getIrValueParameter
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
@@ -120,6 +126,7 @@
import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.parentAsClass
+import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.referenceFunction
import org.jetbrains.kotlin.ir.util.startOffset
import org.jetbrains.kotlin.ir.util.statements
@@ -127,7 +134,6 @@
import org.jetbrains.kotlin.ir.util.typeSubstitutionMap
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
-import org.jetbrains.kotlin.js.translate.callTranslator.getReturnType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.findFirstFunction
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
@@ -227,7 +233,37 @@
}
}
+ override fun visitCall(expression: IrCall): IrExpression {
+ if (!ComposeFlags.COMPOSER_PARAM) return super.visitCall(expression)
+ val meta = context.state.irTrace[ComposeWritableSlices.COMPOSER_IR_METADATA, expression]
+ if (meta != null) {
+ val emitMetadata = context.state.irTrace[
+ ComposeWritableSlices.COMPOSABLE_EMIT_METADATA,
+ expression
+ ]
+ val descriptor = expression.descriptor
+ val returnType = descriptor.returnType
+ return if(emitMetadata != null) {
+ context.createIrBuilder(declarationStack.last().symbol).irBlock {
+ +irComposableEmit(expression.transformChildren(), meta, emitMetadata)
+ }
+ } else if ((returnType == null || returnType.isUnit()) && !descriptor.isInline) {
+ context.createIrBuilder(declarationStack.last().symbol).irBlock {
+ +irComposableCall(expression.transformChildren(), meta)
+ }
+ } else {
+ context
+ .createIrBuilder(declarationStack.last().symbol)
+ .irComposableExpr(expression.transformChildren(), meta)
+ }
+ } else {
+ assert(!expression.isComposable())
+ }
+ return super.visitCall(expression)
+ }
+
override fun visitBlock(expression: IrBlock): IrExpression {
+ if (ComposeFlags.COMPOSER_PARAM) return super.visitBlock(expression)
if (expression.origin != COMPOSABLE_EMIT_OR_CALL) {
return super.visitBlock(expression)
}
@@ -304,11 +340,40 @@
}
private fun IrBlockBuilder.irComposableCall(
+ original: IrCall,
+ meta: ComposerMetadata
+ ): IrExpression {
+ assert(ComposeFlags.COMPOSER_PARAM)
+ val composerArg = original.getValueArgument(original.valueArgumentsCount - 1)!!
+ // TODO(lmr): we may want to rewrite this in a way that doesn't do a deepCopy...
+ val getComposer = { composerArg.deepCopyWithVariables() }
+ return irComposableCallBase(
+ original,
+ getComposer,
+ meta
+ )
+ }
+
+ private fun IrBlockBuilder.irComposableCall(
composerCall: IrCall,
original: IrCall,
descriptor: ComposableFunctionDescriptor
): IrExpression {
+ assert(!ComposeFlags.COMPOSER_PARAM)
val composerTemp = irTemporary(composerCall)
+ val meta = descriptor.composerMetadata
+ return irComposableCallBase(
+ original,
+ { irGet(composerTemp) },
+ meta
+ )
+ }
+
+ private fun IrBlockBuilder.irComposableCallBase(
+ original: IrCall,
+ getComposer: () -> IrExpression,
+ meta: ComposerMetadata
+ ): IrExpression {
/*
@@ -323,8 +388,6 @@
block = { Foo(attr_text) }
)
*/
- val composer = descriptor.composerCall.resultingDescriptor as PropertyDescriptor
-
// TODO(lmr): the way we grab temporaries here feels wrong. We should investigate the right
// way to do this. Additionally, we are creating temporary vars for variables which is
// causing larger stack space than needed in our generated code.
@@ -341,12 +404,11 @@
val tmpExtensionReceiver = original.extensionReceiver?.let { irTemporary(it) }
// TODO(lmr): come up with a better way to find this?
- val callDescriptor = descriptor
- .composerMetadata
+ val callDescriptor = meta
.callDescriptors
.first { it.typeParametersCount == 0 }
- val joinKeyDescriptor = composer
+ val joinKeyDescriptor = meta
.type
.memberScope
.findFirstFunction(KtxNameConventions.JOINKEY.identifier) {
@@ -364,13 +426,13 @@
callee = symbolTable.referenceFunction(callDescriptor),
type = builtIns.unitType // TODO(lmr): refactor call(...) to return a type
).apply {
- dispatchReceiver = irGet(composerTemp)
+ dispatchReceiver = getComposer()
putValueArgument(
getCallParameter(KtxNameConventions.CALL_KEY_PARAMETER),
irGroupKey(
original = original,
- getComposer = { irGet(composerTemp) },
+ getComposer = getComposer,
joinKey = joinKeyDescriptor,
pivotals = irGetArguments.mapNotNull { (param, getExpr) ->
if (!param.hasPivotalAnnotation()) null
@@ -389,7 +451,9 @@
.findFirstFunction("changed") { it.typeParametersCount == 1 }
val validatedArguments: List<IrExpression> =
- irGetArguments.mapNotNull { (_, getExpr) -> getExpr() } +
+ irGetArguments
+ .filter { (desc, _) -> !desc.isComposerParam() }
+ .mapNotNull { (_, getExpr) -> getExpr() } +
listOfNotNull(
tmpDispatchReceiver?.let { irGet(it) },
tmpExtensionReceiver?.let { irGet(it) }
@@ -452,7 +516,9 @@
type = blockParameter.type.toIrType()
) {
+irCall(
- callee = symbolTable.referenceFunction(original.descriptor),
+ callee = IrSimpleFunctionSymbolImpl(original.descriptor).also {
+ it.bind(original.symbol.owner as IrSimpleFunction)
+ },
type = original.type
).apply {
copyTypeArgumentsFrom(original)
@@ -470,112 +536,141 @@
}
private fun DeclarationIrBuilder.irComposableExpr(
+ original: IrCall,
+ meta: ComposerMetadata
+ ): IrExpression {
+ assert(ComposeFlags.COMPOSER_PARAM)
+ return irBlock(resultType = original.descriptor.returnType?.toIrType()) {
+ val composerParam = nearestComposer()
+ val getComposer = { irGet(composerParam) }
+ irComposableExprBase(
+ original,
+ getComposer,
+ meta
+ )
+ }
+ }
+
+ private fun DeclarationIrBuilder.irComposableExpr(
composerCall: IrCall,
original: IrCall,
descriptor: ComposableCallableDescriptor
): IrExpression {
+ assert(!ComposeFlags.COMPOSER_PARAM)
return irBlock(resultType = descriptor.returnType?.toIrType()) {
-
val composerTemp = irTemporary(composerCall)
+ val meta = descriptor.composerMetadata
+ irComposableExprBase(
+ original,
+ { irGet(composerTemp) },
+ meta
+ )
+ }
+ }
- /*
+ private fun IrBlockBuilder.irComposableExprBase(
+ original: IrCall,
+ getComposer: () -> IrExpression,
+ meta: ComposerMetadata
+ ) {
+ /*
- Foo(text="foo")
+ Foo(text="foo")
- // transforms into
+ // transforms into
- composer.startExpr(123)
- val result = Foo(text="foo")
- composer.endExpr()
- result
- */
- val composer = descriptor.composerCall.resultingDescriptor as PropertyDescriptor
+ composer.startExpr(123)
+ val result = Foo(text="foo")
+ composer.endExpr()
+ result
+ */
- // TODO(lmr): the way we grab temporaries here feels wrong. We should investigate the right
- // way to do this. Additionally, we are creating temporary vars for variables which is
- // causing larger stack space than needed in our generated code.
+ // TODO(lmr): the way we grab temporaries here feels wrong. We should investigate the right
+ // way to do this. Additionally, we are creating temporary vars for variables which is
+ // causing larger stack space than needed in our generated code.
- val irGetArguments = original
- .descriptor
- .valueParameters
- .map {
- val arg = original.getValueArgument(it)
- it to getParameterExpression(it, arg)
- }
+ val irGetArguments = original
+ .descriptor
+ .valueParameters
+ .map {
+ val arg = original.getValueArgument(it)
+ it to getParameterExpression(it, arg)
+ }
- val startExpr = composer
- .type
- .memberScope
- .findFirstFunction(KtxNameConventions.START_EXPR.identifier) {
- it.valueParameters.size == 1
- }
+ val startExpr = meta
+ .type
+ .memberScope
+ .findFirstFunction(KtxNameConventions.START_EXPR.identifier) {
+ it.valueParameters.size == 1
+ }
- val endExpr = composer
- .type
- .memberScope
- .findFirstFunction(KtxNameConventions.END_EXPR.identifier) {
- it.valueParameters.size == 0
- }
+ val endExpr = meta
+ .type
+ .memberScope
+ .findFirstFunction(KtxNameConventions.END_EXPR.identifier) {
+ it.valueParameters.size == 0
+ }
- val joinKeyDescriptor = composer
- .type
- .memberScope
- .findFirstFunction(KtxNameConventions.JOINKEY.identifier) {
- it.valueParameters.size == 2
- }
+ val joinKeyDescriptor = meta
+ .type
+ .memberScope
+ .findFirstFunction(KtxNameConventions.JOINKEY.identifier) {
+ it.valueParameters.size == 2
+ }
- val startCall = irCall(
- callee = symbolTable.referenceFunction(startExpr),
- type = builtIns.unitType
- ).apply {
- dispatchReceiver = irGet(composerTemp)
- putValueArgument(
- startExpr.valueParameters.first(),
- irGroupKey(
- original = original,
- getComposer = { irGet(composerTemp) },
- joinKey = joinKeyDescriptor,
- pivotals = irGetArguments.mapNotNull { (param, getExpr) ->
- if (!param.hasPivotalAnnotation()) null
- else getExpr()
- }
- )
+ val startCall = irCall(
+ callee = symbolTable.referenceFunction(startExpr),
+ type = builtIns.unitType
+ ).apply {
+ dispatchReceiver = getComposer()
+ putValueArgument(
+ startExpr.valueParameters.first(),
+ irGroupKey(
+ original = original,
+ getComposer = getComposer,
+ joinKey = joinKeyDescriptor,
+ pivotals = irGetArguments.mapNotNull { (param, getExpr) ->
+ if (!param.hasPivotalAnnotation()) null
+ else getExpr()
+ }
)
+ )
+ }
+
+ val newCall = irCall(
+ callee = IrSimpleFunctionSymbolImpl(original.descriptor).also {
+ it.bind(original.symbol.owner as IrSimpleFunction)
+ },
+ type = original.type
+ ).apply {
+ copyTypeArgumentsFrom(original)
+
+ dispatchReceiver = original.dispatchReceiver
+ extensionReceiver = original.extensionReceiver
+
+ irGetArguments.forEach { (param, getExpr) ->
+ putValueArgument(param, getExpr())
}
+ }
- val newCall = irCall(
- callee = symbolTable.referenceFunction(original.descriptor),
- type = original.type
- ).apply {
- copyTypeArgumentsFrom(original)
+ val endCall = irCall(
+ callee = symbolTable.referenceFunction(endExpr),
+ type = builtIns.unitType
+ ).apply {
+ dispatchReceiver = getComposer()
+ }
- dispatchReceiver = original.dispatchReceiver
- extensionReceiver = original.extensionReceiver
+ val hasResult = !original.type.isUnit()
- irGetArguments.forEach { (param, getExpr) ->
- putValueArgument(param, getExpr())
- }
- }
-
- val endCall = irCall(
- callee = symbolTable.referenceFunction(endExpr),
- type = builtIns.unitType
- ).apply {
- dispatchReceiver = irGet(composerTemp)
- }
-
- val hasResult = !original.type.isUnit()
-
- if (hasResult) {
- +startCall
- val tmpResult = irTemporary(newCall, irType = original.type)
- +endCall
- +irGet(tmpResult)
- } else {
- +startCall
- +newCall
- +endCall
- }
+ if (hasResult) {
+ +startCall
+ val tmpResult = irTemporary(newCall, irType = original.type)
+ +endCall
+ +irGet(tmpResult)
+ } else {
+ +startCall
+ +newCall
+ +endCall
}
}
@@ -602,20 +697,59 @@
else -> {
val temp = irTemporary(
covertLambdaIfNecessary(expr),
- typeHint = desc.type,
- irType = desc.type.toIrType()
+ irType = expr.type
)
({ irGet(temp) })
}
}
}
+ private fun nearestComposer(): IrValueParameter {
+ for (fn in declarationStack.asReversed().asSequence()) {
+ val param = fn.valueParameters.lastOrNull()
+ if (param != null && param.isComposerParam()) {
+ return param
+ }
+ }
+ error("Couldn't find composer parameter")
+ }
+
+ private fun IrBlockBuilder.irComposableEmit(
+ original: IrCall,
+ meta: ComposerMetadata,
+ emitMetadata: ComposableEmitMetadata
+ ): IrExpression {
+ assert(ComposeFlags.COMPOSER_PARAM)
+ val composerParam = nearestComposer()
+ return irComposableEmitBase(
+ original,
+ { irGet(composerParam) },
+ meta,
+ emitMetadata
+ )
+ }
+
private fun IrBlockBuilder.irComposableEmit(
composerCall: IrCall,
original: IrCall,
descriptor: ComposableEmitDescriptor
): IrExpression {
+ assert(!ComposeFlags.COMPOSER_PARAM)
val composerTemp = irTemporary(composerCall)
+ return irComposableEmitBase(
+ original,
+ { irGet(composerTemp) },
+ descriptor.composerMetadata,
+ descriptor
+ )
+ }
+
+ private fun IrBlockBuilder.irComposableEmitBase(
+ original: IrCall,
+ getComposer: () -> IrExpression,
+ meta: ComposerMetadata,
+ emitMetadata: ComposableEmitMetadata
+ ): IrExpression {
/*
TextView(text="foo")
@@ -629,8 +763,8 @@
update = { set(attr_text) { text -> this.text = text } }
)
*/
-
- val parametersByName = descriptor
+ val parametersByName = original
+ .descriptor
.valueParameters
.mapNotNull { desc ->
original.getValueArgument(desc)?.let { desc to it }
@@ -640,7 +774,7 @@
}
.toMap()
- val emitCall = descriptor.emitCall
+ val emitCall = emitMetadata.emitCall
val emitFunctionDescriptor = emitCall.candidateDescriptor
val emitParameters = emitFunctionDescriptor.valueParameters
@@ -652,8 +786,8 @@
val emitFunctionSymbol = symbolTable.referenceFunction(emitFunctionDescriptor)
- val joinKeyDescriptor = descriptor.composer
- .getReturnType()
+ val joinKeyDescriptor = meta
+ .type
.memberScope
.findFirstFunction(KtxNameConventions.JOINKEY.identifier) {
it.valueParameters.size == 2
@@ -666,7 +800,7 @@
callee = emitFunctionSymbol,
type = builtIns.unitType
).apply {
- dispatchReceiver = irGet(composerTemp)
+ dispatchReceiver = getComposer()
// TODO(lmr): extensionReceiver.
// We would want to do this to enable "emit" and "call" implementations that are
// extensions on the composer
@@ -677,9 +811,9 @@
getEmitParameter(KtxNameConventions.EMIT_KEY_PARAMETER),
irGroupKey(
original = original,
- getComposer = { irGet(composerTemp) },
+ getComposer = getComposer,
joinKey = joinKeyDescriptor,
- pivotals = descriptor.pivotals.map { irGetParameter(it) }
+ pivotals = emitMetadata.pivotals.map { irGetParameter(it) }
)
)
@@ -696,7 +830,7 @@
type = ctorParam.type.toIrType()
) { fn ->
- val ctorCall = descriptor.ctorCall
+ val ctorCall = emitMetadata.ctorCall
val ctorCallSymbol = symbolTable.referenceConstructor(
ctorCall.candidateDescriptor as ClassConstructorDescriptor
@@ -714,7 +848,7 @@
irGet(fn.getIrValueParameter(a))
)
}
- descriptor.ctorParams.forEach { name ->
+ emitMetadata.ctorParams.forEach { name ->
val param = ctorCall
.candidateDescriptor
.valueParameters
@@ -745,7 +879,7 @@
descriptor = updateLambdaDescriptor,
type = updateParam.type.toIrType()
) { fn ->
- descriptor.validations.forEach {
+ emitMetadata.validations.forEach {
// set(attr_text) { text -> this.text = text }
val arg = irGetParameter(it.name)
+irValidatedAssignment(
@@ -761,7 +895,7 @@
}
)
- if (descriptor.hasChildren) {
+ if (emitMetadata.hasChildren) {
val bodyParam = getEmitParameter(KtxNameConventions.EMIT_CHILDREN_PARAMETER)
val childrenExpr = irGetParameter("\$CHILDREN")
@@ -1156,6 +1290,10 @@
}
}
+ private fun IrCall.isComposable(): Boolean {
+ return context.state.irTrace[ComposeWritableSlices.IS_COMPOSABLE_CALL, this] ?: false
+ }
+
private fun isComposable(declaration: IrFunction): Boolean {
val tmpTrace =
DelegatingBindingTrace(
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
index 197190e..b2f543e 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
@@ -19,6 +19,9 @@
import androidx.compose.plugins.kotlin.ComposableAnnotationChecker
import androidx.compose.plugins.kotlin.ComposeFqNames
import androidx.compose.plugins.kotlin.KtxNameConventions
+import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices
+import androidx.compose.plugins.kotlin.irTrace
+import androidx.compose.plugins.kotlin.isEmitInline
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedFunctionDescriptorWithContainerSource
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
@@ -29,6 +32,7 @@
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrStatement
@@ -47,6 +51,7 @@
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
+import org.jetbrains.kotlin.ir.declarations.copyAttributes
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -80,6 +85,7 @@
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi2ir.findFirstFunction
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import org.jetbrains.kotlin.types.KotlinType
@@ -227,6 +233,12 @@
type,
ownerFn.symbol
).also {
+ it.copyAttributes(this)
+ context.state.irTrace.record(
+ ComposeWritableSlices.IS_COMPOSABLE_CALL,
+ it,
+ true
+ )
it.copyTypeArgumentsFrom(this)
it.dispatchReceiver = dispatchReceiver
it.extensionReceiver = extensionReceiver
@@ -426,13 +438,6 @@
}
}
- override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
- if (declaration.parent == oldFn) {
- declaration.parent = fn
- }
- return super.visitDeclaration(declaration)
- }
-
override fun visitCall(expression: IrCall): IrExpression {
val expr = if (!isNestedScope)
expression.withComposerParamIfNeeded(composerParam)
@@ -458,6 +463,9 @@
)
)
return true
+ if (it.isEmitInline(context.state.bindingContext)) {
+ return true
+ }
}
}
return false
@@ -522,5 +530,12 @@
}
}
+fun IrValueParameter.isComposerParam(): Boolean =
+ (descriptor as? ValueParameterDescriptor)?.isComposerParam() ?: false
+
+fun ValueParameterDescriptor.isComposerParam(): Boolean =
+ name == KtxNameConventions.COMPOSER_PARAMETER &&
+ type.constructor.declarationDescriptor?.fqNameSafe == ComposeFqNames.Composer
+
private val COMPOSABLE_DECOY_IMPL =
object : IrDeclarationOriginImpl("COMPOSABLE_DECOY_IMPL", isSynthetic = true) {}
\ No newline at end of file
diff --git a/compose/compose-runtime/api/0.1.0-dev04.txt b/compose/compose-runtime/api/0.1.0-dev04.txt
index faa8880..8fbba54 100644
--- a/compose/compose-runtime/api/0.1.0-dev04.txt
+++ b/compose/compose-runtime/api/0.1.0-dev04.txt
@@ -476,6 +476,7 @@
public final class ViewComposerKt {
method public static androidx.compose.ViewComposer! getComposer();
method public static Boolean? registerAdapter(androidx.compose.ViewComposer, kotlin.jvm.functions.Function2<java.lang.Object,java.lang.Object,?> adapter);
+ method public static <T> T! runWithComposing(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
method public static <T> T! runWithCurrent(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
}
diff --git a/compose/compose-runtime/api/current.txt b/compose/compose-runtime/api/current.txt
index faa8880..8fbba54 100644
--- a/compose/compose-runtime/api/current.txt
+++ b/compose/compose-runtime/api/current.txt
@@ -476,6 +476,7 @@
public final class ViewComposerKt {
method public static androidx.compose.ViewComposer! getComposer();
method public static Boolean? registerAdapter(androidx.compose.ViewComposer, kotlin.jvm.functions.Function2<java.lang.Object,java.lang.Object,?> adapter);
+ method public static <T> T! runWithComposing(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
method public static <T> T! runWithCurrent(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
}
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt b/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt
index faa8880..8fbba54 100644
--- a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt
@@ -476,6 +476,7 @@
public final class ViewComposerKt {
method public static androidx.compose.ViewComposer! getComposer();
method public static Boolean? registerAdapter(androidx.compose.ViewComposer, kotlin.jvm.functions.Function2<java.lang.Object,java.lang.Object,?> adapter);
+ method public static <T> T! runWithComposing(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
method public static <T> T! runWithCurrent(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
}
diff --git a/compose/compose-runtime/api/public_plus_experimental_current.txt b/compose/compose-runtime/api/public_plus_experimental_current.txt
index faa8880..8fbba54 100644
--- a/compose/compose-runtime/api/public_plus_experimental_current.txt
+++ b/compose/compose-runtime/api/public_plus_experimental_current.txt
@@ -476,6 +476,7 @@
public final class ViewComposerKt {
method public static androidx.compose.ViewComposer! getComposer();
method public static Boolean? registerAdapter(androidx.compose.ViewComposer, kotlin.jvm.functions.Function2<java.lang.Object,java.lang.Object,?> adapter);
+ method public static <T> T! runWithComposing(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
method public static <T> T! runWithCurrent(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
}
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev04.txt b/compose/compose-runtime/api/restricted_0.1.0-dev04.txt
index faa8880..8fbba54 100644
--- a/compose/compose-runtime/api/restricted_0.1.0-dev04.txt
+++ b/compose/compose-runtime/api/restricted_0.1.0-dev04.txt
@@ -476,6 +476,7 @@
public final class ViewComposerKt {
method public static androidx.compose.ViewComposer! getComposer();
method public static Boolean? registerAdapter(androidx.compose.ViewComposer, kotlin.jvm.functions.Function2<java.lang.Object,java.lang.Object,?> adapter);
+ method public static <T> T! runWithComposing(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
method public static <T> T! runWithCurrent(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
}
diff --git a/compose/compose-runtime/api/restricted_current.txt b/compose/compose-runtime/api/restricted_current.txt
index faa8880..8fbba54 100644
--- a/compose/compose-runtime/api/restricted_current.txt
+++ b/compose/compose-runtime/api/restricted_current.txt
@@ -476,6 +476,7 @@
public final class ViewComposerKt {
method public static androidx.compose.ViewComposer! getComposer();
method public static Boolean? registerAdapter(androidx.compose.ViewComposer, kotlin.jvm.functions.Function2<java.lang.Object,java.lang.Object,?> adapter);
+ method public static <T> T! runWithComposing(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
method public static <T> T! runWithCurrent(androidx.compose.Composer<?>, kotlin.jvm.functions.Function0<? extends T> block);
}
diff --git a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ViewComposer.kt b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ViewComposer.kt
index 75e3744..06ac93f 100644
--- a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ViewComposer.kt
+++ b/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/ViewComposer.kt
@@ -410,6 +410,16 @@
}
}
+actual fun <T> Composer<*>.runWithComposing(block: () -> T): T {
+ val wasComposing = isComposing
+ try {
+ isComposing = true
+ return block()
+ } finally {
+ isComposing = wasComposing
+ }
+}
+
fun ViewComposer.registerAdapter(
adapter: (parent: Any, child: Any) -> Any?
) = adapters?.register(adapter)
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ViewComposerCommon.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ViewComposerCommon.kt
index 2e4fa09..88b157f 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ViewComposerCommon.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/ViewComposerCommon.kt
@@ -24,6 +24,18 @@
internal expect fun createComposer(root: Any, context: Context, recomposer: Recomposer): Composer<*>
expect fun <T> Composer<*>.runWithCurrent(block: () -> T): T
+// TODO(b/147710889): Once composer param work is complete, this API should be removed and
+// replaced with an internal API used for invoking composables.
+/**
+ * Execute a block of code with the composer in "composing" mode. After executing, the composer
+ * will revert back to it's previous "composing" state. This can be useful for manually starting
+ * a composition.
+ *
+ * @param block the code to execute
+ */
+@TestOnly
+expect fun <T> Composer<*>.runWithComposing(block: () -> T): T
+
@PublishedApi
internal val invocation = Any()
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/FrameIdSet.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/FrameIdSet.kt
index 2de03af..78cef21 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/FrameIdSet.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/frames/FrameIdSet.kt
@@ -127,7 +127,7 @@
newUpperSet,
newLowerSet,
newLowerBound,
- newBelowBound?.toIntArray()
+ newBelowBound?.toIntArray() ?: belowBound
).set(bit)
}
} else {
diff --git a/compose/compose-runtime/src/commonTest/kotlin/androidx/compose/frames/FrameIdSetTests.kt b/compose/compose-runtime/src/commonTest/kotlin/androidx/compose/frames/FrameIdSetTests.kt
index 3104793..15e0cc0 100644
--- a/compose/compose-runtime/src/commonTest/kotlin/androidx/compose/frames/FrameIdSetTests.kt
+++ b/compose/compose-runtime/src/commonTest/kotlin/androidx/compose/frames/FrameIdSetTests.kt
@@ -7,7 +7,7 @@
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
+ * Unless required by applicable law or agreed: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
@@ -20,6 +20,7 @@
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
+import kotlin.test.assertTrue
class FrameIdSetTests {
@@ -166,6 +167,5371 @@
clear.shouldBe(it, bitSet[it])
}
}
+
+ @Test // Regression: b/147836978
+ fun shouldValidWhenSetIsLarge() {
+ val data = """
+2:true,
+2:false,
+3:true,
+3:false,
+4:true,
+4:false,
+5:true,
+5:false,
+6:true,
+6:false,
+7:true,
+7:false,
+8:true,
+8:false,
+9:true,
+9:false,
+10:true,
+10:false,
+11:true,
+11:false,
+12:true,
+12:false,
+13:true,
+13:false,
+14:true,
+14:false,
+15:true,
+15:false,
+16:true,
+16:false,
+17:true,
+17:false,
+18:true,
+18:false,
+19:true,
+19:false,
+20:true,
+20:false,
+21:true,
+21:false,
+22:true,
+22:false,
+23:true,
+23:false,
+24:true,
+24:false,
+25:true,
+25:false,
+26:true,
+26:false,
+27:true,
+27:false,
+28:true,
+28:false,
+29:true,
+29:false,
+30:true,
+30:false,
+31:true,
+31:false,
+32:true,
+32:false,
+33:true,
+33:false,
+34:true,
+34:false,
+35:true,
+35:false,
+36:true,
+36:false,
+37:true,
+37:false,
+38:true,
+38:false,
+39:true,
+39:false,
+40:true,
+40:false,
+41:true,
+41:false,
+42:true,
+42:false,
+43:true,
+43:false,
+44:true,
+44:false,
+45:true,
+45:false,
+46:true,
+46:false,
+47:true,
+47:false,
+48:true,
+48:false,
+49:true,
+49:false,
+50:true,
+50:false,
+51:true,
+51:false,
+52:true,
+52:false,
+53:true,
+53:false,
+54:true,
+54:false,
+55:true,
+55:false,
+56:true,
+56:false,
+57:true,
+57:false,
+58:true,
+58:false,
+59:true,
+59:false,
+60:true,
+60:false,
+61:true,
+61:false,
+62:true,
+62:false,
+63:true,
+63:false,
+64:true,
+64:false,
+65:true,
+65:false,
+66:true,
+66:false,
+67:true,
+67:false,
+68:true,
+68:false,
+69:true,
+69:false,
+70:true,
+70:false,
+71:true,
+71:false,
+72:true,
+72:false,
+73:true,
+73:false,
+74:true,
+74:false,
+75:true,
+75:false,
+76:true,
+76:false,
+77:true,
+77:false,
+78:true,
+78:false,
+79:true,
+79:false,
+80:true,
+80:false,
+81:true,
+81:false,
+82:true,
+82:false,
+83:true,
+83:false,
+84:true,
+84:false,
+85:true,
+85:false,
+86:true,
+86:false,
+87:true,
+87:false,
+88:true,
+88:false,
+89:true,
+89:false,
+90:true,
+90:false,
+91:true,
+91:false,
+92:true,
+92:false,
+93:true,
+93:false,
+94:true,
+94:false,
+95:true,
+95:false,
+96:true,
+96:false,
+97:true,
+97:false,
+98:true,
+98:false,
+99:true,
+99:false,
+100:true,
+100:false,
+101:true,
+101:false,
+102:true,
+102:false,
+103:true,
+103:false,
+104:true,
+104:false,
+105:true,
+105:false,
+106:true,
+106:false,
+107:true,
+107:false,
+108:true,
+108:false,
+109:true,
+109:false,
+110:true,
+110:false,
+111:true,
+111:false,
+112:true,
+112:false,
+113:true,
+113:false,
+114:true,
+114:false,
+115:true,
+115:false,
+116:true,
+116:false,
+117:true,
+117:false,
+118:true,
+118:false,
+119:true,
+119:false,
+120:true,
+120:false,
+121:true,
+121:false,
+122:true,
+122:false,
+123:true,
+123:false,
+124:true,
+124:false,
+125:true,
+125:false,
+126:true,
+126:false,
+127:true,
+127:false,
+128:true,
+128:false,
+129:true,
+129:false,
+130:true,
+130:false,
+131:true,
+131:false,
+132:true,
+132:false,
+133:true,
+133:false,
+134:true,
+134:false,
+135:true,
+135:false,
+136:true,
+136:false,
+137:true,
+137:false,
+138:true,
+138:false,
+139:true,
+139:false,
+140:true,
+140:false,
+141:true,
+141:false,
+142:true,
+142:false,
+143:true,
+143:false,
+144:true,
+144:false,
+145:true,
+145:false,
+146:true,
+146:false,
+147:true,
+147:false,
+148:true,
+148:false,
+149:true,
+149:false,
+150:true,
+150:false,
+151:true,
+151:false,
+152:true,
+152:false,
+153:true,
+153:false,
+154:true,
+154:false,
+155:true,
+155:false,
+156:true,
+156:false,
+157:true,
+157:false,
+158:true,
+158:false,
+159:true,
+159:false,
+160:true,
+160:false,
+161:true,
+161:false,
+162:true,
+162:false,
+163:true,
+163:false,
+164:true,
+164:false,
+165:true,
+165:false,
+166:true,
+166:false,
+167:true,
+167:false,
+168:true,
+168:false,
+169:true,
+169:false,
+170:true,
+170:false,
+171:true,
+171:false,
+172:true,
+172:false,
+173:true,
+173:false,
+174:true,
+174:false,
+175:true,
+175:false,
+176:true,
+176:false,
+177:true,
+177:false,
+178:true,
+178:false,
+179:true,
+179:false,
+180:true,
+180:false,
+181:true,
+181:false,
+182:true,
+182:false,
+183:true,
+183:false,
+184:true,
+184:false,
+185:true,
+185:false,
+186:true,
+186:false,
+187:true,
+187:false,
+188:true,
+188:false,
+189:true,
+189:false,
+190:true,
+190:false,
+191:true,
+191:false,
+192:true,
+192:false,
+193:true,
+193:false,
+194:true,
+194:false,
+195:true,
+195:false,
+196:true,
+196:false,
+197:true,
+197:false,
+198:true,
+198:false,
+199:true,
+199:false,
+200:true,
+200:false,
+201:true,
+201:false,
+202:true,
+202:false,
+203:true,
+203:false,
+204:true,
+204:false,
+205:true,
+205:false,
+206:true,
+206:false,
+207:true,
+207:false,
+208:true,
+208:false,
+209:true,
+209:false,
+210:true,
+210:false,
+211:true,
+211:false,
+212:true,
+212:false,
+213:true,
+213:false,
+214:true,
+214:false,
+215:true,
+215:false,
+216:true,
+216:false,
+217:true,
+217:false,
+218:true,
+218:false,
+219:true,
+219:false,
+220:true,
+220:false,
+221:true,
+221:false,
+222:true,
+222:false,
+223:true,
+223:false,
+224:true,
+224:false,
+225:true,
+225:false,
+226:true,
+226:false,
+227:true,
+227:false,
+228:true,
+228:false,
+229:true,
+229:false,
+230:true,
+230:false,
+231:true,
+231:false,
+232:true,
+232:false,
+233:true,
+233:false,
+234:true,
+234:false,
+235:true,
+235:false,
+236:true,
+236:false,
+237:true,
+237:false,
+238:true,
+238:false,
+239:true,
+239:false,
+240:true,
+240:false,
+241:true,
+241:false,
+242:true,
+242:false,
+243:true,
+243:false,
+244:true,
+244:false,
+245:true,
+245:false,
+246:true,
+246:false,
+247:true,
+247:false,
+248:true,
+248:false,
+249:true,
+249:false,
+250:true,
+250:false,
+251:true,
+251:false,
+252:true,
+252:false,
+253:true,
+253:false,
+254:true,
+254:false,
+255:true,
+255:false,
+256:true,
+256:false,
+257:true,
+257:false,
+258:true,
+258:false,
+259:true,
+259:false,
+260:true,
+260:false,
+261:true,
+261:false,
+262:true,
+262:false,
+263:true,
+263:false,
+264:true,
+264:false,
+265:true,
+265:false,
+266:true,
+266:false,
+267:true,
+267:false,
+268:true,
+268:false,
+269:true,
+269:false,
+270:true,
+270:false,
+271:true,
+271:false,
+272:true,
+272:false,
+273:true,
+273:false,
+274:true,
+274:false,
+275:true,
+275:false,
+276:true,
+276:false,
+277:true,
+277:false,
+278:true,
+278:false,
+279:true,
+279:false,
+280:true,
+280:false,
+281:true,
+281:false,
+282:true,
+282:false,
+283:true,
+283:false,
+284:true,
+284:false,
+285:true,
+285:false,
+286:true,
+286:false,
+287:true,
+287:false,
+288:true,
+288:false,
+289:true,
+289:false,
+290:true,
+290:false,
+291:true,
+291:false,
+292:true,
+292:false,
+293:true,
+293:false,
+294:true,
+294:false,
+295:true,
+295:false,
+296:true,
+296:false,
+297:true,
+297:false,
+298:true,
+298:false,
+299:true,
+299:false,
+300:true,
+300:false,
+301:true,
+301:false,
+302:true,
+302:false,
+303:true,
+303:false,
+304:true,
+304:false,
+305:true,
+305:false,
+306:true,
+306:false,
+307:true,
+307:false,
+308:true,
+308:false,
+309:true,
+309:false,
+310:true,
+310:false,
+311:true,
+311:false,
+312:true,
+312:false,
+313:true,
+313:false,
+314:true,
+314:false,
+315:true,
+315:false,
+316:true,
+316:false,
+317:true,
+317:false,
+318:true,
+318:false,
+319:true,
+319:false,
+320:true,
+320:false,
+321:true,
+321:false,
+322:true,
+322:false,
+323:true,
+323:false,
+324:true,
+324:false,
+325:true,
+325:false,
+326:true,
+326:false,
+327:true,
+327:false,
+328:true,
+328:false,
+329:true,
+329:false,
+330:true,
+330:false,
+331:true,
+331:false,
+332:true,
+332:false,
+333:true,
+333:false,
+334:true,
+334:false,
+335:true,
+335:false,
+336:true,
+336:false,
+337:true,
+337:false,
+338:true,
+338:false,
+339:true,
+339:false,
+340:true,
+340:false,
+341:true,
+341:false,
+342:true,
+342:false,
+343:true,
+343:false,
+344:true,
+344:false,
+345:true,
+345:false,
+346:true,
+346:false,
+347:true,
+347:false,
+348:true,
+348:false,
+349:true,
+349:false,
+350:true,
+350:false,
+351:true,
+351:false,
+352:true,
+352:false,
+353:true,
+353:false,
+354:true,
+354:false,
+355:true,
+355:false,
+356:true,
+356:false,
+357:true,
+357:false,
+358:true,
+358:false,
+359:true,
+359:false,
+360:true,
+360:false,
+361:true,
+361:false,
+362:true,
+362:false,
+363:true,
+363:false,
+364:true,
+364:false,
+365:true,
+365:false,
+366:true,
+366:false,
+367:true,
+367:false,
+368:true,
+368:false,
+369:true,
+369:false,
+370:true,
+370:false,
+371:true,
+371:false,
+372:true,
+372:false,
+373:true,
+373:false,
+374:true,
+374:false,
+375:true,
+375:false,
+376:true,
+376:false,
+377:true,
+377:false,
+378:true,
+378:false,
+379:true,
+379:false,
+380:true,
+380:false,
+381:true,
+381:false,
+382:true,
+382:false,
+383:true,
+383:false,
+384:true,
+384:false,
+385:true,
+385:false,
+386:true,
+386:false,
+387:true,
+387:false,
+388:true,
+388:false,
+389:true,
+389:false,
+390:true,
+390:false,
+391:true,
+391:false,
+392:true,
+392:false,
+393:true,
+393:false,
+394:true,
+394:false,
+395:true,
+395:false,
+396:true,
+396:false,
+397:true,
+397:false,
+398:true,
+398:false,
+399:true,
+399:false,
+400:true,
+400:false,
+401:true,
+401:false,
+402:true,
+402:false,
+403:true,
+403:false,
+404:true,
+404:false,
+405:true,
+405:false,
+406:true,
+406:false,
+407:true,
+407:false,
+408:true,
+408:false,
+409:true,
+409:false,
+410:true,
+410:false,
+411:true,
+411:false,
+412:true,
+412:false,
+413:true,
+413:false,
+414:true,
+414:false,
+415:true,
+415:false,
+416:true,
+416:false,
+417:true,
+417:false,
+418:true,
+418:false,
+419:true,
+419:false,
+420:true,
+420:false,
+421:true,
+421:false,
+422:true,
+422:false,
+423:true,
+423:false,
+424:true,
+424:false,
+425:true,
+425:false,
+426:true,
+426:false,
+427:true,
+427:false,
+428:true,
+428:false,
+429:true,
+429:false,
+430:true,
+430:false,
+431:true,
+431:false,
+432:true,
+432:false,
+433:true,
+433:false,
+434:true,
+434:false,
+435:true,
+435:false,
+436:true,
+436:false,
+437:true,
+437:false,
+438:true,
+438:false,
+439:true,
+439:false,
+440:true,
+440:false,
+441:true,
+441:false,
+442:true,
+442:false,
+443:true,
+443:false,
+444:true,
+444:false,
+445:true,
+445:false,
+446:true,
+446:false,
+447:true,
+447:false,
+448:true,
+448:false,
+449:true,
+449:false,
+450:true,
+450:false,
+451:true,
+451:false,
+452:true,
+452:false,
+453:true,
+453:false,
+454:true,
+454:false,
+455:true,
+455:false,
+456:true,
+456:false,
+457:true,
+457:false,
+458:true,
+458:false,
+459:true,
+459:false,
+460:true,
+460:false,
+461:true,
+461:false,
+462:true,
+462:false,
+463:true,
+463:false,
+464:true,
+464:false,
+465:true,
+465:false,
+466:true,
+466:false,
+467:true,
+467:false,
+468:true,
+468:false,
+469:true,
+469:false,
+470:true,
+470:false,
+471:true,
+471:false,
+472:true,
+472:false,
+473:true,
+473:false,
+474:true,
+474:false,
+475:true,
+475:false,
+476:true,
+476:false,
+477:true,
+477:false,
+478:true,
+478:false,
+479:true,
+479:false,
+480:true,
+480:false,
+481:true,
+481:false,
+482:true,
+482:false,
+483:true,
+483:false,
+484:true,
+484:false,
+485:true,
+485:false,
+486:true,
+486:false,
+487:true,
+487:false,
+488:true,
+488:false,
+489:true,
+489:false,
+490:true,
+490:false,
+491:true,
+491:false,
+492:true,
+492:false,
+493:true,
+493:false,
+494:true,
+494:false,
+495:true,
+495:false,
+496:true,
+496:false,
+497:true,
+497:false,
+498:true,
+498:false,
+499:true,
+499:false,
+500:true,
+500:false,
+501:true,
+501:false,
+502:true,
+502:false,
+503:true,
+503:false,
+504:true,
+504:false,
+505:true,
+505:false,
+506:true,
+506:false,
+507:true,
+507:false,
+508:true,
+508:false,
+509:true,
+509:false,
+510:true,
+510:false,
+511:true,
+511:false,
+512:true,
+512:false,
+513:true,
+513:false,
+514:true,
+514:false,
+515:true,
+515:false,
+516:true,
+516:false,
+517:true,
+517:false,
+518:true,
+518:false,
+519:true,
+519:false,
+520:true,
+520:false,
+521:true,
+521:false,
+522:true,
+522:false,
+523:true,
+523:false,
+524:true,
+524:false,
+525:true,
+525:false,
+526:true,
+526:false,
+527:true,
+527:false,
+528:true,
+528:false,
+529:true,
+529:false,
+530:true,
+530:false,
+531:true,
+531:false,
+532:true,
+532:false,
+533:true,
+533:false,
+534:true,
+534:false,
+535:true,
+535:false,
+536:true,
+536:false,
+537:true,
+537:false,
+538:true,
+538:false,
+539:true,
+539:false,
+540:true,
+540:false,
+541:true,
+541:false,
+542:true,
+542:false,
+543:true,
+543:false,
+544:true,
+544:false,
+545:true,
+545:false,
+546:true,
+546:false,
+547:true,
+547:false,
+548:true,
+548:false,
+549:true,
+549:false,
+550:true,
+550:false,
+551:true,
+551:false,
+552:true,
+552:false,
+553:true,
+553:false,
+554:true,
+554:false,
+555:true,
+555:false,
+556:true,
+556:false,
+557:true,
+557:false,
+558:true,
+558:false,
+559:true,
+559:false,
+560:true,
+560:false,
+561:true,
+561:false,
+562:true,
+562:false,
+563:true,
+563:false,
+564:true,
+565:true,
+565:false,
+564:false,
+566:true,
+566:false,
+567:true,
+567:false,
+568:true,
+568:false,
+569:true,
+569:false,
+570:true,
+570:false,
+571:true,
+571:false,
+572:true,
+572:false,
+573:true,
+573:false,
+574:true,
+574:false,
+575:true,
+575:false,
+576:true,
+576:false,
+577:true,
+577:false,
+578:true,
+578:false,
+579:true,
+579:false,
+580:true,
+580:false,
+581:true,
+581:false,
+582:true,
+582:false,
+583:true,
+583:false,
+584:true,
+584:false,
+585:true,
+585:false,
+586:true,
+586:false,
+587:true,
+587:false,
+588:true,
+588:false,
+589:true,
+589:false,
+590:true,
+590:false,
+591:true,
+591:false,
+592:true,
+592:false,
+593:true,
+593:false,
+594:true,
+594:false,
+595:true,
+595:false,
+596:true,
+596:false,
+597:true,
+597:false,
+598:true,
+598:false,
+599:true,
+599:false,
+600:true,
+600:false,
+601:true,
+601:false,
+602:true,
+603:true,
+603:false,
+602:false,
+604:true,
+604:false,
+605:true,
+605:false,
+606:true,
+606:false,
+607:true,
+607:false,
+608:true,
+608:false,
+609:true,
+609:false,
+610:true,
+610:false,
+611:true,
+611:false,
+612:true,
+612:false,
+613:true,
+613:false,
+614:true,
+614:false,
+615:true,
+615:false,
+616:true,
+616:false,
+617:true,
+617:false,
+618:true,
+618:false,
+619:true,
+619:false,
+620:true,
+620:false,
+621:true,
+621:false,
+622:true,
+622:false,
+623:true,
+623:false,
+624:true,
+624:false,
+625:true,
+625:false,
+626:true,
+626:false,
+627:true,
+627:false,
+628:true,
+628:false,
+629:true,
+629:false,
+630:true,
+630:false,
+631:true,
+631:false,
+632:true,
+632:false,
+633:true,
+633:false,
+634:true,
+634:false,
+635:true,
+635:false,
+636:true,
+636:false,
+637:true,
+637:false,
+638:true,
+638:false,
+639:true,
+639:false,
+640:true,
+640:false,
+641:true,
+641:false,
+642:true,
+642:false,
+643:true,
+643:false,
+644:true,
+644:false,
+645:true,
+645:false,
+646:true,
+646:false,
+647:true,
+647:false,
+648:true,
+648:false,
+649:true,
+649:false,
+650:true,
+650:false,
+651:true,
+651:false,
+652:true,
+652:false,
+653:true,
+653:false,
+654:true,
+654:false,
+655:true,
+655:false,
+656:true,
+656:false,
+657:true,
+657:false,
+658:true,
+658:false,
+659:true,
+659:false,
+660:true,
+660:false,
+661:true,
+661:false,
+662:true,
+662:false,
+663:true,
+663:false,
+664:true,
+664:false,
+665:true,
+665:false,
+666:true,
+666:false,
+667:true,
+667:false,
+668:true,
+668:false,
+669:true,
+669:false,
+670:true,
+670:false,
+671:true,
+671:false,
+672:true,
+672:false,
+673:true,
+673:false,
+674:true,
+675:true,
+675:false,
+674:false,
+676:true,
+676:false,
+677:true,
+677:false,
+678:true,
+678:false,
+679:true,
+679:false,
+680:true,
+680:false,
+681:true,
+681:false,
+682:true,
+682:false,
+683:true,
+683:false,
+684:true,
+684:false,
+685:true,
+685:false,
+686:true,
+686:false,
+687:true,
+687:false,
+688:true,
+688:false,
+689:true,
+689:false,
+690:true,
+690:false,
+691:true,
+691:false,
+692:true,
+692:false,
+693:true,
+693:false,
+694:true,
+694:false,
+695:true,
+695:false,
+696:true,
+696:false,
+697:true,
+697:false,
+698:true,
+698:false,
+699:true,
+699:false,
+700:true,
+700:false,
+701:true,
+701:false,
+702:true,
+702:false,
+703:true,
+703:false,
+704:true,
+704:false,
+705:true,
+705:false,
+706:true,
+706:false,
+707:true,
+707:false,
+708:true,
+708:false,
+709:true,
+709:false,
+710:true,
+710:false,
+711:true,
+711:false,
+712:true,
+712:false,
+713:true,
+713:false,
+714:true,
+714:false,
+715:true,
+715:false,
+716:true,
+716:false,
+717:true,
+717:false,
+718:true,
+718:false,
+719:true,
+719:false,
+720:true,
+720:false,
+721:true,
+721:false,
+722:true,
+722:false,
+723:true,
+723:false,
+724:true,
+724:false,
+725:true,
+725:false,
+726:true,
+726:false,
+727:true,
+727:false,
+728:true,
+728:false,
+729:true,
+729:false,
+730:true,
+730:false,
+731:true,
+731:false,
+732:true,
+732:false,
+733:true,
+733:false,
+734:true,
+734:false,
+735:true,
+735:false,
+736:true,
+736:false,
+737:true,
+737:false,
+738:true,
+738:false,
+739:true,
+739:false,
+740:true,
+740:false,
+741:true,
+741:false,
+742:true,
+742:false,
+743:true,
+743:false,
+744:true,
+744:false,
+745:true,
+745:false,
+746:true,
+746:false,
+747:true,
+747:false,
+748:true,
+749:true,
+749:false,
+750:true,
+750:false,
+751:true,
+751:false,
+748:false,
+752:true,
+752:false,
+753:true,
+753:false,
+754:true,
+754:false,
+755:true,
+755:false,
+756:true,
+756:false,
+757:true,
+757:false,
+758:true,
+758:false,
+759:true,
+759:false,
+760:true,
+760:false,
+761:true,
+761:false,
+762:true,
+762:false,
+763:true,
+763:false,
+764:true,
+764:false,
+765:true,
+765:false,
+766:true,
+766:false,
+767:true,
+767:false,
+768:true,
+768:false,
+769:true,
+769:false,
+770:true,
+770:false,
+771:true,
+771:false,
+772:true,
+772:false,
+773:true,
+773:false,
+774:true,
+774:false,
+775:true,
+775:false,
+776:true,
+776:false,
+777:true,
+777:false,
+778:true,
+778:false,
+779:true,
+779:false,
+780:true,
+780:false,
+781:true,
+781:false,
+782:true,
+782:false,
+783:true,
+783:false,
+784:true,
+784:false,
+785:true,
+785:false,
+786:true,
+786:false,
+787:true,
+787:false,
+788:true,
+788:false,
+789:true,
+789:false,
+790:true,
+790:false,
+791:true,
+791:false,
+792:true,
+792:false,
+793:true,
+793:false,
+794:true,
+794:false,
+795:true,
+795:false,
+796:true,
+796:false,
+797:true,
+797:false,
+798:true,
+798:false,
+799:true,
+799:false,
+800:true,
+800:false,
+801:true,
+801:false,
+802:true,
+802:false,
+803:true,
+803:false,
+804:true,
+804:false,
+805:true,
+805:false,
+806:true,
+806:false,
+807:true,
+807:false,
+808:true,
+808:false,
+809:true,
+809:false,
+810:true,
+810:false,
+811:true,
+811:false,
+812:true,
+812:false,
+813:true,
+813:false,
+814:true,
+814:false,
+815:true,
+815:false,
+816:true,
+816:false,
+817:true,
+817:false,
+818:true,
+818:false,
+819:true,
+819:false,
+820:true,
+820:false,
+821:true,
+821:false,
+822:true,
+822:false,
+823:true,
+823:false,
+824:true,
+824:false,
+825:true,
+825:false,
+826:true,
+826:false,
+827:true,
+827:false,
+828:true,
+828:false,
+829:true,
+829:false,
+830:true,
+830:false,
+831:true,
+831:false,
+832:true,
+832:false,
+833:true,
+833:false,
+834:true,
+834:false,
+835:true,
+835:false,
+836:true,
+836:false,
+837:true,
+837:false,
+838:true,
+838:false,
+839:true,
+839:false,
+840:true,
+840:false,
+841:true,
+841:false,
+842:true,
+842:false,
+843:true,
+843:false,
+844:true,
+844:false,
+845:true,
+845:false,
+846:true,
+846:false,
+847:true,
+847:false,
+848:true,
+848:false,
+849:true,
+849:false,
+850:true,
+850:false,
+851:true,
+851:false,
+852:true,
+852:false,
+853:true,
+853:false,
+854:true,
+854:false,
+855:true,
+855:false,
+856:true,
+856:false,
+857:true,
+857:false,
+858:true,
+858:false,
+859:true,
+859:false,
+860:true,
+860:false,
+861:true,
+861:false,
+862:true,
+862:false,
+863:true,
+863:false,
+864:true,
+864:false,
+865:true,
+865:false,
+866:true,
+866:false,
+867:true,
+867:false,
+868:true,
+868:false,
+869:true,
+869:false,
+870:true,
+870:false,
+871:true,
+871:false,
+872:true,
+872:false,
+873:true,
+873:false,
+874:true,
+874:false,
+875:true,
+875:false,
+876:true,
+876:false,
+877:true,
+877:false,
+878:true,
+878:false,
+879:true,
+879:false,
+880:true,
+880:false,
+881:true,
+881:false,
+882:true,
+882:false,
+883:true,
+883:false,
+884:true,
+884:false,
+885:true,
+885:false,
+886:true,
+886:false,
+887:true,
+887:false,
+888:true,
+888:false,
+889:true,
+889:false,
+890:true,
+890:false,
+891:true,
+891:false,
+892:true,
+892:false,
+893:true,
+893:false,
+894:true,
+894:false,
+895:true,
+895:false,
+896:true,
+896:false,
+897:true,
+897:false,
+898:true,
+898:false,
+899:true,
+899:false,
+900:true,
+900:false,
+901:true,
+901:false,
+902:true,
+902:false,
+903:true,
+903:false,
+904:true,
+904:false,
+905:true,
+905:false,
+906:true,
+906:false,
+907:true,
+907:false,
+908:true,
+908:false,
+909:true,
+909:false,
+910:true,
+910:false,
+911:true,
+911:false,
+912:true,
+912:false,
+913:true,
+913:false,
+914:true,
+914:false,
+915:true,
+915:false,
+916:true,
+916:false,
+917:true,
+917:false,
+918:true,
+918:false,
+919:true,
+919:false,
+920:true,
+920:false,
+921:true,
+921:false,
+922:true,
+922:false,
+923:true,
+923:false,
+924:true,
+924:false,
+925:true,
+925:false,
+926:true,
+926:false,
+927:true,
+927:false,
+928:true,
+928:false,
+929:true,
+929:false,
+930:true,
+930:false,
+931:true,
+931:false,
+932:true,
+932:false,
+933:true,
+933:false,
+934:true,
+934:false,
+935:true,
+935:false,
+936:true,
+936:false,
+937:true,
+937:false,
+938:true,
+938:false,
+939:true,
+939:false,
+940:true,
+940:false,
+941:true,
+941:false,
+942:true,
+942:false,
+943:true,
+943:false,
+944:true,
+944:false,
+945:true,
+945:false,
+946:true,
+946:false,
+947:true,
+947:false,
+948:true,
+948:false,
+949:true,
+949:false,
+950:true,
+950:false,
+951:true,
+951:false,
+952:true,
+952:false,
+953:true,
+953:false,
+954:true,
+954:false,
+955:true,
+955:false,
+956:true,
+956:false,
+957:true,
+957:false,
+958:true,
+958:false,
+959:true,
+959:false,
+960:true,
+960:false,
+961:true,
+961:false,
+962:true,
+962:false,
+963:true,
+963:false,
+964:true,
+964:false,
+965:true,
+965:false,
+966:true,
+966:false,
+967:true,
+967:false,
+968:true,
+968:false,
+969:true,
+969:false,
+970:true,
+970:false,
+971:true,
+971:false,
+972:true,
+972:false,
+973:true,
+973:false,
+974:true,
+974:false,
+975:true,
+975:false,
+976:true,
+976:false,
+977:true,
+977:false,
+978:true,
+978:false,
+979:true,
+979:false,
+980:true,
+980:false,
+981:true,
+981:false,
+982:true,
+982:false,
+983:true,
+983:false,
+984:true,
+984:false,
+985:true,
+985:false,
+986:true,
+986:false,
+987:true,
+987:false,
+988:true,
+988:false,
+989:true,
+989:false,
+990:true,
+990:false,
+991:true,
+991:false,
+992:true,
+992:false,
+993:true,
+993:false,
+994:true,
+994:false,
+995:true,
+995:false,
+996:true,
+996:false,
+997:true,
+997:false,
+998:true,
+998:false,
+999:true,
+999:false,
+1000:true,
+1000:false,
+1001:true,
+1001:false,
+1002:true,
+1002:false,
+1003:true,
+1003:false,
+1004:true,
+1004:false,
+1005:true,
+1005:false,
+1006:true,
+1006:false,
+1007:true,
+1007:false,
+1008:true,
+1008:false,
+1009:true,
+1009:false,
+1010:true,
+1010:false,
+1011:true,
+1011:false,
+1012:true,
+1012:false,
+1013:true,
+1013:false,
+1014:true,
+1014:false,
+1015:true,
+1015:false,
+1016:true,
+1016:false,
+1017:true,
+1017:false,
+1018:true,
+1018:false,
+1019:true,
+1019:false,
+1020:true,
+1020:false,
+1021:true,
+1021:false,
+1022:true,
+1022:false,
+1023:true,
+1023:false,
+1024:true,
+1024:false,
+1025:true,
+1025:false,
+1026:true,
+1026:false,
+1027:true,
+1027:false,
+1028:true,
+1028:false,
+1029:true,
+1029:false,
+1030:true,
+1030:false,
+1031:true,
+1031:false,
+1032:true,
+1032:false,
+1033:true,
+1033:false,
+1034:true,
+1034:false,
+1035:true,
+1035:false,
+1036:true,
+1036:false,
+1037:true,
+1037:false,
+1038:true,
+1038:false,
+1039:true,
+1039:false,
+1040:true,
+1040:false,
+1041:true,
+1041:false,
+1042:true,
+1042:false,
+1043:true,
+1043:false,
+1044:true,
+1044:false,
+1045:true,
+1045:false,
+1046:true,
+1046:false,
+1047:true,
+1047:false,
+1048:true,
+1048:false,
+1049:true,
+1049:false,
+1050:true,
+1050:false,
+1051:true,
+1051:false,
+1052:true,
+1052:false,
+1053:true,
+1053:false,
+1054:true,
+1054:false,
+1055:true,
+1055:false,
+1056:true,
+1056:false,
+1057:true,
+1057:false,
+1058:true,
+1058:false,
+1059:true,
+1059:false,
+1060:true,
+1060:false,
+1061:true,
+1061:false,
+1062:true,
+1062:false,
+1063:true,
+1063:false,
+1064:true,
+1064:false,
+1065:true,
+1065:false,
+1066:true,
+1066:false,
+1067:true,
+1067:false,
+1068:true,
+1068:false,
+1069:true,
+1069:false,
+1070:true,
+1070:false,
+1071:true,
+1071:false,
+1072:true,
+1072:false,
+1073:true,
+1073:false,
+1074:true,
+1074:false,
+1075:true,
+1075:false,
+1076:true,
+1076:false,
+1077:true,
+1077:false,
+1078:true,
+1078:false,
+1079:true,
+1079:false,
+1080:true,
+1080:false,
+1081:true,
+1081:false,
+1082:true,
+1082:false,
+1083:true,
+1083:false,
+1084:true,
+1084:false,
+1085:true,
+1085:false,
+1086:true,
+1086:false,
+1087:true,
+1087:false,
+1088:true,
+1088:false,
+1089:true,
+1089:false,
+1090:true,
+1090:false,
+1091:true,
+1091:false,
+1092:true,
+1092:false,
+1093:true,
+1093:false,
+1094:true,
+1094:false,
+1095:true,
+1095:false,
+1096:true,
+1096:false,
+1097:true,
+1097:false,
+1098:true,
+1098:false,
+1099:true,
+1099:false,
+1100:true,
+1100:false,
+1101:true,
+1101:false,
+1102:true,
+1102:false,
+1103:true,
+1103:false,
+1104:true,
+1104:false,
+1105:true,
+1105:false,
+1106:true,
+1106:false,
+1107:true,
+1107:false,
+1108:true,
+1108:false,
+1109:true,
+1109:false,
+1110:true,
+1110:false,
+1111:true,
+1111:false,
+1112:true,
+1112:false,
+1113:true,
+1113:false,
+1114:true,
+1114:false,
+1115:true,
+1115:false,
+1116:true,
+1116:false,
+1117:true,
+1117:false,
+1118:true,
+1118:false,
+1119:true,
+1119:false,
+1120:true,
+1120:false,
+1121:true,
+1121:false,
+1122:true,
+1122:false,
+1123:true,
+1123:false,
+1124:true,
+1124:false,
+1125:true,
+1125:false,
+1126:true,
+1126:false,
+1127:true,
+1127:false,
+1128:true,
+1128:false,
+1129:true,
+1129:false,
+1130:true,
+1130:false,
+1131:true,
+1131:false,
+1132:true,
+1132:false,
+1133:true,
+1133:false,
+1134:true,
+1134:false,
+1135:true,
+1135:false,
+1136:true,
+1136:false,
+1137:true,
+1137:false,
+1138:true,
+1138:false,
+1139:true,
+1139:false,
+1140:true,
+1140:false,
+1141:true,
+1141:false,
+1142:true,
+1142:false,
+1143:true,
+1143:false,
+1144:true,
+1144:false,
+1145:true,
+1145:false,
+1146:true,
+1146:false,
+1147:true,
+1147:false,
+1148:true,
+1148:false,
+1149:true,
+1149:false,
+1150:true,
+1150:false,
+1151:true,
+1151:false,
+1152:true,
+1152:false,
+1153:true,
+1153:false,
+1154:true,
+1154:false,
+1155:true,
+1155:false,
+1156:true,
+1156:false,
+1157:true,
+1157:false,
+1158:true,
+1158:false,
+1159:true,
+1159:false,
+1160:true,
+1160:false,
+1161:true,
+1161:false,
+1162:true,
+1162:false,
+1163:true,
+1163:false,
+1164:true,
+1164:false,
+1165:true,
+1165:false,
+1166:true,
+1166:false,
+1167:true,
+1167:false,
+1168:true,
+1168:false,
+1169:true,
+1169:false,
+1170:true,
+1170:false,
+1171:true,
+1171:false,
+1172:true,
+1172:false,
+1173:true,
+1173:false,
+1174:true,
+1174:false,
+1175:true,
+1175:false,
+1176:true,
+1176:false,
+1177:true,
+1177:false,
+1178:true,
+1178:false,
+1179:true,
+1179:false,
+1180:true,
+1180:false,
+1181:true,
+1181:false,
+1182:true,
+1182:false,
+1183:true,
+1183:false,
+1184:true,
+1184:false,
+1185:true,
+1185:false,
+1186:true,
+1186:false,
+1187:true,
+1187:false,
+1188:true,
+1188:false,
+1189:true,
+1189:false,
+1190:true,
+1190:false,
+1191:true,
+1191:false,
+1192:true,
+1192:false,
+1193:true,
+1193:false,
+1194:true,
+1194:false,
+1195:true,
+1195:false,
+1196:true,
+1196:false,
+1197:true,
+1197:false,
+1198:true,
+1198:false,
+1199:true,
+1199:false,
+1200:true,
+1200:false,
+1201:true,
+1201:false,
+1202:true,
+1202:false,
+1203:true,
+1203:false,
+1204:true,
+1204:false,
+1205:true,
+1205:false,
+1206:true,
+1206:false,
+1207:true,
+1207:false,
+1208:true,
+1208:false,
+1209:true,
+1209:false,
+1210:true,
+1210:false,
+1211:true,
+1211:false,
+1212:true,
+1212:false,
+1213:true,
+1213:false,
+1214:true,
+1214:false,
+1215:true,
+1215:false,
+1216:true,
+1216:false,
+1217:true,
+1217:false,
+1218:true,
+1218:false,
+1219:true,
+1219:false,
+1220:true,
+1220:false,
+1221:true,
+1221:false,
+1222:true,
+1222:false,
+1223:true,
+1223:false,
+1224:true,
+1224:false,
+1225:true,
+1225:false,
+1226:true,
+1226:false,
+1227:true,
+1227:false,
+1228:true,
+1228:false,
+1229:true,
+1229:false,
+1230:true,
+1230:false,
+1231:true,
+1231:false,
+1232:true,
+1232:false,
+1233:true,
+1233:false,
+1234:true,
+1234:false,
+1235:true,
+1235:false,
+1236:true,
+1236:false,
+1237:true,
+1237:false,
+1238:true,
+1238:false,
+1239:true,
+1239:false,
+1240:true,
+1240:false,
+1241:true,
+1241:false,
+1242:true,
+1242:false,
+1243:true,
+1243:false,
+1244:true,
+1244:false,
+1245:true,
+1245:false,
+1246:true,
+1246:false,
+1247:true,
+1247:false,
+1248:true,
+1248:false,
+1249:true,
+1249:false,
+1250:true,
+1250:false,
+1251:true,
+1251:false,
+1252:true,
+1252:false,
+1253:true,
+1253:false,
+1254:true,
+1254:false,
+1255:true,
+1255:false,
+1256:true,
+1256:false,
+1257:true,
+1257:false,
+1258:true,
+1258:false,
+1259:true,
+1259:false,
+1260:true,
+1260:false,
+1261:true,
+1261:false,
+1262:true,
+1262:false,
+1263:true,
+1263:false,
+1264:true,
+1264:false,
+1265:true,
+1265:false,
+1266:true,
+1266:false,
+1267:true,
+1267:false,
+1268:true,
+1268:false,
+1269:true,
+1269:false,
+1270:true,
+1270:false,
+1271:true,
+1271:false,
+1272:true,
+1272:false,
+1273:true,
+1273:false,
+1274:true,
+1274:false,
+1275:true,
+1275:false,
+1276:true,
+1276:false,
+1277:true,
+1277:false,
+1278:true,
+1278:false,
+1279:true,
+1279:false,
+1280:true,
+1280:false,
+1281:true,
+1281:false,
+1282:true,
+1282:false,
+1283:true,
+1283:false,
+1284:true,
+1284:false,
+1285:true,
+1285:false,
+1286:true,
+1286:false,
+1287:true,
+1287:false,
+1288:true,
+1288:false,
+1289:true,
+1289:false,
+1290:true,
+1290:false,
+1291:true,
+1291:false,
+1292:true,
+1292:false,
+1293:true,
+1293:false,
+1294:true,
+1294:false,
+1295:true,
+1295:false,
+1296:true,
+1296:false,
+1297:true,
+1297:false,
+1298:true,
+1298:false,
+1299:true,
+1299:false,
+1300:true,
+1300:false,
+1301:true,
+1301:false,
+1302:true,
+1302:false,
+1303:true,
+1303:false,
+1304:true,
+1304:false,
+1305:true,
+1305:false,
+1306:true,
+1306:false,
+1307:true,
+1307:false,
+1308:true,
+1308:false,
+1309:true,
+1309:false,
+1310:true,
+1310:false,
+1311:true,
+1311:false,
+1312:true,
+1312:false,
+1313:true,
+1313:false,
+1314:true,
+1314:false,
+1315:true,
+1315:false,
+1316:true,
+1316:false,
+1317:true,
+1317:false,
+1318:true,
+1318:false,
+1319:true,
+1319:false,
+1320:true,
+1320:false,
+1321:true,
+1321:false,
+1322:true,
+1322:false,
+1323:true,
+1323:false,
+1324:true,
+1324:false,
+1325:true,
+1325:false,
+1326:true,
+1326:false,
+1327:true,
+1327:false,
+1328:true,
+1328:false,
+1329:true,
+1329:false,
+1330:true,
+1330:false,
+1331:true,
+1331:false,
+1332:true,
+1332:false,
+1333:true,
+1333:false,
+1334:true,
+1334:false,
+1335:true,
+1335:false,
+1336:true,
+1336:false,
+1337:true,
+1337:false,
+1338:true,
+1338:false,
+1339:true,
+1339:false,
+1340:true,
+1340:false,
+1341:true,
+1341:false,
+1342:true,
+1342:false,
+1343:true,
+1343:false,
+1344:true,
+1344:false,
+1345:true,
+1345:false,
+1346:true,
+1346:false,
+1347:true,
+1347:false,
+1348:true,
+1348:false,
+1349:true,
+1349:false,
+1350:true,
+1350:false,
+1351:true,
+1351:false,
+1352:true,
+1352:false,
+1353:true,
+1353:false,
+1354:true,
+1354:false,
+1355:true,
+1355:false,
+1356:true,
+1356:false,
+1357:true,
+1357:false,
+1358:true,
+1358:false,
+1359:true,
+1359:false,
+1360:true,
+1360:false,
+1361:true,
+1361:false,
+1362:true,
+1362:false,
+1363:true,
+1363:false,
+1364:true,
+1364:false,
+1365:true,
+1365:false,
+1366:true,
+1366:false,
+1367:true,
+1367:false,
+1368:true,
+1368:false,
+1369:true,
+1369:false,
+1370:true,
+1370:false,
+1371:true,
+1371:false,
+1372:true,
+1372:false,
+1373:true,
+1373:false,
+1374:true,
+1374:false,
+1375:true,
+1375:false,
+1376:true,
+1376:false,
+1377:true,
+1377:false,
+1378:true,
+1378:false,
+1379:true,
+1379:false,
+1380:true,
+1380:false,
+1381:true,
+1381:false,
+1382:true,
+1382:false,
+1383:true,
+1383:false,
+1384:true,
+1384:false,
+1385:true,
+1385:false,
+1386:true,
+1386:false,
+1387:true,
+1387:false,
+1388:true,
+1388:false,
+1389:true,
+1389:false,
+1390:true,
+1390:false,
+1391:true,
+1391:false,
+1392:true,
+1392:false,
+1393:true,
+1393:false,
+1394:true,
+1394:false,
+1395:true,
+1395:false,
+1396:true,
+1396:false,
+1397:true,
+1397:false,
+1398:true,
+1398:false,
+1399:true,
+1399:false,
+1400:true,
+1400:false,
+1401:true,
+1401:false,
+1402:true,
+1402:false,
+1403:true,
+1403:false,
+1404:true,
+1404:false,
+1405:true,
+1405:false,
+1406:true,
+1406:false,
+1407:true,
+1407:false,
+1408:true,
+1408:false,
+1409:true,
+1409:false,
+1410:true,
+1410:false,
+1411:true,
+1411:false,
+1412:true,
+1412:false,
+1413:true,
+1413:false,
+1414:true,
+1414:false,
+1415:true,
+1415:false,
+1416:true,
+1416:false,
+1417:true,
+1417:false,
+1418:true,
+1418:false,
+1419:true,
+1419:false,
+1420:true,
+1420:false,
+1421:true,
+1421:false,
+1422:true,
+1422:false,
+1423:true,
+1423:false,
+1424:true,
+1424:false,
+1425:true,
+1425:false,
+1426:true,
+1426:false,
+1427:true,
+1427:false,
+1428:true,
+1428:false,
+1429:true,
+1429:false,
+1430:true,
+1430:false,
+1431:true,
+1431:false,
+1432:true,
+1432:false,
+1433:true,
+1433:false,
+1434:true,
+1434:false,
+1435:true,
+1435:false,
+1436:true,
+1436:false,
+1437:true,
+1437:false,
+1438:true,
+1438:false,
+1439:true,
+1439:false,
+1440:true,
+1440:false,
+1441:true,
+1441:false,
+1442:true,
+1442:false,
+1443:true,
+1443:false,
+1444:true,
+1444:false,
+1445:true,
+1445:false,
+1446:true,
+1446:false,
+1447:true,
+1447:false,
+1448:true,
+1448:false,
+1449:true,
+1449:false,
+1450:true,
+1450:false,
+1451:true,
+1451:false,
+1452:true,
+1452:false,
+1453:true,
+1453:false,
+1454:true,
+1454:false,
+1455:true,
+1455:false,
+1456:true,
+1456:false,
+1457:true,
+1457:false,
+1458:true,
+1458:false,
+1459:true,
+1459:false,
+1460:true,
+1460:false,
+1461:true,
+1461:false,
+1462:true,
+1462:false,
+1463:true,
+1463:false,
+1464:true,
+1464:false,
+1465:true,
+1465:false,
+1466:true,
+1466:false,
+1467:true,
+1467:false,
+1468:true,
+1468:false,
+1469:true,
+1469:false,
+1470:true,
+1470:false,
+1471:true,
+1471:false,
+1472:true,
+1472:false,
+1473:true,
+1473:false,
+1474:true,
+1474:false,
+1475:true,
+1475:false,
+1476:true,
+1476:false,
+1477:true,
+1477:false,
+1478:true,
+1478:false,
+1479:true,
+1479:false,
+1480:true,
+1480:false,
+1481:true,
+1481:false,
+1482:true,
+1482:false,
+1483:true,
+1483:false,
+1484:true,
+1484:false,
+1485:true,
+1485:false,
+1486:true,
+1486:false,
+1487:true,
+1487:false,
+1488:true,
+1488:false,
+1489:true,
+1489:false,
+1490:true,
+1490:false,
+1491:true,
+1491:false,
+1492:true,
+1492:false,
+1493:true,
+1493:false,
+1494:true,
+1494:false,
+1495:true,
+1495:false,
+1496:true,
+1496:false,
+1497:true,
+1497:false,
+1498:true,
+1498:false,
+1499:true,
+1499:false,
+1500:true,
+1500:false,
+1501:true,
+1501:false,
+1502:true,
+1502:false,
+1503:true,
+1503:false,
+1504:true,
+1504:false,
+1505:true,
+1505:false,
+1506:true,
+1506:false,
+1507:true,
+1507:false,
+1508:true,
+1508:false,
+1509:true,
+1509:false,
+1510:true,
+1510:false,
+1511:true,
+1511:false,
+1512:true,
+1512:false,
+1513:true,
+1513:false,
+1514:true,
+1514:false,
+1515:true,
+1515:false,
+1516:true,
+1516:false,
+1517:true,
+1517:false,
+1518:true,
+1518:false,
+1519:true,
+1519:false,
+1520:true,
+1520:false,
+1521:true,
+1521:false,
+1522:true,
+1522:false,
+1523:true,
+1523:false,
+1524:true,
+1524:false,
+1525:true,
+1525:false,
+1526:true,
+1526:false,
+1527:true,
+1527:false,
+1528:true,
+1528:false,
+1529:true,
+1529:false,
+1530:true,
+1530:false,
+1531:true,
+1531:false,
+1532:true,
+1532:false,
+1533:true,
+1533:false,
+1534:true,
+1534:false,
+1535:true,
+1535:false,
+1536:true,
+1536:false,
+1537:true,
+1537:false,
+1538:true,
+1538:false,
+1539:true,
+1539:false,
+1540:true,
+1540:false,
+1541:true,
+1541:false,
+1542:true,
+1542:false,
+1543:true,
+1543:false,
+1544:true,
+1544:false,
+1545:true,
+1545:false,
+1546:true,
+1546:false,
+1547:true,
+1547:false,
+1548:true,
+1548:false,
+1549:true,
+1549:false,
+1550:true,
+1550:false,
+1551:true,
+1551:false,
+1552:true,
+1552:false,
+1553:true,
+1553:false,
+1554:true,
+1554:false,
+1555:true,
+1555:false,
+1556:true,
+1556:false,
+1557:true,
+1557:false,
+1558:true,
+1558:false,
+1559:true,
+1559:false,
+1560:true,
+1560:false,
+1561:true,
+1561:false,
+1562:true,
+1562:false,
+1563:true,
+1563:false,
+1564:true,
+1564:false,
+1565:true,
+1565:false,
+1566:true,
+1566:false,
+1567:true,
+1567:false,
+1568:true,
+1568:false,
+1569:true,
+1569:false,
+1570:true,
+1570:false,
+1571:true,
+1571:false,
+1572:true,
+1572:false,
+1573:true,
+1573:false,
+1574:true,
+1574:false,
+1575:true,
+1575:false,
+1576:true,
+1576:false,
+1577:true,
+1577:false,
+1578:true,
+1578:false,
+1579:true,
+1579:false,
+1580:true,
+1580:false,
+1581:true,
+1581:false,
+1582:true,
+1582:false,
+1583:true,
+1584:true,
+1584:false,
+1583:false,
+1585:true,
+1585:false,
+1586:true,
+1586:false,
+1587:true,
+1587:false,
+1588:true,
+1588:false,
+1589:true,
+1589:false,
+1590:true,
+1590:false,
+1591:true,
+1591:false,
+1592:true,
+1592:false,
+1593:true,
+1593:false,
+1594:true,
+1594:false,
+1595:true,
+1595:false,
+1596:true,
+1596:false,
+1597:true,
+1597:false,
+1598:true,
+1598:false,
+1599:true,
+1599:false,
+1600:true,
+1600:false,
+1601:true,
+1601:false,
+1602:true,
+1602:false,
+1603:true,
+1603:false,
+1604:true,
+1604:false,
+1605:true,
+1605:false,
+1606:true,
+1606:false,
+1607:true,
+1607:false,
+1608:true,
+1608:false,
+1609:true,
+1609:false,
+1610:true,
+1610:false,
+1611:true,
+1611:false,
+1612:true,
+1612:false,
+1613:true,
+1613:false,
+1614:true,
+1614:false,
+1615:true,
+1615:false,
+1616:true,
+1616:false,
+1617:true,
+1617:false,
+1618:true,
+1618:false,
+1619:true,
+1619:false,
+1620:true,
+1620:false,
+1621:true,
+1621:false,
+1622:true,
+1622:false,
+1623:true,
+1624:true,
+1624:false,
+1623:false,
+1625:true,
+1625:false,
+1626:true,
+1626:false,
+1627:true,
+1627:false,
+1628:true,
+1628:false,
+1629:true,
+1629:false,
+1630:true,
+1630:false,
+1631:true,
+1631:false,
+1632:true,
+1632:false,
+1633:true,
+1633:false,
+1634:true,
+1634:false,
+1635:true,
+1635:false,
+1636:true,
+1636:false,
+1637:true,
+1637:false,
+1638:true,
+1638:false,
+1639:true,
+1639:false,
+1640:true,
+1640:false,
+1641:true,
+1641:false,
+1642:true,
+1642:false,
+1643:true,
+1643:false,
+1644:true,
+1644:false,
+1645:true,
+1645:false,
+1646:true,
+1646:false,
+1647:true,
+1647:false,
+1648:true,
+1648:false,
+1649:true,
+1649:false,
+1650:true,
+1650:false,
+1651:true,
+1651:false,
+1652:true,
+1652:false,
+1653:true,
+1653:false,
+1654:true,
+1654:false,
+1655:true,
+1656:true,
+1656:false,
+1655:false,
+1657:true,
+1657:false,
+1658:true,
+1658:false,
+1659:true,
+1659:false,
+1660:true,
+1660:false,
+1661:true,
+1661:false,
+1662:true,
+1662:false,
+1663:true,
+1663:false,
+1664:true,
+1664:false,
+1665:true,
+1665:false,
+1666:true,
+1666:false,
+1667:true,
+1667:false,
+1668:true,
+1668:false,
+1669:true,
+1669:false,
+1670:true,
+1670:false,
+1671:true,
+1671:false,
+1672:true,
+1672:false,
+1673:true,
+1673:false,
+1674:true,
+1674:false,
+1675:true,
+1675:false,
+1676:true,
+1676:false,
+1677:true,
+1677:false,
+1678:true,
+1678:false,
+1679:true,
+1679:false,
+1680:true,
+1680:false,
+1681:true,
+1681:false,
+1682:true,
+1682:false,
+1683:true,
+1683:false,
+1684:true,
+1684:false,
+1685:true,
+1685:false,
+1686:true,
+1686:false,
+1687:true,
+1687:false,
+1688:true,
+1688:false,
+1689:true,
+1689:false,
+1690:true,
+1690:false,
+1691:true,
+1691:false,
+1692:true,
+1692:false,
+1693:true,
+1694:true,
+1694:false,
+1693:false,
+1695:true,
+1695:false,
+1696:true,
+1696:false,
+1697:true,
+1697:false,
+1698:true,
+1698:false,
+1699:true,
+1699:false,
+1700:true,
+1700:false,
+1701:true,
+1701:false,
+1702:true,
+1702:false,
+1703:true,
+1703:false,
+1704:true,
+1704:false,
+1705:true,
+1705:false,
+1706:true,
+1706:false,
+1707:true,
+1707:false,
+1708:true,
+1708:false,
+1709:true,
+1709:false,
+1710:true,
+1710:false,
+1711:true,
+1711:false,
+1712:true,
+1712:false,
+1713:true,
+1713:false,
+1714:true,
+1714:false,
+1715:true,
+1715:false,
+1716:true,
+1716:false,
+1717:true,
+1717:false,
+1718:true,
+1718:false,
+1719:true,
+1719:false,
+1720:true,
+1720:false,
+1721:true,
+1721:false,
+1722:true,
+1722:false,
+1723:true,
+1723:false,
+1724:true,
+1724:false,
+1725:true,
+1726:true,
+1726:false,
+1725:false,
+1727:true,
+1727:false,
+1728:true,
+1728:false,
+1729:true,
+1729:false,
+1730:true,
+1730:false,
+1731:true,
+1731:false,
+1732:true,
+1732:false,
+1733:true,
+1733:false,
+1734:true,
+1734:false,
+1735:true,
+1735:false,
+1736:true,
+1736:false,
+1737:true,
+1737:false,
+1738:true,
+1738:false,
+1739:true,
+1739:false,
+1740:true,
+1740:false,
+1741:true,
+1741:false,
+1742:true,
+1742:false,
+1743:true,
+1743:false,
+1744:true,
+1744:false,
+1745:true,
+1745:false,
+1746:true,
+1746:false,
+1747:true,
+1747:false,
+1748:true,
+1748:false,
+1749:true,
+1749:false,
+1750:true,
+1750:false,
+1751:true,
+1751:false,
+1752:true,
+1752:false,
+1753:true,
+1753:false,
+1754:true,
+1754:false,
+1755:true,
+1755:false,
+1756:true,
+1756:false,
+1757:true,
+1757:false,
+1758:true,
+1758:false,
+1759:true,
+1759:false,
+1760:true,
+1760:false,
+1761:true,
+1761:false,
+1762:true,
+1762:false,
+1763:true,
+1763:false,
+1764:true,
+1764:false,
+1765:true,
+1765:false,
+1766:true,
+1766:false,
+1767:true,
+1767:false,
+1768:true,
+1768:false,
+1769:true,
+1769:false,
+1770:true,
+1770:false,
+1771:true,
+1771:false,
+1772:true,
+1772:false,
+1773:true,
+1773:false,
+1774:true,
+1774:false,
+1775:true,
+1775:false,
+1776:true,
+1776:false,
+1777:true,
+1777:false,
+1778:true,
+1778:false,
+1779:true,
+1779:false,
+1780:true,
+1780:false,
+1781:true,
+1781:false,
+1782:true,
+1782:false,
+1783:true,
+1783:false,
+1784:true,
+1784:false,
+1785:true,
+1785:false,
+1786:true,
+1786:false,
+1787:true,
+1787:false,
+1788:true,
+1788:false,
+1789:true,
+1789:false,
+1790:true,
+1790:false,
+1791:true,
+1791:false,
+1792:true,
+1792:false,
+1793:true,
+1793:false,
+1794:true,
+1794:false,
+1795:true,
+1795:false,
+1796:true,
+1796:false,
+1797:true,
+1797:false,
+1798:true,
+1798:false,
+1799:true,
+1799:false,
+1800:true,
+1800:false,
+1801:true,
+1801:false,
+1802:true,
+1802:false,
+1803:true,
+1803:false,
+1804:true,
+1804:false,
+1805:true,
+1805:false,
+1806:true,
+1806:false,
+1807:true,
+1807:false,
+1808:true,
+1808:false,
+1809:true,
+1809:false,
+1810:true,
+1810:false,
+1811:true,
+1811:false,
+1812:true,
+1812:false,
+1813:true,
+1813:false,
+1814:true,
+1814:false,
+1815:true,
+1815:false,
+1816:true,
+1816:false,
+1817:true,
+1817:false,
+1818:true,
+1818:false,
+1819:true,
+1819:false,
+1820:true,
+1820:false,
+1821:true,
+1821:false,
+1822:true,
+1822:false,
+1823:true,
+1823:false,
+1824:true,
+1824:false,
+1825:true,
+1825:false,
+1826:true,
+1826:false,
+1827:true,
+1827:false,
+1828:true,
+1828:false,
+1829:true,
+1829:false,
+1830:true,
+1830:false,
+1831:true,
+1831:false,
+1832:true,
+1832:false,
+1833:true,
+1833:false,
+1834:true,
+1834:false,
+1835:true,
+1835:false,
+1836:true,
+1836:false,
+1837:true,
+1837:false,
+1838:true,
+1838:false,
+1839:true,
+1839:false,
+1840:true,
+1840:false,
+1841:true,
+1841:false,
+1842:true,
+1842:false,
+1843:true,
+1843:false,
+1844:true,
+1844:false,
+1845:true,
+1845:false,
+1846:true,
+1846:false,
+1847:true,
+1847:false,
+1848:true,
+1848:false,
+1849:true,
+1849:false,
+1850:true,
+1850:false,
+1851:true,
+1851:false,
+1852:true,
+1852:false,
+1853:true,
+1853:false,
+1854:true,
+1854:false,
+1855:true,
+1855:false,
+1856:true,
+1856:false,
+1857:true,
+1857:false,
+1858:true,
+1858:false,
+1859:true,
+1859:false,
+1860:true,
+1860:false,
+1861:true,
+1861:false,
+1862:true,
+1862:false,
+1863:true,
+1863:false,
+1864:true,
+1864:false,
+1865:true,
+1865:false,
+1866:true,
+1866:false,
+1867:true,
+1867:false,
+1868:true,
+1868:false,
+1869:true,
+1869:false,
+1870:true,
+1870:false,
+1871:true,
+1871:false,
+1872:true,
+1872:false,
+1873:true,
+1873:false,
+1874:true,
+1874:false,
+1875:true,
+1875:false,
+1876:true,
+1876:false,
+1877:true,
+1877:false,
+1878:true,
+1878:false,
+1879:true,
+1879:false,
+1880:true,
+1880:false,
+1881:true,
+1881:false,
+1882:true,
+1882:false,
+1883:true,
+1883:false,
+1884:true,
+1884:false,
+1885:true,
+1885:false,
+1886:true,
+1886:false,
+1887:true,
+1887:false,
+1888:true,
+1889:true,
+1889:false,
+1890:true,
+1890:false,
+1891:true,
+1891:false,
+1888:false,
+1892:true,
+1892:false,
+1893:true,
+1894:true,
+1894:false,
+1895:true,
+1895:false,
+1896:true,
+1896:false,
+1897:true,
+1897:false,
+1898:true,
+1898:false,
+1893:false,
+1899:true,
+1899:false,
+1900:true,
+1901:true,
+1901:false,
+1902:true,
+1902:false,
+1903:true,
+1903:false,
+1900:false,
+1904:true,
+1904:false,
+1905:true,
+1906:true,
+1906:false,
+1907:true,
+1907:false,
+1908:true,
+1908:false,
+1909:true,
+1909:false,
+1910:true,
+1910:false,
+1911:true,
+1911:false,
+1912:true,
+1912:false,
+1913:true,
+1913:false,
+1914:true,
+1914:false,
+1915:true,
+1915:false,
+1916:true,
+1916:false,
+1917:true,
+1917:false,
+1918:true,
+1918:false,
+1919:true,
+1919:false,
+1920:true,
+1920:false,
+1921:true,
+1921:false,
+1922:true,
+1922:false,
+1923:true,
+1923:false,
+1924:true,
+1924:false,
+1925:true,
+1925:false,
+1926:true,
+1926:false,
+1927:true,
+1927:false,
+1905:false,
+1928:true,
+1928:false,
+1929:true,
+1929:false,
+1930:true,
+1930:false,
+1931:true,
+1932:true,
+1932:false,
+1933:true,
+1933:false,
+1934:true,
+1934:false,
+1935:true,
+1935:false,
+1936:true,
+1936:false,
+1937:true,
+1937:false,
+1938:true,
+1938:false,
+1939:true,
+1939:false,
+1931:false,
+1940:true,
+1941:true,
+1940:false,
+1942:true,
+1941:false,
+1943:true,
+1944:true,
+1944:false,
+1943:false,
+1945:true,
+1945:false,
+1946:true,
+1946:false,
+1947:true,
+1947:false,
+1948:true,
+1948:false,
+1949:true,
+1949:false,
+1950:true,
+1950:false,
+1951:true,
+1951:false,
+1952:true,
+1952:false,
+1953:true,
+1953:false,
+1954:true,
+1954:false,
+1955:true,
+1955:false,
+1956:true,
+1956:false,
+1957:true,
+1957:false,
+1958:true,
+1958:false,
+1959:true,
+1959:false,
+1960:true,
+1960:false,
+1961:true,
+1961:false,
+1962:true,
+1962:false,
+1963:true,
+1963:false,
+1964:true,
+1964:false,
+1965:true,
+1965:false,
+1966:true,
+1966:false,
+1967:true,
+1967:false,
+1968:true,
+1968:false,
+1969:true,
+1969:false,
+1970:true,
+1970:false,
+1971:true,
+1971:false,
+1972:true,
+1972:false,
+1973:true,
+1973:false,
+1974:true,
+1974:false,
+1975:true,
+1975:false,
+1976:true,
+1976:false,
+1977:true,
+1977:false,
+1978:true,
+1978:false,
+1942:false,
+1979:true,
+1979:false,
+1980:true,
+1981:true,
+1981:false,
+1982:true,
+1982:false,
+1983:true,
+1983:false,
+1984:true,
+1984:false,
+1985:true,
+1985:false,
+1986:true,
+1986:false,
+1987:true,
+1987:false,
+1988:true,
+1988:false,
+1989:true,
+1989:false,
+1990:true,
+1990:false,
+1991:true,
+1991:false,
+1992:true,
+1992:false,
+1993:true,
+1993:false,
+1994:true,
+1994:false,
+1995:true,
+1995:false,
+1996:true,
+1996:false,
+1997:true,
+1997:false,
+1998:true,
+1998:false,
+1999:true,
+1999:false,
+2000:true,
+2000:false,
+2001:true,
+2001:false,
+2002:true,
+2002:false,
+2003:true,
+2003:false,
+2004:true,
+2004:false,
+2005:true,
+2005:false,
+2006:true,
+2006:false,
+2007:true,
+2007:false,
+2008:true,
+2008:false,
+2009:true,
+2010:true,
+2011:true,
+2011:false,
+2010:false,
+2012:true,
+2012:false,
+2009:false,
+2013:true,
+2013:false,
+1980:false,
+2014:true,
+2014:false,
+2015:true,
+2016:true,
+2016:false,
+2017:true,
+2017:false,
+2018:true,
+2018:false,
+2019:true,
+2019:false,
+2020:true,
+2020:false,
+2021:true,
+2021:false,
+2022:true,
+2022:false,
+2023:true,
+2023:false,
+2024:true,
+2024:false,
+2025:true,
+2025:false,
+2026:true,
+2026:false,
+2027:true,
+2027:false,
+2028:true,
+2028:false,
+2029:true,
+2029:false,
+2030:true,
+2030:false,
+2015:false,
+2031:true,
+2031:false,
+2032:true,
+2033:true,
+2033:false,
+2034:true,
+2034:false,
+2035:true,
+2035:false,
+2036:true,
+2036:false,
+2037:true,
+2038:true,
+2037:false,
+2038:false,
+2039:true,
+2039:false,
+2040:true,
+2040:false,
+2041:true,
+2042:true,
+2042:false,
+2041:false,
+2043:true,
+2043:false,
+2044:true,
+2044:false,
+2045:true,
+2045:false,
+2046:true,
+2047:true,
+2047:false,
+2046:false,
+2048:true,
+2048:false,
+2032:false,
+2049:true,
+2049:false,
+2050:true,
+2051:true,
+2051:false,
+2052:true,
+2052:false,
+2053:true,
+2053:false,
+2054:true,
+2054:false,
+2055:true,
+2055:false,
+2056:true,
+2056:false,
+2057:true,
+2058:true,
+2058:false,
+2057:false,
+2059:true,
+2059:false,
+2060:true,
+2060:false,
+2061:true,
+2061:false,
+2062:true,
+2062:false,
+2063:true,
+2050:false,
+2064:true,
+2064:false,
+2065:true,
+2063:false,
+2066:true,
+2067:true,
+2068:true,
+2069:true,
+2070:true,
+2071:true,
+2072:true,
+2073:true,
+2074:true,
+2075:true,
+2076:true,
+2077:true,
+2078:true,
+2079:true,
+2080:true,
+2081:true,
+2082:true,
+2083:true,
+2084:true,
+2085:true,
+2086:true,
+2087:true,
+2088:true,
+2089:true,
+2090:true,
+2091:true,
+2092:true,
+2093:true,
+2094:true,
+2095:true,
+2096:true,
+2097:true,
+2098:true,
+2099:true,
+2100:true,
+2101:true,
+2102:true,
+2103:true,
+2104:true,
+2105:true,
+2106:true,
+2107:true,
+2108:true,
+2109:true,
+2110:true,
+2111:true,
+2112:true,
+2113:true,
+2114:true,
+2115:true,
+2116:true,
+2117:true,
+2118:true,
+2119:true,
+2120:true,
+2121:true,
+2122:true,
+2123:true,
+2124:true,
+2125:true,
+2126:true,
+2127:true,
+2128:true,
+2129:true,
+2130:true,
+2131:true,
+2132:true,
+2133:true,
+2134:true,
+2135:true,
+2136:true,
+2137:true,
+2138:true,
+2139:true,
+2140:true,
+2141:true,
+2142:true,
+2143:true,
+2144:true,
+2145:true,
+2146:true,
+2147:true,
+2148:true,
+2149:true,
+2150:true,
+2151:true,
+2152:true,
+2153:true,
+2154:true,
+2155:true,
+2156:true,
+2157:true,
+2158:true,
+2159:true,
+2160:true,
+2161:true,
+2162:true,
+2163:true,
+2164:true,
+2165:true,
+2166:true,
+2167:true,
+2168:true,
+2169:true,
+2170:true,
+2171:true,
+2172:true,
+2173:true,
+2174:true,
+2175:true,
+2176:true,
+2177:true,
+2178:true,
+2179:true,
+2180:true,
+2181:true,
+2182:true,
+2183:true,
+2184:true,
+2185:true,
+2186:true,
+2187:true,
+2188:true,
+2189:true,
+2190:true,
+2191:true,
+2192:true,
+2193:true,
+2194:true,
+2195:true,
+2196:true,
+2197:true,
+2198:true,
+2199:true,
+2200:true,
+2201:true,
+2202:true,
+2203:true,
+2204:true,
+2205:true,
+2206:true,
+2207:true,
+2208:true,
+2209:true,
+2210:true,
+2211:true,
+2212:true,
+2213:true,
+2214:true,
+2215:true,
+2216:true,
+2217:true,
+2218:true,
+2219:true,
+2220:true,
+2221:true,
+2222:true,
+2223:true,
+2224:true,
+2225:true,
+2226:true,
+2227:true,
+2228:true,
+2229:true,
+2230:true,
+2231:true,
+2232:true,
+2233:true,
+2234:true,
+2235:true,
+2236:true,
+2237:true,
+2238:true,
+2239:true,
+2240:true,
+2241:true,
+2242:true,
+2243:true,
+2244:true,
+2245:true,
+2246:true,
+2247:true,
+2248:true,
+2249:true,
+2250:true,
+2251:true,
+2252:true,
+2253:true,
+2254:true,
+2255:true,
+2256:true,
+2257:true,
+2258:true,
+2259:true,
+2260:true,
+2261:true,
+2262:true,
+2263:true,
+2264:true,
+2265:true,
+2266:true,
+2267:true,
+2268:true,
+2269:true,
+2270:true,
+2271:true,
+2272:true,
+2273:true,
+2274:true,
+2275:true,
+2276:true,
+2277:true,
+2278:true,
+2279:true,
+2280:true,
+2281:true,
+2282:true,
+2283:true,
+2284:true,
+2285:true,
+2286:true,
+2287:true,
+2288:true,
+2289:true,
+2290:true,
+2291:true,
+2292:true,
+2293:true,
+2294:true,
+2295:true,
+2296:true,
+2297:true,
+2298:true,
+2299:true,
+2300:true,
+2301:true,
+2302:true,
+2303:true,
+2304:true,
+2305:true,
+2306:true,
+2307:true,
+2308:true,
+2309:true,
+2310:true,
+2311:true,
+2312:true,
+2313:true,
+2314:true,
+2315:true,
+2316:true,
+2317:true,
+2318:true,
+2319:true,
+2320:true,
+2321:true,
+2322:true,
+2323:true,
+2324:true,
+2325:true,
+2326:true,
+2327:true,
+2328:true,
+2329:true,
+2330:true,
+2331:true,
+2332:true,
+2333:true,
+2334:true,
+2335:true,
+2336:true,
+2337:true,
+2338:true,
+2339:true,
+2340:true,
+2341:true,
+2342:true,
+2343:true,
+2344:true,
+2345:true,
+2346:true,
+2347:true,
+2348:true,
+2349:true,
+2350:true,
+2351:true,
+2352:true,
+2353:true,
+2354:true,
+2355:true,
+2356:true,
+2357:true,
+2358:true,
+2359:true,
+2360:true,
+2361:true,
+2362:true,
+2363:true,
+2364:true,
+2365:true,
+2366:true,
+2367:true,
+2368:true,
+2369:true,
+2370:true,
+2371:true,
+2372:true,
+2373:true,
+2374:true,
+2375:true,
+2376:true,
+2377:true,
+2378:true,
+2379:true,
+2380:true,
+2381:true,
+2382:true,
+2383:true,
+2384:true,
+2385:true,
+2386:true,
+2387:true,
+2388:true,
+2389:true,
+2390:true,
+2391:true,
+2392:true,
+2393:true,
+2394:true,
+2395:true,
+2396:true,
+2397:true,
+2398:true,
+2399:true,
+2400:true,
+2401:true,
+2402:true,
+2403:true,
+2404:true,
+2405:true,
+2406:true,
+2407:true,
+2408:true,
+2409:true,
+2410:true,
+2411:true,
+2412:true,
+2413:true,
+2414:true,
+2415:true,
+2416:true,
+2417:true,
+2418:true,
+2065:false,
+2419:true,
+2420:true,
+2421:true,
+2422:true,
+2423:true,
+2424:true,
+2425:true,
+2426:true,
+2427:true,
+2428:true,
+2429:true,
+2430:true,
+2431:true,
+2432:true,
+2433:true,
+2425:false,
+2434:true,
+2435:true,
+2436:true,
+2437:true,
+2438:true,
+2439:true,
+2440:true,
+2441:true,
+2442:true,
+2443:true,
+2444:true,
+2445:true,
+2446:true,
+2447:true,
+2448:true,
+2449:true,
+2450:true,
+2451:true,
+2452:true,
+2453:true,
+2454:true,
+2455:true,
+2456:true,
+2457:true,
+2458:true,
+2459:true,
+2460:true,
+2461:true,
+2462:true,
+2463:true,
+2464:true,
+2465:true,
+2466:true,
+2467:true,
+2468:true,
+2469:true,
+2470:true,
+2471:true,
+2472:true,
+2473:true,
+2474:true,
+2475:true,
+2476:true,
+2477:true,
+2478:true,
+2479:true,
+2480:true,
+2481:true,
+2482:true,
+2483:true,
+2484:true,
+2485:true,
+2486:true,
+2487:true,
+2488:true,
+2489:true,
+2490:true,
+2491:true,
+2492:true,
+2493:true,
+2494:true,
+2495:true,
+2496:true,
+2497:true,
+2498:true,
+2499:true,
+2500:true,
+2501:true,
+2502:true,
+2503:true,
+2504:true,
+2505:true,
+2506:true,
+2507:true,
+2508:true,
+2509:true,
+2510:true,
+2511:true,
+2512:true,
+2513:true,
+2514:true,
+2515:true,
+2516:true,
+2517:true,
+2518:true,
+2519:true,
+2520:true,
+2521:true,
+2522:true,
+2523:true,
+2524:true,
+2525:true,
+2526:true,
+2527:true,
+2528:true,
+2529:true,
+2530:true,
+2531:true,
+2532:true,
+2533:true,
+2534:true,
+2535:true,
+2536:true,
+2537:true,
+2538:true,
+2539:true,
+2540:true,
+2541:true,
+2542:true,
+2543:true,
+2544:true,
+2545:true,
+2546:true,
+2547:true,
+2548:true,
+2549:true,
+2550:true,
+2551:true,
+2552:true,
+2553:true,
+2554:true,
+2555:true,
+2556:true,
+2557:true,
+2558:true,
+2559:true,
+2560:true,
+2561:true,
+2562:true,
+2563:true,
+2564:true,
+2565:true,
+2566:true,
+2567:true,
+2568:true,
+2569:true,
+2570:true,
+2571:true,
+2572:true,
+2573:true,
+2574:true,
+2575:true,
+2576:true,
+2577:true,
+2578:true,
+2579:true,
+2580:true,
+2581:true,
+2582:true,
+2583:true,
+2584:true,
+2585:true,
+2586:true,
+2587:true,
+2588:true,
+2589:true,
+2590:true,
+2591:true,
+2592:true,
+2593:true,
+2594:true,
+2595:true,
+2596:true,
+2597:true,
+2598:true,
+2599:true,
+2600:true,
+2601:true,
+2602:true,
+2603:true,
+2604:true,
+2605:true,
+2606:true,
+2607:true,
+2608:true,
+2609:true,
+2610:true,
+2611:true,
+2612:true,
+2613:true,
+2614:true,
+2615:true,
+2616:true,
+2617:true,
+2618:true,
+2619:true,
+2620:true,
+2621:true,
+2622:true,
+2623:true,
+2624:true,
+2625:true,
+2626:true,
+2627:true,
+2628:true,
+2629:true,
+2630:true,
+2631:true,
+2632:true,
+2633:true,
+2634:true,
+2635:true,
+2636:true,
+2637:true,
+2638:true,
+2639:true,
+2640:true,
+2641:true,
+2642:true,
+2643:true,
+2644:true,
+2645:true,
+2646:true,
+2647:true,
+2648:true,
+2649:true,
+2650:true,
+2651:true,
+2652:true,
+2653:true,
+2654:true,
+2655:true,
+2656:true,
+2657:true,
+2658:true,
+2659:true,
+2660:true,
+2661:true,
+2662:true,
+2663:true,
+2664:true,
+2665:true,
+2666:true,
+2667:true,
+2668:true,
+2669:true,
+2670:true,
+2671:true,
+2672:true,
+2673:true,
+2674:true,
+2675:true,
+2676:true,
+2677:true,
+2678:true,
+2679:true,
+2680:true,
+2681:true,
+2682:true,
+2683:true,
+2684:true,
+2685:true,
+2686:true,
+2687:true,
+2688:true,
+2689:true,
+2690:true,
+2691:true,
+2692:true,
+2693:true,
+2694:true,
+2695:true,
+2696:true,
+2697:true,
+2698:true,
+2699:true,
+2700:true,
+2701:true,
+2702:true,
+2703:true,
+2704:true,
+2705:true,
+2706:true,
+2707:true,
+2708:true,
+2709:true,
+2710:true,
+2711:true,
+2712:true,
+2713:true,
+2714:true,
+2715:true,
+2716:true,
+2717:true,
+2718:true,
+2719:true,
+2720:true,
+2721:true,
+2722:true,
+2723:true,
+2724:true,
+2725:true,
+2726:true,
+2727:true,
+2728:true,
+2729:true,
+2730:true,
+2731:true,
+2732:true,
+2733:true,
+2734:true,
+2735:true,
+2736:true,
+2737:true,
+2738:true,
+2739:true,
+2740:true,
+2741:true,
+2742:true,
+2743:true,
+2744:true,
+2745:true,
+2746:true,
+2747:true,
+2748:true,
+2749:true,
+2750:true,
+2751:true,
+2752:true,
+2753:true,
+2754:true,
+2755:true,
+2756:true,
+2757:true,
+2758:true,
+2759:true,
+2760:true,
+2761:true,
+2762:true,
+2763:true,
+2764:true,
+2765:true,
+2766:true,
+2767:true,
+2768:true,
+2769:true,
+2770:true,
+2771:true,
+2772:true,
+2773:true,
+2774:true,
+2775:true,
+2776:true,
+2777:true,
+2778:true,
+2779:true,
+2780:true,
+2781:true,
+2782:true,
+2783:true,
+2784:true,
+2785:true,
+2786:true,
+2787:true,
+2788:true,
+2789:true,
+2790:true,
+2791:true,
+2792:true,
+2793:true,
+2794:true,
+2795:true,
+2796:true,
+2797:true,
+2798:true,
+2799:true,
+2800:true,
+2801:true,
+2802:true,
+2803:true,
+2804:true,
+2805:true,
+2806:true,
+2807:true,
+2808:true,
+2809:true,
+2810:true,
+2811:true,
+2812:true,
+2813:true,
+2814:true,
+2815:true,
+2816:true,
+2817:true,
+2434:false,
+2818:true,
+2819:true,
+2820:true,
+2821:true,
+2822:true,
+2823:true,
+2820:false,
+2824:true,
+2825:true,
+2826:true,
+2827:true,
+2828:true,
+2829:true,
+2830:true,
+2831:true,
+2832:true,
+2833:true,
+2834:true,
+2835:true,
+2836:true,
+2837:true,
+2838:true,
+2839:true,
+2840:true,
+2841:true,
+2842:true,
+2843:true,
+2844:true,
+2845:true,
+2846:true,
+2847:true,
+2848:true,
+2849:true,
+2850:true,
+2851:true,
+2852:true,
+2853:true,
+2854:true,
+2855:true,
+2856:true,
+2857:true,
+2858:true,
+2859:true,
+2860:true,
+2861:true,
+2862:true,
+2863:true,
+2864:true,
+2865:true,
+2866:true,
+2867:true,
+2868:true,
+2869:true,
+2870:true,
+2871:true,
+2872:true,
+2873:true,
+2874:true,
+2875:true,
+2876:true,
+2877:true,
+2878:true,
+2879:true,
+2880:true,
+2881:true,
+2882:true,
+2883:true,
+2884:true,
+2885:true,
+2886:true,
+2887:true,
+2888:true,
+2889:true,
+2890:true,
+2891:true,
+2892:true,
+2893:true,
+2894:true,
+2895:true,
+2896:true,
+2897:true,
+2898:true,
+2899:true,
+2900:true,
+2901:true,
+2902:true,
+2903:true,
+2904:true,
+2905:true,
+2906:true,
+2907:true,
+2908:true,
+2909:true,
+2910:true,
+2911:true,
+2912:true,
+2913:true,
+2914:true,
+2915:true,
+2916:true,
+2917:true,
+2918:true,
+2919:true,
+2920:true,
+2921:true,
+2922:true,
+2923:true,
+2924:true,
+2925:true,
+2926:true,
+2927:true,
+2928:true,
+2929:true,
+2930:true,
+2931:true,
+2932:true,
+2933:true,
+2934:true,
+2935:true,
+2936:true,
+2937:true,
+2938:true,
+2939:true,
+2940:true,
+2941:true,
+2942:true,
+2943:true,
+2944:true,
+2945:true,
+2946:true,
+2947:true,
+2948:true,
+2949:true,
+2950:true,
+2951:true,
+2952:true,
+2953:true,
+2954:true,
+2955:true,
+2956:true,
+2957:true,
+2958:true,
+2959:true,
+2960:true,
+2961:true,
+2962:true,
+2963:true,
+2964:true,
+2965:true,
+2966:true,
+2967:true,
+2968:true,
+2969:true,
+2970:true,
+2971:true,
+2972:true,
+2973:true,
+2974:true,
+2975:true,
+2976:true,
+2977:true,
+2978:true,
+2979:true,
+2980:true,
+2981:true,
+2982:true,
+2983:true,
+2984:true,
+2985:true,
+2986:true,
+2987:true,
+2988:true,
+2989:true,
+2990:true,
+2991:true,
+2992:true,
+2993:true,
+2994:true,
+2995:true,
+2996:true,
+2997:true,
+2998:true,
+2999:true,
+3000:true,
+3001:true,
+3002:true,
+3003:true,
+3004:true,
+3005:true,
+3006:true,
+3007:true,
+3008:true,
+3009:true,
+3010:true,
+3011:true,
+3012:true,
+3013:true,
+3014:true,
+3015:true,
+3016:true,
+3017:true,
+3018:true,
+3019:true,
+3020:true,
+3021:true,
+3022:true,
+3023:true,
+3024:true,
+3025:true,
+3026:true,
+3027:true,
+3028:true,
+3029:true,
+3030:true,
+3031:true,
+3032:true,
+3033:true,
+3034:true,
+3035:true,
+3036:true,
+3037:true,
+3038:true,
+3039:true,
+3040:true,
+3041:true,
+3042:true,
+3043:true,
+3044:true,
+3045:true,
+3046:true,
+3047:true,
+3048:true,
+3049:true,
+3050:true,
+3051:true,
+3052:true,
+3053:true,
+3054:true,
+3055:true,
+3056:true,
+3057:true,
+3058:true,
+3059:true,
+3060:true,
+3061:true,
+3062:true,
+3063:true,
+3064:true,
+3065:true,
+3066:true,
+3067:true,
+3068:true,
+3069:true,
+3070:true,
+3070:false,
+3069:false,
+3068:false,
+3067:false,
+3066:false,
+3065:false,
+3064:false,
+3063:false,
+3062:false,
+3061:false,
+3060:false,
+3059:false,
+3058:false,
+3057:false,
+3056:false,
+3055:false,
+3054:false,
+3053:false,
+3052:false,
+3051:false,
+3050:false,
+3049:false,
+3048:false,
+3047:false,
+3046:false,
+3045:false,
+3044:false,
+3043:false,
+3042:false,
+3041:false,
+3040:false,
+3039:false,
+3038:false,
+3037:false,
+3036:false,
+3035:false,
+3034:false,
+3033:false,
+3032:false,
+3031:false,
+3030:false,
+3029:false,
+3028:false,
+3027:false,
+3026:false,
+3025:false,
+3024:false,
+3023:false,
+3022:false,
+3021:false,
+3020:false,
+3019:false,
+3018:false,
+3017:false,
+3016:false,
+3015:false,
+3014:false,
+3013:false,
+3012:false,
+3011:false,
+3010:false,
+3009:false,
+3008:false,
+3007:false,
+3006:false,
+3005:false,
+3004:false,
+3003:false,
+3002:false,
+3001:false,
+3000:false,
+2999:false,
+2998:false,
+2997:false,
+2996:false,
+2995:false,
+2994:false,
+2993:false,
+2992:false,
+2991:false,
+2990:false,
+2989:false,
+2988:false,
+2987:false,
+2986:false,
+2985:false,
+2984:false,
+2983:false,
+2982:false,
+2981:false,
+2980:false,
+2979:false,
+2978:false,
+2977:false,
+2976:false,
+2975:false,
+2974:false,
+2973:false,
+2972:false,
+2971:false,
+2970:false,
+2969:false,
+2968:false,
+2967:false,
+2966:false,
+2965:false,
+2964:false,
+2963:false,
+2962:false,
+2961:false,
+2960:false,
+2959:false,
+2958:false,
+2957:false,
+2956:false,
+2955:false,
+2954:false,
+2953:false,
+2952:false,
+2951:false,
+2950:false,
+2949:false,
+2948:false,
+2947:false,
+2946:false,
+2945:false,
+2944:false,
+2943:false,
+2942:false,
+2941:false,
+2940:false,
+2939:false,
+2938:false,
+2937:false,
+2936:false,
+2935:false,
+2934:false,
+2933:false,
+2932:false,
+2931:false,
+2930:false,
+2929:false,
+2928:false,
+2927:false,
+2926:false,
+2925:false,
+2924:false,
+2824:false,
+2923:false,
+2922:false,
+3071:true,
+2921:false,
+2920:false,
+2919:false,
+2918:false,
+2917:false,
+2916:false,
+2915:false,
+2914:false,
+2913:false,
+2912:false,
+2911:false,
+2910:false,
+2909:false,
+2908:false,
+2907:false,
+3071:false,
+3072:true,
+2906:false,
+3073:true,
+3073:false,
+3074:true,
+3074:false,
+3075:true,
+3075:false,
+3076:true,
+3076:false,
+3077:true,
+3077:false,
+3078:true,
+3078:false,
+3079:true,
+3079:false,
+3080:true,
+3072:false,
+3081:true,
+3081:false,
+3082:true,
+3083:true,
+3083:false,
+3080:false,
+3084:true,
+3084:false,
+3085:true,
+3085:false,
+3086:true,
+3086:false,
+3087:true,
+3087:false,
+3082:false,
+3088:true,
+3088:false,
+3089:true,
+3090:true,
+3090:false,
+3091:true,
+3091:false,
+3092:true,
+3092:false,
+3089:false,
+3093:true"""
+
+ val operations = data.replace("\n", "").split(",").filter {
+ it.isNotEmpty()
+ }.map {
+ it.split(":").let {
+ it[0].toInt() to it[1].toBoolean()
+ }
+ }
+ operations.fold(FrameIdSet.EMPTY) { prev, (value, op) ->
+ assertTrue(prev.get(value) != op, "Error on bit $value, expected ${!op}, received $op")
+ val result = if (op) prev.set(value) else prev.clear(value)
+ result
+ }
+ }
}
private fun FrameIdSet.shouldBe(index: Int, value: Boolean): FrameIdSet {
diff --git a/core/core-ktx/api/api_lint.ignore b/core/core-ktx/api/api_lint.ignore
index f510d88..de50ebe 100644
--- a/core/core-ktx/api/api_lint.ignore
+++ b/core/core-ktx/api/api_lint.ignore
@@ -31,6 +31,10 @@
Method ViewGroupKt.iterator appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+NoByteOrShort: androidx.core.util.HalfKt#toHalf(short) parameter #0:
+ Should avoid odd sized primitives; use `int` instead of `short` in parameter $this$toHalf in androidx.core.util.HalfKt.toHalf(short $this$toHalf)
+
+
PairedRegistration: androidx.core.animation.AnimatorKt#addListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>):
Found addListener but not removeListener in androidx.core.animation.AnimatorKt
PairedRegistration: androidx.core.animation.AnimatorKt#addPauseListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>):
diff --git a/exifinterface/exifinterface/api/api_lint.ignore b/exifinterface/exifinterface/api/api_lint.ignore
index 0e7bfcf..d22dffd 100644
--- a/exifinterface/exifinterface/api/api_lint.ignore
+++ b/exifinterface/exifinterface/api/api_lint.ignore
@@ -11,5 +11,219 @@
Missing nullability on parameter `location` in method `setGpsInfo`
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#ALTITUDE_ABOVE_SEA_LEVEL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.ALTITUDE_ABOVE_SEA_LEVEL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#ALTITUDE_BELOW_SEA_LEVEL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.ALTITUDE_BELOW_SEA_LEVEL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#CONTRAST_HARD:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.CONTRAST_HARD
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#CONTRAST_NORMAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.CONTRAST_NORMAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#CONTRAST_SOFT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.CONTRAST_SOFT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_MODE_AUTO:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_MODE_AUTO
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_MODE_AUTO_BRACKET:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_MODE_AUTO_BRACKET
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_MODE_MANUAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_MODE_MANUAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_ACTION:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_ACTION
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_APERTURE_PRIORITY:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_APERTURE_PRIORITY
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_CREATIVE:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_CREATIVE
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_LANDSCAPE_MODE:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_LANDSCAPE_MODE
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_MANUAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_MANUAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_NORMAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_NORMAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_NOT_DEFINED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_NOT_DEFINED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_PORTRAIT_MODE:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_PORTRAIT_MODE
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#EXPOSURE_PROGRAM_SHUTTER_PRIORITY:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.EXPOSURE_PROGRAM_SHUTTER_PRIORITY
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FILE_SOURCE_DSC:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FILE_SOURCE_DSC
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FILE_SOURCE_OTHER:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FILE_SOURCE_OTHER
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FILE_SOURCE_REFLEX_SCANNER:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FILE_SOURCE_REFLEX_SCANNER
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FILE_SOURCE_TRANSPARENT_SCANNER:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FILE_SOURCE_TRANSPARENT_SCANNER
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FLAG_FLASH_FIRED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FLAG_FLASH_FIRED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FLAG_FLASH_MODE_AUTO:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FLAG_FLASH_MODE_AUTO
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FLAG_FLASH_MODE_COMPULSORY_FIRING:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FLAG_FLASH_MODE_COMPULSORY_FIRING
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FLAG_FLASH_MODE_COMPULSORY_SUPPRESSION:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FLAG_FLASH_MODE_COMPULSORY_SUPPRESSION
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FLAG_FLASH_NO_FLASH_FUNCTION:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FLAG_FLASH_NO_FLASH_FUNCTION
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FLAG_FLASH_RED_EYE_SUPPORTED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FLAG_FLASH_RED_EYE_SUPPORTED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FLAG_FLASH_RETURN_LIGHT_DETECTED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FLAG_FLASH_RETURN_LIGHT_DETECTED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FLAG_FLASH_RETURN_LIGHT_NOT_DETECTED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FLAG_FLASH_RETURN_LIGHT_NOT_DETECTED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FORMAT_CHUNKY:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FORMAT_CHUNKY
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#FORMAT_PLANAR:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.FORMAT_PLANAR
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#GAIN_CONTROL_HIGH_GAIN_DOWN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.GAIN_CONTROL_HIGH_GAIN_DOWN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#GAIN_CONTROL_HIGH_GAIN_UP:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.GAIN_CONTROL_HIGH_GAIN_UP
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#GAIN_CONTROL_LOW_GAIN_DOWN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.GAIN_CONTROL_LOW_GAIN_DOWN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#GAIN_CONTROL_LOW_GAIN_UP:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.GAIN_CONTROL_LOW_GAIN_UP
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#GAIN_CONTROL_NONE:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.GAIN_CONTROL_NONE
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#GPS_MEASUREMENT_DIFFERENTIAL_CORRECTED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.GPS_MEASUREMENT_DIFFERENTIAL_CORRECTED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#GPS_MEASUREMENT_NO_DIFFERENTIAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.GPS_MEASUREMENT_NO_DIFFERENTIAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_CLOUDY_WEATHER:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_CLOUDY_WEATHER
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_COOL_WHITE_FLUORESCENT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_COOL_WHITE_FLUORESCENT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_D50:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_D50
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_D55:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_D55
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_D65:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_D65
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_D75:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_D75
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_DAYLIGHT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_DAYLIGHT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_DAYLIGHT_FLUORESCENT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_DAYLIGHT_FLUORESCENT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_DAY_WHITE_FLUORESCENT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_DAY_WHITE_FLUORESCENT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_FINE_WEATHER:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_FINE_WEATHER
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_FLASH:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_FLASH
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_FLUORESCENT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_FLUORESCENT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_ISO_STUDIO_TUNGSTEN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_ISO_STUDIO_TUNGSTEN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_OTHER:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_OTHER
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_SHADE:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_SHADE
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_STANDARD_LIGHT_A:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_STANDARD_LIGHT_A
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_STANDARD_LIGHT_B:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_STANDARD_LIGHT_B
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_STANDARD_LIGHT_C:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_STANDARD_LIGHT_C
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_TUNGSTEN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_TUNGSTEN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_UNKNOWN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_UNKNOWN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_WARM_WHITE_FLUORESCENT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_WARM_WHITE_FLUORESCENT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#LIGHT_SOURCE_WHITE_FLUORESCENT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.LIGHT_SOURCE_WHITE_FLUORESCENT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#METERING_MODE_AVERAGE:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.METERING_MODE_AVERAGE
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#METERING_MODE_CENTER_WEIGHT_AVERAGE:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.METERING_MODE_CENTER_WEIGHT_AVERAGE
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#METERING_MODE_MULTI_SPOT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.METERING_MODE_MULTI_SPOT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#METERING_MODE_OTHER:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.METERING_MODE_OTHER
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#METERING_MODE_PARTIAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.METERING_MODE_PARTIAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#METERING_MODE_PATTERN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.METERING_MODE_PATTERN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#METERING_MODE_SPOT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.METERING_MODE_SPOT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#METERING_MODE_UNKNOWN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.METERING_MODE_UNKNOWN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#RENDERED_PROCESS_CUSTOM:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.RENDERED_PROCESS_CUSTOM
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#RENDERED_PROCESS_NORMAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.RENDERED_PROCESS_NORMAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#RESOLUTION_UNIT_CENTIMETERS:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.RESOLUTION_UNIT_CENTIMETERS
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#RESOLUTION_UNIT_INCHES:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.RESOLUTION_UNIT_INCHES
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SATURATION_HIGH:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SATURATION_HIGH
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SATURATION_LOW:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SATURATION_LOW
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SATURATION_NORMAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SATURATION_NORMAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SCENE_CAPTURE_TYPE_LANDSCAPE:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SCENE_CAPTURE_TYPE_LANDSCAPE
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SCENE_CAPTURE_TYPE_NIGHT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SCENE_CAPTURE_TYPE_NIGHT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SCENE_CAPTURE_TYPE_PORTRAIT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SCENE_CAPTURE_TYPE_PORTRAIT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SCENE_CAPTURE_TYPE_STANDARD:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SCENE_CAPTURE_TYPE_STANDARD
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SCENE_TYPE_DIRECTLY_PHOTOGRAPHED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SCENE_TYPE_DIRECTLY_PHOTOGRAPHED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSITIVITY_TYPE_ISO_SPEED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_ISO_SPEED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSITIVITY_TYPE_REI:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_REI
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSITIVITY_TYPE_REI_AND_ISO:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_REI_AND_ISO
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSITIVITY_TYPE_SOS:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_SOS
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSITIVITY_TYPE_SOS_AND_ISO:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_SOS_AND_ISO
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSITIVITY_TYPE_SOS_AND_REI:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_SOS_AND_REI
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSITIVITY_TYPE_SOS_AND_REI_AND_ISO:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_SOS_AND_REI_AND_ISO
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSITIVITY_TYPE_UNKNOWN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSITIVITY_TYPE_UNKNOWN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSOR_TYPE_COLOR_SEQUENTIAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSOR_TYPE_COLOR_SEQUENTIAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSOR_TYPE_COLOR_SEQUENTIAL_LINEAR:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSOR_TYPE_COLOR_SEQUENTIAL_LINEAR
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSOR_TYPE_NOT_DEFINED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSOR_TYPE_NOT_DEFINED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSOR_TYPE_ONE_CHIP:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSOR_TYPE_ONE_CHIP
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSOR_TYPE_THREE_CHIP:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSOR_TYPE_THREE_CHIP
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSOR_TYPE_TRILINEAR:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSOR_TYPE_TRILINEAR
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SENSOR_TYPE_TWO_CHIP:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SENSOR_TYPE_TWO_CHIP
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SHARPNESS_HARD:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SHARPNESS_HARD
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SHARPNESS_NORMAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SHARPNESS_NORMAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SHARPNESS_SOFT:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SHARPNESS_SOFT
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SUBJECT_DISTANCE_RANGE_CLOSE_VIEW:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SUBJECT_DISTANCE_RANGE_CLOSE_VIEW
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SUBJECT_DISTANCE_RANGE_DISTANT_VIEW:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SUBJECT_DISTANCE_RANGE_DISTANT_VIEW
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SUBJECT_DISTANCE_RANGE_MACRO:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SUBJECT_DISTANCE_RANGE_MACRO
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#SUBJECT_DISTANCE_RANGE_UNKNOWN:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.SUBJECT_DISTANCE_RANGE_UNKNOWN
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#WHITE_BALANCE_AUTO:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.WHITE_BALANCE_AUTO
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#WHITE_BALANCE_MANUAL:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.WHITE_BALANCE_MANUAL
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#Y_CB_CR_POSITIONING_CENTERED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.Y_CB_CR_POSITIONING_CENTERED
+NoByteOrShort: androidx.exifinterface.media.ExifInterface#Y_CB_CR_POSITIONING_CO_SITED:
+ Should avoid odd sized primitives; use `int` instead of `short` in field androidx.exifinterface.media.ExifInterface.Y_CB_CR_POSITIONING_CO_SITED
+
+
UseParcelFileDescriptor: androidx.exifinterface.media.ExifInterface#ExifInterface(java.io.FileDescriptor) parameter #0:
Must use ParcelFileDescriptor instead of FileDescriptor in parameter fileDescriptor in androidx.exifinterface.media.ExifInterface(java.io.FileDescriptor fileDescriptor)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/DefaultSpecialEffectsControllerTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/DefaultSpecialEffectsControllerTest.kt
new file mode 100644
index 0000000..a62f7bb
--- /dev/null
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/DefaultSpecialEffectsControllerTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fragment.app
+
+import android.view.ViewGroup
+import androidx.fragment.app.test.EmptyFragmentTestActivity
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class DefaultSpecialEffectsControllerTest {
+ @Test
+ fun fragmentManagerGetSetSpecialEffectsController() {
+ with(ActivityScenario.launch(EmptyFragmentTestActivity::class.java)) {
+ val fm = withActivity { supportFragmentManager }
+ val factory = SpecialEffectsControllerFactory {
+ mock(SpecialEffectsController::class.java)
+ }
+ fm.specialEffectsControllerFactory = factory
+ assertThat(fm.specialEffectsControllerFactory)
+ .isEqualTo(factory)
+ }
+ }
+
+ /**
+ * Ensure that FragmentManager returns [DefaultSpecialEffectsController] implementations
+ * from its default factory
+ */
+ @Test
+ fun fragmentManagerDefaultFactory() {
+ with(ActivityScenario.launch(EmptyFragmentTestActivity::class.java)) {
+ val container = withActivity { findViewById<ViewGroup>(android.R.id.content) }
+ val fm = withActivity { supportFragmentManager }
+ val factory = fm.specialEffectsControllerFactory
+ val controller = factory.createController(container)
+ assertThat(controller)
+ .isInstanceOf(DefaultSpecialEffectsController::class.java)
+ }
+ }
+}
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/SpecialEffectsControllerTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/SpecialEffectsControllerTest.kt
new file mode 100644
index 0000000..58476a1
--- /dev/null
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/SpecialEffectsControllerTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fragment.app
+
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class SpecialEffectsControllerTest {
+ @Test
+ fun factoryCreateController() {
+ val map = mutableMapOf<ViewGroup, TestSpecialEffectsController>()
+ val factory = SpecialEffectsControllerFactory { container ->
+ TestSpecialEffectsController(container).also {
+ map[container] = it
+ }
+ }
+ val container = FrameLayout(InstrumentationRegistry.getInstrumentation().context)
+ val controller = factory.createController(container)
+ assertThat(controller)
+ .isEqualTo(map[container])
+ assertThat(controller.container)
+ .isEqualTo(container)
+
+ // Ensure that a new container gets a new controller
+ val secondContainer = FrameLayout(InstrumentationRegistry.getInstrumentation().context)
+ val secondController = factory.createController(secondContainer)
+ assertThat(secondController)
+ .isEqualTo(map[secondContainer])
+ assertThat(secondController)
+ .isNotEqualTo(controller)
+ }
+
+ @Test
+ fun getOrCreateController() {
+ var count = 0
+ val map = mutableMapOf<ViewGroup, TestSpecialEffectsController>()
+ val factory = SpecialEffectsControllerFactory { container ->
+ count++
+ TestSpecialEffectsController(container).also {
+ map[container] = it
+ }
+ }
+ val container = FrameLayout(InstrumentationRegistry.getInstrumentation().context)
+ val controller = SpecialEffectsController.getOrCreateController(container, factory)
+ assertThat(controller)
+ .isEqualTo(map[container])
+ assertThat(controller.container)
+ .isEqualTo(container)
+ assertThat(count)
+ .isEqualTo(1)
+
+ // Recreating the controller shouldn't cause the count to increase
+ val recreatedController = SpecialEffectsController.getOrCreateController(
+ container, factory)
+ assertThat(recreatedController)
+ .isEqualTo(controller)
+ assertThat(recreatedController.container)
+ .isEqualTo(container)
+ assertThat(count)
+ .isEqualTo(1)
+
+ // But creating a controller for a different view returns a new instance
+ val secondContainer = FrameLayout(InstrumentationRegistry.getInstrumentation().context)
+ val secondController = SpecialEffectsController.getOrCreateController(
+ secondContainer, factory)
+ assertThat(secondController)
+ .isEqualTo(map[secondContainer])
+ assertThat(secondController.container)
+ .isEqualTo(secondContainer)
+ assertThat(count)
+ .isEqualTo(2)
+ }
+}
+
+internal class TestSpecialEffectsController(
+ container: ViewGroup
+) : SpecialEffectsController(container)
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
new file mode 100644
index 0000000..6f232b3
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fragment.app;
+
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+
+/**
+ * A SpecialEffectsController that hooks into the existing Fragment APIs to run
+ * animations and transitions.
+ */
+class DefaultSpecialEffectsController extends SpecialEffectsController {
+ DefaultSpecialEffectsController(@NonNull ViewGroup container) {
+ super(container);
+ }
+}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 6c88c68..4a6c559 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -389,6 +389,15 @@
return getHost().instantiate(getHost().getContext(), className, null);
}
};
+ private SpecialEffectsControllerFactory mSpecialEffectsControllerFactory = null;
+ private SpecialEffectsControllerFactory mDefaultSpecialEffectsControllerFactory =
+ new SpecialEffectsControllerFactory() {
+ @NonNull
+ @Override
+ public SpecialEffectsController createController(@NonNull ViewGroup container) {
+ return new DefaultSpecialEffectsController(container);
+ }
+ };
private boolean mNeedMenuInvalidate;
private boolean mStateSaved;
@@ -2809,6 +2818,39 @@
return mHostFragmentFactory;
}
+ /**
+ * Set a {@link SpecialEffectsControllerFactory} for this FragmentManager that will be used
+ * to create new SpecialEffectsController instances from this point onward.
+ *
+ * @param specialEffectsControllerFactory the factory to use to create new
+ * SpecialEffectsController instances.
+ */
+ void setSpecialEffectsControllerFactory(
+ @NonNull SpecialEffectsControllerFactory specialEffectsControllerFactory) {
+ mSpecialEffectsControllerFactory = specialEffectsControllerFactory;
+ }
+
+ /**
+ * Gets the current {@link SpecialEffectsControllerFactory} used to instantiate new
+ * SpecialEffectsController instances.
+ *
+ * @return the current SpecialEffectsControllerFactory
+ */
+ @NonNull
+ SpecialEffectsControllerFactory getSpecialEffectsControllerFactory() {
+ if (mSpecialEffectsControllerFactory != null) {
+ return mSpecialEffectsControllerFactory;
+ }
+ if (mParent != null) {
+ // This can't call setSpecialEffectsControllerFactory since we need to
+ // compute this each time getSpecialEffectsControllerFactory() is called
+ // so that if the parent's SpecialEffectsControllerFactory changes, we
+ // pick the change up here.
+ return mParent.mFragmentManager.getSpecialEffectsControllerFactory();
+ }
+ return mDefaultSpecialEffectsControllerFactory;
+ }
+
@NonNull
FragmentLifecycleCallbacksDispatcher getLifecycleCallbacksDispatcher() {
return mLifecycleCallbacksDispatcher;
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.java b/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.java
new file mode 100644
index 0000000..9a42ca4
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fragment.app;
+
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.R;
+
+/**
+ * Controller for all "special effects" (such as Animation, Animator, framework Transition, and
+ * AndroidX Transition) that can be applied to a Fragment as part of the addition or removal
+ * of that Fragment from its container.
+ * <p>
+ * Each SpecialEffectsController is responsible for a single {@link ViewGroup} container.
+ */
+abstract class SpecialEffectsController {
+
+ /**
+ * Get the {@link SpecialEffectsController} for a given container if it already exists
+ * or create it using the given {@link SpecialEffectsControllerFactory} if it does not.
+ *
+ * @param container ViewGroup to find the associated SpecialEffectsController for.
+ * @param factory The factory to use to create a new SpecialEffectsController if one does
+ * not already exist for this container.
+ * @return a SpecialEffectsController for the given container
+ */
+ @NonNull
+ static SpecialEffectsController getOrCreateController(
+ @NonNull ViewGroup container,
+ @NonNull SpecialEffectsControllerFactory factory) {
+ Object controller = container.getTag(R.id.special_effects_controller_view_tag);
+ if (controller instanceof SpecialEffectsController) {
+ return (SpecialEffectsController) controller;
+ }
+ // Else, create a new SpecialEffectsController
+ SpecialEffectsController newController = factory.createController(container);
+ container.setTag(R.id.special_effects_controller_view_tag, newController);
+ return newController;
+ }
+
+ private final ViewGroup mContainer;
+
+ SpecialEffectsController(@NonNull ViewGroup container) {
+ mContainer = container;
+ }
+
+ @NonNull
+ public ViewGroup getContainer() {
+ return mContainer;
+ }
+}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsControllerFactory.java b/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsControllerFactory.java
new file mode 100644
index 0000000..f6211d6
--- /dev/null
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsControllerFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fragment.app;
+
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Factory for constructing instances of {@link SpecialEffectsController} on demand.
+ */
+interface SpecialEffectsControllerFactory {
+ /**
+ * Create a new {@link SpecialEffectsController} for the given container.
+ *
+ * @param container The ViewGroup the created SpecialEffectsController should control.
+ * @return a new instance of SpecialEffectsController
+ */
+ @NonNull
+ SpecialEffectsController createController(@NonNull ViewGroup container);
+}
diff --git a/fragment/fragment/src/main/res/values/ids.xml b/fragment/fragment/src/main/res/values/ids.xml
index 7dad6a7..9b77156 100644
--- a/fragment/fragment/src/main/res/values/ids.xml
+++ b/fragment/fragment/src/main/res/values/ids.xml
@@ -17,5 +17,6 @@
<resources>
<item type="id" name="fragment_container_view_tag" />
+ <item type="id" name="special_effects_controller_view_tag" />
<item type="id" name="visible_removing_fragment_view_tag" />
</resources>
\ No newline at end of file
diff --git a/jetifier/jetifier/migration.config b/jetifier/jetifier/migration.config
index f7f1416..ec4ed79 100644
--- a/jetifier/jetifier/migration.config
+++ b/jetifier/jetifier/migration.config
@@ -421,10 +421,22 @@
"to": "ignore"
},
{
+ "from": "androidx/fragment/app/DefaultSpecialEffectsController(.*)",
+ "to": "android/support/v4/app/DefaultSpecialEffectsController{0}"
+ },
+ {
"from": "androidx/fragment/app/LogWriter(.*)",
"to": "android/support/v4/app/LogWriter{0}"
},
{
+ "from": "androidx/fragment/app/SpecialEffectsControllerFactory(.*)",
+ "to": "android/support/v4/app/SpecialEffectsControllerFactory{0}"
+ },
+ {
+ "from": "androidx/fragment/app/SpecialEffectsController(.*)",
+ "to": "android/support/v4/app/SpecialEffectsController{0}"
+ },
+ {
"from": "androidx/fragment/app/(.+)Kt(.*)",
"to": "ignore"
},
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
index 82c658e..52d934a 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
@@ -43,7 +43,8 @@
"androidx.activity_activity-ktx.version",
"androidx.lifecycle_lifecycle-runtime-ktx.version",
"androidx.dynamicanimation_dynamicanimation-ktx.version",
- "androidx.annotation_annotation-experimental.version"
+ "androidx.annotation_annotation-experimental.version",
+ "androidx.navigation_navigation-testing.version"
)
}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
index f074256..a4ac0ca 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
@@ -152,7 +152,9 @@
public void testPlayVideoOnTextureView() throws Throwable {
final VideoView.OnViewTypeChangedListener mockViewTypeListener =
mock(VideoView.OnViewTypeChangedListener.class);
-
+ if (setViewTypeMayCrash()) {
+ return;
+ }
DefaultPlayerCallback callback = new DefaultPlayerCallback();
PlayerWrapper playerWrapper = createPlayerWrapper(callback, mMediaItem, null);
setPlayerWrapper(playerWrapper);
@@ -181,6 +183,9 @@
public void testPlayVideoWithVisibilityChange() throws Throwable {
final VideoView.OnViewTypeChangedListener mockViewTypeListener =
mock(VideoView.OnViewTypeChangedListener.class);
+ if (setViewTypeMayCrash()) {
+ return;
+ }
DefaultPlayerCallback callback = new DefaultPlayerCallback();
PlayerWrapper playerWrapper = createPlayerWrapper(callback, mMediaItem, null);
@@ -240,6 +245,9 @@
@Test
public void testSetViewType() throws Throwable {
+ if (setViewTypeMayCrash()) {
+ return;
+ }
final VideoView.OnViewTypeChangedListener mockViewTypeListener =
mock(VideoView.OnViewTypeChangedListener.class);
@@ -415,6 +423,15 @@
}
}
+ private boolean setViewTypeMayCrash() {
+ // TODO(b/143496920): Remove this method which is a guard to avoid crash.
+ // Need to skip the tests, which call VV#setViewType(), on the emulator with API 26.
+ if (Build.DEVICE.startsWith("generic_") && Build.VERSION.SDK_INT == 26) {
+ return true;
+ }
+ return false;
+ }
+
private Bitmap getVideoScreenshot() {
Bitmap bitmap = Bitmap.createBitmap(mVideoView.getWidth(),
mVideoView.getHeight(), Bitmap.Config.RGB_565);
diff --git a/navigation/navigation-testing/build.gradle b/navigation/navigation-testing/build.gradle
index 7345623..547d928 100644
--- a/navigation/navigation-testing/build.gradle
+++ b/navigation/navigation-testing/build.gradle
@@ -47,7 +47,7 @@
androidx {
name = "Android Navigation Testing"
- publish = Publish.NONE
+ publish = Publish.SNAPSHOT_AND_RELEASE
mavenVersion = LibraryVersions.NAVIGATION
mavenGroup = LibraryGroups.NAVIGATION
inceptionYear = "2017"
diff --git a/paging/common/api/restricted_3.0.0-alpha01.txt b/paging/common/api/restricted_3.0.0-alpha01.txt
index c0968a0..a435181 100644
--- a/paging/common/api/restricted_3.0.0-alpha01.txt
+++ b/paging/common/api/restricted_3.0.0-alpha01.txt
@@ -72,11 +72,6 @@
field public final int requestedLoadSize;
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class LegacyPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
- ctor public LegacyPagingSource(internal androidx.paging.DataSource<Key,Value> dataSource, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
- method public suspend Object load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
- }
-
public abstract sealed class LoadState {
}
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index c0968a0..a435181 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -72,11 +72,6 @@
field public final int requestedLoadSize;
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class LegacyPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
- ctor public LegacyPagingSource(internal androidx.paging.DataSource<Key,Value> dataSource, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
- method public suspend Object load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
- }
-
public abstract sealed class LoadState {
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt b/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
index 1ee8a23..61706529 100644
--- a/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
@@ -198,19 +198,15 @@
private val loadStates = mutableMapOf<LoadType, LoadState>()
fun add(event: PageEvent<T>) {
when (event) {
- is PageEvent.Insert<T> -> {
- handleInsert(event)
- }
- is PageEvent.StateUpdate<T> -> {
- handleStatusUpdate(event)
- }
- is PageEvent.Drop<T> -> {
- handlePageDrop(event)
- }
+ is PageEvent.Insert<T> -> handleInsert(event)
+ is PageEvent.Drop<T> -> handlePageDrop(event)
+ is PageEvent.LoadStateUpdate<T> -> handleLoadStateUpdate(event)
}
}
private fun handlePageDrop(event: PageEvent.Drop<T>) {
+ loadStates[event.loadType] = LoadState.Idle
+
when (event.loadType) {
LoadType.START -> {
placeholdersStart = event.placeholdersRemaining
@@ -228,10 +224,6 @@
}
}
- private fun handleStatusUpdate(event: PageEvent.StateUpdate<T>) {
- loadStates[event.loadType] = event.loadState
- }
-
private fun handleInsert(event: PageEvent.Insert<T>) {
event.loadStates.entries.forEach {
loadStates[it.key] = it.value
@@ -256,18 +248,12 @@
}
}
+ private fun handleLoadStateUpdate(event: PageEvent.LoadStateUpdate<T>) {
+ loadStates[event.loadType] = event.loadState
+ }
+
fun getAsEvents(): List<PageEvent<T>> {
val events = mutableListOf<PageEvent<T>>()
- loadStates.forEach { entry ->
- if (entry.value != LoadState.Idle) {
- events.add(
- PageEvent.StateUpdate(
- loadType = entry.key,
- loadState = entry.value
- )
- )
- }
- }
if (pages.isNotEmpty()) {
events.add(
PageEvent.Insert.Refresh(
@@ -277,7 +263,14 @@
loadStates = loadStates.toMap() // copy
)
)
+ } else {
+ loadStates.forEach { entry ->
+ if (entry.value == LoadState.Loading || entry.value is LoadState.Error) {
+ events.add(PageEvent.LoadStateUpdate(entry.key, entry.value))
+ }
+ }
}
+
return events
}
}
\ No newline at end of file
diff --git a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
index 6822e14..af0cd01 100644
--- a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
@@ -16,7 +16,6 @@
package androidx.paging
-import androidx.annotation.RestrictTo
import androidx.paging.DataSource.KeyType.ITEM_KEYED
import androidx.paging.DataSource.KeyType.PAGE_KEYED
import androidx.paging.DataSource.KeyType.POSITIONAL
@@ -26,11 +25,8 @@
/**
* A wrapper around [DataSource] which adapts it to the [PagingSource] API.
- *
- * @hide
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class LegacyPagingSource<Key : Any, Value : Any>(
+internal class LegacyPagingSource<Key : Any, Value : Any>(
internal val dataSource: DataSource<Key, Value>,
private val fetchDispatcher: CoroutineDispatcher = DirectDispatcher
) : PagingSource<Key, Value>() {
diff --git a/paging/common/src/main/kotlin/androidx/paging/LoadType.kt b/paging/common/src/main/kotlin/androidx/paging/LoadType.kt
index 9b722a5..dae6bca 100644
--- a/paging/common/src/main/kotlin/androidx/paging/LoadType.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/LoadType.kt
@@ -17,28 +17,28 @@
package androidx.paging
/**
- * Type of load a [PagedList] can trigger a [PagingSource] to perform.
+ * Type of load a [PagingData] can trigger a [PagingSource] to perform.
*
* [LoadState] of any [LoadType] may be observed for UI purposes by registering a listener via
- * [androidx.paging.PagedListAdapter.addLoadStateListener] or
- * [androidx.paging.AsyncPagedListDiffer.addLoadStateListener].
+ * [androidx.paging.PagingDataAdapter.addLoadStateListener] or
+ * [androidx.paging.AsyncPagingDataDiffer.addLoadStateListener].
*
* @see LoadState
*/
enum class LoadType {
/**
- * [PagedList] content being refreshed, which can also be a result of [PagingSource]
+ * [PagingData] content being refreshed, which can be a result of [PagingSource]
* invalidation, refresh that may contain content updates, or the initial load.
*/
REFRESH,
/**
- * Load at the start of the [PagedList].
+ * Load at the start of a [PagingData].
*/
START,
/**
- * Load at the end of the [PagedList].
+ * Load at the end of a [PagingData].
*/
END
}
\ No newline at end of file
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt b/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
index 7ba91cb..3bb6c27 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
@@ -188,22 +188,19 @@
// no empty pages encountered
this
} else {
- Drop(
- loadType,
- newCount,
- placeholdersRemaining
- )
+ Drop(loadType, newCount, placeholdersRemaining)
}
}
}
- data class StateUpdate<T : Any>(
+ data class LoadStateUpdate<T : Any>(
val loadType: LoadType,
val loadState: LoadState
) : PageEvent<T>() {
init {
- require(loadType != REFRESH || loadState != LoadState.Done) {
- "Refresh state may not be Done"
+ require(loadState == LoadState.Loading || loadState is LoadState.Error) {
+ "LoadStateUpdates can only be used for Loading or Error. To update loadState to " +
+ "Idle or Done, use Insert / Drop events."
}
}
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt b/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
index 73db604..17a1131 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
@@ -123,7 +123,7 @@
when (pageEvent) {
is PageEvent.Insert -> insertPage(pageEvent, callback)
is PageEvent.Drop -> dropPages(pageEvent, callback)
- is PageEvent.StateUpdate -> {
+ is PageEvent.LoadStateUpdate -> {
callback.onStateUpdate(pageEvent.loadType, pageEvent.loadState)
}
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/Pager.kt b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
index 28b23b0..173aa6c 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Pager.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
@@ -150,7 +150,7 @@
if (state.loadStates[START] == Done) {
return@transformLatest
} else if (state.failedHintsByLoadType[START] == null) {
- state.updateLoadState(START, Idle)
+ state.loadStates[START] = Idle
}
}
@@ -180,7 +180,7 @@
if (state.loadStates[END] == Done) {
return@transformLatest
} else if (state.failedHintsByLoadType[END] == null) {
- state.updateLoadState(END, Idle)
+ state.loadStates[END] = Idle
}
}
@@ -210,7 +210,7 @@
)
private suspend fun doInitialLoad(state: PagerState<Key, Value>) {
- stateLock.withLock { state.updateLoadState(REFRESH, Loading) }
+ stateLock.withLock { state.setLoading(REFRESH) }
val result = pagingSource.load(REFRESH, initialKey)
stateLock.withLock {
@@ -218,9 +218,10 @@
is LoadResult.Page<Key, Value> -> {
val insertApplied = state.insert(0, REFRESH, result)
- state.updateLoadState(REFRESH, Idle)
- if (result.prevKey == null) state.updateLoadState(START, Done)
- if (result.nextKey == null) state.updateLoadState(END, Done)
+ // Update loadStates which are sent along with this load's Insert PageEvent.
+ state.loadStates[REFRESH] = Idle
+ if (result.prevKey == null) state.loadStates[START] = Done
+ if (result.nextKey == null) state.loadStates[END] = Done
// Send insert event after load state updates, so that Done / Idle is
// correctly reflected in the insert event. Note that we only send the event
@@ -232,7 +233,7 @@
}
}
}
- is LoadResult.Error -> state.updateLoadState(
+ is LoadResult.Error -> state.setError(
REFRESH, Error(result.throwable), ViewportHint.DUMMY_VALUE
)
}
@@ -256,9 +257,7 @@
indexInPage,
pageIndex,
hintOffset
- )?.also {
- updateLoadState(loadType, Loading)
- }
+ )?.also { setLoading(loadType) }
}
}
}
@@ -283,11 +282,7 @@
}
is LoadResult.Error -> {
stateLock.withLock {
- state.updateLoadState(
- loadType,
- Error(result.throwable),
- generationalHint.hint
- )
+ state.setError(loadType, Error(result.throwable), generationalHint.hint)
}
return
}
@@ -300,7 +295,6 @@
}
state.dropInfo(dropType)?.let { info ->
- state.updateLoadState(dropType, Idle)
state.drop(dropType, info.pageCount, info.placeholdersRemaining)
pageEventCh.send(Drop(dropType, info.pageCount, info.placeholdersRemaining))
}
@@ -321,7 +315,7 @@
// Update load state to success if this is the final load result for this
// load hint, and only if we didn't error out.
if (loadKey == null && state.failedHintsByLoadType[loadType] == null) {
- state.updateLoadState(loadType, if (updateLoadStateToDone) Done else Idle)
+ state.loadStates[loadType] = if (updateLoadStateToDone) Done else Idle
}
// Send page event for successful insert, now that PagerState has been updated.
@@ -333,24 +327,25 @@
}
}
- private suspend fun PagerState<Key, Value>.updateLoadState(
- loadType: LoadType,
- loadState: LoadState,
- hint: ViewportHint? = null
- ) {
- require(loadState is Error || hint == null) {
- "Failed hint supplied for a loadState other than Error, which will be ignored."
+ private suspend fun PagerState<Key, Value>.setLoading(loadType: LoadType) {
+ if (loadStates[loadType] != Loading) {
+ loadStates[loadType] = Loading
+ pageEventCh.send(PageEvent.LoadStateUpdate(loadType, Loading))
}
+ }
- if (loadStates[loadType] != loadState) {
+ private suspend fun PagerState<Key, Value>.setError(
+ loadType: LoadType,
+ loadState: Error,
+ hint: ViewportHint
+ ) {
+ if (loadStates[loadType] !is Error) {
loadStates[loadType] = loadState
- pageEventCh.send(PageEvent.StateUpdate(loadType, loadState))
+ pageEventCh.send(PageEvent.LoadStateUpdate(loadType, loadState))
}
// Save the hint for retry on incoming retry signal, typically sent from user interaction.
- if (loadState is Error && hint != null) {
- failedHintsByLoadType[loadType] = hint
- }
+ failedHintsByLoadType[loadType] = hint
}
/**
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagerState.kt b/paging/common/src/main/kotlin/androidx/paging/PagerState.kt
index b8abb00..71d5b80 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagerState.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagerState.kt
@@ -166,6 +166,9 @@
check(pages.size >= pageCount) {
"invalid drop count. have ${pages.size} but wanted to drop $pageCount"
}
+
+ loadStates[loadType] = Idle
+
when (loadType) {
START -> {
repeat(pageCount) { _pages.removeAt(0) }
diff --git a/paging/common/src/main/kotlin/androidx/paging/Separators.kt b/paging/common/src/main/kotlin/androidx/paging/Separators.kt
index c171bfe..1f26c2d 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Separators.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Separators.kt
@@ -19,8 +19,8 @@
import androidx.paging.LoadType.END
import androidx.paging.LoadType.START
import androidx.paging.PageEvent.Drop
+import androidx.paging.PageEvent.LoadStateUpdate
import androidx.paging.PageEvent.Insert
-import androidx.paging.PageEvent.StateUpdate
/**
* Create a TransformablePage with separators inside (ignoring edges)
@@ -158,5 +158,5 @@
placeholdersRemaining = placeholdersRemaining
)
}
- is StateUpdate -> this as PageEvent<R>
+ is LoadStateUpdate -> this as PageEvent<R>
}
\ No newline at end of file
diff --git a/paging/common/src/test/kotlin/androidx/paging/FlattenedPageEventStorageTest.kt b/paging/common/src/test/kotlin/androidx/paging/FlattenedPageEventStorageTest.kt
index 6eeb9c9..38f4100 100644
--- a/paging/common/src/test/kotlin/androidx/paging/FlattenedPageEventStorageTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/FlattenedPageEventStorageTest.kt
@@ -274,17 +274,11 @@
is Drop -> {
throw IllegalStateException("shouldn't have any drops")
}
- is PageEvent.StateUpdate -> {
+ is PageEvent.LoadStateUpdate -> {
when (event.loadType) {
- REFRESH -> snapshot.copy(
- refreshState = event.loadState
- )
- START -> snapshot.copy(
- startState = event.loadState
- )
- END -> snapshot.copy(
- endState = event.loadState
- )
+ REFRESH -> snapshot.copy(refreshState = event.loadState)
+ START -> snapshot.copy(startState = event.loadState)
+ END -> snapshot.copy(endState = event.loadState)
}
}
}
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt
index ddfb202..0a9cb22 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt
@@ -17,7 +17,6 @@
package androidx.paging
import androidx.paging.LoadState.Idle
-import androidx.paging.LoadState.Loading
import androidx.paging.LoadType.END
import androidx.paging.LoadType.REFRESH
import androidx.paging.LoadType.START
@@ -25,6 +24,7 @@
import androidx.paging.PageEvent.Insert.Companion.End
import androidx.paging.PageEvent.Insert.Companion.Refresh
import androidx.paging.PageEvent.Insert.Companion.Start
+import androidx.paging.PageEvent.LoadStateUpdate
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -138,10 +138,7 @@
@Test
fun stateTransform() {
- val state = PageEvent.StateUpdate<Char>(
- loadType = REFRESH,
- loadState = Loading
- )
+ val state = LoadStateUpdate<Char>(loadType = REFRESH, loadState = LoadState.Loading)
assertSame(state, state.map { it + 1 })
assertSame(state, state.flatMap { listOf(it, it) })
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
index 5cb5050..034ddb4 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
@@ -343,7 +343,7 @@
val callback = PresenterCallbackCapture()
data.dropPages(true, pagesToDrop, newNulls, callback)
- assertEquals(events + listOf(StateEvent(START, Idle)), callback.getAllAndClear())
+ assertEvents(events + listOf(StateEvent(START, Idle)), callback.getAllAndClear())
// assert final list state
val finalData = initialPages.take(initialPages.size - pagesToDrop).reversed().flatten()
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
index 2105eff..6acc0db 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
@@ -27,7 +27,7 @@
import androidx.paging.PageEvent.Insert.Companion.End
import androidx.paging.PageEvent.Insert.Companion.Refresh
import androidx.paging.PageEvent.Insert.Companion.Start
-import androidx.paging.PageEvent.StateUpdate
+import androidx.paging.PageEvent.LoadStateUpdate
import androidx.paging.TestPagingSource.Companion.LOAD_ERROR
import androidx.paging.TestPagingSource.Companion.items
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -111,11 +111,9 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(1..2),
- StateUpdate(START, Loading),
- StateUpdate(START, Done),
+ LoadStateUpdate(START, Loading),
createPrepend(pageOffset = -1, range = 0..0, startState = Done)
)
@@ -135,16 +133,12 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(1..2),
- StateUpdate(START, Loading),
- StateUpdate(START, Done),
+ LoadStateUpdate(START, Loading),
createPrepend(pageOffset = -1, range = 0..0, startState = Done),
- StateUpdate(END, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(END, Loading),
Drop(START, 1, 1),
- StateUpdate(END, Idle),
createAppend(pageOffset = 1, range = 3..3, startState = Idle, endState = Idle)
)
@@ -162,11 +156,9 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 97..98),
- StateUpdate(END, Loading),
- StateUpdate(END, Done),
+ LoadStateUpdate(END, Loading),
createAppend(pageOffset = 1, range = 99..99, endState = Done)
)
@@ -186,16 +178,12 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 97..98),
- StateUpdate(END, Loading),
- StateUpdate(END, Done),
+ LoadStateUpdate(END, Loading),
createAppend(pageOffset = 1, range = 99..99, startState = Idle, endState = Done),
- StateUpdate(START, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(START, Loading),
Drop(END, 1, 1),
- StateUpdate(START, Idle),
createPrepend(pageOffset = -1, range = 96..96, startState = Idle, endState = Idle)
)
@@ -211,9 +199,7 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
- StateUpdate(START, Done),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 0..1, startState = Done, endState = Idle)
)
@@ -229,9 +215,7 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
- StateUpdate(END, Done),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 98..99, startState = Idle, endState = Done)
)
@@ -247,8 +231,7 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51)
)
@@ -265,11 +248,9 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51),
- StateUpdate(START, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(START, Loading),
createPrepend(pageOffset = -1, range = 49..49)
)
@@ -287,11 +268,9 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51),
- StateUpdate(START, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(START, Loading),
createPrepend(pageOffset = -1, range = 49..49)
)
@@ -312,15 +291,12 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51),
- StateUpdate(START, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(START, Loading),
createPrepend(pageOffset = -1, range = 49..49),
- StateUpdate(START, Loading),
+ LoadStateUpdate(START, Loading),
Drop(END, 1, 50),
- StateUpdate(START, Idle),
createPrepend(pageOffset = -2, range = 48..48)
)
@@ -345,17 +321,13 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51),
- StateUpdate(START, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(START, Loading),
createPrepend(pageOffset = -1, range = 49..49),
- StateUpdate(START, Loading),
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(START, Loading),
+ LoadStateUpdate(END, Loading),
Drop(END, 1, 50),
- StateUpdate(START, Idle),
createPrepend(pageOffset = -2, range = 48..48)
)
@@ -381,12 +353,10 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..52),
- StateUpdate(START, Loading),
+ LoadStateUpdate(START, Loading),
createPrepend(pageOffset = -1, range = 49..49, startState = Loading),
- StateUpdate(START, Idle),
createPrepend(-2, 48..48)
)
@@ -404,11 +374,9 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51),
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(END, Loading),
createAppend(1, 52..52)
)
@@ -433,12 +401,10 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..52),
- StateUpdate(END, Loading),
+ LoadStateUpdate(END, Loading),
createAppend(1, 53..53, startState = Idle, endState = Loading),
- StateUpdate(END, Idle),
createAppend(2, 54..54)
)
@@ -458,15 +424,12 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51),
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(END, Loading),
createAppend(pageOffset = 1, range = 52..52),
- StateUpdate(END, Loading),
+ LoadStateUpdate(END, Loading),
Drop(START, 1, 52),
- StateUpdate(END, Idle),
createAppend(pageOffset = 2, range = 53..53)
)
@@ -490,17 +453,13 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51),
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(END, Loading),
createAppend(pageOffset = 1, range = 52..52),
- StateUpdate(END, Loading),
- StateUpdate(START, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(END, Loading),
+ LoadStateUpdate(START, Loading),
Drop(START, 1, 52),
- StateUpdate(END, Idle),
createAppend(pageOffset = 2, range = 53..53, startState = Idle, endState = Idle)
)
@@ -518,13 +477,11 @@
val expected: List<List<PageEvent<Int>>> = listOf(
listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51)
),
listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51)
)
)
@@ -549,16 +506,13 @@
val expected: List<List<PageEvent<Int>>> = listOf(
listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51),
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(END, Loading),
createAppend(1, 52..52)
),
listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(51..52)
)
)
@@ -582,13 +536,11 @@
val job = launch { pager.pageEventFlow.collect { pageEvents.add(it) } }
val expected = listOf<PageEvent<Int>>(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51),
- StateUpdate(END, Loading),
- StateUpdate(END, Error(LOAD_ERROR)),
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(END, Loading),
+ LoadStateUpdate(END, Error(LOAD_ERROR)),
+ LoadStateUpdate(END, Loading),
createAppend(1, 52..52)
)
@@ -614,11 +566,9 @@
val job = launch { pager.pageEventFlow.collect { pageEvents.add(it) } }
val expected = listOf<PageEvent<Int>>(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51),
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(END, Loading),
createAppend(1, 52..52)
)
@@ -643,13 +593,11 @@
val job = launch { pager.pageEventFlow.collect { pageEvents.add(it) } }
val expected = listOf<PageEvent<Int>>(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51),
- StateUpdate(END, Loading),
- StateUpdate(END, Error(LOAD_ERROR)),
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(END, Loading),
+ LoadStateUpdate(END, Error(LOAD_ERROR)),
+ LoadStateUpdate(END, Loading),
createAppend(1, 52..52)
)
@@ -677,21 +625,18 @@
val job = launch { pager.pageEventFlow.collect { pageEvents.add(it) } }
val expected = listOf<PageEvent<Int>>(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51),
- StateUpdate(END, Loading),
- StateUpdate(END, Error(LOAD_ERROR)),
- StateUpdate(START, Loading),
- StateUpdate(START, Error(LOAD_ERROR)),
- StateUpdate(START, Loading),
- StateUpdate(END, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(END, Loading),
+ LoadStateUpdate(END, Error(LOAD_ERROR)),
+ LoadStateUpdate(START, Loading),
+ LoadStateUpdate(START, Error(LOAD_ERROR)),
+ LoadStateUpdate(START, Loading),
+ LoadStateUpdate(END, Loading),
createPrepend(
pageOffset = -1, range = 49..49, startState = Idle, endState = Loading
),
Drop(START, 1, 50),
- StateUpdate(END, Idle),
createAppend(1, 52..52)
)
@@ -720,10 +665,9 @@
val job = launch { pager.pageEventFlow.collect { pageEvents.add(it) } }
val expected = listOf<PageEvent<Int>>(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Error(LOAD_ERROR)),
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
+ LoadStateUpdate(REFRESH, Error(LOAD_ERROR)),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51)
)
@@ -747,13 +691,11 @@
val job = launch { pager.pageEventFlow.collect { pageEvents.add(it) } }
val expected = listOf<PageEvent<Int>>(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Error(LOAD_ERROR)),
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
+ LoadStateUpdate(REFRESH, Error(LOAD_ERROR)),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(50..51),
- StateUpdate(START, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(START, Loading),
createPrepend(pageOffset = -1, range = 49..49)
)
@@ -784,8 +726,7 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51).let { Refresh(it.pages, 0, 0, it.loadStates) }
)
@@ -810,11 +751,9 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51).let { Refresh(it.pages, 0, 0, it.loadStates) },
- StateUpdate(START, Loading),
- StateUpdate(START, Idle),
+ LoadStateUpdate(START, Loading),
createPrepend(-1, 49..49).let { Start(it.pages, 0, it.loadStates) }
)
@@ -839,11 +778,9 @@
advanceUntilIdle()
val expected: List<PageEvent<Int>> = listOf(
- StateUpdate(REFRESH, Loading),
- StateUpdate(REFRESH, Idle),
+ LoadStateUpdate(REFRESH, Loading),
createRefresh(range = 50..51).let { Refresh(it.pages, 0, 0, it.loadStates) },
- StateUpdate(END, Loading),
- StateUpdate(END, Idle),
+ LoadStateUpdate(END, Loading),
createAppend(1, 52..52).let { End(it.pages, 0, it.loadStates) }
)
diff --git a/paging/runtime/api/3.0.0-alpha01.txt b/paging/runtime/api/3.0.0-alpha01.txt
index c38b03f..34eeffe 100644
--- a/paging/runtime/api/3.0.0-alpha01.txt
+++ b/paging/runtime/api/3.0.0-alpha01.txt
@@ -57,6 +57,11 @@
method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, kotlinx.coroutines.CoroutineScope coroutineScope = GlobalScope, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher = Dispatchers.IO);
}
+ public final class LivePagingData {
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ }
+
public final class NullPaddedListDiffHelperKt {
}
diff --git a/paging/runtime/api/current.txt b/paging/runtime/api/current.txt
index c38b03f..34eeffe 100644
--- a/paging/runtime/api/current.txt
+++ b/paging/runtime/api/current.txt
@@ -57,6 +57,11 @@
method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, kotlinx.coroutines.CoroutineScope coroutineScope = GlobalScope, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher = Dispatchers.IO);
}
+ public final class LivePagingData {
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ }
+
public final class NullPaddedListDiffHelperKt {
}
diff --git a/paging/runtime/api/public_plus_experimental_3.0.0-alpha01.txt b/paging/runtime/api/public_plus_experimental_3.0.0-alpha01.txt
index c38b03f..34eeffe 100644
--- a/paging/runtime/api/public_plus_experimental_3.0.0-alpha01.txt
+++ b/paging/runtime/api/public_plus_experimental_3.0.0-alpha01.txt
@@ -57,6 +57,11 @@
method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, kotlinx.coroutines.CoroutineScope coroutineScope = GlobalScope, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher = Dispatchers.IO);
}
+ public final class LivePagingData {
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ }
+
public final class NullPaddedListDiffHelperKt {
}
diff --git a/paging/runtime/api/public_plus_experimental_current.txt b/paging/runtime/api/public_plus_experimental_current.txt
index c38b03f..34eeffe 100644
--- a/paging/runtime/api/public_plus_experimental_current.txt
+++ b/paging/runtime/api/public_plus_experimental_current.txt
@@ -57,6 +57,11 @@
method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, kotlinx.coroutines.CoroutineScope coroutineScope = GlobalScope, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher = Dispatchers.IO);
}
+ public final class LivePagingData {
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ }
+
public final class NullPaddedListDiffHelperKt {
}
diff --git a/paging/runtime/api/restricted_3.0.0-alpha01.txt b/paging/runtime/api/restricted_3.0.0-alpha01.txt
index c38b03f..34eeffe 100644
--- a/paging/runtime/api/restricted_3.0.0-alpha01.txt
+++ b/paging/runtime/api/restricted_3.0.0-alpha01.txt
@@ -57,6 +57,11 @@
method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, kotlinx.coroutines.CoroutineScope coroutineScope = GlobalScope, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher = Dispatchers.IO);
}
+ public final class LivePagingData {
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ }
+
public final class NullPaddedListDiffHelperKt {
}
diff --git a/paging/runtime/api/restricted_current.txt b/paging/runtime/api/restricted_current.txt
index c38b03f..34eeffe 100644
--- a/paging/runtime/api/restricted_current.txt
+++ b/paging/runtime/api/restricted_current.txt
@@ -57,6 +57,11 @@
method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, kotlinx.coroutines.CoroutineScope coroutineScope = GlobalScope, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher = Dispatchers.IO);
}
+ public final class LivePagingData {
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, Key? initialKey, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagingData<Value>> create(androidx.paging.PagingConfig config, kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory);
+ }
+
public final class NullPaddedListDiffHelperKt {
}
diff --git a/paging/runtime/build.gradle b/paging/runtime/build.gradle
index 6c43f30..9ef2307 100644
--- a/paging/runtime/build.gradle
+++ b/paging/runtime/build.gradle
@@ -34,12 +34,12 @@
// Ensure that the -ktx dependency graph mirrors the Java dependency graph
api(project(":paging:paging-common-ktx"))
- api("androidx.arch.core:core-runtime:2.1.0")
- api("androidx.lifecycle:lifecycle-runtime:2.1.0")
- api("androidx.lifecycle:lifecycle-livedata:2.1.0")
+ api(project(":lifecycle:lifecycle-runtime-ktx"))
+ api(project(":lifecycle:lifecycle-livedata-ktx"))
api("androidx.recyclerview:recyclerview:1.1.0")
api(KOTLIN_STDLIB)
api(KOTLIN_COROUTINES_ANDROID)
+ implementation(project(":core:core-ktx"))
androidTestImplementation project(':paging:paging-testutils')
androidTestImplementation project(':internal-testutils-common')
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListTest.kt
index 43db18a..ce5c600 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListTest.kt
@@ -76,19 +76,14 @@
) {
}
- override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
- }
+ override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {}
}
private val dataSourceFactory = object : DataSource.Factory<Int, String>() {
- override fun create(): DataSource<Int, String> {
- return dataSource
- }
+ override fun create(): DataSource<Int, String> = dataSource
}
- private val pagingSource = LegacyPagingSource(dataSource, Dispatchers.Main)
-
- private val pagingSourceFactory = { pagingSource }
+ private val pagingSourceFactory = dataSourceFactory.asPagingSourceFactory(Dispatchers.Main)
private val config = Config(10)
}
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.kt b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.kt
index 1ce9fb4..deead61 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.kt
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.kt
@@ -198,7 +198,7 @@
*/
fun build(): LiveData<PagedList<Value>> {
val pagingSourceFactory = pagingSourceFactory
- ?: dataSourceFactory?.let { { LegacyPagingSource(it.create()) } }
+ ?: dataSourceFactory?.asPagingSourceFactory(fetchDispatcher)
check(pagingSourceFactory != null) {
"LivePagedList cannot be built without a PagingSourceFactory or DataSource.Factory"
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagingData.kt b/paging/runtime/src/main/java/androidx/paging/LivePagingData.kt
new file mode 100644
index 0000000..2d3ef52
--- /dev/null
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagingData.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 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.
+ */
+
+@file:JvmName("LivePagingData")
+
+package androidx.paging
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.asLiveData
+
+/**
+ * Construct the primary Paging reactive stream: `LiveData<PagingData<T>>`.
+ *
+ * Creates a stream of [PagingData] objects, each of which represents a single generation of
+ * paginated data. These objects can be transformed to alter data as it loads, and presented in a
+ * `RecyclerView`.
+ */
+@Suppress("FunctionName")
+@JvmName("create")
+fun <Key : Any, Value : Any> LivePagingData(
+ config: PagingConfig,
+ initialKey: Key?,
+ pagingSourceFactory: () -> PagingSource<Key, Value>
+): LiveData<PagingData<Value>> = PagingDataFlow(config, initialKey, pagingSourceFactory)
+ .asLiveData()
+
+/**
+ * Construct the primary Paging reactive stream: `LiveData<PagingData<T>>`.
+ *
+ * Creates a stream of [PagingData] objects, each of which represents a single generation of
+ * paginated data. These objects can be transformed to alter data as it loads, and presented in a
+ * `RecyclerView`.
+ */
+@Suppress("FunctionName")
+@JvmName("create")
+fun <Key : Any, Value : Any> LivePagingData(
+ config: PagingConfig,
+ pagingSourceFactory: () -> PagingSource<Key, Value>
+): LiveData<PagingData<Value>> = PagingDataFlow(config, pagingSourceFactory)
+ .asLiveData()
diff --git a/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.kt b/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.kt
index 1875e23..578b36d 100644
--- a/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.kt
+++ b/paging/rxjava2/src/main/java/androidx/paging/RxPagedListBuilder.kt
@@ -212,18 +212,16 @@
* @return The [Observable] of PagedLists
*/
fun buildObservable(): Observable<PagedList<Value>> {
- if (notifyDispatcher == null) {
- notifyScheduler = ScheduledExecutor(ArchTaskExecutor.getMainThreadExecutor())
- notifyDispatcher = notifyScheduler!!.asCoroutineDispatcher()
- }
- if (fetchDispatcher == null) {
- val scheduledExecutor = ScheduledExecutor(ArchTaskExecutor.getIOThreadExecutor())
- fetchScheduler = scheduledExecutor
- fetchDispatcher = fetchScheduler!!.asCoroutineDispatcher()
- }
+ val notifyScheduler = notifyScheduler
+ ?: ScheduledExecutor(ArchTaskExecutor.getMainThreadExecutor())
+ val notifyDispatcher = notifyDispatcher ?: notifyScheduler.asCoroutineDispatcher()
+
+ val fetchScheduler = fetchScheduler
+ ?: ScheduledExecutor(ArchTaskExecutor.getIOThreadExecutor())
+ val fetchDispatcher = fetchDispatcher ?: fetchScheduler.asCoroutineDispatcher()
val pagingSourceFactory = pagingSourceFactory
- ?: dataSourceFactory?.let { { LegacyPagingSource(it.create()) } }
+ ?: dataSourceFactory?.asPagingSourceFactory(fetchDispatcher)
check(pagingSourceFactory != null) {
"LivePagedList cannot be built without a PagingSourceFactory or DataSource.Factory"
@@ -236,8 +234,8 @@
config,
boundaryCallback,
pagingSourceFactory,
- notifyDispatcher!!,
- fetchDispatcher!!
+ notifyDispatcher,
+ fetchDispatcher
)
)
.observeOn(notifyScheduler)
diff --git a/recyclerview/recyclerview/api/1.2.0-alpha01.txt b/recyclerview/recyclerview/api/1.2.0-alpha01.txt
index 4a6f640..83d6c44 100644
--- a/recyclerview/recyclerview/api/1.2.0-alpha01.txt
+++ b/recyclerview/recyclerview/api/1.2.0-alpha01.txt
@@ -473,6 +473,7 @@
method public abstract int getItemCount();
method public long getItemId(int);
method public int getItemViewType(int);
+ method public final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy getStateRestorationStrategy();
method public final boolean hasObservers();
method public final boolean hasStableIds();
method public final void notifyDataSetChanged();
@@ -496,9 +497,16 @@
method public void onViewRecycled(VH);
method public void registerAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
method public void setHasStableIds(boolean);
+ method public void setStateRestorationStrategy(androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy);
method public void unregisterAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
}
+ public enum RecyclerView.Adapter.StateRestorationStrategy {
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy ALLOW;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT_WHEN_EMPTY;
+ }
+
public abstract static class RecyclerView.AdapterDataObserver {
ctor public RecyclerView.AdapterDataObserver();
method public void onChanged();
@@ -507,6 +515,7 @@
method public void onItemRangeInserted(int, int);
method public void onItemRangeMoved(int, int, int);
method public void onItemRangeRemoved(int, int);
+ method public void onStateRestorationStrategyChanged();
}
public static interface RecyclerView.ChildDrawingOrderCallback {
diff --git a/recyclerview/recyclerview/api/current.txt b/recyclerview/recyclerview/api/current.txt
index 4a6f640..83d6c44 100644
--- a/recyclerview/recyclerview/api/current.txt
+++ b/recyclerview/recyclerview/api/current.txt
@@ -473,6 +473,7 @@
method public abstract int getItemCount();
method public long getItemId(int);
method public int getItemViewType(int);
+ method public final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy getStateRestorationStrategy();
method public final boolean hasObservers();
method public final boolean hasStableIds();
method public final void notifyDataSetChanged();
@@ -496,9 +497,16 @@
method public void onViewRecycled(VH);
method public void registerAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
method public void setHasStableIds(boolean);
+ method public void setStateRestorationStrategy(androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy);
method public void unregisterAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
}
+ public enum RecyclerView.Adapter.StateRestorationStrategy {
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy ALLOW;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT_WHEN_EMPTY;
+ }
+
public abstract static class RecyclerView.AdapterDataObserver {
ctor public RecyclerView.AdapterDataObserver();
method public void onChanged();
@@ -507,6 +515,7 @@
method public void onItemRangeInserted(int, int);
method public void onItemRangeMoved(int, int, int);
method public void onItemRangeRemoved(int, int);
+ method public void onStateRestorationStrategyChanged();
}
public static interface RecyclerView.ChildDrawingOrderCallback {
diff --git a/recyclerview/recyclerview/api/public_plus_experimental_1.2.0-alpha01.txt b/recyclerview/recyclerview/api/public_plus_experimental_1.2.0-alpha01.txt
index 4a6f640..83d6c44 100644
--- a/recyclerview/recyclerview/api/public_plus_experimental_1.2.0-alpha01.txt
+++ b/recyclerview/recyclerview/api/public_plus_experimental_1.2.0-alpha01.txt
@@ -473,6 +473,7 @@
method public abstract int getItemCount();
method public long getItemId(int);
method public int getItemViewType(int);
+ method public final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy getStateRestorationStrategy();
method public final boolean hasObservers();
method public final boolean hasStableIds();
method public final void notifyDataSetChanged();
@@ -496,9 +497,16 @@
method public void onViewRecycled(VH);
method public void registerAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
method public void setHasStableIds(boolean);
+ method public void setStateRestorationStrategy(androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy);
method public void unregisterAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
}
+ public enum RecyclerView.Adapter.StateRestorationStrategy {
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy ALLOW;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT_WHEN_EMPTY;
+ }
+
public abstract static class RecyclerView.AdapterDataObserver {
ctor public RecyclerView.AdapterDataObserver();
method public void onChanged();
@@ -507,6 +515,7 @@
method public void onItemRangeInserted(int, int);
method public void onItemRangeMoved(int, int, int);
method public void onItemRangeRemoved(int, int);
+ method public void onStateRestorationStrategyChanged();
}
public static interface RecyclerView.ChildDrawingOrderCallback {
diff --git a/recyclerview/recyclerview/api/public_plus_experimental_current.txt b/recyclerview/recyclerview/api/public_plus_experimental_current.txt
index 4a6f640..83d6c44 100644
--- a/recyclerview/recyclerview/api/public_plus_experimental_current.txt
+++ b/recyclerview/recyclerview/api/public_plus_experimental_current.txt
@@ -473,6 +473,7 @@
method public abstract int getItemCount();
method public long getItemId(int);
method public int getItemViewType(int);
+ method public final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy getStateRestorationStrategy();
method public final boolean hasObservers();
method public final boolean hasStableIds();
method public final void notifyDataSetChanged();
@@ -496,9 +497,16 @@
method public void onViewRecycled(VH);
method public void registerAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
method public void setHasStableIds(boolean);
+ method public void setStateRestorationStrategy(androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy);
method public void unregisterAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
}
+ public enum RecyclerView.Adapter.StateRestorationStrategy {
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy ALLOW;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT_WHEN_EMPTY;
+ }
+
public abstract static class RecyclerView.AdapterDataObserver {
ctor public RecyclerView.AdapterDataObserver();
method public void onChanged();
@@ -507,6 +515,7 @@
method public void onItemRangeInserted(int, int);
method public void onItemRangeMoved(int, int, int);
method public void onItemRangeRemoved(int, int);
+ method public void onStateRestorationStrategyChanged();
}
public static interface RecyclerView.ChildDrawingOrderCallback {
diff --git a/recyclerview/recyclerview/api/restricted_1.2.0-alpha01.txt b/recyclerview/recyclerview/api/restricted_1.2.0-alpha01.txt
index 6d5fda3..3da68a4 100644
--- a/recyclerview/recyclerview/api/restricted_1.2.0-alpha01.txt
+++ b/recyclerview/recyclerview/api/restricted_1.2.0-alpha01.txt
@@ -473,6 +473,7 @@
method public abstract int getItemCount();
method public long getItemId(int);
method public int getItemViewType(int);
+ method public final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy getStateRestorationStrategy();
method public final boolean hasObservers();
method public final boolean hasStableIds();
method public final void notifyDataSetChanged();
@@ -496,9 +497,16 @@
method public void onViewRecycled(VH);
method public void registerAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
method public void setHasStableIds(boolean);
+ method public void setStateRestorationStrategy(androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy);
method public void unregisterAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
}
+ public enum RecyclerView.Adapter.StateRestorationStrategy {
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy ALLOW;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT_WHEN_EMPTY;
+ }
+
public abstract static class RecyclerView.AdapterDataObserver {
ctor public RecyclerView.AdapterDataObserver();
method public void onChanged();
@@ -507,6 +515,7 @@
method public void onItemRangeInserted(int, int);
method public void onItemRangeMoved(int, int, int);
method public void onItemRangeRemoved(int, int);
+ method public void onStateRestorationStrategyChanged();
}
public static interface RecyclerView.ChildDrawingOrderCallback {
diff --git a/recyclerview/recyclerview/api/restricted_current.txt b/recyclerview/recyclerview/api/restricted_current.txt
index 6d5fda3..3da68a4 100644
--- a/recyclerview/recyclerview/api/restricted_current.txt
+++ b/recyclerview/recyclerview/api/restricted_current.txt
@@ -473,6 +473,7 @@
method public abstract int getItemCount();
method public long getItemId(int);
method public int getItemViewType(int);
+ method public final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy getStateRestorationStrategy();
method public final boolean hasObservers();
method public final boolean hasStableIds();
method public final void notifyDataSetChanged();
@@ -496,9 +497,16 @@
method public void onViewRecycled(VH);
method public void registerAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
method public void setHasStableIds(boolean);
+ method public void setStateRestorationStrategy(androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy);
method public void unregisterAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
}
+ public enum RecyclerView.Adapter.StateRestorationStrategy {
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy ALLOW;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT;
+ enum_constant public static final androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy PREVENT_WHEN_EMPTY;
+ }
+
public abstract static class RecyclerView.AdapterDataObserver {
ctor public RecyclerView.AdapterDataObserver();
method public void onChanged();
@@ -507,6 +515,7 @@
method public void onItemRangeInserted(int, int);
method public void onItemRangeMoved(int, int, int);
method public void onItemRangeRemoved(int, int);
+ method public void onStateRestorationStrategyChanged();
}
public static interface RecyclerView.ChildDrawingOrderCallback {
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index 431d8ad9..9f19aac 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -22,6 +22,7 @@
androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+ androidTestImplementation(TRUTH)
androidTestImplementation(JUNIT)
androidTestImplementation(KOTLIN_STDLIB)
androidTestImplementation(project(":internal-testutils-espresso"))
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LazyStateRestorationTest.kt b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LazyStateRestorationTest.kt
new file mode 100644
index 0000000..0ee2d89
--- /dev/null
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LazyStateRestorationTest.kt
@@ -0,0 +1,272 @@
+/*
+ * 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.recyclerview.widget
+
+import android.content.Context
+import android.graphics.Rect
+import android.os.Parcel
+import android.os.Parcelable
+import android.view.View
+import android.view.View.MeasureSpec.AT_MOST
+import android.view.ViewGroup
+import androidx.recyclerview.widget.BaseRecyclerViewInstrumentationTest.Item
+import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy.PREVENT_WHEN_EMPTY
+import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy.ALLOW
+import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationStrategy.PREVENT
+import androidx.recyclerview.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE
+import androidx.recyclerview.widget.StaggeredGridLayoutManager.VERTICAL
+import androidx.test.annotation.UiThreadTest
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.LinkedHashMap
+import java.util.UUID
+
+@RunWith(Parameterized::class)
+@SmallTest
+class LazyStateRestorationTest(
+ private val layoutManagerFactory: LayoutManagerFactory
+) {
+ private lateinit var recyclerView: RecyclerView
+ private val items = (0..99).map { Item(it, "text $it") }
+ @Before
+ fun init() {
+ recyclerView = RecyclerView(getApplicationContext())
+ recyclerView.itemAnimator = null
+ recyclerView.adapter = LazyStateAdapter(items)
+ recyclerView.layoutManager = createLayoutManager()
+ }
+
+ private fun createLayoutManager(): RecyclerView.LayoutManager {
+ return layoutManagerFactory.create(getApplicationContext())
+ }
+
+ private fun measureAndLayout() {
+ measure()
+ layout()
+ }
+
+ private fun measure() {
+ recyclerView.measure(AT_MOST or 320, AT_MOST or 240)
+ }
+
+ private fun layout() {
+ recyclerView.layout(0, 0, 320, 320)
+ }
+
+ private fun restore(
+ setAdapter: Boolean
+ ) {
+ val prevAdapter = checkNotNull(recyclerView.adapter as? LazyStateAdapter) {
+ "Previous RecyclerView should have a LazyStateAdapter for the test"
+ }
+ val savedState = saveState()
+ recyclerView = RecyclerView(getApplicationContext())
+ recyclerView.layoutManager = createLayoutManager()
+ recyclerView.onRestoreInstanceState(savedState)
+ if (setAdapter) {
+ recyclerView.adapter = LazyStateAdapter(prevAdapter.items)
+ }
+ }
+
+ private fun saveState(): Parcelable {
+ val parcel = Parcel.obtain()
+ val parcelSuffix = UUID.randomUUID().toString()
+ val savedState = recyclerView.onSaveInstanceState()
+ savedState!!.writeToParcel(parcel, 0)
+ parcel.writeString(parcelSuffix)
+ // reset position for reading
+ parcel.setDataPosition(0)
+ return savedState
+ }
+
+ @Test
+ @UiThreadTest
+ fun default() {
+ measureAndLayout()
+ recyclerView.scrollBy(0, 113)
+ val coordinates = recyclerView.collectChildCoordinates()
+ restore(setAdapter = true)
+ measureAndLayout()
+ val restoredCoordinates = recyclerView.collectChildCoordinates()
+ assertThat(restoredCoordinates).isEqualTo(coordinates)
+ }
+
+ @Test
+ @UiThreadTest
+ fun countBased() {
+ measureAndLayout()
+ recyclerView.scrollBy(0, 113)
+ val coordinates = recyclerView.collectChildCoordinates()
+ restore(setAdapter = false)
+ val adapter = LazyStateAdapter(emptyList())
+ adapter.stateRestorationStrategy = PREVENT_WHEN_EMPTY
+ recyclerView.adapter = adapter
+ measureAndLayout()
+ // test sanity
+ assertThat(recyclerView.collectChildCoordinates()).isEmpty()
+ adapter.items = items
+ adapter.notifyDataSetChanged()
+ measureAndLayout()
+ val restored = recyclerView.collectChildCoordinates()
+ assertThat(restored).isEqualTo(coordinates)
+ }
+
+ @Test
+ @UiThreadTest
+ fun manual() {
+ measureAndLayout()
+ recyclerView.scrollBy(0, 113)
+ val coordinates = recyclerView.collectChildCoordinates()
+ restore(setAdapter = true)
+ val adapter = recyclerView.adapter as LazyStateAdapter
+ adapter.stateRestorationStrategy = PREVENT
+ measureAndLayout()
+ // test sanity, we should layout whatever is available
+ assertThat(recyclerView.collectChildCoordinates()).isNotEmpty()
+ // make sure we didn't restore
+ assertThat(recyclerView.collectChildCoordinates()).isNotEqualTo(coordinates)
+
+ // notifying item change does not matter
+ adapter.items = adapter.items.subList(0, 90)
+ adapter.notifyDataSetChanged()
+ measureAndLayout()
+ // still not restored
+ assertThat(recyclerView.collectChildCoordinates()).isNotEqualTo(coordinates)
+ adapter.stateRestorationStrategy = ALLOW
+ assertThat(recyclerView.isLayoutRequested).isTrue()
+ measureAndLayout()
+ // now we should restore
+ val restored = recyclerView.collectChildCoordinates()
+ assertThat(restored).isEqualTo(coordinates)
+ }
+
+ @Test
+ @UiThreadTest
+ fun scrollToPositionOverridesState() {
+ measureAndLayout()
+ recyclerView.scrollBy(0, 113)
+ restore(setAdapter = true)
+ val adapter = recyclerView.adapter as LazyStateAdapter
+ adapter.stateRestorationStrategy = PREVENT
+ measureAndLayout()
+ // state is not restored yet, trigger a scroll
+ recyclerView.scrollToPosition(40)
+ measureAndLayout()
+ // relayout so that SGLM can settle
+ recyclerView.requestLayout()
+ measureAndLayout()
+ val scrolled = recyclerView.collectChildCoordinates()
+
+ assertThat(recyclerView.collectChildCoordinates()).isEqualTo(scrolled)
+ // now restore state, it should be ignored
+ adapter.stateRestorationStrategy = ALLOW
+ measureAndLayout()
+ val coordinates = recyclerView.collectChildCoordinates()
+ assertThat(coordinates).isEqualTo(scrolled)
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun params(): List<LayoutManagerFactory> = listOf(
+ LinearLayoutManagerFactory(),
+ GridLayoutManagerFactory(),
+ StaggeredGridLayoutManagerFactory()
+ )
+ }
+
+ abstract class LayoutManagerFactory {
+ abstract fun create(context: Context): RecyclerView.LayoutManager
+ abstract fun describe(): String
+ override fun toString() = describe()
+ }
+
+ private class LinearLayoutManagerFactory : LayoutManagerFactory() {
+ override fun create(context: Context): RecyclerView.LayoutManager {
+ return LinearLayoutManager(context)
+ }
+
+ override fun describe() = "LinearLayoutManager"
+ }
+
+ private class GridLayoutManagerFactory : LayoutManagerFactory() {
+ override fun create(context: Context): RecyclerView.LayoutManager {
+ return GridLayoutManager(context, 3)
+ }
+
+ override fun describe() = "GridLayoutManager"
+ }
+
+ private class StaggeredGridLayoutManagerFactory : LayoutManagerFactory() {
+ override fun create(context: Context): RecyclerView.LayoutManager {
+ return StaggeredGridLayoutManager(3, VERTICAL).also {
+ it.gapStrategy = GAP_HANDLING_NONE
+ }
+ }
+
+ override fun describe() = "StaggeredGridLayoutManager"
+ }
+
+ private class LazyStateAdapter(
+ var items: List<Item>
+ ) : RecyclerView.Adapter<LazyStateViewHolder>() {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LazyStateViewHolder {
+ return LazyStateViewHolder(parent.context)
+ }
+
+ override fun onBindViewHolder(holder: LazyStateViewHolder, position: Int) {
+ holder.bindTo(items[position])
+ }
+
+ override fun getItemCount() = items.size
+ }
+
+ private class LazyStateViewHolder(
+ context: Context
+ ) : RecyclerView.ViewHolder(
+ View(context)
+ ) {
+ var item: Item? = null
+ fun bindTo(item: Item) {
+ this.item = item
+ itemView.layoutParams = RecyclerView.LayoutParams(
+ RecyclerView.LayoutParams.MATCH_PARENT,
+ 25 + (item.mId % 10)
+ )
+ }
+ }
+
+ private fun RecyclerView.collectChildCoordinates(): Map<Item, Rect> {
+ val items = LinkedHashMap<Item, Rect>()
+ val layoutBounds = Rect(0, 0, width, height)
+ for (i in 0 until childCount) {
+ val child = getChildAt(i)
+ val lp = child!!.layoutParams as RecyclerView.LayoutParams
+ val vh = lp.mViewHolder as LazyStateViewHolder
+ val rect = Rect(child.left, child.top, child.right, child.bottom)
+ if (rect.intersect(layoutBounds)) {
+ items[vh.item!!] = rect
+ }
+ }
+ return items
+ }
+}
\ No newline at end of file
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerSavedStateTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerSavedStateTest.java
index 78bf4da..8ace7e8 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerSavedStateTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerSavedStateTest.java
@@ -47,7 +47,7 @@
public LinearLayoutManagerSavedStateTest(Config config, boolean waitForLayout,
boolean loadDataAfterRestore, PostLayoutRunnable postLayoutOperation,
- PostRestoreRunnable postRestoreOperation) {
+ PostRestoreRunnable postRestoreOperation, int index) {
mConfig = config;
mWaitForLayout = waitForLayout;
mLoadDataAfterRestore = loadDataAfterRestore;
@@ -80,7 +80,7 @@
}
@Parameterized.Parameters(name = "{0},waitForLayout:{1},loadDataAfterRestore:{2}"
- + ",postLayout:{3},postRestore:{4}")
+ + ",postLayout:{3},postRestore:{4},testIndex:{5}")
public static Iterable<Object[]> params()
throws IllegalAccessException, CloneNotSupportedException, NoSuchFieldException {
PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
@@ -250,6 +250,7 @@
variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true);
List<Object[]> params = new ArrayList<>();
+ int index = 0;
for (Config config : variations) {
for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) {
for (boolean waitForLayout : waitForLayoutOptions) {
@@ -257,7 +258,8 @@
for (boolean loadDataAfterRestore : loadDataAfterRestoreOptions) {
params.add(new Object[]{
config.clone(), waitForLayout,
- loadDataAfterRestore, postLayoutRunnable, postRestoreRunnable
+ loadDataAfterRestore, postLayoutRunnable, postRestoreRunnable,
+ index++
});
}
}
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewBasicTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewBasicTest.java
index a8b94e7..ccd9716d 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewBasicTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewBasicTest.java
@@ -20,7 +20,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -311,19 +310,17 @@
savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
RecyclerView restored = new RecyclerView(getContext());
+ mRecyclerView = restored;
MockLayoutManager mlmRestored = new MockLayoutManager();
restored.setLayoutManager(mlmRestored);
restored.setAdapter(new MockAdapter(3));
restored.onRestoreInstanceState(savedState);
-
+ layout();
assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
parcel.readString());
assertEquals("When unmarshalling, all of the parcel should be read", 0, parcel.dataAvail());
assertEquals("uuid in layout manager should be preserved properly", mlm.mUuid,
mlmRestored.mUuid);
- assertNotSame("stateless parameter should not be preserved", mlm.mLayoutCount,
- mlmRestored.mLayoutCount);
- layout();
}
@Test
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerSavedStateTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerSavedStateTest.java
index f696531..b5f19cc 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerSavedStateTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerSavedStateTest.java
@@ -16,7 +16,10 @@
package androidx.recyclerview.widget;
+import static androidx.recyclerview.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.graphics.Rect;
import android.os.Parcel;
@@ -40,24 +43,32 @@
private final Config mConfig;
private final boolean mLoadDataAfterRestore;
private final PostLayoutRunnable mPostLayoutOperations;
+ private final PostRestoreRunnable mPostRestoreOperations;
public StaggeredGridLayoutManagerSavedStateTest(
Config config,
boolean loadDataAfterRestore,
- PostLayoutRunnable postLayoutOperations) throws CloneNotSupportedException {
+ PostLayoutRunnable postLayoutOperations,
+ PostRestoreRunnable postRestoreOperations,
+ int index) throws CloneNotSupportedException {
this.mConfig = (Config) config.clone();
this.mLoadDataAfterRestore = loadDataAfterRestore;
this.mPostLayoutOperations = postLayoutOperations;
+ this.mPostRestoreOperations = postRestoreOperations;
if (postLayoutOperations != null) {
postLayoutOperations.mTest = this;
}
+ if (mPostRestoreOperations != null) {
+ mPostRestoreOperations.mTest = this;
+ }
}
- @Parameterized.Parameters(name = "config={0},loadDataAfterRestore={1},postLayoutRunnable={2}")
+ @Parameterized.Parameters(name = "config={0},loadDataAfterRestore={1},postLayoutRunnable={2}"
+ + ",postRestoreRunnable={3},index={4}")
public static List<Object[]> getParams() throws CloneNotSupportedException {
List<Config> variations = createBaseVariations();
- PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
+ final PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
new PostLayoutRunnable() {
@Override
public void run() throws Throwable {
@@ -111,6 +122,43 @@
}
}
};
+ PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{
+ new PostRestoreRunnable() {
+ @Override
+ String describe() {
+ return "doing_nothing";
+ }
+ },
+ new PostRestoreRunnable() {
+ int mPosition;
+
+ @Override
+ void onAfterRestore(Config config) throws Throwable {
+ mPosition = adapter().getItemCount() / 2;
+ layoutManager().scrollToPosition(mPosition);
+ }
+
+ @Override
+ boolean shouldLayoutMatch(Config config) {
+ return adapter().getItemCount() <= config.mSpanCount
+ && config.mGapStrategy != GAP_HANDLING_NONE;
+ }
+
+ @Override
+ void onAfterReLayout(Config config) {
+ if (adapter().getItemCount() > 0) {
+ assertNotNull(
+ "view at " + mPosition + " should be visible",
+ layoutManager().findViewByPosition(mPosition));
+ }
+ }
+
+ @Override
+ String describe() {
+ return "scroll_to_pos_" + mPosition;
+ }
+ }
+ };
boolean[] loadDataAfterRestoreOptions = new boolean[]{false, true};
List<Config> testVariations = new ArrayList<Config>();
testVariations.addAll(variations);
@@ -123,12 +171,15 @@
testVariations.add(clone);
}
+ int index = 0;
List<Object[]> params = new ArrayList<>();
for (Config config : testVariations) {
- for (PostLayoutRunnable runnable : postLayoutOptions) {
- for (boolean loadDataAfterRestore : loadDataAfterRestoreOptions) {
- params.add(new Object[]{config, loadDataAfterRestore,
- runnable});
+ for (PostLayoutRunnable postLayout : postLayoutOptions) {
+ for (PostRestoreRunnable postRestore : postRestoreOptions) {
+ for (boolean loadDataAfterRestore : loadDataAfterRestoreOptions) {
+ params.add(new Object[]{config, loadDataAfterRestore,
+ postLayout, postRestore, index++});
+ }
}
}
}
@@ -192,7 +243,7 @@
if (mLoadDataAfterRestore) {
mAdapter.resetItemsTo(mItems);
}
-
+ mPostRestoreOperations.onAfterRestore(mConfig);
assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
parcel.readString());
mLayoutManager.expectLayouts(1);
@@ -206,16 +257,23 @@
mConfig.mSpanCount, mLayoutManager.getSpanCount());
assertEquals(mConfig + " on saved state, gap strategy should be preserved",
mConfig.mGapStrategy, mLayoutManager.getGapStrategy());
- assertEquals(mConfig + " on saved state, first completely visible child position should"
- + " be preserved", firstCompletelyVisiblePosition,
- mLayoutManager.findFirstVisibleItemPositionInt());
+ if (mPostRestoreOperations.shouldLayoutMatch(mConfig)) {
+ assertEquals(mConfig + " on saved state, first completely visible child "
+ + "position should be preserved", firstCompletelyVisiblePosition,
+ mLayoutManager.findFirstVisibleItemPositionInt());
+ }
final boolean strictItemEquality = !mLoadDataAfterRestore;
- assertRectSetsEqual(mConfig + "\npost layout op:" + mPostLayoutOperations.describe()
- + ": on restore, previous view positions should be preserved",
- before, mLayoutManager.collectChildCoordinates(), strictItemEquality);
+ if (mPostRestoreOperations.shouldLayoutMatch(mConfig)) {
+ assertRectSetsEqual(mConfig + "\npost layout op:" + mPostLayoutOperations.describe()
+ + ": on restore, previous view positions should be preserved",
+ before, mLayoutManager.collectChildCoordinates(), strictItemEquality);
- // TODO add tests for changing values after restore before layout
+ } else {
+ assertRectSetsNotEqual(mConfig + "\npost layout op:" + mPostLayoutOperations.describe()
+ + ": on restore, previous view positions should be different",
+ before, mLayoutManager.collectChildCoordinates());
+ }
}
static abstract class PostLayoutRunnable {
@@ -250,4 +308,38 @@
return describe();
}
}
+
+ abstract static class PostRestoreRunnable {
+ StaggeredGridLayoutManagerSavedStateTest mTest;
+
+ public void setup(StaggeredGridLayoutManagerSavedStateTest test) {
+ mTest = test;
+ }
+
+ public WrappedLayoutManager layoutManager() {
+ return mTest.mLayoutManager;
+ }
+
+ public GridTestAdapter adapter() {
+ return mTest.mAdapter;
+ }
+
+ void onAfterRestore(Config config) throws Throwable {
+ }
+
+ abstract String describe();
+
+ boolean shouldLayoutMatch(Config config) {
+ return true;
+ }
+
+ void onAfterReLayout(Config config) {
+
+ }
+
+ @Override
+ public String toString() {
+ return describe();
+ }
+ }
}
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java
index 7ab6cba..a0b6157 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java
@@ -120,6 +120,11 @@
int mPendingScrollPosition = RecyclerView.NO_POSITION;
/**
+ * Set if {@link #scrollToPosition(int)} or its variants is called. Even if the state is
+ * restored lazily, we'll ignore the saved state.
+ */
+ boolean mIgnoreSavedScrollPosition = false;
+ /**
* Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is
* called.
*/
@@ -278,6 +283,9 @@
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
mPendingSavedState = (SavedState) state;
+ if (mIgnoreSavedScrollPosition) {
+ mPendingSavedState.invalidateAnchor();
+ }
requestLayout();
if (DEBUG) {
Log.d(TAG, "loaded saved state");
@@ -1072,6 +1080,7 @@
if (mPendingSavedState != null) {
mPendingSavedState.invalidateAnchor();
}
+ mIgnoreSavedScrollPosition = true;
requestLayout();
}
@@ -1100,6 +1109,7 @@
if (mPendingSavedState != null) {
mPendingSavedState.invalidateAnchor();
}
+ mIgnoreSavedScrollPosition = true;
requestLayout();
}
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index 5cc9511..baa2caf 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -366,7 +366,7 @@
final Recycler mRecycler = new Recycler();
- private SavedState mPendingSavedState;
+ SavedState mPendingSavedState;
/**
* Handles adapter updates
@@ -1394,9 +1394,6 @@
mPendingSavedState = (SavedState) state;
super.onRestoreInstanceState(mPendingSavedState.getSuperState());
- if (mLayout != null && mPendingSavedState.mLayoutState != null) {
- mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
- }
}
/**
@@ -4124,13 +4121,17 @@
mAdapterHelper.consumeUpdatesInOnePass();
mState.mItemCount = mAdapter.getItemCount();
mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
-
+ if (mPendingSavedState != null && mAdapter.canRestoreState()) {
+ if (mPendingSavedState.mLayoutState != null) {
+ mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
+ }
+ mPendingSavedState = null;
+ }
// Step 2: Run layout
mState.mInPreLayout = false;
mLayout.onLayoutChildren(mRecycler, mState);
mState.mStructureChanged = false;
- mPendingSavedState = null;
// onLayoutChildren may have caused client code to disable item animations; re-check
mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
@@ -5578,6 +5579,20 @@
requestLayout();
}
}
+
+ @Override
+ public void onStateRestorationStrategyChanged() {
+ if (mPendingSavedState == null) {
+ return;
+ }
+ // If there is a pending saved state and the new mode requires us to restore it,
+ // we'll request a layout which will call the adapter to see if it can restore state
+ // and trigger state restoration
+ Adapter<?> adapter = mAdapter;
+ if (adapter != null && adapter.canRestoreState()) {
+ requestLayout();
+ }
+ }
}
/**
@@ -6980,6 +6995,7 @@
public abstract static class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
private boolean mHasStableIds = false;
+ private StateRestorationStrategy mStateRestorationStrategy = StateRestorationStrategy.ALLOW;
/**
* Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
@@ -7527,6 +7543,91 @@
public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
mObservable.notifyItemRangeRemoved(positionStart, itemCount);
}
+
+ /**
+ * Sets the state restoration strategy for the Adapter.
+ *
+ * By default, it is set to {@link StateRestorationStrategy#ALLOW} which means RecyclerView
+ * expects any set Adapter to be immediately capable of restoring the RecyclerView's saved
+ * scroll position.
+ * <p>
+ * This behaviour might be undesired if the Adapter's data is loaded asynchronously, and
+ * thus unavailable during initial layout (e.g. after Activity rotation). To avoid losing
+ * scroll position, you can change this to be either
+ * {@link StateRestorationStrategy#PREVENT_WHEN_EMPTY} or
+ * {@link StateRestorationStrategy#PREVENT}.
+ * Note that the former means your RecyclerView will restore state as soon as Adapter has
+ * 1 or more items while the latter requires you to call
+ * {@link #setStateRestorationStrategy(StateRestorationStrategy)} with either
+ * {@link StateRestorationStrategy#ALLOW} or
+ * {@link StateRestorationStrategy#PREVENT_WHEN_EMPTY} again when the Adapter is
+ * ready to restore its state.
+ * <p>
+ * RecyclerView will still layout even when State restoration is disabled. The behavior of
+ * how State is restored is up to the {@link LayoutManager}. All default LayoutManagers
+ * will override current state with restored state when state restoration happens (unless
+ * an explicit call to {@link LayoutManager#scrollToPosition(int)} is made).
+ * <p>
+ * Calling this method after state is restored will not have any effect other than changing
+ * the return value of {@link #getStateRestorationStrategy()}.
+ *
+ * @param strategy The saved state restoration strategy for this Adapter.
+ * @see #getStateRestorationStrategy()
+ */
+ public void setStateRestorationStrategy(@NonNull StateRestorationStrategy strategy) {
+ mStateRestorationStrategy = strategy;
+ mObservable.notifyStateRestorationStrategyChanged();
+ }
+
+ /**
+ * Returns when this Adapter wants to restore the state.
+ *
+ * @return The current {@link StateRestorationStrategy} for this Adapter. Defaults to
+ * {@link StateRestorationStrategy#ALLOW}.
+ * @see #setStateRestorationStrategy(StateRestorationStrategy)
+ */
+ @NonNull
+ public final StateRestorationStrategy getStateRestorationStrategy() {
+ return mStateRestorationStrategy;
+ }
+
+ /**
+ * Called by the RecyclerView to decide whether the SavedState should be given to the
+ * LayoutManager or not.
+ *
+ * @return {@code true} if the Adapter is ready to restore its state, {@code false}
+ * otherwise.
+ */
+ boolean canRestoreState() {
+ switch (mStateRestorationStrategy) {
+ case PREVENT: return false;
+ case PREVENT_WHEN_EMPTY: return getItemCount() > 0;
+ default: return true;
+ }
+ }
+
+ /**
+ * Defines how this Adapter wants to restore its state after a view reconstruction (e.g.
+ * configuration change).
+ */
+ public enum StateRestorationStrategy {
+ /**
+ * Adapter is ready to restore State immediately, RecyclerView will provide the state
+ * to the LayoutManager in the next layout pass.
+ */
+ ALLOW,
+ /**
+ * Adapter is ready to restore State when it has more than 0 items. RecyclerView will
+ * provide the state to the LayoutManager as soon as the Adapter has 1 or more items.
+ */
+ PREVENT_WHEN_EMPTY,
+ /**
+ * RecyclerView will not restore the state for the Adapter until a call to
+ * {@link #setStateRestorationStrategy(StateRestorationStrategy)} is made with either
+ * {@link #ALLOW} or {@link #PREVENT_WHEN_EMPTY}.
+ */
+ PREVENT
+ }
}
@SuppressWarnings("unchecked")
@@ -10293,7 +10394,16 @@
return null;
}
-
+ /**
+ * Called when the RecyclerView is ready to restore the state based on a previous
+ * RecyclerView.
+ *
+ * Notice that this might happen after an actual layout, based on how Adapter prefers to
+ * restore State. See {@link Adapter#getStateRestorationStrategy()} for more information.
+ *
+ * @param state The parcelable that was returned by the previous LayoutManager's
+ * {@link #onSaveInstanceState()} method.
+ */
public void onRestoreInstanceState(Parcelable state) {
}
@@ -11728,6 +11838,18 @@
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
// do nothing
}
+
+ /**
+ * Called when the {@link Adapter.StateRestorationStrategy} of the {@link Adapter} changed.
+ * When this method is called, the Adapter might be ready to restore its state if it has
+ * not already been restored.
+ *
+ * @see Adapter#getStateRestorationStrategy()
+ * @see Adapter#setStateRestorationStrategy(Adapter.StateRestorationStrategy)
+ */
+ public void onStateRestorationStrategyChanged() {
+ // do nothing
+ }
}
/**
@@ -12238,6 +12360,12 @@
}
}
+ public void notifyStateRestorationStrategyChanged() {
+ for (int i = mObservers.size() - 1; i >= 0; i--) {
+ mObservers.get(i).onStateRestorationStrategyChanged();
+ }
+ }
+
public void notifyItemRangeChanged(int positionStart, int itemCount) {
notifyItemRangeChanged(positionStart, itemCount, null);
}
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java
index 0df077d..3072c89 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java
@@ -151,6 +151,13 @@
int mPendingScrollPositionOffset = INVALID_OFFSET;
/**
+ * Set if {@link #scrollToPosition(int)} or its variants is called. Even if the state is
+ * restored lazily, we'll ignore the saved state because we'll have different span information
+ * and other state cannot be used anymore.
+ */
+ private boolean mIgnoreSavedStatePositions = false;
+
+ /**
* Keeps the mapping between the adapter positions and spans. This is necessary to provide
* a consistent experience when user scrolls the list.
*/
@@ -1220,6 +1227,10 @@
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
mPendingSavedState = (SavedState) state;
+ if (mIgnoreSavedStatePositions) {
+ mPendingSavedState.invalidateAnchorPositionInfo();
+ mPendingSavedState.invalidateSpanInfo();
+ }
requestLayout();
} else if (DEBUG) {
Log.d(TAG, "invalid saved state class");
@@ -2040,6 +2051,7 @@
}
mPendingScrollPosition = position;
mPendingScrollPositionOffset = INVALID_OFFSET;
+ mIgnoreSavedStatePositions = true;
requestLayout();
}
@@ -2062,6 +2074,7 @@
}
mPendingScrollPosition = position;
mPendingScrollPositionOffset = offset;
+ mIgnoreSavedStatePositions = true;
requestLayout();
}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/SchemaProcessor.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/SchemaProcessor.kt
index 45c481e..f0ed964 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/SchemaProcessor.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/SchemaProcessor.kt
@@ -16,7 +16,8 @@
package androidx.serialization.compiler
-import androidx.serialization.compiler.processing.steps.EnumCompilationStep
+import androidx.serialization.compiler.codegen.CodeGenEnvironment
+import androidx.serialization.compiler.processing.steps.EnumProcessingStep
import com.google.auto.common.BasicAnnotationProcessor
import com.google.auto.service.AutoService
import com.google.common.collect.ImmutableList
@@ -31,9 +32,12 @@
@AutoService(Processor::class)
@IncrementalAnnotationProcessor(ISOLATING)
class SchemaProcessor : BasicAnnotationProcessor() {
- override fun initSteps(): List<ProcessingStep> = ImmutableList.of(
- EnumCompilationStep(processingEnv)
- )
+ override fun initSteps(): List<ProcessingStep> {
+ val codeGenEnv = CodeGenEnvironment(processingEnv, this::class.qualifiedName)
+ return ImmutableList.of(
+ EnumProcessingStep(processingEnv, codeGenEnv)
+ )
+ }
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/CodeGenEnvironment.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/CodeGenEnvironment.kt
new file mode 100644
index 0000000..d60d2d4
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/CodeGenEnvironment.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen
+
+import androidx.serialization.compiler.nullability.NULLABILITY_ANNOTATIONS
+import androidx.serialization.compiler.nullability.Nullability
+import androidx.serialization.compiler.processing.isClassPresent
+import androidx.serialization.compiler.processing.packageElement
+import com.google.auto.common.GeneratedAnnotations
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.TypeElement
+
+/**
+ * Details about the code generation environment, including the generating class and the
+ * availability of annotations.
+ */
+internal data class CodeGenEnvironment(
+ val generatingClassName: String? = null,
+ val generated: Generated? = null,
+ val nullability: Nullability? = null
+) {
+ constructor(
+ processingEnv: ProcessingEnvironment,
+ generatingClass: String? = null
+ ) : this(
+ generatingClass,
+
+ generated = GeneratedAnnotations
+ .generatedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
+ .map { Generated(it) }
+ .orElse(null),
+
+ nullability = NULLABILITY_ANNOTATIONS.find { nullability ->
+ processingEnv.isClassPresent(nullability.qualifiedNonNullName) &&
+ processingEnv.isClassPresent(nullability.qualifiedNullableName)
+ }
+ )
+
+ data class Generated(
+ val packageName: String,
+ val simpleName: String = "Generated"
+ ) {
+ constructor(typeElement: TypeElement) : this(
+ packageName = typeElement.packageElement.qualifiedName.toString(),
+ simpleName = typeElement.simpleName.toString()
+ )
+ }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/ComplexTypeExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/ComplexTypeExt.kt
new file mode 100644
index 0000000..f883ac2
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/ComplexTypeExt.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen
+
+import androidx.serialization.compiler.schema.ProcessingType
+import androidx.serialization.schema.ComplexType
+import javax.lang.model.element.TypeElement
+
+/**
+ * The originating type element if present or null.
+ *
+ * This makes it easier for code generators to work with the base interface types for testing in
+ * isolation.
+ */
+internal val ComplexType.originatingElement: TypeElement?
+ get() = when (this) {
+ is ProcessingType -> this.element
+ else -> null
+ }
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/StringExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/StringExt.kt
new file mode 100644
index 0000000..54236f4
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/StringExt.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen
+
+import java.util.Locale
+
+/** Convert a string to lower camel case, treating '_' as a delimiter. */
+internal fun String.toLowerCamelCase(): String {
+ return if (contains('_')) {
+ split('_').joinToString(separator = "") { it.safeCapitalize() }.safeDecapitalize()
+ } else {
+ safeDecapitalize()
+ }
+}
+
+/** Replacement for [String.capitalize] that does not use experimental APIs. */
+private fun String.safeCapitalize(): String {
+ return when (length) {
+ 0 -> this
+ 1 -> toUpperCase(Locale.ENGLISH)
+ else -> substring(0, 1).toLowerCase(Locale.ENGLISH) + substring(1)
+ }
+}
+
+/** Replacement for [String.decapitalize] that does not use experimental APIs. */
+private fun String.safeDecapitalize(): String {
+ return when (length) {
+ 0 -> this
+ 1 -> toLowerCase(Locale.ENGLISH)
+ else -> substring(0, 1).toLowerCase(Locale.ENGLISH) + substring(1)
+ }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/EnumCoder.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/EnumCoder.kt
new file mode 100644
index 0000000..ac59842
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/EnumCoder.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import androidx.serialization.EnumValue
+import androidx.serialization.compiler.codegen.originatingElement
+import androidx.serialization.compiler.codegen.toLowerCamelCase
+import androidx.serialization.schema.Enum
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.Modifier.FINAL
+import javax.lang.model.element.Modifier.PRIVATE
+import javax.lang.model.element.Modifier.PUBLIC
+import javax.lang.model.element.Modifier.STATIC
+
+/** Get the name used for an enum coder for the supplied class name. */
+internal fun enumCoderName(enumName: ClassName): ClassName {
+ return ClassName.get(
+ enumName.packageName(),
+ enumName.simpleNames().joinToString(
+ prefix = "\$Serialization",
+ separator = "_",
+ postfix = "EnumCoder"
+ )
+ )
+}
+
+/** Generate the Java source file for an enum coder. */
+internal fun generateEnumCoder(enum: Enum, javaGenEnv: JavaGenEnvironment): JavaFile {
+ val enumClass = enum.name.toClassName()
+ val variableName = nameAllocatorOf("encode", "decode")
+ .newName(enum.name.simpleName.toLowerCamelCase())
+
+ val default = enum.values.first { it.id == EnumValue.DEFAULT }
+ val values = enum.values.filter { it.id != EnumValue.DEFAULT }.sortedBy { it.id }
+
+ return buildClass(enumCoderName(enumClass), javaGenEnv, enum.originatingElement) {
+ addModifiers(PUBLIC, FINAL)
+ addJavadoc("Serialization of enum {@link $T}.\n", enumClass)
+
+ constructor { addModifiers(PRIVATE) }
+
+ method("encode") {
+ addModifiers(PUBLIC, STATIC)
+ parameter(variableName, enumClass, javaGenEnv.nullable)
+ returns(TypeName.INT)
+
+ controlFlow("if ($N != null)", variableName) {
+ controlFlow("switch ($N)", variableName) {
+ for (value in values) {
+ switchCase("\$N", value.name) {
+ addStatement("return $L", value.id)
+ }
+ }
+ }
+ }
+
+ addCode("return $L; // $N\n", EnumValue.DEFAULT, default.name)
+ }
+
+ method("decode") {
+ addModifiers(PUBLIC, STATIC)
+ parameter("value", TypeName.INT)
+ returns(enumClass, javaGenEnv.nonNull)
+
+ controlFlow("switch (value)") {
+ for (value in values) {
+ switchCase("\$L", value.id) {
+ addStatement("return $T.$N", enumClass, value.name)
+ }
+ }
+
+ switchDefault {
+ addStatement("return $T.$N", enumClass, default.name)
+ }
+ }
+ }
+ }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaGenEnvironment.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaGenEnvironment.kt
new file mode 100644
index 0000000..1527353
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaGenEnvironment.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import androidx.serialization.compiler.codegen.CodeGenEnvironment
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeSpec
+
+/** Code generation environment with [AnnotationSpec] wrappers. */
+internal data class JavaGenEnvironment(
+ val generatingClassName: String? = null,
+ val generated: AnnotationSpec? = null,
+ val nonNull: AnnotationSpec? = null,
+ val nullable: AnnotationSpec? = null
+) {
+ fun applyGenerated(typeSpec: TypeSpec.Builder) {
+ when {
+ generated != null -> typeSpec.addAnnotation(generated)
+ generatingClassName != null -> {
+ typeSpec.addJavadoc("\nGenerated by \$L. Do not modify.\n", generatingClassName)
+ }
+ }
+ }
+
+ constructor(codeGenEnv: CodeGenEnvironment) : this(
+ generatingClassName = codeGenEnv.generatingClassName,
+
+ generated = codeGenEnv.generated?.let { generated ->
+ codeGenEnv.generatingClassName?.let { className ->
+ AnnotationSpec.builder(ClassName.get(generated.packageName, generated.simpleName))
+ .addMember("value", "\$S", className)
+ .build()
+ }
+ },
+
+ nonNull = codeGenEnv.nullability?.let {
+ AnnotationSpec.builder(ClassName.get(it.packageName, it.nonNull)).build()
+ },
+
+ nullable = codeGenEnv.nullability?.let {
+ AnnotationSpec.builder(ClassName.get(it.packageName, it.nullable)).build()
+ }
+ )
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaPoetExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaPoetExt.kt
new file mode 100644
index 0000000..0a2f2f3
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaPoetExt.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.NameAllocator
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Element
+
+internal const val L = "\$L"
+internal const val N = "\$N"
+internal const val S = "\$S"
+internal const val T = "\$T"
+
+internal fun nameAllocatorOf(vararg names: String): NameAllocator {
+ return NameAllocator().apply { names.forEach { newName(it, it) } }
+}
+
+internal inline fun buildClass(
+ className: ClassName,
+ javaGenEnv: JavaGenEnvironment,
+ vararg originatingElements: Element?,
+ init: TypeSpec.Builder.() -> Unit
+): JavaFile {
+ return TypeSpec.classBuilder(className).apply {
+ init()
+ afterInit(javaGenEnv, originatingElements)
+ }.toJavaFile(className)
+}
+
+internal fun TypeSpec.Builder.toJavaFile(className: ClassName): JavaFile {
+ return JavaFile.builder(className.packageName(), build()).indent(" ").build()
+}
+
+internal inline fun TypeSpec.Builder.constructor(init: MethodSpec.Builder.() -> Unit) {
+ addMethod(MethodSpec.constructorBuilder().apply(init).build())
+}
+
+internal inline fun TypeSpec.Builder.method(name: String, init: MethodSpec.Builder.() -> Unit) {
+ addMethod(MethodSpec.methodBuilder(name).apply(init).build())
+}
+
+internal fun MethodSpec.Builder.parameter(
+ name: String,
+ type: TypeName,
+ vararg annotations: AnnotationSpec?
+) {
+ addParameter(ParameterSpec.builder(type, name).run {
+ annotations.forEach { if (it != null) addAnnotation(it) }
+ build()
+ })
+}
+
+internal fun MethodSpec.Builder.returns(
+ type: TypeName,
+ vararg annotations: AnnotationSpec?
+) {
+ returns(type)
+ annotations.forEach { if (it != null) addAnnotation(it) }
+}
+
+internal inline fun MethodSpec.Builder.controlFlow(
+ format: String,
+ vararg args: Any,
+ body: MethodSpec.Builder.() -> Unit
+) {
+ beginControlFlow(format, *args)
+ body()
+ endControlFlow()
+}
+
+internal inline fun MethodSpec.Builder.switchCase(
+ format: String,
+ vararg args: Any,
+ body: MethodSpec.Builder.() -> Unit
+) {
+ addCode("case $format:\n$>", *args)
+ body()
+ addCode("$<")
+}
+
+internal inline fun MethodSpec.Builder.switchDefault(
+ body: MethodSpec.Builder.() -> Unit
+) {
+ addCode("default:\n$>")
+ body()
+ addCode("$<")
+}
+
+internal fun TypeSpec.Builder.afterInit(
+ javaGenEnv: JavaGenEnvironment,
+ originatingElements: Array<out Element?>
+) {
+ javaGenEnv.applyGenerated(this)
+ originatingElements.forEach { if (it != null) addOriginatingElement(it) }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/TypeNameExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/TypeNameExt.kt
new file mode 100644
index 0000000..387bedb
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/TypeNameExt.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import androidx.serialization.schema.TypeName
+import com.squareup.javapoet.ClassName
+
+/** Convert a type name to a JavaPoet class name */
+internal fun TypeName.toClassName(): ClassName {
+ return when (names.size) {
+ 1 -> ClassName.get(packageName.orEmpty(), names.single())
+ else -> ClassName.get(
+ packageName.orEmpty(),
+ names.first(),
+ *names.drop(1).toTypedArray()
+ )
+ }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/KnownAnnotations.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/KnownAnnotations.kt
new file mode 100644
index 0000000..00f798a
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/KnownAnnotations.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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.serialization.compiler.nullability
+
+/**
+ * A list of [Nullability] annotations ordered by preference.
+ *
+ * This is used for code generation, as `androidx.annotation` may not be present in the
+ * compile-time class path and the code generator may fall back to other annotation packages.
+ * These are derived from the set of annotations recognized by the Kotlin compiler.
+ *
+ * These will also be used for determining nullability of nested messages.
+ */
+internal val NULLABILITY_ANNOTATIONS = listOf(
+ Nullability("androidx.annotation"),
+ Nullability("org.jetbrains.annotations", nonNull = "NotNull"),
+ Nullability("javax.annotation", nonNull = "Nonnull"),
+ Nullability("android.support.annotation"),
+ Nullability("android.annotation"),
+ Nullability("com.android.annotations"),
+ Nullability("org.eclipse.jdt.annotation"),
+ Nullability("org.checkerframework.checker.nullness.qual"),
+ Nullability("io.reactivex.annotations")
+)
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/Nullability.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/Nullability.kt
new file mode 100644
index 0000000..8bbe9a8
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/Nullability.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 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.serialization.compiler.nullability
+
+/** A pair of non-null and nullable annotations */
+internal data class Nullability(
+ val packageName: String,
+ val nonNull: String = "NonNull",
+ val nullable: String = "Nullable"
+) {
+ val qualifiedNonNullName = "$packageName.$nonNull"
+ val qualifiedNullableName = "$packageName.$nullable"
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ElementExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ElementExt.kt
index e0ffbe2..643661f 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ElementExt.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ElementExt.kt
@@ -20,8 +20,10 @@
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.Element
import javax.lang.model.element.Modifier
+import javax.lang.model.element.PackageElement
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
+import javax.lang.model.util.SimpleElementVisitor6
import kotlin.reflect.KClass
/** Casts this element to a [TypeElement] using [MoreElements.asType]. */
@@ -39,6 +41,33 @@
return Modifier.PRIVATE in modifiers
}
+/**
+ * Determines if this element is visible to its own package.
+ *
+ * A private element or an element enclosed within a private element is not visible to its package.
+ */
+internal fun Element.isVisibleToPackage(): Boolean {
+ return accept(IsVisibleToPackageVisitor, null)
+}
+
+private object IsVisibleToPackageVisitor : SimpleElementVisitor6<Boolean, Nothing?>() {
+ override fun visitPackage(e: PackageElement, p: Nothing?): Boolean {
+ return true
+ }
+
+ override fun defaultAction(e: Element, p: Nothing?): Boolean {
+ return if (e.isPrivate()) {
+ false
+ } else {
+ e.enclosingElement.accept(this, null)
+ }
+ }
+}
+
+/** Gets the enclosing package element using [MoreElements.getPackage]. */
+internal val Element.packageElement: PackageElement
+ get() = MoreElements.getPackage(this)
+
/** Get an annotation mirror on this element, throwing if it is not directly present. */
internal operator fun Element.get(annotationClass: KClass<out Annotation>): AnnotationMirror {
return requireNotNull(getAnnotationMirror(annotationClass)) {
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/MessagerExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ProcessingExt.kt
similarity index 86%
rename from serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/MessagerExt.kt
rename to serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ProcessingExt.kt
index 2418978..6c5a561 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/MessagerExt.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ProcessingExt.kt
@@ -17,6 +17,7 @@
package androidx.serialization.compiler.processing
import javax.annotation.processing.Messager
+import javax.annotation.processing.ProcessingEnvironment
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.AnnotationValue
import javax.lang.model.element.Element
@@ -24,6 +25,11 @@
import javax.tools.Diagnostic.Kind.WARNING
import kotlin.reflect.KClass
+/** Determine if a qualified class name is present in the processing environment. */
+internal fun ProcessingEnvironment.isClassPresent(qualifiedName: String): Boolean {
+ return elementUtils.getTypeElement(qualifiedName) != null
+}
+
/** Print [message] as a warning with optional positional information. */
internal inline fun Messager.warn(
element: Element? = null,
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/TypeMirrorExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/TypeMirrorExt.kt
deleted file mode 100644
index 92173a5..0000000
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/TypeMirrorExt.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2020 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.serialization.compiler.processing
-
-import com.google.auto.common.MoreTypes
-import javax.lang.model.element.Element
-import javax.lang.model.element.TypeElement
-import javax.lang.model.type.TypeMirror
-
-/** Get the element represented by this type using [MoreTypes.asElement]. */
-internal fun TypeMirror.asElement(): Element {
- return MoreTypes.asElement(this)
-}
-
-/** Convenience wrapper for [asElement] that casts to type element. */
-internal fun TypeMirror.asTypeElement(): TypeElement {
- return asElement().asTypeElement()
-}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractStep.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractProcessingStep.kt
similarity index 97%
rename from serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractStep.kt
rename to serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractProcessingStep.kt
index 6e50b3d..09be6e4 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractStep.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractProcessingStep.kt
@@ -24,7 +24,7 @@
import kotlin.reflect.KClass
/** Wrapper for [ProcessingStep] for more idiomatic Kotlin. */
-internal abstract class AbstractStep(
+internal abstract class AbstractProcessingStep(
vararg annotations: KClass<out Annotation>
) : ProcessingStep {
private val javaAnnotations = ImmutableSet
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStep.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStep.kt
similarity index 71%
rename from serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStep.kt
rename to serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStep.kt
index 7153645..d8dd73b 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStep.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStep.kt
@@ -18,11 +18,15 @@
import androidx.serialization.EnumValue
import androidx.serialization.Reserved
+import androidx.serialization.compiler.codegen.CodeGenEnvironment
+import androidx.serialization.compiler.codegen.java.JavaGenEnvironment
+import androidx.serialization.compiler.codegen.java.generateEnumCoder
import androidx.serialization.compiler.processing.asTypeElement
import androidx.serialization.compiler.processing.asVariableElement
import androidx.serialization.compiler.processing.error
import androidx.serialization.compiler.processing.get
import androidx.serialization.compiler.processing.getAnnotationMirror
+import androidx.serialization.compiler.processing.isVisibleToPackage
import androidx.serialization.compiler.processing.isPrivate
import androidx.serialization.compiler.processing.processReserved
import androidx.serialization.compiler.schema.Enum
@@ -35,16 +39,18 @@
import kotlin.reflect.KClass
/** Processing step that parses and validates enums, and generates enum coders. */
-internal class EnumCompilationStep(
+internal class EnumProcessingStep(
private val processingEnv: ProcessingEnvironment,
+ private val codeGenEnv: CodeGenEnvironment,
private val onEnum: ((Enum) -> Unit)? = null
-) : AbstractStep(EnumValue::class, Reserved::class) {
+) : AbstractProcessingStep(EnumValue::class, Reserved::class) {
+ private val javaGenEnv = JavaGenEnvironment(codeGenEnv)
private val messager: Messager = processingEnv.messager
override fun process(elementsByAnnotation: Map<KClass<out Annotation>, Set<Element>>) {
elementsByAnnotation[EnumValue::class]
?.let(::processEnumValues)
- ?.forEach(::processEnumType)
+ ?.forEach(::processEnumClass)
}
/**
@@ -57,11 +63,11 @@
private fun processEnumValues(elements: Set<Element>): Set<TypeElement> {
if (elements.isEmpty()) return emptySet()
- val types = mutableSetOf<TypeElement>()
+ val enumClasses = mutableSetOf<TypeElement>()
for (element in elements) {
if (element.kind == ENUM_CONSTANT) {
- types += element.enclosingElement.asTypeElement()
+ enumClasses += element.enclosingElement.asTypeElement()
} else {
messager.error(element, EnumValue::class) {
"@${EnumValue::class.simpleName} must annotate an enum constant"
@@ -69,7 +75,7 @@
}
}
- return types
+ return enumClasses
}
/**
@@ -80,23 +86,31 @@
* reads [EnumValue.id] and constructs an [Enum] and dispatches it to [onEnum]. It fills
* [Enum.reserved] using [processReserved].
*/
- private fun processEnumType(typeElement: TypeElement) {
- check(typeElement.kind == ElementKind.ENUM) {
- "Expected $typeElement to be an enum class"
+ private fun processEnumClass(enumClass: TypeElement) {
+ check(enumClass.kind == ElementKind.ENUM) {
+ "Expected $enumClass to be an enum class"
}
var hasError = false
- if (typeElement.isPrivate()) {
- messager.error(typeElement) {
- "Enum ${typeElement.qualifiedName} is private and cannot be serialized"
+ if (!enumClass.isVisibleToPackage()) {
+ if (enumClass.isPrivate()) {
+ messager.error(enumClass) {
+ "Enum ${enumClass.qualifiedName} is private and cannot be serialized"
+ }
+ } else {
+ messager.error(enumClass) {
+ "Enum ${enumClass.qualifiedName} is not visible to its package and cannot " +
+ "be serialized"
+ }
}
+
hasError = true
}
val values = mutableSetOf<Enum.Value>()
- for (element in typeElement.enclosedElements) {
+ for (element in enumClass.enclosedElements) {
if (element.kind == ENUM_CONSTANT) {
val annotation = element.getAnnotationMirror(EnumValue::class)
@@ -112,6 +126,10 @@
}
}
- if (!hasError) onEnum?.invoke(Enum(typeElement, values, processReserved(typeElement)))
+ if (!hasError) {
+ val enum = Enum(enumClass, values, processReserved(enumClass))
+ generateEnumCoder(enum, javaGenEnv).writeTo(processingEnv.filer)
+ onEnum?.invoke(enum)
+ }
}
}
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/SchemaProcessorTest.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/SchemaProcessorTest.kt
index d602752..4ba5dc8 100644
--- a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/SchemaProcessorTest.kt
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/SchemaProcessorTest.kt
@@ -23,10 +23,10 @@
import org.junit.Test
import javax.tools.JavaFileObject
-/** Integration tests for [SchemaProcessor]. */
+/** Unit tests for [SchemaProcessor]. */
class SchemaProcessorTest {
@Test
- fun testSucceedsWithoutWarningsa() {
+ fun testSucceedsWithoutWarnings() {
val testClass = JavaFileObjects.forSourceString("Test", "public class Test {}")
assertThat(compile(testClass)).succeededWithoutWarnings()
}
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/TestEnum.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/TestEnum.kt
new file mode 100644
index 0000000..aa0c509
--- /dev/null
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/TestEnum.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 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.serialization.compiler
+
+import androidx.serialization.schema.Enum
+import androidx.serialization.schema.Reserved
+import androidx.serialization.schema.TypeName
+
+/** Customizable [Enum] implementation with default values scaffolding. */
+internal data class TestEnum(
+ override val name: TypeName = TypeName("com.example", "TestEnum"),
+ override val values: Set<Value> = setOf(
+ Value(0, "DEFAULT"),
+ Value(1, "ONE"),
+ Value(2, "TWO")
+ ),
+ override val reserved: Reserved = Reserved.empty()
+) : Enum {
+ data class Value(
+ override val id: Int,
+ override val name: String
+ ) : Enum.Value
+}
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/Environment.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/Environment.kt
new file mode 100644
index 0000000..b3a875c
--- /dev/null
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/Environment.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen
+
+import androidx.serialization.compiler.codegen.CodeGenEnvironment.Generated
+import androidx.serialization.compiler.codegen.java.JavaGenEnvironment
+import androidx.serialization.compiler.nullability.Nullability
+import kotlin.reflect.KClass
+
+internal fun codeGenEnv(
+ testClass: KClass<*>,
+ generated: Generated? = DEFAULT_GENERATED,
+ nullability: Nullability? = DEFAULT_NULLABILITY
+): CodeGenEnvironment {
+ return CodeGenEnvironment(testClass.qualifiedName, generated, nullability)
+}
+
+internal fun javaGenEnv(
+ testClass: KClass<*>,
+ generated: Generated? = DEFAULT_GENERATED,
+ nullability: Nullability? = DEFAULT_NULLABILITY
+): JavaGenEnvironment {
+ return JavaGenEnvironment(codeGenEnv(testClass, generated, nullability))
+}
+
+internal val DEFAULT_GENERATED = Generated(packageName = "javax.annotation")
+internal val DEFAULT_NULLABILITY = Nullability("androidx.annotation")
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/java/EnumCoderTest.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/java/EnumCoderTest.kt
new file mode 100644
index 0000000..57c12f8
--- /dev/null
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/java/EnumCoderTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import androidx.serialization.compiler.TestEnum
+import androidx.serialization.compiler.codegen.javaGenEnv
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ClassName
+import org.junit.Test
+
+/** Unit tests for [generateEnumCoder] and [enumCoderName]. */
+class EnumCoderTest {
+ @Test
+ fun testEnumCoderName() {
+ assertThat(enumCoderName(ClassName.get("com.example", "Test")))
+ .isEqualTo(ClassName.get("com.example", "\$SerializationTestEnumCoder"))
+
+ assertThat(enumCoderName(ClassName.get("", "Outer", "Inner", "TestEnum")))
+ .isEqualTo(ClassName.get("", "\$SerializationOuter_Inner_TestEnumEnumCoder"))
+ }
+
+ @Test
+ fun testGenerateEnumCoder() {
+ assertThat(generateEnumCoder(TestEnum(), javaGenEnv(this::class)).toString()).contains("""
+ package com.example;
+
+ import androidx.annotation.NonNull;
+ import androidx.annotation.Nullable;
+ import javax.annotation.Generated;
+
+ /**
+ * Serialization of enum {@link TestEnum}.
+ */
+ @Generated("androidx.serialization.compiler.codegen.java.EnumCoderTest")
+ public final class ${'$'}SerializationTestEnumEnumCoder {
+ private ${'$'}SerializationTestEnumEnumCoder() {
+ }
+
+ public static int encode(@Nullable TestEnum testEnum) {
+ if (testEnum != null) {
+ switch (testEnum) {
+ case ONE:
+ return 1;
+ case TWO:
+ return 2;
+ }
+ }
+ return 0; // DEFAULT
+ }
+
+ @NonNull
+ public static TestEnum decode(int value) {
+ switch (value) {
+ case 1:
+ return TestEnum.ONE;
+ case 2:
+ return TestEnum.TWO;
+ default:
+ return TestEnum.DEFAULT;
+ }
+ }
+ }
+ """.trimIndent())
+ }
+}
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/ProcessReservedTest.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/ProcessReservedTest.kt
index 02dcb83..a6c517d 100644
--- a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/ProcessReservedTest.kt
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/ProcessReservedTest.kt
@@ -16,7 +16,7 @@
package androidx.serialization.compiler.processing
-import androidx.serialization.compiler.processing.steps.AbstractStep
+import androidx.serialization.compiler.processing.steps.AbstractProcessingStep
import androidx.serialization.schema.Reserved
import com.google.auto.common.BasicAnnotationProcessor
import com.google.common.truth.Truth.assertThat
@@ -76,9 +76,9 @@
return processor.reserved
}
- private class ReservedStep(
+ private class ReservedProcessingStep(
private val onReserved: (Reserved) -> Unit
- ) : AbstractStep(androidx.serialization.Reserved::class) {
+ ) : AbstractProcessingStep(androidx.serialization.Reserved::class) {
override fun process(elementsByAnnotation: Map<KClass<out Annotation>, Set<Element>>) {
elementsByAnnotation[androidx.serialization.Reserved::class]?.forEach {
onReserved(processReserved(it.asTypeElement()))
@@ -90,7 +90,7 @@
lateinit var reserved: Reserved
override fun initSteps(): List<ProcessingStep> = listOf(
- ReservedStep { reserved = it }
+ ReservedProcessingStep { reserved = it }
)
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStepTest.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStepTest.kt
similarity index 70%
rename from serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStepTest.kt
rename to serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStepTest.kt
index d0b7919..d83a3ee 100644
--- a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStepTest.kt
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStepTest.kt
@@ -16,6 +16,7 @@
package androidx.serialization.compiler.processing.steps
+import androidx.serialization.compiler.codegen.CodeGenEnvironment
import androidx.serialization.compiler.schema.Enum
import androidx.serialization.schema.Reserved
import com.google.auto.common.BasicAnnotationProcessor
@@ -29,15 +30,15 @@
import javax.lang.model.SourceVersion
import javax.tools.JavaFileObject
-/** Unit tests for [EnumCompilationStep]. */
-class EnumCompilationStepTest {
+/** Unit tests for [EnumProcessingStep]. */
+class EnumProcessingStepTest {
private val enumValueCorrespondence = Correspondence.from({
actual: Enum.Value?, expected: Pair<Int, String>? ->
actual?.id == expected?.first && actual?.name == expected?.second
}, "has ID and name")
@Test
- fun testEnum() {
+ fun testParsing() {
val enum = compileEnum(JavaFileObjects.forSourceString("TestEnum", """
import androidx.serialization.EnumValue;
@@ -70,8 +71,25 @@
""".trimIndent())
assertThat(compile(testEnum)).hadErrorContaining(
- "Enum com.example.PrivateEnumTest.PrivateEnum is private and cannot be serialized"
- )
+ "Enum com.example.PrivateEnumTest.PrivateEnum is private and cannot be serialized")
+ }
+
+ @Test
+ fun testInvalidPrivateNestedEnum() {
+ val testEnum = JavaFileObjects.forSourceString("PrivateNestedEnumTest", """
+ import androidx.serialization.EnumValue;
+
+ public class PrivateNestedEnumTest {
+ private static class NestedClass {
+ public enum NestedEnum {
+ @EnumValue(EnumValue.DEFAULT) TEST
+ }
+ }
+ }
+ """.trimIndent())
+
+ assertThat(compile(testEnum)).hadErrorContaining(
+ "Enum PrivateNestedEnumTest.NestedClass.NestedEnum is not visible to its package")
}
@Test
@@ -106,6 +124,26 @@
"annotated with @EnumValue")
}
+ @Test
+ fun testCoderGeneration() {
+ val testEnum = JavaFileObjects.forSourceString("com.example.Test", """
+ package com.example;
+ import androidx.serialization.EnumValue;
+
+ public enum Test {
+ @EnumValue(EnumValue.DEFAULT)
+ DEFAULT,
+ @EnumValue(1)
+ ONE,
+ @EnumValue(2)
+ TWO
+ }
+ """.trimIndent())
+
+ assertThat(compile(testEnum))
+ .generatedSourceFile("com.example.\$SerializationTestEnumCoder")
+ }
+
private fun compile(vararg sources: JavaFileObject): Compilation {
return javac().withProcessors(SchemaCompilationProcessor()).compile(*sources)
}
@@ -115,18 +153,16 @@
assertThat(javac().withProcessors(processor).compile(source))
.succeededWithoutWarnings()
- val enums = processor.enums
- assertThat(enums).hasSize(1)
-
- return enums.single()
+ return processor.enum
}
private class SchemaCompilationProcessor : BasicAnnotationProcessor() {
- val enums = mutableSetOf<Enum>()
+ lateinit var enum: Enum
- override fun initSteps(): List<ProcessingStep> = listOf(
- EnumCompilationStep(processingEnv) { enums += it }
- )
+ override fun initSteps(): List<ProcessingStep> {
+ val codeGenEnv = CodeGenEnvironment(EnumProcessingStepTest::class.qualifiedName)
+ return listOf(EnumProcessingStep(processingEnv, codeGenEnv) { enum = it })
+ }
override fun getSupportedSourceVersion(): SourceVersion {
return SourceVersion.latest()
diff --git a/settings.gradle b/settings.gradle
index 6b55449..f28d43b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -247,6 +247,11 @@
includeProject(":slice-benchmark", "slices/benchmark")
includeProject(":slice-remotecallback", "slices/remotecallback")
includeProject(":slidingpanelayout:slidingpanelayout", "slidingpanelayout/slidingpanelayout")
+includeProject(":startup:startup-runtime", "startup/startup-runtime")
+includeProject(":startup:startup-runtime-lint", "startup/startup-runtime-lint")
+includeProject(":startup:integration-tests:first-library", "startup/integration-tests/first-library")
+includeProject(":startup:integration-tests:second-library", "startup/integration-tests/second-library")
+includeProject(":startup:integration-tests:test-app", "startup/integration-tests/test-app")
includeProject(":sqlite:sqlite", "sqlite/sqlite")
includeProject(":sqlite:sqlite-ktx", "sqlite/sqlite-ktx")
includeProject(":sqlite:sqlite-framework", "sqlite/sqlite-framework")
diff --git a/slices/builders/api/1.1.0-alpha02.txt b/slices/builders/api/1.1.0-alpha02.txt
index 23896e6..63b9453 100644
--- a/slices/builders/api/1.1.0-alpha02.txt
+++ b/slices/builders/api/1.1.0-alpha02.txt
@@ -66,6 +66,8 @@
public static class ListBuilder.InputRangeBuilder {
ctor public ListBuilder.InputRangeBuilder();
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction, boolean);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setContentDescription(CharSequence);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(android.app.PendingIntent);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(androidx.remotecallback.RemoteCallback);
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
index 23896e6..63b9453 100644
--- a/slices/builders/api/current.txt
+++ b/slices/builders/api/current.txt
@@ -66,6 +66,8 @@
public static class ListBuilder.InputRangeBuilder {
ctor public ListBuilder.InputRangeBuilder();
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction, boolean);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setContentDescription(CharSequence);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(android.app.PendingIntent);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(androidx.remotecallback.RemoteCallback);
diff --git a/slices/builders/api/public_plus_experimental_1.1.0-alpha02.txt b/slices/builders/api/public_plus_experimental_1.1.0-alpha02.txt
index 23896e6..63b9453 100644
--- a/slices/builders/api/public_plus_experimental_1.1.0-alpha02.txt
+++ b/slices/builders/api/public_plus_experimental_1.1.0-alpha02.txt
@@ -66,6 +66,8 @@
public static class ListBuilder.InputRangeBuilder {
ctor public ListBuilder.InputRangeBuilder();
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction, boolean);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setContentDescription(CharSequence);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(android.app.PendingIntent);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(androidx.remotecallback.RemoteCallback);
diff --git a/slices/builders/api/public_plus_experimental_current.txt b/slices/builders/api/public_plus_experimental_current.txt
index 23896e6..63b9453 100644
--- a/slices/builders/api/public_plus_experimental_current.txt
+++ b/slices/builders/api/public_plus_experimental_current.txt
@@ -66,6 +66,8 @@
public static class ListBuilder.InputRangeBuilder {
ctor public ListBuilder.InputRangeBuilder();
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction, boolean);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setContentDescription(CharSequence);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(android.app.PendingIntent);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(androidx.remotecallback.RemoteCallback);
diff --git a/slices/builders/api/restricted_1.1.0-alpha02.txt b/slices/builders/api/restricted_1.1.0-alpha02.txt
index b300308..895aec1 100644
--- a/slices/builders/api/restricted_1.1.0-alpha02.txt
+++ b/slices/builders/api/restricted_1.1.0-alpha02.txt
@@ -67,6 +67,8 @@
public static class ListBuilder.InputRangeBuilder {
ctor public ListBuilder.InputRangeBuilder();
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction, boolean);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setContentDescription(CharSequence);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(android.app.PendingIntent);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(androidx.remotecallback.RemoteCallback);
diff --git a/slices/builders/api/restricted_current.txt b/slices/builders/api/restricted_current.txt
index b300308..895aec1 100644
--- a/slices/builders/api/restricted_current.txt
+++ b/slices/builders/api/restricted_current.txt
@@ -67,6 +67,8 @@
public static class ListBuilder.InputRangeBuilder {
ctor public ListBuilder.InputRangeBuilder();
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder addEndItem(androidx.slice.builders.SliceAction, boolean);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setContentDescription(CharSequence);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(android.app.PendingIntent);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(androidx.remotecallback.RemoteCallback);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
index 069d545..719984c 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
@@ -687,6 +687,10 @@
private IconCompat mTitleIcon;
private int mTitleImageMode;
private boolean mTitleItemLoading;
+ private boolean mHasDefaultToggle;
+ private List<Object> mEndItems = new ArrayList<>();
+ private List<Integer> mEndTypes = new ArrayList<>();
+ private List<Boolean> mEndLoads = new ArrayList<>();
/**
* Builder to construct a input range row.
@@ -716,6 +720,41 @@
}
/**
+ * Adds an action to the end items of the input range builder. Only one non-custom toggle
+ * can be added. If a non-custom toggle has already been added, this will throw
+ * {@link IllegalStateException}.
+ */
+ @NonNull
+ public InputRangeBuilder addEndItem(@NonNull SliceAction action) {
+ return addEndItem(action, false /* isLoading */);
+ }
+
+ /**
+ * Adds an action to the end items of the input range builder. Only one non-custom toggle
+ * can be added. If a non-custom toggle has already been added, this will throw
+ * {@link IllegalStateException}.
+ * <p>
+ * Use this method to specify content that will appear in the template once it's been
+ * loaded.
+ * </p>
+ * @param isLoading indicates whether the app is doing work to load the added content in the
+ * background or not.
+ */
+ @NonNull
+ public InputRangeBuilder addEndItem(@NonNull SliceAction action, boolean isLoading) {
+ if (mHasDefaultToggle) {
+ throw new IllegalStateException("Only one non-custom toggle can be added "
+ + "in a single row. If you would like to include multiple toggles "
+ + "in a row, set a custom icon for each toggle.");
+ }
+ mEndItems.add(action);
+ mEndTypes.add(TYPE_ACTION);
+ mEndLoads.add(isLoading);
+ mHasDefaultToggle = action.getImpl().isDefaultToggle();
+ return this;
+ }
+
+ /**
* Sets the title item to be the provided icon. There can only be one title item, this
* will replace any other title items that may have been set.
* <p>
@@ -879,6 +918,36 @@
* @hide
*/
@RestrictTo(LIBRARY)
+ public static final int TYPE_ACTION = 2;
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
+ public List<Object> getEndItems() {
+ return mEndItems;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
+ public List<Integer> getEndTypes() {
+ return mEndTypes;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
+ public List<Boolean> getEndLoads() {
+ return mEndLoads;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
public int getMin() {
return mMin;
}
@@ -1698,6 +1767,7 @@
* @hide
*/
@RestrictTo(LIBRARY)
+ @Nullable
public CharSequence getSubtitle() {
return mSubtitle;
}
@@ -1714,6 +1784,7 @@
* @hide
*/
@RestrictTo(LIBRARY)
+ @Nullable
public CharSequence getSummary() {
return mSummary;
}
@@ -1730,6 +1801,7 @@
* @hide
*/
@RestrictTo(LIBRARY)
+ @Nullable
public SliceAction getPrimaryAction() {
return mPrimaryAction;
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
index 288bdba..b224476 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java
@@ -373,6 +373,7 @@
private PendingIntent mAction;
private IconCompat mThumb;
private Slice mStartItem;
+ private ArrayList<Slice> mEndItems = new ArrayList<>();
InputRangeBuilderImpl(Slice.Builder sb, InputRangeBuilder builder) {
super(sb, null);
@@ -391,6 +392,14 @@
setTitleItem(builder.getTitleIcon(), builder.getTitleImageMode(),
builder.isTitleItemLoading());
}
+ List<Object> endItems = builder.getEndItems();
+ List<Integer> endTypes = builder.getEndTypes();
+ List<Boolean> endLoads = builder.getEndLoads();
+ for (int i = 0; i < endItems.size(); i++) {
+ if (endTypes.get(i) == InputRangeBuilder.TYPE_ACTION) {
+ addEndItem((SliceAction) endItems.get(i), endLoads.get(i));
+ }
+ }
}
void setTitleItem(IconCompat icon, int imageMode, boolean isLoading) {
@@ -402,6 +411,14 @@
mStartItem = sb.addHints(HINT_TITLE).build();
}
+ private void addEndItem(@NonNull SliceAction action, boolean isLoading) {
+ Slice.Builder sb = new Slice.Builder(getBuilder());
+ if (isLoading) {
+ sb.addHints(HINT_PARTIAL);
+ }
+ mEndItems.add(action.buildSlice(sb));
+ }
+
@Override
public void apply(Slice.Builder builder) {
if (mAction == null) {
@@ -416,6 +433,9 @@
if (mStartItem != null) {
builder.addSubSlice(mStartItem);
}
+ for (int i = 0; i < mEndItems.size(); i++) {
+ builder.addSubSlice(mEndItems.get(i));
+ }
}
}
diff --git a/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java b/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
index 0ae0e6c..8a76e87 100644
--- a/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
+++ b/slices/test/src/main/java/androidx/slice/test/SampleSliceProvider.java
@@ -814,6 +814,8 @@
private Slice createRichInputRange(Uri sliceUri) {
IconCompat thumbIcon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
IconCompat titleIcon = IconCompat.createWithResource(getContext(), R.drawable.ic_car);
+ IconCompat checkBoxIcon = IconCompat.createWithResource(getContext(),
+ R.drawable.toggle_check);
SliceAction primaryAction = SliceAction.create(
getBroadcastIntent(ACTION_TOAST, "open rich star rating"), thumbIcon, ICON_IMAGE,
"Rate");
@@ -823,6 +825,9 @@
.setTitle("Rich InputRangeBuilder demo"))
.addInputRange(new InputRangeBuilder()
.setTitleItem(titleIcon, ListBuilder.ICON_IMAGE)
+ .addEndItem(SliceAction.createToggle(
+ getBroadcastIntent(ACTION_TOAST, "click checkbox"), checkBoxIcon,
+ "checkbox", false))
.setTitle("Rich star rating")
.setMin(5)
.setThumb(thumbIcon)
diff --git a/slices/view/src/main/java/androidx/slice/widget/RowContent.java b/slices/view/src/main/java/androidx/slice/widget/RowContent.java
index 88735f8..236ac24 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RowContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RowContent.java
@@ -97,26 +97,40 @@
// Filter anything not viable for displaying in a row
ArrayList<SliceItem> rowItems = filterInvalidItems(rowSlice);
// If we've only got one item that's a slice / action use those items instead
+ boolean isOneItem = false;
if (rowItems.size() == 1 && (FORMAT_ACTION.equals(rowItems.get(0).getFormat())
|| FORMAT_SLICE.equals(rowItems.get(0).getFormat()))
&& !rowItems.get(0).hasAnyHints(HINT_SHORTCUT, HINT_TITLE)) {
if (isValidRow(rowItems.get(0))) {
+ isOneItem = true;
rowSlice = rowItems.get(0);
rowItems = filterInvalidItems(rowSlice);
}
}
if (SUBTYPE_RANGE.equals(rowSlice.getSubType())) {
- //It must be a Range or InputRange
- if (mStartItem == null) {
+ // It must be a Range or InputRange without StartItem/EndItem.
+ if (SliceQuery.findSubtype(rowSlice, FORMAT_ACTION, SUBTYPE_RANGE) == null
+ || isOneItem) {
mRange = rowSlice;
} else {
// Remove the startItem we already know about
rowItems.remove(mStartItem);
- //After removing startItem, then it must be action<range>
- if (isValidRow(rowItems.get(0))) {
- rowSlice = rowItems.get(0);
- rowItems = filterInvalidItems(rowSlice);
- mRange = rowSlice;
+ // After removing startItem, if size == 1, then it must be action<range>
+ if (rowItems.size() == 1) {
+ if (isValidRow(rowItems.get(0))) {
+ rowSlice = rowItems.get(0);
+ rowItems = filterInvalidItems(rowSlice);
+ mRange = rowSlice;
+ // Remove thumb icon, don't let it be added into endItems
+ rowItems.remove(getInputRangeThumb());
+ }
+ } else { // It must has end items.
+ mRange = SliceQuery.findSubtype(rowSlice, FORMAT_ACTION, SUBTYPE_RANGE);
+ // Refactor the rowItems, then it can be parsed correctly
+ ArrayList<SliceItem> rangeItems = filterInvalidItems(mRange);
+ rangeItems.remove(getInputRangeThumb());
+ rowItems.remove(mRange);
+ rowItems.addAll(rangeItems);
}
}
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/RowView.java b/slices/view/src/main/java/androidx/slice/widget/RowView.java
index cfd0f31..0f9a429 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RowView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RowView.java
@@ -117,6 +117,7 @@
private LinearLayout mRootView;
private LinearLayout mStartContainer;
private LinearLayout mContent;
+ private LinearLayout mSubContent;
private TextView mPrimaryText;
private TextView mSecondaryText;
private TextView mLastUpdatedText;
@@ -187,6 +188,7 @@
mStartContainer = (LinearLayout) findViewById(R.id.icon_frame);
mContent = (LinearLayout) findViewById(android.R.id.content);
+ mSubContent = (LinearLayout) findViewById(R.id.subcontent);
mPrimaryText = (TextView) findViewById(android.R.id.title);
mSecondaryText = (TextView) findViewById(android.R.id.summary);
mLastUpdatedText = (TextView) findViewById(R.id.last_updated);
@@ -271,7 +273,7 @@
*/
private int getRowContentHeight() {
int rowHeight = mRowContent.getHeight(mSliceStyle, mViewPolicy);
- if (mRangeBar != null) {
+ if (mRangeBar != null && mStartItem == null) {
rowHeight -= mSliceStyle.getRowRangeHeight();
}
if (mSelectionSpinner != null) {
@@ -338,7 +340,7 @@
} else {
mRootView.setVisibility(View.GONE);
}
- if (mRangeBar != null) {
+ if (mRangeBar != null && mStartItem == null) {
// If we're on a platform where SeekBar can't be stretched vertically, find out the
// exact size it would like to be so we can honor that in onLayout.
if (sCanSpecifyLargerRangeBarHeight) {
@@ -369,7 +371,7 @@
int leftPadding = getPaddingLeft();
mRootView.layout(leftPadding, mInsetTop, mRootView.getMeasuredWidth() + leftPadding,
getRowContentHeight() + mInsetTop);
- if (mRangeBar != null) {
+ if (mRangeBar != null && mStartItem == null) {
// If we're on a platform where SeekBar can't be stretched vertically, then
// mMeasuredRangeHeight can (and probably will) be smaller than the ideal height, so we
// need to add some padding to make mRangeBar look like it's the larger size.
@@ -473,7 +475,10 @@
setRangeBounds();
addRange();
}
- return;
+ // if mStartItem is not null, then RowView should update end items.
+ if (mStartItem == null) {
+ return;
+ }
}
final SliceItem selection = mRowContent.getSelection();
@@ -489,7 +494,7 @@
@SuppressWarnings("unchecked")
private void updateEndItems() {
- if (mRowContent == null) {
+ if (mRowContent == null || (mRowContent.getRange() != null && mStartItem == null)) {
return;
}
mEndContainer.removeAllViews();
@@ -685,9 +690,11 @@
}
final boolean isSeekBar = FORMAT_ACTION.equals(mRangeItem.getFormat());
- final ProgressBar progressBar = isSeekBar
- ? new SeekBar(getContext())
- : new ProgressBar(getContext(), null, android.R.attr.progressBarStyleHorizontal);
+ final boolean renderInNewLine = mStartItem == null;
+ final ProgressBar progressBar = isSeekBar ? (renderInNewLine ? new SeekBar(getContext()) :
+ (SeekBar) LayoutInflater.from(getContext()).inflate(R.layout.abc_slice_seekbar_view,
+ this, false)) :
+ new ProgressBar(getContext(), null, android.R.attr.progressBarStyleHorizontal);
Drawable progressDrawable = DrawableCompat.wrap(progressBar.getProgressDrawable());
if (mTintColor != -1 && progressDrawable != null) {
DrawableCompat.setTint(progressDrawable, mTintColor);
@@ -698,7 +705,12 @@
progressBar.setMax(mRangeMaxValue - mRangeMinValue);
progressBar.setProgress(mRangeValue);
progressBar.setVisibility(View.VISIBLE);
- addView(progressBar);
+ if (mStartItem == null) {
+ addView(progressBar);
+ } else {
+ mSubContent.setVisibility(GONE);
+ mContent.addView(progressBar, 1);
+ }
mRangeBar = progressBar;
if (isSeekBar) {
SliceItem thumb = mRowContent.getInputRangeThumb();
@@ -847,7 +859,13 @@
if (isIcon && color != -1) {
iv.setColorFilter(color);
}
- container.addView(iv);
+ // Because of sliding, the title icon is added many times.
+ if (mIsRangeSliding) {
+ container.removeAllViews();
+ container.addView(iv);
+ } else {
+ container.addView(iv);
+ }
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
lp.width = useIntrinsicSize ? Math.round(d.getIntrinsicWidth() / density) : mImageSize;
lp.height = useIntrinsicSize ? Math.round(d.getIntrinsicHeight() / density) :
@@ -1034,7 +1052,6 @@
mToggles.clear();
mActions.clear();
mRowAction = null;
- mStartItem = null;
mBottomDivider.setVisibility(GONE);
mActionDivider.setVisibility(GONE);
if (mSeeMoreView != null) {
@@ -1050,9 +1067,15 @@
mLastSentRangeUpdate = 0;
mHandler = null;
if (mRangeBar != null) {
- removeView(mRangeBar);
+ if (mStartItem == null) {
+ removeView(mRangeBar);
+ } else {
+ mContent.removeView(mRangeBar);
+ }
mRangeBar = null;
}
+ mSubContent.setVisibility(VISIBLE);
+ mStartItem = null;
mActionSpinner.setVisibility(GONE);
if (mSelectionSpinner != null) {
removeView(mSelectionSpinner);
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceStyle.java b/slices/view/src/main/java/androidx/slice/widget/SliceStyle.java
index 71a05b7..5675d7e 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceStyle.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceStyle.java
@@ -231,7 +231,8 @@
return maxHeight;
}
- if (row.getRange() != null) {
+ // If no StartItem, keep to use original layout.
+ if (row.getRange() != null && row.getStartItem() == null) {
// Range element always has set height and then the height of the text
// area on the row will vary depending on if 1 or 2 lines of text.
int textAreaHeight = row.getLineCount() > 1 ? mRowTextWithRangeHeight
diff --git a/slices/view/src/main/res/layout/abc_slice_seekbar_view.xml b/slices/view/src/main/res/layout/abc_slice_seekbar_view.xml
new file mode 100644
index 0000000..ce68c3a
--- /dev/null
+++ b/slices/view/src/main/res/layout/abc_slice_seekbar_view.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020 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.
+ -->
+
+<SeekBar xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Widget.AppCompat.SeekBar.Inline">
+</SeekBar>
\ No newline at end of file
diff --git a/slices/view/src/main/res/layout/abc_slice_small_template.xml b/slices/view/src/main/res/layout/abc_slice_small_template.xml
index f5a4c3a..76afbd5 100644
--- a/slices/view/src/main/res/layout/abc_slice_small_template.xml
+++ b/slices/view/src/main/res/layout/abc_slice_small_template.xml
@@ -35,9 +35,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
- android:orientation="horizontal"
- android:paddingEnd="12dp"
- android:paddingRight="12dp"/>
+ android:orientation="horizontal"/>
<LinearLayout
android:id="@android:id/content"
@@ -52,10 +50,13 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="end"
- android:maxLines="1"/>
+ android:maxLines="1"
+ android:paddingStart="12dp"
+ android:paddingLeft="12dp"/>
<LinearLayout
- android:layout_width="wrap_content"
+ android:id="@+id/subcontent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
@@ -65,7 +66,9 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
- android:maxLines="1" />
+ android:maxLines="1"
+ android:layout_marginStart="12dp"
+ android:layout_marginLeft="12dp"/>
<TextView
android:id="@+id/last_updated"
diff --git a/slices/view/src/main/res/values/styles.xml b/slices/view/src/main/res/values/styles.xml
index 4b27820..61cfd63 100644
--- a/slices/view/src/main/res/values/styles.xml
+++ b/slices/view/src/main/res/values/styles.xml
@@ -30,4 +30,10 @@
<item name="gridTitleSize">12sp</item>
<item name="gridSubtitleSize">12sp</item>
</style>
+
+ <style name="Widget.AppCompat.SeekBar.Inline" parent="Widget.AppCompat.SeekBar">
+ <item name="android:layout_marginTop">5dp</item>
+ <item name="android:paddingStart">12dp</item>
+ <item name="android:paddingLeft">12dp</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/startup/OWNERS b/startup/OWNERS
new file mode 100644
index 0000000..0bef8c9
--- /dev/null
+++ b/startup/OWNERS
@@ -0,0 +1,4 @@
+rahulrav@google.com
+sumir@google.com
+yboyar@google.com
+danysantiago@google.com
diff --git a/startup/README.md b/startup/README.md
new file mode 100644
index 0000000..f5cb442
--- /dev/null
+++ b/startup/README.md
@@ -0,0 +1,5 @@
+# androidx.startup
+
+This is a new library which helps with application initialization.
+
+[Library owners](OWNERS)
diff --git a/startup/integration-tests/first-library/build.gradle b/startup/integration-tests/first-library/build.gradle
new file mode 100644
index 0000000..8fea9e5
--- /dev/null
+++ b/startup/integration-tests/first-library/build.gradle
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.AndroidXExtension
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+project.ext.noDocs = true
+
+android {
+ buildTypes {
+ debug {
+ // Breaks Kotlin compiler
+ testCoverageEnabled = false
+ }
+ }
+}
+
+dependencies {
+ api(KOTLIN_STDLIB)
+ implementation(project(":startup:startup-runtime"))
+ implementation project(':work:work-runtime-ktx')
+
+ androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+ androidTestImplementation(ANDROIDX_TEST_CORE)
+ androidTestImplementation(ANDROIDX_TEST_RUNNER)
+ androidTestImplementation(ESPRESSO_CORE)
+ androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+ androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+ testImplementation(JUNIT)
+}
diff --git a/startup/integration-tests/first-library/src/main/AndroidManifest.xml b/startup/integration-tests/first-library/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..dfe739eb
--- /dev/null
+++ b/startup/integration-tests/first-library/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="androidx.startup.first_library">
+
+ <application>
+
+ <!-- This meta-data entry is useful for ComponentInitializer discovery. -->
+ <meta-data
+ android:name="androidx.startup.first_library.WorkManagerComponentInitializer"
+ android:value="@string/androidx_startup" />
+
+ <!-- Remove provider for WorkManager on-demand initialization -->
+ <provider
+ android:name="androidx.work.impl.WorkManagerInitializer"
+ android:authorities="${applicationId}.workmanager-init"
+ android:exported="false"
+ tools:node="remove" />
+
+ </application>
+
+</manifest>
diff --git a/startup/integration-tests/first-library/src/main/java/androidx/startup/first_library/WorkManagerComponentInitializer.kt b/startup/integration-tests/first-library/src/main/java/androidx/startup/first_library/WorkManagerComponentInitializer.kt
new file mode 100644
index 0000000..0f9f746
--- /dev/null
+++ b/startup/integration-tests/first-library/src/main/java/androidx/startup/first_library/WorkManagerComponentInitializer.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 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.startup.first_library
+
+import android.content.Context
+import android.util.Log
+import androidx.startup.ComponentInitializer
+import androidx.work.Configuration
+import androidx.work.WorkManager
+
+/**
+ * A [ComponentInitializer] which initializes [WorkManager].
+ */
+class WorkManagerComponentInitializer : ComponentInitializer<WorkManager> {
+ override fun create(context: Context): WorkManager {
+ val configuration = Configuration.Builder()
+ .setMinimumLoggingLevel(Log.DEBUG)
+ .build()
+
+ WorkManager.initialize(context, configuration)
+ return WorkManager.getInstance(context)
+ }
+
+ override fun dependencies(): List<Class<out ComponentInitializer<*>>> = emptyList()
+}
diff --git a/startup/integration-tests/second-library/build.gradle b/startup/integration-tests/second-library/build.gradle
new file mode 100644
index 0000000..f57fee9
--- /dev/null
+++ b/startup/integration-tests/second-library/build.gradle
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.AndroidXExtension
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+project.ext.noDocs = true
+
+android {
+ buildTypes {
+ debug {
+ // Breaks Kotlin compiler
+ testCoverageEnabled = false
+ }
+ }
+}
+
+dependencies {
+ api(KOTLIN_STDLIB)
+ implementation(project(":startup:startup-runtime"))
+ implementation(project(":startup:integration-tests:first-library"))
+ androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+ androidTestImplementation(ANDROIDX_TEST_CORE)
+ androidTestImplementation(ANDROIDX_TEST_RUNNER)
+ androidTestImplementation(ESPRESSO_CORE)
+ androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+ androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+ testImplementation(JUNIT)
+}
diff --git a/startup/integration-tests/second-library/src/main/AndroidManifest.xml b/startup/integration-tests/second-library/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..90e90a1
--- /dev/null
+++ b/startup/integration-tests/second-library/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.startup.second_library">
+
+ <application>
+
+ <!-- This meta-data entry is useful for ComponentInitializer discovery. -->
+ <meta-data
+ android:name="androidx.startup.second_library.DependentComponentInitializer"
+ android:value="@string/androidx_startup" />
+
+ </application>
+</manifest>
diff --git a/startup/integration-tests/second-library/src/main/java/androidx/startup/second_library/DependentComponentInitializer.kt b/startup/integration-tests/second-library/src/main/java/androidx/startup/second_library/DependentComponentInitializer.kt
new file mode 100644
index 0000000..65e7a24
--- /dev/null
+++ b/startup/integration-tests/second-library/src/main/java/androidx/startup/second_library/DependentComponentInitializer.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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.startup.second_library
+
+import android.content.Context
+import android.util.Log
+import androidx.startup.ComponentInitializer
+import androidx.startup.first_library.WorkManagerComponentInitializer
+
+/**
+ * A [ComponentInitializer] that depends on [WorkManagerComponentInitializer].
+ */
+class DependentComponentInitializer : ComponentInitializer<Unit> {
+ override fun create(context: Context) {
+ Log.i(TAG, "Created.")
+ }
+
+ override fun dependencies(): List<Class<out ComponentInitializer<*>>> =
+ listOf(WorkManagerComponentInitializer::class.java)
+
+ companion object {
+ private const val TAG = "DepComponentInit"
+ }
+}
diff --git a/startup/integration-tests/test-app/build.gradle b/startup/integration-tests/test-app/build.gradle
new file mode 100644
index 0000000..5e14656
--- /dev/null
+++ b/startup/integration-tests/test-app/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.application")
+ id("kotlin-android")
+}
+
+project.ext.noDocs = true
+
+android {
+ buildTypes {
+ getByName("release") {
+ minifyEnabled = true
+ }
+ }
+}
+
+dependencies {
+ implementation(KOTLIN_STDLIB)
+ implementation(project(":startup:integration-tests:first-library"))
+ implementation(project(":startup:integration-tests:second-library"))
+ implementation(CONSTRAINT_LAYOUT, { transitive = true })
+ implementation("androidx.arch.core:core-runtime:2.1.0")
+ implementation("androidx.appcompat:appcompat:1.1.0")
+ implementation(MATERIAL)
+}
diff --git a/startup/integration-tests/test-app/src/main/AndroidManifest.xml b/startup/integration-tests/test-app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e0b77c6
--- /dev/null
+++ b/startup/integration-tests/test-app/src/main/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.startup.testapp">
+
+ <application
+ android:allowBackup="true"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat">
+
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
diff --git a/startup/integration-tests/test-app/src/main/java/androidx/startup/testapp/MainActivity.kt b/startup/integration-tests/test-app/src/main/java/androidx/startup/testapp/MainActivity.kt
new file mode 100644
index 0000000..70e9561
--- /dev/null
+++ b/startup/integration-tests/test-app/src/main/java/androidx/startup/testapp/MainActivity.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 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.startup.testapp
+
+import android.os.Bundle
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+
+class MainActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ val notice = findViewById<TextView>(R.id.txtNotice)
+ notice.setText(R.string.app_notice)
+ }
+}
diff --git a/startup/integration-tests/test-app/src/main/res/layout/activity_main.xml b/startup/integration-tests/test-app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..766d6dc
--- /dev/null
+++ b/startup/integration-tests/test-app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright 2020 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.
+-->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/txtNotice"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Preview Some Text" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/startup/integration-tests/test-app/src/main/res/values/strings.xml b/startup/integration-tests/test-app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b046144
--- /dev/null
+++ b/startup/integration-tests/test-app/src/main/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2020 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.
+ -->
+
+<resources>
+ <string name="app_notice">Application initialized.</string>
+</resources>
diff --git a/startup/startup-runtime-lint/build.gradle b/startup/startup-runtime-lint/build.gradle
new file mode 100644
index 0000000..d974dad
--- /dev/null
+++ b/startup/startup-runtime-lint/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.AndroidXExtension
+import androidx.build.CompilationTarget
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SdkHelperKt
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("kotlin")
+}
+
+dependencies {
+ // compileOnly because we use lintChecks and it doesn't allow other types of deps
+ // this ugly hack exists because of b/63873667
+ if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
+ compileOnly LINT_API_LATEST
+ } else {
+ compileOnly LINT_API_MIN
+ }
+ compileOnly KOTLIN_STDLIB
+
+ testImplementation KOTLIN_STDLIB
+ testImplementation LINT_CORE
+ testImplementation LINT_TESTS
+}
+
+androidx {
+ name = "Android App Startup Runtime Lint Checks"
+ toolingProject = true
+ publish = Publish.NONE
+ mavenVersion = LibraryVersions.STARTUP
+ mavenGroup = LibraryGroups.STARTUP
+ inceptionYear = "2020"
+ description = "Android App Startup Runtime"
+ url = AndroidXExtension.ARCHITECTURE_URL
+ compilationTarget = CompilationTarget.HOST
+}
diff --git a/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/ComponentInitializerConstructorDetector.kt b/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/ComponentInitializerConstructorDetector.kt
new file mode 100644
index 0000000..9c0858c
--- /dev/null
+++ b/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/ComponentInitializerConstructorDetector.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2020 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.startup.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UClass
+import java.util.EnumSet
+
+/**
+ * A [Detector] which ensures that every `ComponentInitializer` has a no argument constructor.
+ */
+class ComponentInitializerConstructorDetector : Detector(), SourceCodeScanner {
+ companion object {
+ private const val DESCRIPTION = "Missing ComponentInitializer no-arg constructor"
+ val ISSUE = Issue.create(
+ id = "EnsureComponentInitializerNoArgConstructor",
+ briefDescription = DESCRIPTION,
+ explanation = """
+ Every `ComponentInitializer` must have a no argument constructor.
+ """,
+ androidSpecific = true,
+ category = Category.CORRECTNESS,
+ severity = Severity.FATAL,
+ implementation = Implementation(
+ ComponentInitializerConstructorDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE)
+ )
+ )
+ }
+
+ override fun applicableSuperClasses() = listOf("androidx.startup.ComponentInitializer")
+
+ override fun visitClass(context: JavaContext, declaration: UClass) {
+ val name = declaration.qualifiedName
+
+ if (name == "androidx.startup.ComponentInitializer") {
+ // This is the component initializer itself.
+ return
+ }
+
+ if (!declaration.isInterface) {
+ for (constructor in declaration.constructors) {
+ if (!constructor.hasParameters()) {
+ // Found a no argument constructor.
+ return
+ }
+ }
+ val location = context.getLocation(declaration.javaPsi)
+ context.report(
+ issue = ISSUE,
+ location = location,
+ message = DESCRIPTION
+ )
+ }
+ }
+}
diff --git a/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/EnsureComponentInitializerMetadataDetector.kt b/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/EnsureComponentInitializerMetadataDetector.kt
new file mode 100644
index 0000000..6259474
--- /dev/null
+++ b/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/EnsureComponentInitializerMetadataDetector.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.startup.lint
+
+import com.android.SdkConstants.ANDROID_URI
+import com.android.SdkConstants.ATTR_NAME
+import com.android.SdkConstants.ATTR_VALUE
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.android.tools.lint.detector.api.XmlContext
+import com.android.tools.lint.detector.api.XmlScanner
+import org.jetbrains.uast.UClass
+import org.w3c.dom.Element
+import java.util.EnumSet
+
+/**
+ * A [Detector] which ensures that every `ComponentInitializer` is accompanied by a corresponding
+ * entry in the `AndroidManifest.xml`.
+ */
+class EnsureComponentInitializerMetadataDetector : Detector(), SourceCodeScanner, XmlScanner {
+ // Keeps track of all the declared components
+ private val components = mutableSetOf<String>()
+
+ companion object {
+ private const val DESCRIPTION = "Every ComponentInitializer needs to be accompanied by a " +
+ "corresponding <meta-data> entry in the AndroidManifest.xml file."
+
+ val ISSUE = Issue.create(
+ id = "EnsureComponentInitializerMetadata",
+ briefDescription = DESCRIPTION,
+ explanation = """
+ When a library defines a ComponentInitializer, it needs to be accompanied by a \
+ corresponding <meta-data> entry in the AndroidManifest.xml file.
+ """,
+ androidSpecific = true,
+ category = Category.CORRECTNESS,
+ severity = Severity.FATAL,
+ implementation = Implementation(
+ EnsureComponentInitializerMetadataDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.MANIFEST)
+ )
+ )
+ }
+
+ override fun applicableSuperClasses() = listOf("androidx.startup.ComponentInitializer")
+
+ override fun getApplicableElements() = listOf("meta-data")
+
+ override fun visitClass(context: JavaContext, declaration: UClass) {
+ val name = declaration.qualifiedName
+
+ if (name == "androidx.startup.ComponentInitializer") {
+ // This is the component initializer itself.
+ return
+ }
+
+ if (!declaration.isInterface && declaration.qualifiedName !in components) {
+ val location = context.getLocation(declaration.javaPsi)
+ context.report(
+ issue = ISSUE,
+ location = location,
+ message = DESCRIPTION
+ )
+ }
+ }
+
+ override fun visitElement(context: XmlContext, element: Element) {
+ // Track all <meta-data> elements with value androidx.startup
+ val name = element.getAttributeNS(ANDROID_URI, ATTR_NAME)
+ val value = element.getAttributeNS(ANDROID_URI, ATTR_VALUE)
+ if (value == "androidx.startup") {
+ components.add(name)
+ }
+ }
+}
diff --git a/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/StartupRuntimeIssueRegistry.kt b/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/StartupRuntimeIssueRegistry.kt
new file mode 100644
index 0000000..eb100f8
--- /dev/null
+++ b/startup/startup-runtime-lint/src/main/java/androidx/startup/lint/StartupRuntimeIssueRegistry.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.startup.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.detector.api.Issue
+
+/**
+ * The [IssueRegistry] for `androidx.startup`.
+ */
+class StartupRuntimeIssueRegistry : IssueRegistry() {
+ override val issues: List<Issue>
+ get() = listOf(
+ ComponentInitializerConstructorDetector.ISSUE,
+ EnsureComponentInitializerMetadataDetector.ISSUE
+ )
+}
diff --git a/startup/startup-runtime-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry b/startup/startup-runtime-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
new file mode 100644
index 0000000..27f1bca
--- /dev/null
+++ b/startup/startup-runtime-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
@@ -0,0 +1 @@
+androidx.startup.lint.StartupRuntimeIssueRegistry
\ No newline at end of file
diff --git a/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/ComponentInitializerConstructorTest.kt b/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/ComponentInitializerConstructorTest.kt
new file mode 100644
index 0000000..2205499
--- /dev/null
+++ b/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/ComponentInitializerConstructorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 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.startup.lint
+
+import androidx.startup.lint.Stubs.COMPONENT_INITIALIZER
+import androidx.startup.lint.Stubs.TEST_COMPONENT
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import org.junit.Test
+
+class ComponentInitializerConstructorTest {
+ @Test
+ fun testSuccessWhenNoArgumentConstructorIsPresent() {
+ lint()
+ .files(
+ COMPONENT_INITIALIZER,
+ TEST_COMPONENT
+ )
+ .issues(ComponentInitializerConstructorDetector.ISSUE)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testFailureWhenZeroNoArgumentConstructorsArePresent() {
+ val component: TestFile = kotlin(
+ "com/example/TestComponentInitializer.kt",
+ """
+ package com.example
+
+ import androidx.startup.ComponentInitializer
+
+ class TestComponentInitializer(val int: Int): ComponentInitializer<Unit> {
+
+ }
+ """
+ ).indented().within("src")
+
+ lint()
+ .files(
+ COMPONENT_INITIALIZER,
+ TEST_COMPONENT,
+ component
+ )
+ .issues(ComponentInitializerConstructorDetector.ISSUE)
+ .run()
+ /* ktlint-disable max-line-length */
+ .expect(
+ """
+ src/com/example/TestComponentInitializer.kt:5: Error: Missing ComponentInitializer no-arg constructor [EnsureComponentInitializerNoArgConstructor]
+ class TestComponentInitializer(val int: Int): ComponentInitializer<Unit> {
+ ^
+ 1 errors, 0 warnings
+ """.trimIndent()
+ )
+ /* ktlint-enable max-line-length */
+ }
+}
diff --git a/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/EnsureComponentInitializerMetadataTest.kt b/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/EnsureComponentInitializerMetadataTest.kt
new file mode 100644
index 0000000..a3c1424
--- /dev/null
+++ b/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/EnsureComponentInitializerMetadataTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 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.startup.lint
+
+import androidx.startup.lint.Stubs.TEST_COMPONENT
+import androidx.startup.lint.Stubs.COMPONENT_INITIALIZER
+import com.android.tools.lint.checks.infrastructure.TestFiles.manifest
+import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import org.junit.Test
+
+class EnsureComponentInitializerMetadataTest {
+ @Test
+ fun testFailureWhenNoMetadataIsProvided() {
+ lint()
+ .files(
+ COMPONENT_INITIALIZER,
+ TEST_COMPONENT
+ )
+ .issues(EnsureComponentInitializerMetadataDetector.ISSUE)
+ .run()
+ /* ktlint-disable max-line-length */
+ .expect(
+ """
+ src/com/example/TestComponentInitializer.kt:5: Error: Every ComponentInitializer needs to be accompanied by a corresponding <meta-data> entry in the AndroidManifest.xml file. [EnsureComponentInitializerMetadata]
+ class TestComponentInitializer: ComponentInitializer<Unit> {
+ ^
+ 1 errors, 0 warnings
+ """.trimIndent()
+ )
+ /* ktlint-enable max-line-length */
+ }
+
+ @Test
+ fun testSuccessWhenMetadataIsProvided() {
+ val manifest = manifest(
+ """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.example">
+ <application>
+ <meta-data
+ android:name="com.example.TestComponentInitializer"
+ android:value="androidx.startup" />
+ </application>
+ </manifest>
+ """
+ ).indented()
+
+ lint()
+ .files(
+ COMPONENT_INITIALIZER,
+ TEST_COMPONENT,
+ manifest
+ )
+ .issues(EnsureComponentInitializerMetadataDetector.ISSUE)
+ .run()
+ .expectClean()
+ }
+}
diff --git a/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/Stubs.kt b/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/Stubs.kt
new file mode 100644
index 0000000..fe61cc3
--- /dev/null
+++ b/startup/startup-runtime-lint/src/test/java/androidx/startup/lint/Stubs.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 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.startup.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
+
+object Stubs {
+
+ /**
+ * The Test component.
+ */
+ val TEST_COMPONENT: TestFile = kotlin(
+ "com/example/TestComponentInitializer.kt",
+ """
+ package com.example
+
+ import androidx.startup.ComponentInitializer
+
+ class TestComponentInitializer: ComponentInitializer<Unit> {
+
+ }
+ """
+ ).indented().within("src")
+
+ /**
+ * The ComponentInitializer.
+ */
+ val COMPONENT_INITIALIZER: TestFile = kotlin(
+ "androidx/startup/ComponentInitializer.kt",
+ """
+ package androidx.startup
+ interface ComponentInitializer<out T : Any> {
+ }
+ """
+ ).indented().within("src")
+}
diff --git a/startup/startup-runtime/build.gradle b/startup/startup-runtime/build.gradle
new file mode 100644
index 0000000..36830cd
--- /dev/null
+++ b/startup/startup-runtime/build.gradle
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.AndroidXExtension
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+android {
+ buildTypes {
+ debug {
+ // Breaks Kotlin compiler
+ testCoverageEnabled = false
+ }
+ }
+}
+
+dependencies {
+ api(KOTLIN_STDLIB)
+ implementation("androidx.annotation:annotation:1.1.0")
+ lintPublish(project(':startup:startup-runtime-lint'))
+ androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+ androidTestImplementation(ANDROIDX_TEST_CORE)
+ androidTestImplementation(ANDROIDX_TEST_RUNNER)
+ androidTestImplementation(ESPRESSO_CORE)
+ androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+ androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+ testImplementation(JUNIT)
+}
+
+androidx {
+ name = "Android App Startup Runtime"
+ publish = Publish.NONE
+ mavenVersion = LibraryVersions.STARTUP
+ mavenGroup = LibraryGroups.STARTUP
+ inceptionYear = "2020"
+ description = "Android App Startup Runtime"
+ url = AndroidXExtension.ARCHITECTURE_URL
+}
diff --git a/startup/startup-runtime/proguard-rules.pro b/startup/startup-runtime/proguard-rules.pro
new file mode 100644
index 0000000..78991b3
--- /dev/null
+++ b/startup/startup-runtime/proguard-rules.pro
@@ -0,0 +1,3 @@
+# This Proguard rule ensures that ComponentInitializers are are neither shrunk nor obfuscated.
+# This is because they are discovered and instantiated during application initialization.
+-keep class * extends androidx.startup.ComponentInitializer
diff --git a/startup/startup-runtime/src/androidTest/java/androidx/startup/AppInitializerTest.kt b/startup/startup-runtime/src/androidTest/java/androidx/startup/AppInitializerTest.kt
new file mode 100644
index 0000000..080c831
--- /dev/null
+++ b/startup/startup-runtime/src/androidTest/java/androidx/startup/AppInitializerTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2020 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.startup
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.containsString
+import org.hamcrest.Matchers.containsInAnyOrder
+import org.junit.Assert.assertThat
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class AppInitializerTest {
+
+ private lateinit var context: Context
+
+ @Before
+ fun setUp() {
+ context = ApplicationProvider.getApplicationContext()
+ }
+
+ @Test
+ fun basicInitializationTest() {
+ val initializing = mutableSetOf<Class<*>>()
+ val initialized = mutableSetOf<Class<*>>()
+ val components = listOf<Class<*>>(InitializerNoDependencies::class.java)
+ AppInitializer.initialize(context, components, initializing, initialized)
+ assertThat(initializing.size, `is`(0))
+ assertThat(initialized.size, `is`(1))
+ assertThat<Collection<Class<*>>>(
+ initialized,
+ containsInAnyOrder<Class<*>>(*components.toTypedArray())
+ )
+ }
+
+ @Test
+ fun initializationWithDependencies() {
+ val initializing = mutableSetOf<Class<*>>()
+ val initialized = mutableSetOf<Class<*>>()
+ val components = listOf<Class<*>>(InitializerWithDependency::class.java)
+ AppInitializer.initialize(context, components, initializing, initialized)
+ assertThat(initializing.size, `is`(0))
+ assertThat(initialized.size, `is`(2))
+ assertThat<Collection<Class<*>>>(
+ initialized,
+ containsInAnyOrder<Class<*>>(
+ InitializerNoDependencies::class.java,
+ InitializerWithDependency::class.java
+ )
+ )
+ }
+
+ @Test
+ fun initializationWithCyclicDependencies() {
+ val initializing = mutableSetOf<Class<*>>()
+ val initialized = mutableSetOf<Class<*>>()
+ val components = listOf<Class<*>>(CyclicDependencyInitializer::class.java)
+ try {
+ AppInitializer.initialize(context, components, initializing, initialized)
+ fail()
+ } catch (exception: IllegalStateException) {
+ assertThat(exception.localizedMessage, containsString("Cycle detected."))
+ }
+ }
+}
diff --git a/startup/startup-runtime/src/androidTest/java/androidx/startup/CyclicDependencyInitializer.kt b/startup/startup-runtime/src/androidTest/java/androidx/startup/CyclicDependencyInitializer.kt
new file mode 100644
index 0000000..2fd28c3
--- /dev/null
+++ b/startup/startup-runtime/src/androidTest/java/androidx/startup/CyclicDependencyInitializer.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 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.startup
+
+import android.content.Context
+import android.util.Log
+
+/**
+ * Initializer with cyclic dependencies.
+ */
+class CyclicDependencyInitializer : ComponentInitializer<Unit> {
+ override fun create(context: Context) {
+ Log.i(TAG, "Initialized")
+ }
+
+ override fun dependencies(): List<Class<out ComponentInitializer<*>>> {
+ return listOf(CyclicDependencyInitializer::class.java)
+ }
+
+ companion object {
+ const val TAG = "CyclicDependencies"
+ }
+}
diff --git a/startup/startup-runtime/src/androidTest/java/androidx/startup/InitializerNoDependencies.kt b/startup/startup-runtime/src/androidTest/java/androidx/startup/InitializerNoDependencies.kt
new file mode 100644
index 0000000..2556d93
--- /dev/null
+++ b/startup/startup-runtime/src/androidTest/java/androidx/startup/InitializerNoDependencies.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 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.startup
+
+import android.content.Context
+import android.util.Log
+
+/**
+ * Initializer with no dependencies.
+ */
+class InitializerNoDependencies : ComponentInitializer<Unit> {
+ override fun create(context: Context) {
+ Log.i(TAG, "Initialized")
+ }
+
+ override fun dependencies(): List<Class<out ComponentInitializer<*>>> = emptyList()
+
+ companion object {
+ const val TAG = "NoDependencies"
+ }
+}
diff --git a/startup/startup-runtime/src/androidTest/java/androidx/startup/InitializerWithDependency.kt b/startup/startup-runtime/src/androidTest/java/androidx/startup/InitializerWithDependency.kt
new file mode 100644
index 0000000..94dc024
--- /dev/null
+++ b/startup/startup-runtime/src/androidTest/java/androidx/startup/InitializerWithDependency.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 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.startup
+
+import android.content.Context
+import android.util.Log
+
+/**
+ * Initializer with a dependency on [InitializerNoDependencies].
+ */
+class InitializerWithDependency : ComponentInitializer<Unit> {
+ override fun create(context: Context) {
+ Log.i(TAG, "Initialized")
+ }
+
+ override fun dependencies(): List<Class<out ComponentInitializer<*>>> {
+ return listOf(InitializerNoDependencies::class.java)
+ }
+
+ companion object {
+ const val TAG = "HasDependencies"
+ }
+}
diff --git a/startup/startup-runtime/src/main/AndroidManifest.xml b/startup/startup-runtime/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..fa24aec
--- /dev/null
+++ b/startup/startup-runtime/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.startup">
+
+ <application>
+ <provider
+ android:name=".InitializationProvider"
+ android:authorities="androidx.startup"
+ android:exported="false" />
+ </application>
+
+</manifest>
diff --git a/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.kt b/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.kt
new file mode 100644
index 0000000..bf39602
--- /dev/null
+++ b/startup/startup-runtime/src/main/java/androidx/startup/AppInitializer.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2020 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.startup
+
+import android.content.Context
+import android.content.pm.PackageManager
+import androidx.annotation.VisibleForTesting
+
+/**
+ * An [AppInitializer] can be used to initialize all discovered [ComponentInitializer]s.
+ * <br/>
+ * The discovery mechanism is via `<meta-data>` entries in the merged `AndroidManifest.xml`.
+ */
+class AppInitializer {
+ companion object {
+
+ /**
+ * Discovers an initializes all available [ComponentInitializer] classes based on the merged
+ * manifest `<meta-data>` entries in the `AndroidManifest.xml`.
+ * @param context The Application context
+ */
+ @JvmName("initialize")
+ fun initialize(context: Context) {
+ val components = discoverComponents(context)
+ initialize(context, components)
+ }
+
+ /**
+ * Initializes a [List] of [ComponentInitializer] class types.
+ *
+ * @param context The Application context
+ * @param components The [List] of [Class]es that represent all discovered
+ * [ComponentInitializer]s
+ */
+ @JvmName("initialize")
+ fun initialize(context: Context, components: List<Class<*>>) {
+ initialize(context, components, mutableSetOf(), mutableSetOf())
+ }
+
+ @VisibleForTesting
+ internal fun initialize(
+ context: Context,
+ components: List<Class<*>>,
+ initializing: MutableSet<Class<*>>,
+ initialized: MutableSet<Class<*>>
+ ) {
+ for (component in components) {
+ check(component !in initializing) {
+ "Cannot initialize $component. Cycle detected."
+ }
+ if (component !in initialized) {
+ initializing.add(component)
+ val instance = component.newInstance()
+ check(instance is ComponentInitializer<*>) {
+ "Component $component is not a subtype of ComponentInitializer."
+ }
+ val dependencies = instance.dependencies().filterNot {
+ it in initialized
+ }
+ if (dependencies.isNotEmpty()) {
+ initialize(context, dependencies, initializing, initialized)
+ }
+ StartupLogger.i { "Initializing $component" }
+ instance.create(context)
+ StartupLogger.i { "Initialized $component" }
+ initializing.remove(component)
+ initialized.add(component)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ internal fun discoverComponents(context: Context): List<Class<*>> {
+ val metadata = context.packageManager.getApplicationInfo(
+ context.packageName,
+ PackageManager.GET_META_DATA
+ ).metaData
+
+ val startup = context.getString(R.string.androidx_startup)
+ return if (metadata != null && metadata.size() > 0) {
+ val components = mutableListOf<Class<*>>()
+ metadata.keySet().forEach { key ->
+ val value = metadata.getString(key, null)
+ if (startup == value) {
+ try {
+ val clazz = Class.forName(key)
+ StartupLogger.i { "Discovered ($clazz)" }
+ components.add(clazz)
+ } catch (throwable: Throwable) {
+ val message = "Cannot find ComponentInitializer ($key)"
+ StartupLogger.e(message, throwable)
+ throw StartupException(message, throwable)
+ }
+ }
+ }
+ components
+ } else {
+ emptyList()
+ }
+ }
+ }
+}
diff --git a/startup/startup-runtime/src/main/java/androidx/startup/ComponentInitializer.kt b/startup/startup-runtime/src/main/java/androidx/startup/ComponentInitializer.kt
new file mode 100644
index 0000000..fcb27f2
--- /dev/null
+++ b/startup/startup-runtime/src/main/java/androidx/startup/ComponentInitializer.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 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.startup
+
+import android.content.Context
+
+/**
+ * [ComponentInitializer]s can be used to initialize libraries during app startup, without the
+ * need to use additional [android.content.ContentProvider]s.
+ */
+interface ComponentInitializer<out T : Any> {
+
+ /**
+ * Initializes and returns an instance of [T] given the application [Context]
+ *
+ * @param context The application context.
+ */
+ fun create(context: Context): T
+
+ /**
+ * @return A list of dependencies that this [ComponentInitializer] depends on. This is used
+ * to determine initialization order of [ComponentInitializer]s.
+ * <br/>
+ * For e.g. if a [ComponentInitializer] `B` defines another [ComponentInitializer] `A` as
+ * its dependency, then `A` gets initialized before `B`.
+ */
+ fun dependencies(): List<Class<out ComponentInitializer<*>>>
+}
diff --git a/startup/startup-runtime/src/main/java/androidx/startup/InitializationProvider.kt b/startup/startup-runtime/src/main/java/androidx/startup/InitializationProvider.kt
new file mode 100644
index 0000000..654bb0e
--- /dev/null
+++ b/startup/startup-runtime/src/main/java/androidx/startup/InitializationProvider.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 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.startup
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.database.Cursor
+import android.net.Uri
+import androidx.annotation.RestrictTo
+
+/**
+ * The [ContentProvider] which discovers [ComponentInitializer]s in an Application and initializes
+ * them before [android.app.Application.onCreate].
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class InitializationProvider : ContentProvider() {
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ throw IllegalStateException("Not allowed.")
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array<out String>?,
+ selection: String?,
+ selectionArgs: Array<out String>?,
+ sortOrder: String?
+ ): Cursor? {
+ throw IllegalStateException("Not allowed.")
+ }
+
+ override fun onCreate(): Boolean {
+ val context = requireNotNull(context)
+ AppInitializer.initialize(context)
+ return true
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array<out String>?
+ ): Int {
+ throw IllegalStateException("Not allowed.")
+ }
+
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
+ throw IllegalStateException("Not allowed.")
+ }
+
+ override fun getType(uri: Uri): String? {
+ throw IllegalStateException("Not allowed.")
+ }
+}
diff --git a/startup/startup-runtime/src/main/java/androidx/startup/StartupException.kt b/startup/startup-runtime/src/main/java/androidx/startup/StartupException.kt
new file mode 100644
index 0000000..b86575a
--- /dev/null
+++ b/startup/startup-runtime/src/main/java/androidx/startup/StartupException.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2020 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.startup
+
+/**
+ * Is thrown when there is a problem during application startup.
+ */
+internal class StartupException(override val message: String?, override val cause: Throwable?) :
+ Throwable(message, cause)
diff --git a/startup/startup-runtime/src/main/java/androidx/startup/StartupLogger.kt b/startup/startup-runtime/src/main/java/androidx/startup/StartupLogger.kt
new file mode 100644
index 0000000..f10eaed
--- /dev/null
+++ b/startup/startup-runtime/src/main/java/androidx/startup/StartupLogger.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020 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.startup
+
+import android.util.Log
+
+/**
+ * Controls all logging in [androidx.startup].
+ */
+internal object StartupLogger {
+
+ /**
+ * The log tag.
+ */
+ private const val TAG = "StartupLogger"
+
+ private const val DEBUG = false
+
+ inline fun i(crossinline block: () -> String) {
+ @Suppress("ConstantConditionIf")
+ if (DEBUG) {
+ Log.i(TAG, block())
+ }
+ }
+
+ @Suppress("NOTHING_TO_INLINE")
+ inline fun e(message: String, throwable: Throwable? = null) {
+ if (throwable == null) {
+ Log.e(TAG, message)
+ } else {
+ Log.e(TAG, message, throwable)
+ }
+ }
+}
diff --git a/startup/startup-runtime/src/main/res/values/values.xml b/startup/startup-runtime/src/main/res/values/values.xml
new file mode 100644
index 0000000..ab0fa4c
--- /dev/null
+++ b/startup/startup-runtime/src/main/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright 2020 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.
+ -->
+
+<resources>
+ <string name="androidx_startup" translatable="false">androidx.startup</string>
+</resources>
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt
deleted file mode 100644
index 0df203c..0000000
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt
+++ /dev/null
@@ -1,153 +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.core
-
-import androidx.test.filters.LargeTest
-import androidx.ui.benchmark.ComposeBenchmarkRule
-import androidx.ui.benchmark.benchmarkDrawPerf
-import androidx.ui.benchmark.benchmarkFirstCompose
-import androidx.ui.benchmark.benchmarkFirstDraw
-import androidx.ui.benchmark.benchmarkFirstLayout
-import androidx.ui.benchmark.benchmarkFirstMeasure
-import androidx.ui.benchmark.benchmarkLayoutPerf
-import androidx.ui.integration.test.core.text.TextWithSpanTestCase
-import androidx.ui.integration.test.TextBenchmarkTestRule
-import androidx.ui.integration.test.cartesian
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-/**
- * The benchmark for [Text] composable with the input being styled text in form of composable
- * [Span]s.
- */
-@LargeTest
-@RunWith(Parameterized::class)
-class TextWithSpanBenchmark(
- private val textLength: Int
-) {
- @get:Rule
- val benchmarkRule = ComposeBenchmarkRule()
-
- @get:Rule
- val textBenchmarkRule = TextBenchmarkTestRule()
-
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "length={0}")
- fun initParameters() = cartesian(
- arrayOf(32, 512)
- )
- }
-
- /**
- * Measure the time taken to compose a Text composable with [Span]s as input.
- * This is mainly the time used to call the [Text] composable function. Different from other
- * [Text] composable benchmarks, this one include the time used to create [Span] tree.
- */
- @Test
- fun first_compose() {
- textBenchmarkRule.generator { textGenerator ->
- benchmarkRule.benchmarkFirstCompose {
- TextWithSpanTestCase(
- textLength,
- textGenerator
- )
- }
- }
- }
-
- /**
- * Measure the time taken to measure a Text composable with [Span]s as input.
- * This is mainly the time used to measure all the [Measurable]s in the [Text] composable.
- */
- @Test
- fun first_measure() {
- textBenchmarkRule.generator { textGenerator ->
- benchmarkRule.benchmarkFirstMeasure {
- TextWithSpanTestCase(
- textLength,
- textGenerator
- )
- }
- }
- }
-
- /**
- * Measure the time taken to layout a Text composable with [Span]s as input for the first time.
- * This is mainly the time used to place [Placeable]s in [Text] composable.
- */
- @Test
- fun first_layout() {
- textBenchmarkRule.generator { textGenerator ->
- benchmarkRule.benchmarkFirstLayout {
- TextWithSpanTestCase(
- textLength,
- textGenerator
- )
- }
- }
- }
-
- /**
- * Measure the time taken to draw a Text composable with [Span]s as input for the first time.
- */
- @Test
- fun first_draw() {
- textBenchmarkRule.generator { textGenerator ->
- benchmarkRule.benchmarkFirstDraw {
- TextWithSpanTestCase(
- textLength,
- textGenerator
- )
- }
- }
- }
-
- /**
- * Measure the time taken to layout a Text composable with [Span]s as input after layout
- * constrains changed.
- * This is mainly the time used to re-measure and re-layout the composable.
- */
- @Test
- fun layout() {
- textBenchmarkRule.generator { textGenerator ->
- benchmarkRule.benchmarkLayoutPerf {
- TextWithSpanTestCase(
- textLength,
- textGenerator
- )
- }
- }
- }
-
- /**
- * Measure the time taken to draw a Text composable with [Span]s as input.
- */
- @Test
- fun draw() {
- textBenchmarkRule.generator { textGenerator ->
- benchmarkRule.benchmarkDrawPerf {
- TextWithSpanTestCase(
- textLength,
- textGenerator
- )
- }
- }
- }
-}
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/WithConstraintsBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/WithConstraintsBenchmark.kt
index ce493f0..4433072 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/WithConstraintsBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/WithConstraintsBenchmark.kt
@@ -123,7 +123,7 @@
@Composable
private fun ChangingConstraintsLayout(size: State<IntPx>, children: @Composable() () -> Unit) {
Layout(children) { measurables, _ ->
- val constraints = Constraints.tightConstraints(size.value, size.value)
+ val constraints = Constraints.fixed(size.value, size.value)
measurables.first().measure(constraints).place(0.ipx, 0.ipx)
layout(100.ipx, 100.ipx) {}
}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/integration/test/core/text/TextWithSpanTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/integration/test/core/text/TextWithSpanTestCase.kt
deleted file mode 100644
index bfd4629..0000000
--- a/ui/integration-tests/test/src/main/java/androidx/ui/integration/test/core/text/TextWithSpanTestCase.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2020 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.integration.test.core.text
-
-import androidx.compose.Composable
-import androidx.ui.core.Span
-import androidx.ui.core.Text
-import androidx.ui.unit.dp
-import androidx.ui.unit.sp
-import androidx.ui.graphics.Color
-import androidx.ui.layout.LayoutWidth
-import androidx.ui.layout.LayoutWrapped
-import androidx.ui.test.ComposeTestCase
-import androidx.ui.integration.test.RandomTextGenerator
-import androidx.ui.text.TextStyle
-
-/**
- * The benchmark test case for [Text], where the input is some [Span]s with [TextStyle]s on it.
- */
-class TextWithSpanTestCase(
- textLength: Int,
- randomTextGenerator: RandomTextGenerator
-) : ComposeTestCase {
-
- /**
- * Trick to avoid the text word cache.
- * @see TextBasicTestCase.text
- */
- private val textPieces = randomTextGenerator.nextStyledWordList(
- length = textLength,
- hasMetricAffectingStyle = true
- )
-
- @Composable
- override fun emitContent() {
- Text(
- style = TextStyle(color = Color.Black, fontSize = 8.sp),
- modifier = LayoutWrapped + LayoutWidth(160.dp)
- ) {
- textPieces.forEach { (text, style) ->
- Span(text = text, style = style)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/ui/ui-animation-core/src/main/java/androidx/animation/AnimationClock.kt b/ui/ui-animation-core/src/main/java/androidx/animation/AnimationClock.kt
index 888679d..5082c4b 100644
--- a/ui/ui-animation-core/src/main/java/androidx/animation/AnimationClock.kt
+++ b/ui/ui-animation-core/src/main/java/androidx/animation/AnimationClock.kt
@@ -84,7 +84,12 @@
override fun unsubscribe(observer: AnimationClockObserver) {
// TODO: Support removing subscription from non-UI thread
- toBeRemoved.add(observer)
+ if (observers.contains(observer)) {
+ // FIXME(147736746): checking if observers contains the observer can still trigger a
+ // bug, if the observer is subscribed and unsubscribed multiple times per frame. For
+ // example: subscribe(x), unsubscribe(x), unsubscribe(x), subscribe(x)
+ toBeRemoved.add(observer)
+ }
}
internal open fun dispatchTime(frameTimeMillis: Long) {
diff --git a/ui/ui-animation-core/src/test/java/androidx/animation/AnimatedValueTest.kt b/ui/ui-animation-core/src/test/java/androidx/animation/AnimatedValueTest.kt
index ea043bb..e9bc698 100644
--- a/ui/ui-animation-core/src/test/java/androidx/animation/AnimatedValueTest.kt
+++ b/ui/ui-animation-core/src/test/java/androidx/animation/AnimatedValueTest.kt
@@ -106,4 +106,36 @@
assertEquals(1f, animationEndValue!!, epsilon)
assertEquals(AnimationEndReason.TargetReached, animationEndReason)
}
+
+ @Test
+ fun testSubscription() {
+ val recordedFrameTimes = mutableListOf<Long>()
+ val observer = object : AnimationClockObserver {
+ override fun onAnimationFrame(frameTimeMillis: Long) {
+ recordedFrameTimes.add(frameTimeMillis)
+ }
+ }
+
+ clock.clockTimeMillis = 0L
+ clock.clockTimeMillis = 1L
+ clock.unsubscribe(observer)
+ clock.subscribe(observer)
+ // observer should record 1L
+ clock.clockTimeMillis = 2L
+ // observer should record 2L
+ clock.unsubscribe(observer)
+ clock.clockTimeMillis = 3L
+ clock.clockTimeMillis = 4L
+ clock.subscribe(observer)
+ // observer should record 4L
+ clock.clockTimeMillis = 5L
+ // observer should record 5L
+ clock.clockTimeMillis = 6L
+ // observer should record 6L
+ clock.unsubscribe(observer)
+ clock.clockTimeMillis = 7L
+
+ val expectedRecording = listOf(1L, 2L, 4L, 5L, 6L)
+ assertEquals(expectedRecording, recordedFrameTimes)
+ }
}
\ No newline at end of file
diff --git a/ui/ui-core/api/0.1.0-dev04.txt b/ui/ui-core/api/0.1.0-dev04.txt
index 6522680..c72417d 100644
--- a/ui/ui-core/api/0.1.0-dev04.txt
+++ b/ui/ui-core/api/0.1.0-dev04.txt
@@ -74,9 +74,9 @@
}
public static final class Constraints.Companion {
- method public androidx.ui.core.Constraints tightConstraints(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForHeight(androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForWidth(androidx.ui.unit.IntPx width);
+ method public androidx.ui.core.Constraints fixed(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedHeight(androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
}
public final class ConstraintsKt {
@@ -84,17 +84,11 @@
method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
method public static boolean getHasBoundedHeight(androidx.ui.core.Constraints);
method public static boolean getHasBoundedWidth(androidx.ui.core.Constraints);
- method public static boolean getHasTightHeight(androidx.ui.core.Constraints);
- method public static boolean getHasTightWidth(androidx.ui.core.Constraints);
- method public static boolean isTight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedHeight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedWidth(androidx.ui.core.Constraints);
method public static boolean isZero(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMin(androidx.ui.core.Constraints);
method public static androidx.ui.core.Constraints offset(androidx.ui.core.Constraints, androidx.ui.unit.IntPx horizontal = 0.ipx, androidx.ui.unit.IntPx vertical = 0.ipx);
method public static boolean satisfiedBy(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
- method public static androidx.ui.core.Constraints tightMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints tightMin(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints withTight(androidx.ui.core.Constraints, androidx.ui.unit.IntPx? width = null, androidx.ui.unit.IntPx? height = null);
}
public final class ConsumedData {
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index 6522680..c72417d 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -74,9 +74,9 @@
}
public static final class Constraints.Companion {
- method public androidx.ui.core.Constraints tightConstraints(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForHeight(androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForWidth(androidx.ui.unit.IntPx width);
+ method public androidx.ui.core.Constraints fixed(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedHeight(androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
}
public final class ConstraintsKt {
@@ -84,17 +84,11 @@
method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
method public static boolean getHasBoundedHeight(androidx.ui.core.Constraints);
method public static boolean getHasBoundedWidth(androidx.ui.core.Constraints);
- method public static boolean getHasTightHeight(androidx.ui.core.Constraints);
- method public static boolean getHasTightWidth(androidx.ui.core.Constraints);
- method public static boolean isTight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedHeight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedWidth(androidx.ui.core.Constraints);
method public static boolean isZero(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMin(androidx.ui.core.Constraints);
method public static androidx.ui.core.Constraints offset(androidx.ui.core.Constraints, androidx.ui.unit.IntPx horizontal = 0.ipx, androidx.ui.unit.IntPx vertical = 0.ipx);
method public static boolean satisfiedBy(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
- method public static androidx.ui.core.Constraints tightMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints tightMin(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints withTight(androidx.ui.core.Constraints, androidx.ui.unit.IntPx? width = null, androidx.ui.unit.IntPx? height = null);
}
public final class ConsumedData {
diff --git a/ui/ui-core/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-core/api/public_plus_experimental_0.1.0-dev04.txt
index 6522680..c72417d 100644
--- a/ui/ui-core/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-core/api/public_plus_experimental_0.1.0-dev04.txt
@@ -74,9 +74,9 @@
}
public static final class Constraints.Companion {
- method public androidx.ui.core.Constraints tightConstraints(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForHeight(androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForWidth(androidx.ui.unit.IntPx width);
+ method public androidx.ui.core.Constraints fixed(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedHeight(androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
}
public final class ConstraintsKt {
@@ -84,17 +84,11 @@
method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
method public static boolean getHasBoundedHeight(androidx.ui.core.Constraints);
method public static boolean getHasBoundedWidth(androidx.ui.core.Constraints);
- method public static boolean getHasTightHeight(androidx.ui.core.Constraints);
- method public static boolean getHasTightWidth(androidx.ui.core.Constraints);
- method public static boolean isTight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedHeight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedWidth(androidx.ui.core.Constraints);
method public static boolean isZero(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMin(androidx.ui.core.Constraints);
method public static androidx.ui.core.Constraints offset(androidx.ui.core.Constraints, androidx.ui.unit.IntPx horizontal = 0.ipx, androidx.ui.unit.IntPx vertical = 0.ipx);
method public static boolean satisfiedBy(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
- method public static androidx.ui.core.Constraints tightMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints tightMin(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints withTight(androidx.ui.core.Constraints, androidx.ui.unit.IntPx? width = null, androidx.ui.unit.IntPx? height = null);
}
public final class ConsumedData {
diff --git a/ui/ui-core/api/public_plus_experimental_current.txt b/ui/ui-core/api/public_plus_experimental_current.txt
index 6522680..c72417d 100644
--- a/ui/ui-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-core/api/public_plus_experimental_current.txt
@@ -74,9 +74,9 @@
}
public static final class Constraints.Companion {
- method public androidx.ui.core.Constraints tightConstraints(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForHeight(androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForWidth(androidx.ui.unit.IntPx width);
+ method public androidx.ui.core.Constraints fixed(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedHeight(androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
}
public final class ConstraintsKt {
@@ -84,17 +84,11 @@
method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
method public static boolean getHasBoundedHeight(androidx.ui.core.Constraints);
method public static boolean getHasBoundedWidth(androidx.ui.core.Constraints);
- method public static boolean getHasTightHeight(androidx.ui.core.Constraints);
- method public static boolean getHasTightWidth(androidx.ui.core.Constraints);
- method public static boolean isTight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedHeight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedWidth(androidx.ui.core.Constraints);
method public static boolean isZero(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMin(androidx.ui.core.Constraints);
method public static androidx.ui.core.Constraints offset(androidx.ui.core.Constraints, androidx.ui.unit.IntPx horizontal = 0.ipx, androidx.ui.unit.IntPx vertical = 0.ipx);
method public static boolean satisfiedBy(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
- method public static androidx.ui.core.Constraints tightMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints tightMin(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints withTight(androidx.ui.core.Constraints, androidx.ui.unit.IntPx? width = null, androidx.ui.unit.IntPx? height = null);
}
public final class ConsumedData {
diff --git a/ui/ui-core/api/restricted_0.1.0-dev04.txt b/ui/ui-core/api/restricted_0.1.0-dev04.txt
index 6522680..c72417d 100644
--- a/ui/ui-core/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-core/api/restricted_0.1.0-dev04.txt
@@ -74,9 +74,9 @@
}
public static final class Constraints.Companion {
- method public androidx.ui.core.Constraints tightConstraints(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForHeight(androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForWidth(androidx.ui.unit.IntPx width);
+ method public androidx.ui.core.Constraints fixed(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedHeight(androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
}
public final class ConstraintsKt {
@@ -84,17 +84,11 @@
method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
method public static boolean getHasBoundedHeight(androidx.ui.core.Constraints);
method public static boolean getHasBoundedWidth(androidx.ui.core.Constraints);
- method public static boolean getHasTightHeight(androidx.ui.core.Constraints);
- method public static boolean getHasTightWidth(androidx.ui.core.Constraints);
- method public static boolean isTight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedHeight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedWidth(androidx.ui.core.Constraints);
method public static boolean isZero(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMin(androidx.ui.core.Constraints);
method public static androidx.ui.core.Constraints offset(androidx.ui.core.Constraints, androidx.ui.unit.IntPx horizontal = 0.ipx, androidx.ui.unit.IntPx vertical = 0.ipx);
method public static boolean satisfiedBy(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
- method public static androidx.ui.core.Constraints tightMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints tightMin(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints withTight(androidx.ui.core.Constraints, androidx.ui.unit.IntPx? width = null, androidx.ui.unit.IntPx? height = null);
}
public final class ConsumedData {
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index 6522680..c72417d 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -74,9 +74,9 @@
}
public static final class Constraints.Companion {
- method public androidx.ui.core.Constraints tightConstraints(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForHeight(androidx.ui.unit.IntPx height);
- method public androidx.ui.core.Constraints tightConstraintsForWidth(androidx.ui.unit.IntPx width);
+ method public androidx.ui.core.Constraints fixed(androidx.ui.unit.IntPx width, androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedHeight(androidx.ui.unit.IntPx height);
+ method public androidx.ui.core.Constraints fixedWidth(androidx.ui.unit.IntPx width);
}
public final class ConstraintsKt {
@@ -84,17 +84,11 @@
method public static androidx.ui.core.Constraints enforce(androidx.ui.core.Constraints, androidx.ui.core.Constraints otherConstraints);
method public static boolean getHasBoundedHeight(androidx.ui.core.Constraints);
method public static boolean getHasBoundedWidth(androidx.ui.core.Constraints);
- method public static boolean getHasTightHeight(androidx.ui.core.Constraints);
- method public static boolean getHasTightWidth(androidx.ui.core.Constraints);
- method public static boolean isTight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedHeight(androidx.ui.core.Constraints);
+ method public static boolean getHasFixedWidth(androidx.ui.core.Constraints);
method public static boolean isZero(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints looseMin(androidx.ui.core.Constraints);
method public static androidx.ui.core.Constraints offset(androidx.ui.core.Constraints, androidx.ui.unit.IntPx horizontal = 0.ipx, androidx.ui.unit.IntPx vertical = 0.ipx);
method public static boolean satisfiedBy(androidx.ui.core.Constraints, androidx.ui.unit.IntPxSize size);
- method public static androidx.ui.core.Constraints tightMax(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints tightMin(androidx.ui.core.Constraints);
- method public static androidx.ui.core.Constraints withTight(androidx.ui.core.Constraints, androidx.ui.unit.IntPx? width = null, androidx.ui.unit.IntPx? height = null);
}
public final class ConsumedData {
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt b/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
index 83cf619..27c3273 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
@@ -26,17 +26,17 @@
/**
* Immutable constraints used for measuring child [Layout]s. A parent [Layout]
- * can measure their children using the [measure] method on the corresponding [Measurable]s,
- * method which takes the [Constraints] the child has to follow. A [measure]d child is then
- * responsible to choose for themselves and return a size which satisfies the received set
- * of [Constraints]:
+ * can measure their children using the measure method on the corresponding [Measurable]s,
+ * method which takes the [Constraints] the child has to follow. A measured child is then
+ * responsible to choose for themselves and return a size which satisfies the set of [Constraints]
+ * received from their parent:
* - minWidth <= chosenWidth <= maxWidth
* - minHeight <= chosenHeight <= maxHeight
* The parent can then access the child chosen size on the resulting [Placeable]. The parent is
* responsible of defining a valid positioning of the children according to their sizes, so the
* parent needs to measure the children with appropriate [Constraints], such that whatever valid
* sizes children choose, they can be laid out in a way that also respects the parent's incoming
- * [Constraints]. Note that different children can be [measure]d with different [Constraints].
+ * [Constraints]. Note that different children can be measured with different [Constraints].
* A set of [Constraints] can have infinite maxWidth and/or maxHeight. This is a trick often
* used by parents to ask their children for their preferred size: unbounded constraints force
* children whose default behavior is to fill the available space (always size to
@@ -70,15 +70,14 @@
companion object {
/**
- * Creates constraints tight in both dimensions.
+ * Creates constraints for fixed size in both dimensions.
*/
- fun tightConstraints(width: IntPx, height: IntPx) =
- Constraints(width, width, height, height)
+ fun fixed(width: IntPx, height: IntPx) = Constraints(width, width, height, height)
/**
- * Creates constraints with tight width and loose height.
+ * Creates constraints for fixed width and unspecified height.
*/
- fun tightConstraintsForWidth(width: IntPx) = Constraints(
+ fun fixedWidth(width: IntPx) = Constraints(
minWidth = width,
maxWidth = width,
minHeight = IntPx.Zero,
@@ -86,9 +85,9 @@
)
/**
- * Creates constraints with tight height and loose width.
+ * Creates constraints for fixed height and unspecified width.
*/
- fun tightConstraintsForHeight(height: IntPx) = Constraints(
+ fun fixedHeight(height: IntPx) = Constraints(
minWidth = IntPx.Zero,
maxWidth = IntPx.Infinity,
minHeight = height,
@@ -110,24 +109,18 @@
val Constraints.hasBoundedWidth get() = maxWidth.isFinite()
/**
- * Whether there is exactly one size that satisfies the constraints.
- * @see hasTightHeight
- * @see hasTightWidth
- */
-val Constraints.isTight get() = minWidth == maxWidth && minHeight == maxHeight
-
-/**
* Whether there is exactly one width value that satisfies the constraints.
*/
-val Constraints.hasTightWidth get() = maxWidth == minWidth
+val Constraints.hasFixedWidth get() = maxWidth == minWidth
/**
* Whether there is exactly one height value that satisfies the constraints.
*/
-val Constraints.hasTightHeight get() = maxHeight == minHeight
+val Constraints.hasFixedHeight get() = maxHeight == minHeight
/**
- * Whether there is exactly one height value that satisfies the constraints.
+ * Whether the area of a component respecting these constraints will definitely be 0.
+ * This is true when at least one of maxWidth and maxHeight are 0.
*/
val Constraints.isZero get() = maxWidth == IntPx.Zero || maxHeight == IntPx.Zero
@@ -142,16 +135,6 @@
)
/**
- * Returns a copy of the current instance, overriding the specified values to be tight.
- */
-fun Constraints.withTight(width: IntPx? = null, height: IntPx? = null) = Constraints(
- minWidth = width ?: this.minWidth,
- maxWidth = width ?: this.maxWidth,
- minHeight = height ?: this.minHeight,
- maxHeight = height ?: this.maxHeight
-)
-
-/**
* Takes a size and returns the closest size to it that satisfies the constraints.
*/
fun Constraints.constrain(size: IntPxSize) = IntPxSize(
@@ -167,30 +150,6 @@
minHeight <= size.height && size.height <= maxHeight
/**
- * Returns a copy of the current instance with no min constraints.
- */
-fun Constraints.looseMin() = this.copy(minWidth = 0.ipx, minHeight = 0.ipx)
-
-/**
- * Returns a copy of the current instance with no max constraints.
- */
-fun Constraints.looseMax() = this.copy(maxWidth = IntPx.Infinity, maxHeight = IntPx.Infinity)
-
-/**
- * Returns a copy of the current instance with the constraints tightened to their smallest size.
- */
-fun Constraints.tightMin() = this.withTight(width = minWidth, height = minHeight)
-
-/**
- * Returns a copy of the current instance with the constraints tightened to their largest size.
- * Note that if any of the constraints are unbounded, they will be left unchanged.
- */
-fun Constraints.tightMax() = this.copy(
- minWidth = if (hasBoundedWidth) maxWidth else minWidth,
- minHeight = if (hasBoundedHeight) maxHeight else minHeight
-)
-
-/**
* Returns the Constraints obtained by offsetting the current instance with the given values.
*/
fun Constraints.offset(horizontal: IntPx = 0.ipx, vertical: IntPx = 0.ipx) = Constraints(
diff --git a/ui/ui-core/src/test/java/androidx/ui/core/ConstraintsTest.kt b/ui/ui-core/src/test/java/androidx/ui/core/ConstraintsTest.kt
index 1f68ddc..79aa819 100644
--- a/ui/ui-core/src/test/java/androidx/ui/core/ConstraintsTest.kt
+++ b/ui/ui-core/src/test/java/androidx/ui/core/ConstraintsTest.kt
@@ -39,13 +39,13 @@
val constraints = Constraints(0.ipx, 1.ipx, 2.ipx, 3.ipx)
constraints.assertEquals(0.ipx, 1.ipx, 2.ipx, 3.ipx)
- val tightConstraintsForWidth = Constraints.tightConstraintsForWidth(5.ipx)
+ val tightConstraintsForWidth = Constraints.fixedWidth(5.ipx)
tightConstraintsForWidth.assertEquals(5.ipx, 5.ipx, IntPx.Zero, IntPx.Infinity)
- val tightConstraintsForHeight = Constraints.tightConstraintsForHeight(5.ipx)
+ val tightConstraintsForHeight = Constraints.fixedHeight(5.ipx)
tightConstraintsForHeight.assertEquals(IntPx.Zero, IntPx.Infinity, 5.ipx, 5.ipx)
- val tightConstraints = Constraints.tightConstraints(5.ipx, 7.ipx)
+ val tightConstraints = Constraints.fixed(5.ipx, 7.ipx)
tightConstraints.assertEquals(5.ipx, 5.ipx, 7.ipx, 7.ipx)
}
@@ -61,16 +61,14 @@
}
@Test
- fun hasTightDimensions() {
+ fun hasFixedDimensions() {
val untight = Constraints(3.ipx, 4.ipx, 8.ipx, 9.ipx)
- assertFalse(untight.hasTightWidth)
- assertFalse(untight.hasTightHeight)
- assertFalse(untight.isTight)
+ assertFalse(untight.hasFixedWidth)
+ assertFalse(untight.hasFixedHeight)
val tight = Constraints(3.ipx, 3.ipx, 5.ipx, 5.ipx)
- assertTrue(tight.hasTightWidth)
- assertTrue(tight.hasTightHeight)
- assertTrue(tight.isTight)
+ assertTrue(tight.hasFixedWidth)
+ assertTrue(tight.hasFixedHeight)
}
@Test
@@ -100,13 +98,6 @@
}
@Test
- fun withTight() {
- val constraints = Constraints(2.ipx, 3.ipx, 2.ipx, 3.ipx)
- constraints.withTight().assertEquals(2.ipx, 3.ipx, 2.ipx, 3.ipx)
- constraints.withTight(7.ipx, 8.ipx).assertEquals(7.ipx, 7.ipx, 8.ipx, 8.ipx)
- }
-
- @Test
fun constrain() {
val constraints = Constraints(2.ipx, 5.ipx, 2.ipx, 5.ipx)
assertEquals(IntPxSize(2.ipx, 2.ipx), constraints.constrain(IntPxSize(1.ipx, 1.ipx)))
@@ -127,28 +118,6 @@
}
@Test
- fun loose() {
- val bounded = Constraints(2.ipx, 5.ipx, 2.ipx, 5.ipx)
- bounded.looseMin().assertEquals(0.ipx, 5.ipx, 0.ipx, 5.ipx)
- bounded.looseMax().assertEquals(2.ipx, IntPx.Infinity, 2.ipx, IntPx.Infinity)
-
- val unbounded = Constraints(2.ipx, IntPx.Infinity, 2.ipx, IntPx.Infinity)
- unbounded.looseMin().assertEquals(0.ipx, IntPx.Infinity, 0.ipx, IntPx.Infinity)
- unbounded.looseMax().assertEquals(2.ipx, IntPx.Infinity, 2.ipx, IntPx.Infinity)
- }
-
- @Test
- fun tight() {
- val bounded = Constraints(2.ipx, 5.ipx, 2.ipx, 5.ipx)
- bounded.tightMin().assertEquals(2.ipx, 2.ipx, 2.ipx, 2.ipx)
- bounded.tightMax().assertEquals(5.ipx, 5.ipx, 5.ipx, 5.ipx)
-
- val unbounded = Constraints(2.ipx, IntPx.Infinity, 2.ipx, IntPx.Infinity)
- unbounded.tightMin().assertEquals(2.ipx, 2.ipx, 2.ipx, 2.ipx)
- unbounded.tightMax().assertEquals(2.ipx, IntPx.Infinity, 2.ipx, IntPx.Infinity)
- }
-
- @Test
fun offset() {
val constraints = Constraints(2.ipx, 2.ipx, 5.ipx, 5.ipx)
constraints.offset(horizontal = 2.ipx, vertical = 3.ipx).assertEquals(
diff --git a/ui/ui-framework/api/0.1.0-dev04.txt b/ui/ui-framework/api/0.1.0-dev04.txt
index 60c7833..f414076 100644
--- a/ui/ui-framework/api/0.1.0-dev04.txt
+++ b/ui/ui-framework/api/0.1.0-dev04.txt
@@ -124,7 +124,6 @@
public final class TextKt {
method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void Text(androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
method public static void Text(String text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static void Text(androidx.ui.text.AnnotatedString text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static androidx.ui.text.TextStyle currentTextStyle();
@@ -132,24 +131,6 @@
method public static androidx.ui.core.HorizontalAlignmentLine getLastBaseline();
}
- public final class TextSpanComposeKt {
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null);
- method public static void compose(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> composable);
- method public static void disposeComposition(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null);
- }
-
- public final class TextSpanComposer extends androidx.compose.Composer<androidx.ui.text.TextSpan> {
- method public inline void call(Object key, kotlin.jvm.functions.Function1<? super androidx.compose.ViewValidator,java.lang.Boolean> invalid, kotlin.jvm.functions.Function0<kotlin.Unit> block);
- method public inline <T> void call(Object key, kotlin.jvm.functions.Function0<? extends T> ctor, kotlin.jvm.functions.Function2<? super androidx.compose.ViewValidator,? super T,java.lang.Boolean> invalid, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- }
-
- public final class TextSpanScope {
- method public androidx.ui.core.TextSpanComposer getComposer();
- }
-
public final class WrapperKt {
method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.unit.DensityScope,kotlin.Unit> block);
diff --git a/ui/ui-framework/api/current.txt b/ui/ui-framework/api/current.txt
index 60c7833..f414076 100644
--- a/ui/ui-framework/api/current.txt
+++ b/ui/ui-framework/api/current.txt
@@ -124,7 +124,6 @@
public final class TextKt {
method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void Text(androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
method public static void Text(String text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static void Text(androidx.ui.text.AnnotatedString text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static androidx.ui.text.TextStyle currentTextStyle();
@@ -132,24 +131,6 @@
method public static androidx.ui.core.HorizontalAlignmentLine getLastBaseline();
}
- public final class TextSpanComposeKt {
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null);
- method public static void compose(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> composable);
- method public static void disposeComposition(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null);
- }
-
- public final class TextSpanComposer extends androidx.compose.Composer<androidx.ui.text.TextSpan> {
- method public inline void call(Object key, kotlin.jvm.functions.Function1<? super androidx.compose.ViewValidator,java.lang.Boolean> invalid, kotlin.jvm.functions.Function0<kotlin.Unit> block);
- method public inline <T> void call(Object key, kotlin.jvm.functions.Function0<? extends T> ctor, kotlin.jvm.functions.Function2<? super androidx.compose.ViewValidator,? super T,java.lang.Boolean> invalid, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- }
-
- public final class TextSpanScope {
- method public androidx.ui.core.TextSpanComposer getComposer();
- }
-
public final class WrapperKt {
method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.unit.DensityScope,kotlin.Unit> block);
diff --git a/ui/ui-framework/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-framework/api/public_plus_experimental_0.1.0-dev04.txt
index 60c7833..f414076 100644
--- a/ui/ui-framework/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-framework/api/public_plus_experimental_0.1.0-dev04.txt
@@ -124,7 +124,6 @@
public final class TextKt {
method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void Text(androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
method public static void Text(String text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static void Text(androidx.ui.text.AnnotatedString text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static androidx.ui.text.TextStyle currentTextStyle();
@@ -132,24 +131,6 @@
method public static androidx.ui.core.HorizontalAlignmentLine getLastBaseline();
}
- public final class TextSpanComposeKt {
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null);
- method public static void compose(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> composable);
- method public static void disposeComposition(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null);
- }
-
- public final class TextSpanComposer extends androidx.compose.Composer<androidx.ui.text.TextSpan> {
- method public inline void call(Object key, kotlin.jvm.functions.Function1<? super androidx.compose.ViewValidator,java.lang.Boolean> invalid, kotlin.jvm.functions.Function0<kotlin.Unit> block);
- method public inline <T> void call(Object key, kotlin.jvm.functions.Function0<? extends T> ctor, kotlin.jvm.functions.Function2<? super androidx.compose.ViewValidator,? super T,java.lang.Boolean> invalid, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- }
-
- public final class TextSpanScope {
- method public androidx.ui.core.TextSpanComposer getComposer();
- }
-
public final class WrapperKt {
method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.unit.DensityScope,kotlin.Unit> block);
diff --git a/ui/ui-framework/api/public_plus_experimental_current.txt b/ui/ui-framework/api/public_plus_experimental_current.txt
index 60c7833..f414076 100644
--- a/ui/ui-framework/api/public_plus_experimental_current.txt
+++ b/ui/ui-framework/api/public_plus_experimental_current.txt
@@ -124,7 +124,6 @@
public final class TextKt {
method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void Text(androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
method public static void Text(String text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static void Text(androidx.ui.text.AnnotatedString text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static androidx.ui.text.TextStyle currentTextStyle();
@@ -132,24 +131,6 @@
method public static androidx.ui.core.HorizontalAlignmentLine getLastBaseline();
}
- public final class TextSpanComposeKt {
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null);
- method public static void compose(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> composable);
- method public static void disposeComposition(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null);
- }
-
- public final class TextSpanComposer extends androidx.compose.Composer<androidx.ui.text.TextSpan> {
- method public inline void call(Object key, kotlin.jvm.functions.Function1<? super androidx.compose.ViewValidator,java.lang.Boolean> invalid, kotlin.jvm.functions.Function0<kotlin.Unit> block);
- method public inline <T> void call(Object key, kotlin.jvm.functions.Function0<? extends T> ctor, kotlin.jvm.functions.Function2<? super androidx.compose.ViewValidator,? super T,java.lang.Boolean> invalid, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- }
-
- public final class TextSpanScope {
- method public androidx.ui.core.TextSpanComposer getComposer();
- }
-
public final class WrapperKt {
method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.unit.DensityScope,kotlin.Unit> block);
diff --git a/ui/ui-framework/api/restricted_0.1.0-dev04.txt b/ui/ui-framework/api/restricted_0.1.0-dev04.txt
index 60c7833..f414076 100644
--- a/ui/ui-framework/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-framework/api/restricted_0.1.0-dev04.txt
@@ -124,7 +124,6 @@
public final class TextKt {
method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void Text(androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
method public static void Text(String text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static void Text(androidx.ui.text.AnnotatedString text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static androidx.ui.text.TextStyle currentTextStyle();
@@ -132,24 +131,6 @@
method public static androidx.ui.core.HorizontalAlignmentLine getLastBaseline();
}
- public final class TextSpanComposeKt {
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null);
- method public static void compose(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> composable);
- method public static void disposeComposition(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null);
- }
-
- public final class TextSpanComposer extends androidx.compose.Composer<androidx.ui.text.TextSpan> {
- method public inline void call(Object key, kotlin.jvm.functions.Function1<? super androidx.compose.ViewValidator,java.lang.Boolean> invalid, kotlin.jvm.functions.Function0<kotlin.Unit> block);
- method public inline <T> void call(Object key, kotlin.jvm.functions.Function0<? extends T> ctor, kotlin.jvm.functions.Function2<? super androidx.compose.ViewValidator,? super T,java.lang.Boolean> invalid, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- }
-
- public final class TextSpanScope {
- method public androidx.ui.core.TextSpanComposer getComposer();
- }
-
public final class WrapperKt {
method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.unit.DensityScope,kotlin.Unit> block);
diff --git a/ui/ui-framework/api/restricted_current.txt b/ui/ui-framework/api/restricted_current.txt
index 60c7833..f414076 100644
--- a/ui/ui-framework/api/restricted_current.txt
+++ b/ui/ui-framework/api/restricted_current.txt
@@ -124,7 +124,6 @@
public final class TextKt {
method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void Text(androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
method public static void Text(String text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static void Text(androidx.ui.text.AnnotatedString text, androidx.ui.core.Modifier modifier = Modifier.None, androidx.ui.text.TextStyle? style = null, boolean softWrap = true, androidx.ui.text.style.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, int maxLines = 2147483647);
method public static androidx.ui.text.TextStyle currentTextStyle();
@@ -132,24 +131,6 @@
method public static androidx.ui.core.HorizontalAlignmentLine getLastBaseline();
}
- public final class TextSpanComposeKt {
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
- method public static void Span(androidx.ui.core.TextSpanScope, String? text = null, androidx.ui.text.SpanStyle? style = null);
- method public static void compose(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> composable);
- method public static void disposeComposition(androidx.ui.text.TextSpan container, androidx.compose.CompositionReference? parent = null);
- }
-
- public final class TextSpanComposer extends androidx.compose.Composer<androidx.ui.text.TextSpan> {
- method public inline void call(Object key, kotlin.jvm.functions.Function1<? super androidx.compose.ViewValidator,java.lang.Boolean> invalid, kotlin.jvm.functions.Function0<kotlin.Unit> block);
- method public inline <T> void call(Object key, kotlin.jvm.functions.Function0<? extends T> ctor, kotlin.jvm.functions.Function2<? super androidx.compose.ViewValidator,? super T,java.lang.Boolean> invalid, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update);
- method public inline void emit(Object key, kotlin.jvm.functions.Function0<androidx.ui.text.TextSpan> ctor, kotlin.jvm.functions.Function1<? super androidx.compose.ComposerUpdater<androidx.ui.text.TextSpan,androidx.ui.text.TextSpan>,kotlin.Unit> update, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- }
-
- public final class TextSpanScope {
- method public androidx.ui.core.TextSpanComposer getComposer();
- }
-
public final class WrapperKt {
method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.unit.DensityScope,kotlin.Unit> block);
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
index 5d5677b..176e918 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
@@ -78,17 +78,17 @@
)
}) { measurables, constraints ->
val headerPlaceable = measurables.first { it.tag == "header" }.measure(
- Constraints.tightConstraints(constraints.maxWidth, 100.ipx)
+ Constraints.fixed(constraints.maxWidth, 100.ipx)
)
val footerPadding = 50.ipx
val footerPlaceable = measurables.first { it.tag == "footer" }.measure(
- Constraints.tightConstraints(constraints.maxWidth - footerPadding * 2, 100.ipx)
+ Constraints.fixed(constraints.maxWidth - footerPadding * 2, 100.ipx)
)
val itemHeight =
(constraints.maxHeight - headerPlaceable.height - footerPlaceable.height) /
measurables.filter { it.tag == "content" }.size
val contentPlaceables = measurables.filter { it.tag == "content" }.map { measurable ->
- measurable.measure(Constraints.tightConstraints(constraints.maxWidth, itemHeight))
+ measurable.measure(Constraints.fixed(constraints.maxWidth, itemHeight))
}
layout(constraints.maxWidth, constraints.maxHeight) {
diff --git a/ui/ui-framework/integration-tests/samples/src/main/java/androidx/ui/framework/samples/LayoutSample.kt b/ui/ui-framework/integration-tests/samples/src/main/java/androidx/ui/framework/samples/LayoutSample.kt
index 3278903..8d75bc0 100644
--- a/ui/ui-framework/integration-tests/samples/src/main/java/androidx/ui/framework/samples/LayoutSample.kt
+++ b/ui/ui-framework/integration-tests/samples/src/main/java/androidx/ui/framework/samples/LayoutSample.kt
@@ -108,7 +108,7 @@
val placeables = measurables.map { measurable ->
when (measurable.tag) {
// You should use appropriate constraints. Here we measure with dummy constraints.
- "header" -> measurable.measure(Constraints.tightConstraints(100.ipx, 100.ipx))
+ "header" -> measurable.measure(Constraints.fixed(100.ipx, 100.ipx))
"footer" -> measurable.measure(constraints)
else -> error("Unexpected tag")
}
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/PopupTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/PopupTest.kt
index 5f88291..deae4ca1 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/PopupTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/PopupTest.kt
@@ -29,6 +29,7 @@
import androidx.ui.unit.IntPx
import androidx.ui.unit.IntPxPosition
import androidx.ui.unit.IntPxSize
+import androidx.ui.unit.ipx
import androidx.ui.unit.isFinite
import androidx.ui.unit.toPxPosition
import androidx.ui.unit.toPxSize
@@ -557,7 +558,7 @@
Layout(children) { measurables, constraints ->
val measurable = measurables.firstOrNull()
// The child cannot be larger than our max constraints, but we ignore min constraints.
- val placeable = measurable?.measure(constraints.looseMin())
+ val placeable = measurable?.measure(constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx))
// The layout is as large as possible for bounded constraints,
// or wrap content otherwise.
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/TextLayoutTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/TextLayoutTest.kt
index adead6a..e3540d0 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/TextLayoutTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/TextLayoutTest.kt
@@ -180,7 +180,9 @@
override fun run() {
activity.setContent {
Layout(composable) { measurables, constraints ->
- val placeables = measurables.map { it.measure(constraints.looseMin()) }
+ val placeables = measurables.map {
+ it.measure(constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx))
+ }
layout(constraints.maxWidth, constraints.maxHeight) {
var top = 0.px
placeables.forEach {
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
index 5bbd366..0dd8845 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
@@ -54,7 +54,6 @@
import androidx.ui.core.draw
import androidx.ui.core.drawWithContent
import androidx.ui.core.globalPosition
-import androidx.ui.core.looseMin
import androidx.ui.core.offset
import androidx.ui.core.setContent
import androidx.ui.core.tag
@@ -395,8 +394,8 @@
val childrenCount = 3
val childConstraints = arrayOf(
Constraints(),
- Constraints.tightConstraintsForWidth(50.ipx),
- Constraints.tightConstraintsForHeight(50.ipx)
+ Constraints.fixedWidth(50.ipx),
+ Constraints.fixedHeight(50.ipx)
)
val headerChildrenCount = 1
val footerChildrenCount = 2
@@ -2069,7 +2068,7 @@
private val AlignTopLeft = object : LayoutModifier {
override fun DensityScope.modifyConstraints(constraints: Constraints) =
- constraints.looseMin()
+ constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx)
override fun DensityScope.modifySize(
constraints: Constraints,
childSize: IntPxSize
@@ -2302,7 +2301,7 @@
children: @Composable() () -> Unit = {}
) {
Layout(children = children, modifier = modifier) { measurables, _ ->
- val newConstraints = Constraints.tightConstraints(size, size)
+ val newConstraints = Constraints.fixed(size, size)
val placeables = measurables.map { m ->
m.measure(newConstraints)
}
@@ -2380,7 +2379,7 @@
Layout(children = children) { measurables, _ ->
val testConstraints = Constraints()
measurables.forEach { it.measure(testConstraints) }
- val childConstraints = Constraints.tightConstraints(size, size)
+ val childConstraints = Constraints.fixed(size, size)
try {
val placeables2 = measurables.map { it.measure(childConstraints) }
fail("Measuring twice on the same Measurable should throw an exception")
@@ -2681,7 +2680,7 @@
}
override fun DensityScope.modifyConstraints(constraints: Constraints): Constraints {
- return Constraints.tightConstraints(10.ipx, 10.ipx)
+ return Constraints.fixed(10.ipx, 10.ipx)
}
override fun DensityScope.modifySize(
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/WithConstraintsTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/WithConstraintsTest.kt
index 128ad7e..d5d106b 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/WithConstraintsTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/WithConstraintsTest.kt
@@ -146,7 +146,7 @@
}
}) { measurables, constraints3 ->
val placeable = measurables[0].measure(
- Constraints.tightConstraints(
+ Constraints.fixed(
model.size,
model.size
)
@@ -285,13 +285,13 @@
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
- assertEquals(Constraints.tightConstraints(50.ipx, 50.ipx), actualConstraints)
+ assertEquals(Constraints.fixed(50.ipx, 50.ipx), actualConstraints)
latch = CountDownLatch(1)
rule.runOnUiThread { model.value = 100.ipx }
assertTrue(latch.await(1, TimeUnit.SECONDS))
- assertEquals(Constraints.tightConstraints(100.ipx, 100.ipx), actualConstraints)
+ assertEquals(Constraints.fixed(100.ipx, 100.ipx), actualConstraints)
}
@Test
@@ -613,7 +613,7 @@
private fun ChangingConstraintsLayout(size: ValueModel<IntPx>, children: @Composable() () -> Unit) {
Layout(children) { measurables, _ ->
layout(100.ipx, 100.ipx) {
- val constraints = Constraints.tightConstraints(size.value, size.value)
+ val constraints = Constraints.fixed(size.value, size.value)
measurables.first().measure(constraints).place(0.ipx, 0.ipx)
}
}
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 e784dd4..79d3dac 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
@@ -18,9 +18,7 @@
import androidx.compose.Ambient
import androidx.compose.Composable
import androidx.compose.ambient
-import androidx.compose.compositionReference
import androidx.compose.onCommit
-import androidx.compose.onDispose
import androidx.compose.remember
import androidx.compose.state
import androidx.ui.core.selection.Selectable
@@ -34,11 +32,9 @@
import androidx.ui.text.TextDelegate
import androidx.ui.text.TextLayoutResult
import androidx.ui.text.TextRange
-import androidx.ui.text.TextSpan
import androidx.ui.text.TextStyle
import androidx.ui.text.style.TextAlign
import androidx.ui.text.style.TextOverflow
-import androidx.ui.text.toAnnotatedString
import androidx.ui.unit.IntPx
import androidx.ui.unit.ipx
import androidx.ui.unit.max
@@ -52,46 +48,6 @@
internal val DefaultSelectionColor = Color(0x6633B5E5)
/**
- * The Text composable displays text that uses multiple different styles. The text to display is
- * described using a tree of [Span], each of which has an associated style that is used
- * for that subtree. The text might break across multiple lines or might all be displayed on the
- * same line depending on the layout constraints.
- *
- * @param modifier Modifier to apply to this layout node.
- * @param style Style configuration for the text such as color, font, line height etc.
- * @param softWrap Whether the text should break at soft line breaks. If false, the glyphs in the
- * text will be positioned as if there was unlimited horizontal space. If [softWrap] is false,
- * [overflow] and [TextAlign] may have unexpected effects.
- * @param overflow How visual overflow should be handled.
- * @param maxLines An optional maximum number of lines for the text to span, wrapping if
- * necessary. If the text exceeds the given number of lines, it will be truncated according to
- * [overflow] and [softWrap]. If it is not null, then it must be greater than zero.
- */
-@Composable
-fun Text(
- modifier: Modifier = Modifier.None,
- style: TextStyle? = null,
- softWrap: Boolean = DefaultSoftWrap,
- overflow: TextOverflow = DefaultOverflow,
- maxLines: Int = DefaultMaxLines,
- child: @Composable TextSpanScope.() -> Unit
-) {
- val rootTextSpan = remember { TextSpan() }
- val ref = compositionReference()
- compose(rootTextSpan, ref, child)
- onDispose { disposeComposition(rootTextSpan, ref) }
-
- Text(
- text = rootTextSpan.toAnnotatedString(),
- modifier = modifier,
- style = style,
- softWrap = softWrap,
- overflow = overflow,
- maxLines = maxLines
- )
-}
-
-/**
* Simplified version of [Text] component with minimal set of customizations.
*
* @param text The text to be displayed.
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/TextFieldDelegate.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TextFieldDelegate.kt
index 7ac3bc3..84a3909 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/TextFieldDelegate.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TextFieldDelegate.kt
@@ -104,7 +104,7 @@
): Triple<IntPx, IntPx, TextLayoutResult> {
val layoutResult = if (constraints.maxWidth.isFinite()) {
textDelegate.layout(
- Constraints.tightConstraintsForWidth(constraints.maxWidth),
+ Constraints.fixedWidth(constraints.maxWidth),
prevResultText
)
} else {
@@ -112,7 +112,7 @@
// falling back to wrap-content behavior since it may be in the horizontal scroller.
textDelegate.layoutIntrinsics()
textDelegate.layout(
- Constraints.tightConstraintsForWidth(textDelegate.maxIntrinsicWidth),
+ Constraints.fixedWidth(textDelegate.maxIntrinsicWidth),
prevResultText
)
}
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt
deleted file mode 100644
index 5014552..0000000
--- a/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt
+++ /dev/null
@@ -1,260 +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.core
-
-import android.annotation.SuppressLint
-import androidx.compose.Applier
-import androidx.compose.ApplyAdapter
-import androidx.compose.Component
-import androidx.compose.Composable
-import androidx.compose.Composer
-import androidx.compose.ComposerUpdater
-import androidx.compose.CompositionContext
-import androidx.compose.CompositionReference
-import androidx.compose.Recomposer
-import androidx.compose.SlotTable
-import androidx.compose.ViewValidator
-import androidx.compose.cache
-import androidx.ui.text.SpanStyle
-import androidx.ui.text.TextSpan
-import java.util.WeakHashMap
-
-/**
- * This adapter is used by [TextSpanComposer] to build the [TextSpan] tree.
- * @see ApplyAdapter
- */
-internal class TextSpanApplyAdapter : ApplyAdapter<TextSpan> {
- override fun TextSpan.start(instance: TextSpan) {}
-
- override fun TextSpan.end(instance: TextSpan, parent: TextSpan) {}
-
- override fun TextSpan.insertAt(index: Int, instance: TextSpan) {
- children.add(index, instance)
- }
-
- override fun TextSpan.removeAt(index: Int, count: Int) {
- repeat(count) {
- children.removeAt(index)
- }
- }
-
- override fun TextSpan.move(from: Int, to: Int, count: Int) {
- if (from == to) return
-
- if (from > to) {
- val moved = mutableListOf<TextSpan>()
- repeat(count) {
- moved.add(children.removeAt(from))
- }
- children.addAll(to, moved)
- } else {
- // Number of elements between to and from is smaller than count, can't move.
- if (count > to - from) return
- repeat(count) {
- val node = children.removeAt(from)
- children.add(to - 1, node)
- }
- }
- }
-}
-
-typealias TextSpanUpdater<T> = ComposerUpdater<TextSpan, T>
-
-@PublishedApi
-internal val invocation = Object()
-
-/**
- * The composer of [TextSpan].
- */
-class TextSpanComposer internal constructor(
- root: TextSpan,
- recomposer: Recomposer
-) : Composer<TextSpan>(SlotTable(), Applier(root, TextSpanApplyAdapter()), recomposer) {
- inline fun emit(
- key: Any,
- /*crossinline*/
- ctor: () -> TextSpan,
- update: TextSpanUpdater<TextSpan>.() -> Unit
- ) {
- startNode(key)
- @Suppress("UNCHECKED_CAST") val node = if (inserting) ctor().also { emitNode(it) }
- else useNode()
- TextSpanUpdater(this, node).update()
- endNode()
- }
-
- inline fun emit(
- key: Any,
- /*crossinline*/
- ctor: () -> TextSpan,
- update: TextSpanUpdater<TextSpan>.() -> Unit,
- children: () -> Unit
- ) {
- startNode(key)
- @Suppress("UNCHECKED_CAST")val node = if (inserting) ctor().also { emitNode(it) }
- else useNode()
- TextSpanUpdater(this, node).update()
- children()
- endNode()
- }
-
- inline fun call(
- key: Any,
- /*crossinline*/
- invalid: ViewValidator.() -> Boolean,
- block: () -> Unit
- ) {
- startGroup(key)
- if (ViewValidator(this).invalid() || inserting) {
- startGroup(invocation)
- block()
- endGroup()
- } else {
- skipCurrentGroup()
- }
- endGroup()
- }
-
- inline fun <T> call(
- key: Any,
- /*crossinline*/
- ctor: () -> T,
- /*crossinline*/
- invalid: ViewValidator.(f: T) -> Boolean,
- block: (f: T) -> Unit
- ) {
- startGroup(key)
- val f = cache(true, ctor)
- if (ViewValidator(this).invalid(f) || inserting) {
- startGroup(invocation)
- block(f)
- endGroup()
- } else {
- skipCurrentGroup()
- }
- endGroup()
- }
-}
-
-/**
- * As the name indicates, [Root] object is associated with a [TextSpan] tree root. It contains
- * necessary information used to compose and recompose [TextSpan] tree. It's created and stored
- * when the [TextSpan] container is composed for the first time.
- */
-private class Root : Component() {
- fun update() = compositionContext.compose()
- lateinit var scope: TextSpanScope
- lateinit var compositionContext: CompositionContext
- lateinit var composable: @Composable() TextSpanScope.() -> Unit
- @SuppressLint("UnnecessaryLambdaCreation")
- override fun compose() {
- with(scope) {
- composable()
- }
- }
-}
-
-/**
- * The map used store the [Root] object for [TextSpan] trees.
- */
-private val TEXTSPAN_ROOT_COMPONENTS = WeakHashMap<TextSpan, Root>()
-
-/**
- * Get the [Root] object of the given [TextSpan] root node.
- */
-private fun getRootComponent(node: TextSpan): Root? {
- return TEXTSPAN_ROOT_COMPONENTS[node]
-}
-
-/**
- * Store the [Root] object of [node].
- */
-private fun setRoot(node: TextSpan, component: Root) {
- TEXTSPAN_ROOT_COMPONENTS[node] = component
-}
-
-/**
- * Compose a [TextSpan] tree.
- * @param container The root of [TextSpan] tree where the children TextSpans will be attached to.
- * @param parent The parent composition reference, if applicable. Default is null.
- * @param composable The composable function to compose the children of [container].
- * @see CompositionReference
- */
-fun compose(
- container: TextSpan,
- parent: CompositionReference? = null,
- composable: @Composable() TextSpanScope.() -> Unit
-) {
- var root = getRootComponent(container)
- if (root == null) {
- lateinit var composer: TextSpanComposer
- root = Root()
- setRoot(container, root)
- root.compositionContext = CompositionContext.prepare(root, parent) {
- TextSpanComposer(container, this).also { composer = it }
- }
- root.scope = TextSpanScope(composer)
- root.composable = composable
-
- root.update()
- } else {
- root.composable = composable
-
- root.update()
- }
-}
-
-/**
- * Cleanup when the [TextSpan] is no longer used.
- *
- * @param container The root of the [TextSpan] to be disposed.
- * @param parent The [CompositionReference] used together with [container] when [compose] is
- * called.
- */
-fun disposeComposition(
- container: TextSpan,
- parent: CompositionReference? = null
-) {
- // temporary easy way to call correct lifecycles on everything
- compose(container, parent) {}
- TEXTSPAN_ROOT_COMPONENTS.remove(container)
-}
-
-/**
- * The receiver class of the children of Text and TextSpan. Such that [Span] can only be used
- * within [Text] and [TextSpan].
- */
-class TextSpanScope internal constructor(val composer: TextSpanComposer)
-
-@Composable
-fun TextSpanScope.Span(
- text: String? = null,
- style: SpanStyle? = null,
- child: @Composable TextSpanScope.() -> Unit
-) {
- TextSpan(text = text, style = style) {
- child()
- }
-}
-
-@Composable
-fun TextSpanScope.Span(
- text: String? = null,
- style: SpanStyle? = null
-) {
- TextSpan(text = text, style = style)
-}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
index 7b16ba5..7f8a404 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
@@ -29,10 +29,8 @@
import androidx.ui.core.gesture.LongPressDragGestureDetector
import androidx.ui.core.gesture.PressReleasedGestureDetector
import androidx.ui.core.gesture.TouchSlopDragGestureDetector
-import androidx.ui.core.hasTightHeight
-import androidx.ui.core.hasTightWidth
-import androidx.ui.core.looseMin
-import androidx.ui.core.withTight
+import androidx.ui.core.hasFixedHeight
+import androidx.ui.core.hasFixedWidth
import androidx.ui.unit.Dp
import androidx.ui.unit.IntPx
import androidx.ui.unit.IntPxPosition
@@ -187,12 +185,17 @@
) {
Layout(children) { measurables, incomingConstraints ->
val containerConstraints = Constraints()
- .withTight(width?.toIntPx(), height?.toIntPx())
+ .copy(
+ width?.toIntPx() ?: 0.ipx,
+ width?.toIntPx() ?: IntPx.Infinity,
+ height?.toIntPx() ?: 0.ipx,
+ height?.toIntPx() ?: IntPx.Infinity
+ )
.enforce(incomingConstraints)
- val childConstraints = containerConstraints.looseMin()
+ val childConstraints = containerConstraints.copy(minWidth = 0.ipx, minHeight = 0.ipx)
var placeable: Placeable? = null
val containerWidth = if (
- containerConstraints.hasTightWidth &&
+ containerConstraints.hasFixedWidth &&
containerConstraints.maxWidth.isFinite()
) {
containerConstraints.maxWidth
@@ -201,7 +204,7 @@
max((placeable?.width ?: 0.ipx), containerConstraints.minWidth)
}
val containerHeight = if (
- containerConstraints.hasTightHeight &&
+ containerConstraints.hasFixedHeight &&
containerConstraints.maxHeight.isFinite()
) {
containerConstraints.maxHeight
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionHandles.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionHandles.kt
index 0d69168..b94ad32 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionHandles.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionHandles.kt
@@ -21,7 +21,6 @@
import androidx.ui.core.Constraints
import androidx.ui.core.Draw
import androidx.ui.core.Layout
-import androidx.ui.core.withTight
import androidx.ui.geometry.Rect
import androidx.ui.graphics.Color
import androidx.ui.graphics.Paint
@@ -81,7 +80,7 @@
children: @Composable() () -> Unit
) {
Layout(children) { measurables, _ ->
- val constraints = Constraints().withTight(width.toIntPx(), height.toIntPx())
+ val constraints = Constraints.fixed(width.toIntPx(), height.toIntPx())
val placeables = measurables.map { measurable ->
measurable.measure(constraints)
}
diff --git a/ui/ui-framework/src/test/java/androidx/ui/core/TextFieldDelegateTest.kt b/ui/ui-framework/src/test/java/androidx/ui/core/TextFieldDelegateTest.kt
index 54880f5..147c8ee 100644
--- a/ui/ui-framework/src/test/java/androidx/ui/core/TextFieldDelegateTest.kt
+++ b/ui/ui-framework/src/test/java/androidx/ui/core/TextFieldDelegateTest.kt
@@ -498,6 +498,6 @@
assertThat(res.first).isEqualTo(123.ipx)
assertEquals(512.ipx, res.second)
- verify(mDelegate, times(1)).layout(Constraints.tightConstraintsForWidth(123.ipx))
+ verify(mDelegate, times(1)).layout(Constraints.fixedWidth(123.ipx))
}
}
diff --git a/ui/ui-graphics/api/0.1.0-dev04.txt b/ui/ui-graphics/api/0.1.0-dev04.txt
index 02758ea..ea83263 100644
--- a/ui/ui-graphics/api/0.1.0-dev04.txt
+++ b/ui/ui-graphics/api/0.1.0-dev04.txt
@@ -722,34 +722,6 @@
method public androidx.ui.graphics.vector.PathBuilder verticalLineToRelative(float y);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand Close;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand CurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand LineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand MoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand QuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand VerticalLineTo;
- }
-
- public final class PathCommandKt {
- method public static androidx.ui.graphics.vector.PathCommand toPathCommand(char);
- }
-
public abstract sealed class PathNode {
method public final boolean isCurve();
method public final boolean isQuad();
diff --git a/ui/ui-graphics/api/current.txt b/ui/ui-graphics/api/current.txt
index 02758ea..ea83263 100644
--- a/ui/ui-graphics/api/current.txt
+++ b/ui/ui-graphics/api/current.txt
@@ -722,34 +722,6 @@
method public androidx.ui.graphics.vector.PathBuilder verticalLineToRelative(float y);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand Close;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand CurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand LineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand MoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand QuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand VerticalLineTo;
- }
-
- public final class PathCommandKt {
- method public static androidx.ui.graphics.vector.PathCommand toPathCommand(char);
- }
-
public abstract sealed class PathNode {
method public final boolean isCurve();
method public final boolean isQuad();
diff --git a/ui/ui-graphics/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-graphics/api/public_plus_experimental_0.1.0-dev04.txt
index 02758ea..ea83263 100644
--- a/ui/ui-graphics/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-graphics/api/public_plus_experimental_0.1.0-dev04.txt
@@ -722,34 +722,6 @@
method public androidx.ui.graphics.vector.PathBuilder verticalLineToRelative(float y);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand Close;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand CurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand LineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand MoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand QuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand VerticalLineTo;
- }
-
- public final class PathCommandKt {
- method public static androidx.ui.graphics.vector.PathCommand toPathCommand(char);
- }
-
public abstract sealed class PathNode {
method public final boolean isCurve();
method public final boolean isQuad();
diff --git a/ui/ui-graphics/api/public_plus_experimental_current.txt b/ui/ui-graphics/api/public_plus_experimental_current.txt
index 02758ea..ea83263 100644
--- a/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -722,34 +722,6 @@
method public androidx.ui.graphics.vector.PathBuilder verticalLineToRelative(float y);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand Close;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand CurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand LineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand MoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand QuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand VerticalLineTo;
- }
-
- public final class PathCommandKt {
- method public static androidx.ui.graphics.vector.PathCommand toPathCommand(char);
- }
-
public abstract sealed class PathNode {
method public final boolean isCurve();
method public final boolean isQuad();
diff --git a/ui/ui-graphics/api/restricted_0.1.0-dev04.txt b/ui/ui-graphics/api/restricted_0.1.0-dev04.txt
index 02758ea..ea83263 100644
--- a/ui/ui-graphics/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-graphics/api/restricted_0.1.0-dev04.txt
@@ -722,34 +722,6 @@
method public androidx.ui.graphics.vector.PathBuilder verticalLineToRelative(float y);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand Close;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand CurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand LineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand MoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand QuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand VerticalLineTo;
- }
-
- public final class PathCommandKt {
- method public static androidx.ui.graphics.vector.PathCommand toPathCommand(char);
- }
-
public abstract sealed class PathNode {
method public final boolean isCurve();
method public final boolean isQuad();
diff --git a/ui/ui-graphics/api/restricted_current.txt b/ui/ui-graphics/api/restricted_current.txt
index 02758ea..ea83263 100644
--- a/ui/ui-graphics/api/restricted_current.txt
+++ b/ui/ui-graphics/api/restricted_current.txt
@@ -722,34 +722,6 @@
method public androidx.ui.graphics.vector.PathBuilder verticalLineToRelative(float y);
}
- public enum PathCommand {
- method public final char toKey();
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand Close;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand CurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand HorizontalLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand LineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand MoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand QuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand ReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeArcTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeClose;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeHorizontalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeLineTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeMoveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveCurveTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeReflectiveQuadTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand RelativeVerticalTo;
- enum_constant public static final androidx.ui.graphics.vector.PathCommand VerticalLineTo;
- }
-
- public final class PathCommandKt {
- method public static androidx.ui.graphics.vector.PathCommand toPathCommand(char);
- }
-
public abstract sealed class PathNode {
method public final boolean isCurve();
method public final boolean isQuad();
diff --git a/ui/ui-graphics/src/main/java/androidx/ui/graphics/vector/PathCommand.kt b/ui/ui-graphics/src/main/java/androidx/ui/graphics/vector/PathCommand.kt
deleted file mode 100644
index 6884f31..0000000
--- a/ui/ui-graphics/src/main/java/androidx/ui/graphics/vector/PathCommand.kt
+++ /dev/null
@@ -1,100 +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.graphics.vector
-
-enum class PathCommand(private val mKey: Char) {
-
- RelativeClose(RelativeCloseKey),
- Close(CloseKey),
- RelativeMoveTo(RelativeMoveToKey),
- MoveTo(MoveToKey),
- RelativeLineTo(RelativeLineToKey),
- LineTo(LineToKey),
- RelativeHorizontalTo(RelativeHorizontalToKey),
- HorizontalLineTo(HorizontalLineToKey),
- RelativeVerticalTo(RelativeVerticalToKey),
- VerticalLineTo(VerticalLineToKey),
- RelativeCurveTo(RelativeCurveToKey),
- CurveTo(CurveToKey),
- RelativeReflectiveCurveTo(RelativeReflectiveCurveToKey),
- ReflectiveCurveTo(ReflectiveCurveToKey),
- RelativeQuadTo(RelativeQuadToKey),
- QuadTo(QuadToKey),
- RelativeReflectiveQuadTo(RelativeReflectiveQuadToKey),
- ReflectiveQuadTo(ReflectiveQuadToKey),
- RelativeArcTo(RelativeArcToKey),
- ArcTo(ArcToKey);
-
- /**
- * Return the serialized key that represents this path command as a character
- */
- fun toKey(): Char = mKey
-}
-
-/**
- * Return the corresponding PathCommand for the given character key if it exists.
- * If the key is unknown then IllegalArgumentException is thrown
- * @return PathCommand that matches the key
- */
-fun Char.toPathCommand(): PathCommand = when (this) {
- RelativeCloseKey -> PathCommand.RelativeClose
- CloseKey -> PathCommand.Close
- RelativeMoveToKey -> PathCommand.RelativeMoveTo
- MoveToKey -> PathCommand.MoveTo
- RelativeLineToKey -> PathCommand.RelativeLineTo
- LineToKey -> PathCommand.LineTo
- RelativeHorizontalToKey -> PathCommand.RelativeHorizontalTo
- HorizontalLineToKey -> PathCommand.HorizontalLineTo
- RelativeVerticalToKey -> PathCommand.RelativeVerticalTo
- VerticalLineToKey -> PathCommand.VerticalLineTo
- RelativeCurveToKey -> PathCommand.RelativeCurveTo
- CurveToKey -> PathCommand.CurveTo
- RelativeReflectiveCurveToKey -> PathCommand.RelativeReflectiveCurveTo
- ReflectiveCurveToKey -> PathCommand.ReflectiveCurveTo
- RelativeQuadToKey -> PathCommand.RelativeQuadTo
- QuadToKey -> PathCommand.QuadTo
- RelativeReflectiveQuadToKey -> PathCommand.RelativeReflectiveQuadTo
- ReflectiveQuadToKey -> PathCommand.ReflectiveQuadTo
- RelativeArcToKey -> PathCommand.RelativeArcTo
- ArcToKey -> PathCommand.ArcTo
- else -> throw IllegalArgumentException("Unknown command for: $this")
-}
-
-/**
- * Compile time character constants to support exhaustive switch statements and reuse
- * of values between the enum definition and the pathCommandFromKey method
- */
-private const val RelativeCloseKey = 'z'
-private const val CloseKey = 'Z'
-private const val RelativeMoveToKey = 'm'
-private const val MoveToKey = 'M'
-private const val RelativeLineToKey = 'l'
-private const val LineToKey = 'L'
-private const val RelativeHorizontalToKey = 'h'
-private const val HorizontalLineToKey = 'H'
-private const val RelativeVerticalToKey = 'v'
-private const val VerticalLineToKey = 'V'
-private const val RelativeCurveToKey = 'c'
-private const val CurveToKey = 'C'
-private const val RelativeReflectiveCurveToKey = 's'
-private const val ReflectiveCurveToKey = 'S'
-private const val RelativeQuadToKey = 'q'
-private const val QuadToKey = 'Q'
-private const val RelativeReflectiveQuadToKey = 't'
-private const val ReflectiveQuadToKey = 'T'
-private const val RelativeArcToKey = 'a'
-private const val ArcToKey = 'A'
diff --git a/ui/ui-layout/api/0.1.0-dev04.txt b/ui/ui-layout/api/0.1.0-dev04.txt
index 62add70..581a9dc 100644
--- a/ui/ui-layout/api/0.1.0-dev04.txt
+++ b/ui/ui-layout/api/0.1.0-dev04.txt
@@ -136,8 +136,6 @@
@androidx.ui.layout.LayoutScopeMarker public abstract sealed class FlexScope {
method public final androidx.ui.core.ParentDataModifier LayoutFlexible(@FloatRange(from=0.0, fromInclusive=false) float flex, boolean tight = true);
method public final androidx.ui.core.ParentDataModifier RelativeToSiblings(androidx.ui.layout.LayoutGravity, kotlin.jvm.functions.Function1<? super androidx.ui.core.Placeable,androidx.ui.unit.IntPx> alignmentLineBlock);
- method public final androidx.ui.core.ParentDataModifier getLayoutInflexible();
- property public final androidx.ui.core.ParentDataModifier LayoutInflexible;
}
public enum FlowCrossAxisAlignment {
diff --git a/ui/ui-layout/api/current.txt b/ui/ui-layout/api/current.txt
index 62add70..581a9dc 100644
--- a/ui/ui-layout/api/current.txt
+++ b/ui/ui-layout/api/current.txt
@@ -136,8 +136,6 @@
@androidx.ui.layout.LayoutScopeMarker public abstract sealed class FlexScope {
method public final androidx.ui.core.ParentDataModifier LayoutFlexible(@FloatRange(from=0.0, fromInclusive=false) float flex, boolean tight = true);
method public final androidx.ui.core.ParentDataModifier RelativeToSiblings(androidx.ui.layout.LayoutGravity, kotlin.jvm.functions.Function1<? super androidx.ui.core.Placeable,androidx.ui.unit.IntPx> alignmentLineBlock);
- method public final androidx.ui.core.ParentDataModifier getLayoutInflexible();
- property public final androidx.ui.core.ParentDataModifier LayoutInflexible;
}
public enum FlowCrossAxisAlignment {
diff --git a/ui/ui-layout/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-layout/api/public_plus_experimental_0.1.0-dev04.txt
index 62add70..581a9dc 100644
--- a/ui/ui-layout/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-layout/api/public_plus_experimental_0.1.0-dev04.txt
@@ -136,8 +136,6 @@
@androidx.ui.layout.LayoutScopeMarker public abstract sealed class FlexScope {
method public final androidx.ui.core.ParentDataModifier LayoutFlexible(@FloatRange(from=0.0, fromInclusive=false) float flex, boolean tight = true);
method public final androidx.ui.core.ParentDataModifier RelativeToSiblings(androidx.ui.layout.LayoutGravity, kotlin.jvm.functions.Function1<? super androidx.ui.core.Placeable,androidx.ui.unit.IntPx> alignmentLineBlock);
- method public final androidx.ui.core.ParentDataModifier getLayoutInflexible();
- property public final androidx.ui.core.ParentDataModifier LayoutInflexible;
}
public enum FlowCrossAxisAlignment {
diff --git a/ui/ui-layout/api/public_plus_experimental_current.txt b/ui/ui-layout/api/public_plus_experimental_current.txt
index 62add70..581a9dc 100644
--- a/ui/ui-layout/api/public_plus_experimental_current.txt
+++ b/ui/ui-layout/api/public_plus_experimental_current.txt
@@ -136,8 +136,6 @@
@androidx.ui.layout.LayoutScopeMarker public abstract sealed class FlexScope {
method public final androidx.ui.core.ParentDataModifier LayoutFlexible(@FloatRange(from=0.0, fromInclusive=false) float flex, boolean tight = true);
method public final androidx.ui.core.ParentDataModifier RelativeToSiblings(androidx.ui.layout.LayoutGravity, kotlin.jvm.functions.Function1<? super androidx.ui.core.Placeable,androidx.ui.unit.IntPx> alignmentLineBlock);
- method public final androidx.ui.core.ParentDataModifier getLayoutInflexible();
- property public final androidx.ui.core.ParentDataModifier LayoutInflexible;
}
public enum FlowCrossAxisAlignment {
diff --git a/ui/ui-layout/api/restricted_0.1.0-dev04.txt b/ui/ui-layout/api/restricted_0.1.0-dev04.txt
index 62add70..581a9dc 100644
--- a/ui/ui-layout/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-layout/api/restricted_0.1.0-dev04.txt
@@ -136,8 +136,6 @@
@androidx.ui.layout.LayoutScopeMarker public abstract sealed class FlexScope {
method public final androidx.ui.core.ParentDataModifier LayoutFlexible(@FloatRange(from=0.0, fromInclusive=false) float flex, boolean tight = true);
method public final androidx.ui.core.ParentDataModifier RelativeToSiblings(androidx.ui.layout.LayoutGravity, kotlin.jvm.functions.Function1<? super androidx.ui.core.Placeable,androidx.ui.unit.IntPx> alignmentLineBlock);
- method public final androidx.ui.core.ParentDataModifier getLayoutInflexible();
- property public final androidx.ui.core.ParentDataModifier LayoutInflexible;
}
public enum FlowCrossAxisAlignment {
diff --git a/ui/ui-layout/api/restricted_current.txt b/ui/ui-layout/api/restricted_current.txt
index 62add70..581a9dc 100644
--- a/ui/ui-layout/api/restricted_current.txt
+++ b/ui/ui-layout/api/restricted_current.txt
@@ -136,8 +136,6 @@
@androidx.ui.layout.LayoutScopeMarker public abstract sealed class FlexScope {
method public final androidx.ui.core.ParentDataModifier LayoutFlexible(@FloatRange(from=0.0, fromInclusive=false) float flex, boolean tight = true);
method public final androidx.ui.core.ParentDataModifier RelativeToSiblings(androidx.ui.layout.LayoutGravity, kotlin.jvm.functions.Function1<? super androidx.ui.core.Placeable,androidx.ui.unit.IntPx> alignmentLineBlock);
- method public final androidx.ui.core.ParentDataModifier getLayoutInflexible();
- property public final androidx.ui.core.ParentDataModifier LayoutInflexible;
}
public enum FlowCrossAxisAlignment {
diff --git a/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/FlexSample.kt b/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/FlexSample.kt
index 18f02be1..b2269af 100644
--- a/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/FlexSample.kt
+++ b/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/FlexSample.kt
@@ -34,11 +34,8 @@
@Composable
fun SimpleRow() {
Row {
- // The child with no flexibility modifier is inflexible by default, will have the specified
- // size.
+ // The child with no flexibility modifier is inflexible and will have the specified size.
SizedRectangle(color = Color.Magenta, width = 40.dp, height = 80.dp)
- // Inflexible, the child will have the specified size.
- SizedRectangle(LayoutInflexible, color = Color.Red, width = 80.dp, height = 40.dp)
// Flexible, the child will occupy have of the remaining width.
SizedRectangle(LayoutFlexible(1f), color = Color.Yellow, height = 40.dp)
// Flexible not tight, the child will occupy at most half of the remaining width.
@@ -50,11 +47,8 @@
@Composable
fun SimpleColumn() {
Column {
- // The child with no flexibility modifier is inflexible by default, will have the specified
- // size.
+ // The child with no flexibility modifier is inflexible and will have the specified size.
SizedRectangle(color = Color.Magenta, width = 40.dp, height = 80.dp)
- // Inflexible, the child will have the specified size.
- SizedRectangle(LayoutInflexible, color = Color.Red, width = 80.dp, height = 40.dp)
// Flexible, the child will occupy have of the remaining height.
SizedRectangle(LayoutFlexible(1f), color = Color.Yellow, width = 40.dp)
// Flexible not tight, the child will occupy at most half of the remaining height.
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
index b83484e..669bef2 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
@@ -49,6 +49,7 @@
import androidx.ui.layout.Wrap
import androidx.ui.unit.Dp
import androidx.ui.unit.IntPx
+import androidx.ui.unit.Px
import androidx.ui.unit.PxPosition
import androidx.ui.unit.PxSize
import androidx.ui.unit.dp
@@ -2898,30 +2899,29 @@
@Test
fun testFlexModifiersChain_leftMostWins() = withDensity(density) {
val positionedLatch = CountDownLatch(1)
- val containerSize = Ref<PxSize>()
- val containerPosition = Ref<PxPosition>()
- val sizeIntPx = 40.dp.toIntPx()
+ val containerHeight = Ref<Px>()
+ val columnHeight = 24.ipx
show {
Align(Alignment.TopLeft) {
- Column(LayoutHeight.Fill) {
+ Column(LayoutHeight(columnHeight.toDp())) {
OnChildPositioned( coordinates ->
- containerSize.value = coordinates.size
- containerPosition.value = coordinates.localToGlobal(PxPosition(0.px, 0.px))
+ containerHeight.value = coordinates.size.height
positionedLatch.countDown()
}) {
Container(
- LayoutInflexible + LayoutFlexible(1f),
- width = 40.dp, height = 40.dp) {}
+ LayoutFlexible(2f) + LayoutFlexible(1f)
+ ) {}
}
+ Container(LayoutFlexible(1f)) {}
}
}
}
positionedLatch.await(1, TimeUnit.SECONDS)
- assertNotNull(containerSize)
- assertEquals(PxSize(sizeIntPx, sizeIntPx), containerSize.value)
+ assertNotNull(containerHeight.value)
+ Assert.assertEquals(columnHeight.toPx() * 2 / 3, containerHeight.value)
}
@Test
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
index 63ee82d..f30c91c 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
@@ -21,7 +21,6 @@
import androidx.ui.core.Constraints
import androidx.ui.core.Layout
import androidx.ui.core.LayoutModifier
-import androidx.ui.core.looseMin
import androidx.ui.unit.DensityScope
import androidx.ui.unit.IntPxPosition
import androidx.ui.unit.IntPxSize
@@ -46,7 +45,7 @@
Layout(children) { measurables, constraints ->
val measurable = measurables.firstOrNull()
// The child cannot be larger than our max constraints, but we ignore min constraints.
- val placeable = measurable?.measure(constraints.looseMin())
+ val placeable = measurable?.measure(constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx))
// The layout is as large as possible for bounded constraints,
// or wrap content otherwise.
@@ -225,7 +224,7 @@
private val direction: Direction
) : LayoutModifier {
override fun DensityScope.modifyConstraints(constraints: Constraints) = when (direction) {
- Direction.Both -> constraints.looseMin()
+ Direction.Both -> constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx)
Direction.Horizontal -> constraints.copy(minWidth = 0.ipx)
Direction.Vertical -> constraints.copy(minHeight = 0.ipx)
}
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
index 247117c..da98f0d 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
@@ -58,7 +58,7 @@
override fun DensityScope.modifyConstraints(constraints: Constraints): Constraints {
val size = constraints.findSizeWith(aspectRatio)
return if (size != null)
- Constraints.tightConstraints(size.width, size.height)
+ Constraints.fixed(size.width, size.height)
else
constraints
}
@@ -124,7 +124,7 @@
val measurable = measurables.firstOrNull()
val childConstraints = if (size != null) {
- Constraints.tightConstraints(size.width, size.height)
+ Constraints.fixed(size.width, size.height)
} else {
constraints
}
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
index 6eec551..0af085e 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
@@ -23,11 +23,9 @@
import androidx.ui.core.Modifier
import androidx.ui.core.Placeable
import androidx.ui.core.enforce
-import androidx.ui.core.hasTightHeight
-import androidx.ui.core.hasTightWidth
-import androidx.ui.core.looseMin
+import androidx.ui.core.hasFixedHeight
+import androidx.ui.core.hasFixedWidth
import androidx.ui.core.offset
-import androidx.ui.core.withTight
import androidx.ui.unit.Dp
import androidx.ui.unit.IntPxSize
import androidx.ui.unit.dp
@@ -68,15 +66,19 @@
) {
Layout(children, modifier) { measurables, incomingConstraints ->
val containerConstraints = Constraints(constraints)
- .withTight(width?.toIntPx(), height?.toIntPx())
- .enforce(incomingConstraints)
+ .copy(
+ width?.toIntPx() ?: constraints.minWidth.toIntPx(),
+ width?.toIntPx() ?: constraints.maxWidth.toIntPx(),
+ height?.toIntPx() ?: constraints.minHeight.toIntPx(),
+ height?.toIntPx() ?: constraints.maxHeight.toIntPx()
+ ).enforce(incomingConstraints)
val totalHorizontal = padding.left.toIntPx() + padding.right.toIntPx()
val totalVertical = padding.top.toIntPx() + padding.bottom.toIntPx()
val childConstraints = containerConstraints
- .looseMin()
+ .copy(minWidth = 0.ipx, minHeight = 0.ipx)
.offset(-totalHorizontal, -totalVertical)
var placeable: Placeable? = null
- val containerWidth = if ((containerConstraints.hasTightWidth || expanded) &&
+ val containerWidth = if ((containerConstraints.hasFixedWidth || expanded) &&
containerConstraints.maxWidth.isFinite()
) {
containerConstraints.maxWidth
@@ -84,7 +86,7 @@
placeable = measurables.firstOrNull()?.measure(childConstraints)
max((placeable?.width ?: 0.ipx) + totalHorizontal, containerConstraints.minWidth)
}
- val containerHeight = if ((containerConstraints.hasTightHeight || expanded) &&
+ val containerHeight = if ((containerConstraints.hasFixedHeight || expanded) &&
containerConstraints.maxHeight.isFinite()
) {
containerConstraints.maxHeight
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
index f851006..118a77a 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
@@ -169,16 +169,18 @@
}
/**
- * A FlexScope provides a scope for Inflexible/Flexible functions.
+ * Base class for scopes of [Row] and [Column], containing scoped modifiers.
*/
@LayoutScopeMarker
sealed class FlexScope {
/**
- * A layout modifier within a [Column] or [Row] that makes the target component flexible.
- * It will be assigned a space according to its flex weight relative to the flexible siblings.
+ * A layout modifier within a [Column] or [Row] that makes the target component flexible in
+ * the main direction of the parent (vertically in [Column] and horizontally in [Row]).
+ * It will be assigned a space according to its flex weight, proportional to the flex
+ * weights of other flexible siblings. If a sibling is not flexible, its flex weight will be 0.
* When [tight] is set to true, the target component is forced to occupy the entire space
* assigned to it by the parent. [LayoutFlexible] children will be measured after all the
- * [LayoutInflexible] ones have been measured, in order to divide the unclaimed space between
+ * inflexible ones have been measured, in order to divide the unclaimed space between
* them.
*/
fun LayoutFlexible(
@@ -194,31 +196,21 @@
}
/**
- * A layout modifier within a [Column] or [Row] that makes the target component inflexible.
- * All [LayoutInflexible] children will be measured before the [LayoutFlexible] ones. They will
- * be measured in the order they appear, without min constraints and with max constraints in
- * the main direction of the layout (maxHeight for Column and maxWidth for Row) such that
- * the sum of the space occupied by inflexible children will not exceed the incoming constraint
- * of the [Column] or [Row]: for example the first child of a [Column] will be measured with
- * maxHeight = column's maxHeight; the second child will be measured with maxHeight = column's
- * maxHeight - first child's height, and so on.
- */
- val LayoutInflexible: ParentDataModifier = InflexibleModifier
-
- internal companion object {
- val InflexibleModifier: ParentDataModifier = FlexModifier(
- FlexChildProperties(0f, FlexFit.Loose)
- )
- }
-
- /**
- * A layout modifier within a [Column] or [Row] that positions target component in a
- * perpendicular direction according to the [AlignmentLine] which is provided through the
- * [alignmentLineBlock].
- * If target component is the only component with the specified RelativeToSiblings modifier
- * within a Column or Row, then the component will be positioned using
- * [LayoutGravity.Start][ColumnScope.Start] in Column or [LayoutGravity.Top][RowScope.Top] in
- * Row respectively.
+ * A layout modifier within a [Column] or [Row] that positions its target component relative
+ * to all other elements within the container which have [LayoutGravity.RelativeToSiblings].
+ * The [alignmentLineBlock] accepts the [Placeable] of the targeted layout and returns the
+ * position, perpendicular to the layout direction, along which the target should align
+ * such that it coincides with the alignment lines of all other siblings with
+ * [LayoutGravity.RelativeToSiblings].
+ * Within a [Column] or [Row], all components with [LayoutGravity.RelativeToSiblings] will
+ * align using the specified [AlignmentLine]s or values obtained from [alignmentLineBlock]s,
+ * forming a sibling group. At least one element of the sibling group will be placed as it had
+ * [LayoutGravity.Start][ColumnScope.Start] in [Column] or [LayoutGravity.Top][RowScope.Top]
+ * in [Row], respectively, and the alignment of the other siblings will be then determined
+ * such that the alignment lines coincide. Note that if the target component is the only one
+ * with the [RelativeToSiblings] modifier specified, then the component will be positioned
+ * using [LayoutGravity.Start][ColumnScope.Start] in [Column] or
+ * [LayoutGravity.Top][RowScope.Top] in [Row] respectively.
*
* Example usage:
* @sample androidx.ui.layout.samples.SimpleRelativeToSiblings
@@ -233,29 +225,36 @@
* A ColumnScope provides a scope for the children of a [Column].
*/
@Suppress("unused") // Note: Gravity object provides a scope only but is never used itself
-class ColumnScope internal constructor() : FlexScope() {
+class ColumnScope private constructor() : FlexScope() {
/**
- * A layout modifier within a Column that positions target component in a horizontal direction
- * so that its start edge is aligned to the start edge of the horizontal axis.
+ * A layout modifier within a [Column] that positions its target component horizontally
+ * such that its start edge is aligned to the start edge of the [Column].
*/
// TODO: Consider ltr/rtl.
val LayoutGravity.Start: ParentDataModifier get() = StartGravityModifier
/**
- * A layout modifier within a Column that positions target component in a horizontal direction
- * so that its center is in the middle of the horizontal axis.
+ * A layout modifier within a [Column] that positions its target component horizontally
+ * such that its center is in the middle of the [Column].
*/
val LayoutGravity.Center: ParentDataModifier get() = CenterGravityModifier
/**
- * A layout modifier within a Column that positions target component in a horizontal direction
- * so that its end edge is aligned to the end edge of the horizontal axis.
+ * A layout modifier within a [Column] that positions its target component horizontally
+ * such that its end edge is aligned to the end edge of the [Column].
*/
val LayoutGravity.End: ParentDataModifier get() = EndGravityModifier
/**
- * A layout modifier within a [Column] that positions target component in a perpendicular
- * direction according to the [AlignmentLine].
- * If target component is the only component within a Column with the specified
- * RelativeToSiblings modifier, or if the provided alignment line is not defined for the
- * component, the component will be positioned using [LayoutGravity.Start].
+ * A layout modifier within a [Column] that positions its target component horizontally
+ * according to the specified [VerticalAlignmentLine], such that the position of the alignment
+ * line coincides with the alignment lines of all other siblings having their gravity set to
+ * [LayoutGravity.RelativeToSiblings].
+ * Within a [Column], all components with [LayoutGravity.RelativeToSiblings] will align
+ * horizontally using the specified [AlignmentLine]s or values obtained from
+ * [alignmentLineBlocks][FlexScope.RelativeToSiblings], forming a sibling group.
+ * At least one element of the sibling group will be placed as it had
+ * [LayoutGravity.Start][ColumnScope.Start] in [Column], and the alignment of the other
+ * siblings will be then determined such that the alignment lines coincide. Note that if
+ * the target component is the only one with the [RelativeToSiblings] modifier specified,
+ * then the component will be positioned using [LayoutGravity.Start][ColumnScope.Start].
*
* Example usage:
*
@@ -265,6 +264,8 @@
SiblingsAlignedModifier.WithAlignmentLine(alignmentLine)
internal companion object {
+ internal val Instance = ColumnScope()
+
val StartGravityModifier: ParentDataModifier = GravityModifier(CrossAxisAlignment.Start)
val CenterGravityModifier: ParentDataModifier = GravityModifier(CrossAxisAlignment.Center)
val EndGravityModifier: ParentDataModifier = GravityModifier(CrossAxisAlignment.End)
@@ -275,28 +276,35 @@
* A RowScope provides a scope for the children of a [Row].
*/
@Suppress("unused") // Note: Gravity object provides a scope only but is never used itself
-class RowScope internal constructor() : FlexScope() {
+class RowScope private constructor() : FlexScope() {
/**
- * A layout modifier within a Row that positions target component in a vertical direction
- * so that its top edge is aligned to the top edge of the vertical axis.
+ * A layout modifier within a [Row] that positions its target component vertically
+ * such that its top edge is aligned to the top edge of the [Row].
*/
val LayoutGravity.Top: ParentDataModifier get() = TopGravityModifier
/**
- * A layout modifier within a Row that positions target component in a vertical direction
- * so that its center is in the middle of the vertical axis.
+ * A layout modifier within a Row that positions target component vertically
+ * such that its center is in the middle of the [Row].
*/
val LayoutGravity.Center: ParentDataModifier get() = CenterGravityModifier
/**
- * A layout modifier within a Row that positions target component in a vertical direction
- * so that its bottom edge is aligned to the bottom edge of the vertical axis.
+ * A layout modifier within a Row that positions target component vertically
+ * such that its bottom edge is aligned to the bottom edge of the [Row].
*/
val LayoutGravity.Bottom: ParentDataModifier get() = BottomGravityModifier
/**
- * A layout modifier within a [Row] that positions target component in a perpendicular
- * direction according to the [AlignmentLine].
- * If target component is the only component within a Row with the specified
- * RelativeToSiblings modifier, or if the provided alignment line is not defined for the
- * component, the component will be positioned using [LayoutGravity.Top].
+ * A layout modifier within a [Row] that positions its target component vertically
+ * according to the specified [HorizontalAlignmentLine], such that the position of the alignment
+ * line coincides with the alignment lines of all other siblings having their gravity set to
+ * [LayoutGravity.RelativeToSiblings].
+ * Within a [Row], all components with [LayoutGravity.RelativeToSiblings] will align
+ * vertically using the specified [AlignmentLine]s or values obtained from
+ * [alignmentLineBlocks][FlexScope.RelativeToSiblings], forming a sibling group.
+ * At least one element of the sibling group will be placed as it had
+ * [LayoutGravity.Top][RowScope.Top] in [Row], and the alignment of the other
+ * siblings will be then determined such that the alignment lines coincide. Note that if
+ * the target component is the only one with the [RelativeToSiblings] modifier specified,
+ * then the component will be positioned using [LayoutGravity.Top][RowScope.Top].
*
* Example usage:
* @sample androidx.ui.layout.samples.SimpleRelativeToSiblingsInRow
@@ -306,6 +314,8 @@
): ParentDataModifier = SiblingsAlignedModifier.WithAlignmentLine(alignmentLine)
internal companion object {
+ internal val Instance = RowScope()
+
val TopGravityModifier: ParentDataModifier = GravityModifier(CrossAxisAlignment.Start)
val CenterGravityModifier: ParentDataModifier = GravityModifier(CrossAxisAlignment.Center)
val BottomGravityModifier: ParentDataModifier = GravityModifier(CrossAxisAlignment.End)
@@ -313,11 +323,11 @@
}
/**
- * A composable that places its children in a horizontal sequence and is able to assign them widths
- * according to their flex weights provided through [androidx.ui.layout.FlexScope.LayoutFlexible]
- * modifier.
- * If [androidx.ui.layout.FlexScope.LayoutInflexible] or no modifier is provided, the child will be
- * treated as inflexible, and will be sized to its preferred width.
+ * A layout composable that places its children in a horizontal sequence.
+ * The layout model is able to assign children widths according to their flex weights provided
+ * using the [androidx.ui.layout.FlexScope.LayoutFlexible] modifier. If a child is not
+ * [flexible][androidx.ui.layout.FlexScope.LayoutFlexible], it will be considered inflexible
+ * and will be sized to its preferred width.
*
* Example usage:
*
@@ -338,16 +348,16 @@
arrangement = arrangement,
crossAxisAlignment = CrossAxisAlignment.Start,
crossAxisSize = SizeMode.Wrap,
- children = { RowScope().children() }
+ children = { RowScope.Instance.children() }
)
}
/**
- * A composable that places its children in a vertical sequence and is able to assign them heights
- * according to their flex weights provided through [androidx.ui.layout.FlexScope.LayoutFlexible]
- * modifiers.
- * If [androidx.ui.layout.FlexScope.LayoutInflexible] or no modifier is provided, the child will be
- * treated as inflexible, and will be sized to its preferred height.
+ * A layout composable that places its children in a vertical sequence.
+ * The layout model is able to assign children heights according to their flex weights provided
+ * using the [androidx.ui.layout.FlexScope.LayoutFlexible] modifier. If a child is not
+ * [flexible][androidx.ui.layout.FlexScope.LayoutFlexible], it will be considered inflexible
+ * and will be sized to its preferred width.
*
* Example usage:
*
@@ -368,7 +378,7 @@
arrangement = arrangement,
crossAxisAlignment = CrossAxisAlignment.Start,
crossAxisSize = SizeMode.Wrap,
- children = { ColumnScope().children() }
+ children = { ColumnScope.Instance.children() }
)
}
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
index de48dc3..8ca9aac 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
@@ -58,7 +58,7 @@
val measurable = measurables.firstOrNull()
val width = measurable?.minIntrinsicWidth(constraints.maxHeight) ?: 0.ipx
val placeable = measurable?.measure(
- Constraints.tightConstraintsForWidth(width).enforce(constraints)
+ Constraints.fixedWidth(width).enforce(constraints)
)
layout(placeable?.width ?: 0.ipx, placeable?.height ?: 0.ipx) {
placeable?.place(0.ipx, 0.ipx)
@@ -103,7 +103,7 @@
val measurable = measurables.firstOrNull()
val height = measurable?.minIntrinsicHeight(constraints.maxWidth) ?: 0.ipx
val placeable = measurable?.measure(
- Constraints.tightConstraintsForHeight(height).enforce(constraints)
+ Constraints.fixedHeight(height).enforce(constraints)
)
layout(placeable?.width ?: 0.ipx, placeable?.height ?: 0.ipx) {
placeable?.place(0.ipx, 0.ipx)
@@ -148,7 +148,7 @@
val measurable = measurables.firstOrNull()
val width = measurable?.maxIntrinsicWidth(constraints.maxHeight) ?: 0.ipx
val placeable = measurable?.measure(
- Constraints.tightConstraintsForWidth(width).enforce(constraints)
+ Constraints.fixedWidth(width).enforce(constraints)
)
layout(placeable?.width ?: 0.ipx, placeable?.height ?: 0.ipx) {
placeable?.place(0.ipx, 0.ipx)
@@ -193,7 +193,7 @@
val measurable = measurables.firstOrNull()
val height = measurable?.maxIntrinsicHeight(constraints.maxWidth) ?: 0.ipx
val placeable = measurable?.measure(
- Constraints.tightConstraintsForHeight(height).enforce(constraints)
+ Constraints.fixedHeight(height).enforce(constraints)
)
layout(placeable?.width ?: 0.ipx, placeable?.height ?: 0.ipx) {
placeable?.place(0.ipx, 0.ipx)
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/SizeModifiers.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/SizeModifiers.kt
index 3179784..f29edde 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/SizeModifiers.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/SizeModifiers.kt
@@ -23,7 +23,6 @@
import androidx.ui.core.enforce
import androidx.ui.core.hasBoundedHeight
import androidx.ui.core.hasBoundedWidth
-import androidx.ui.core.withTight
import androidx.ui.unit.DensityScope
import androidx.ui.unit.Dp
import androidx.ui.unit.IntPx
@@ -145,7 +144,7 @@
object Fill : LayoutModifier {
override fun DensityScope.modifyConstraints(constraints: Constraints): Constraints =
if (constraints.hasBoundedWidth) {
- constraints.withTight(width = constraints.maxWidth)
+ constraints.copy(minWidth = constraints.maxWidth, maxWidth = constraints.maxWidth)
} else {
constraints
}
@@ -241,7 +240,10 @@
object Fill : LayoutModifier {
override fun DensityScope.modifyConstraints(constraints: Constraints): Constraints =
if (constraints.hasBoundedHeight) {
- constraints.withTight(height = constraints.maxHeight)
+ constraints.copy(
+ minHeight = constraints.maxHeight,
+ maxHeight = constraints.maxHeight
+ )
} else {
constraints
}
@@ -384,11 +386,16 @@
object Fill : LayoutModifier {
override fun DensityScope.modifyConstraints(constraints: Constraints): Constraints =
when {
- constraints.hasBoundedWidth && constraints.hasBoundedHeight -> constraints
- .withTight(width = constraints.maxWidth, height = constraints.maxHeight)
- constraints.hasBoundedWidth -> constraints.withTight(width = constraints.maxWidth)
- constraints.hasBoundedHeight -> constraints.withTight(
- height = constraints.maxHeight)
+ constraints.hasBoundedWidth && constraints.hasBoundedHeight -> constraints.copy(
+ minWidth = constraints.maxWidth,
+ minHeight = constraints.maxHeight
+ )
+ constraints.hasBoundedWidth -> constraints.copy(
+ minWidth = constraints.maxWidth
+ )
+ constraints.hasBoundedHeight -> constraints.copy(
+ minHeight = constraints.maxHeight
+ )
else -> constraints
}
}
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
index a4099a6..a687c77e 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
@@ -24,10 +24,10 @@
import androidx.ui.core.Modifier
import androidx.ui.core.ParentDataModifier
import androidx.ui.core.Placeable
-import androidx.ui.core.looseMin
import androidx.ui.unit.DensityScope
import androidx.ui.unit.IntPx
import androidx.ui.unit.IntPxSize
+import androidx.ui.unit.ipx
import androidx.ui.unit.isFinite
import androidx.ui.unit.max
@@ -51,8 +51,9 @@
Layout(stackChildren, modifier = modifier) { measurables, constraints ->
val placeables = arrayOfNulls<Placeable>(measurables.size)
// First measure aligned children to get the size of the layout.
+ val childConstraints = constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx)
(0 until measurables.size).filter { i -> !measurables[i].stretch }.forEach { i ->
- placeables[i] = measurables[i].measure(constraints.looseMin())
+ placeables[i] = measurables[i].measure(childConstraints)
}
val (stackWidth, stackHeight) = with(placeables.filterNotNull()) {
Pair(
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Table.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Table.kt
index 12e6b35..3cb26c8 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Table.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Table.kt
@@ -610,7 +610,7 @@
}
}
val decorationConstraints =
- Constraints.tightConstraints(tableSize.width, tableSize.height)
+ Constraints.fixed(tableSize.width, tableSize.height)
measurables.filter { it.rowIndex == null }.forEach {
it.measure(decorationConstraints).place(IntPx.Zero, IntPx.Zero)
}
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
index fe392c6..16104a5 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
@@ -21,7 +21,6 @@
import androidx.ui.core.Constraints
import androidx.ui.core.Layout
import androidx.ui.core.LayoutModifier
-import androidx.ui.core.looseMin
import androidx.ui.unit.DensityScope
import androidx.ui.unit.IntPxPosition
import androidx.ui.unit.IntPxSize
@@ -42,7 +41,7 @@
Layout(children) { measurables, constraints ->
val measurable = measurables.firstOrNull()
// The child cannot be larger than our max constraints, but we ignore min constraints.
- val placeable = measurable?.measure(constraints.looseMin())
+ val placeable = measurable?.measure(constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx))
// Try to be as small as possible.
val layoutWidth = max(placeable?.width ?: 0.ipx, constraints.minWidth)
@@ -67,7 +66,8 @@
* size itself to min incoming constraints and place its content in the center.
*/
val LayoutWrapped: LayoutModifier = object : LayoutModifier {
- override fun DensityScope.modifyConstraints(constraints: Constraints) = constraints.looseMin()
+ override fun DensityScope.modifyConstraints(constraints: Constraints) =
+ constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx)
override fun DensityScope.modifySize(
constraints: Constraints,
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/DataTable.kt b/ui/ui-material/src/main/java/androidx/ui/material/DataTable.kt
index 55471c3..c472027 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/DataTable.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/DataTable.kt
@@ -463,7 +463,7 @@
measurables.forEach { measurable ->
val i = measurable.parentData as Int
val placeable = measurable.measure(
- Constraints.tightConstraints(
+ Constraints.fixed(
width = constraints.maxWidth,
height = verticalOffsets[i + 2] - verticalOffsets[i + 1]
)
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt b/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
index 1908d00..e38f9a3 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
@@ -26,6 +26,7 @@
import androidx.ui.animation.PxPropKey
import androidx.ui.animation.Transition
import androidx.ui.core.Alignment
+import androidx.ui.core.Constraints
import androidx.ui.core.FirstBaseline
import androidx.ui.core.LastBaseline
import androidx.ui.core.Layout
@@ -38,7 +39,6 @@
import androidx.ui.core.WithConstraints
import androidx.ui.core.ambientDensity
import androidx.ui.core.tag
-import androidx.ui.core.withTight
import androidx.ui.foundation.ColoredRect
import androidx.ui.foundation.HorizontalScroller
import androidx.ui.foundation.ScrollerPosition
@@ -281,13 +281,13 @@
// The divider is measured with its own height, and width equal to the total width
// of the tab row, and then placed on top of the tabs.
measurables.firstOrNull { it.tag == DividerTag }
- ?.measure(constraints.withTight(width = layoutWidth))
+ ?.measure(constraints.copy(minWidth = layoutWidth, maxWidth = layoutWidth))
?.run { place(IntPx.Zero, layoutHeight - height) }
// The indicator container is measured to fill the entire space occupied by the tab
// row, and then placed on top of the divider.
measurables.firstOrNull { it.tag == IndicatorTag }
- ?.measure(constraints.withTight(width = layoutWidth, height = layoutHeight))
+ ?.measure(Constraints.fixed(layoutWidth, layoutHeight))
?.place(IntPx.Zero, IntPx.Zero)
}
}
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/AndroidOwner.kt b/ui/ui-platform/src/main/java/androidx/ui/core/AndroidOwner.kt
index e287744..2bbf424 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/AndroidOwner.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/AndroidOwner.kt
@@ -109,7 +109,7 @@
}
private val pointerInputEventProcessor = PointerInputEventProcessor(root)
- var constraints = Constraints.tightConstraints(width = IntPx.Zero, height = IntPx.Zero)
+ var constraints = Constraints.fixed(width = IntPx.Zero, height = IntPx.Zero)
// TODO(mount): reinstate when coroutines are supported by IR compiler
// private val ownerScope = CoroutineScope(Dispatchers.Main.immediate + Job())
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
index dc71ccb..598f5c8 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
@@ -823,7 +823,7 @@
/**
* The constraints used the last time [layout] was called.
*/
- var constraints: Constraints = Constraints.tightConstraints(IntPx.Zero, IntPx.Zero)
+ var constraints: Constraints = Constraints.fixed(IntPx.Zero, IntPx.Zero)
/**
* Implementation oddity around composition; used to capture a reference to this
diff --git a/ui/ui-text/api/0.1.0-dev04.txt b/ui/ui-text/api/0.1.0-dev04.txt
index 9463223..74a8877 100644
--- a/ui/ui-text/api/0.1.0-dev04.txt
+++ b/ui/ui-text/api/0.1.0-dev04.txt
@@ -540,18 +540,6 @@
method public static String substring(CharSequence, androidx.ui.text.TextRange range);
}
- public final class TextSpan {
- ctor public TextSpan(androidx.ui.text.SpanStyle? style, String? text, java.util.List<androidx.ui.text.TextSpan> children);
- ctor public TextSpan();
- method public java.util.List<androidx.ui.text.TextSpan> getChildren();
- method public androidx.ui.text.SpanStyle? getStyle();
- method public String? getText();
- }
-
- public final class TextSpanKt {
- method public static androidx.ui.text.AnnotatedString toAnnotatedString(androidx.ui.text.TextSpan);
- }
-
public final class TextStyle {
ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.unit.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.unit.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? textDecoration, androidx.ui.graphics.Shadow? shadow, androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.unit.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
ctor public TextStyle();
diff --git a/ui/ui-text/api/current.txt b/ui/ui-text/api/current.txt
index 9463223..74a8877 100644
--- a/ui/ui-text/api/current.txt
+++ b/ui/ui-text/api/current.txt
@@ -540,18 +540,6 @@
method public static String substring(CharSequence, androidx.ui.text.TextRange range);
}
- public final class TextSpan {
- ctor public TextSpan(androidx.ui.text.SpanStyle? style, String? text, java.util.List<androidx.ui.text.TextSpan> children);
- ctor public TextSpan();
- method public java.util.List<androidx.ui.text.TextSpan> getChildren();
- method public androidx.ui.text.SpanStyle? getStyle();
- method public String? getText();
- }
-
- public final class TextSpanKt {
- method public static androidx.ui.text.AnnotatedString toAnnotatedString(androidx.ui.text.TextSpan);
- }
-
public final class TextStyle {
ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.unit.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.unit.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? textDecoration, androidx.ui.graphics.Shadow? shadow, androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.unit.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
ctor public TextStyle();
diff --git a/ui/ui-text/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-text/api/public_plus_experimental_0.1.0-dev04.txt
index 9463223..74a8877 100644
--- a/ui/ui-text/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-text/api/public_plus_experimental_0.1.0-dev04.txt
@@ -540,18 +540,6 @@
method public static String substring(CharSequence, androidx.ui.text.TextRange range);
}
- public final class TextSpan {
- ctor public TextSpan(androidx.ui.text.SpanStyle? style, String? text, java.util.List<androidx.ui.text.TextSpan> children);
- ctor public TextSpan();
- method public java.util.List<androidx.ui.text.TextSpan> getChildren();
- method public androidx.ui.text.SpanStyle? getStyle();
- method public String? getText();
- }
-
- public final class TextSpanKt {
- method public static androidx.ui.text.AnnotatedString toAnnotatedString(androidx.ui.text.TextSpan);
- }
-
public final class TextStyle {
ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.unit.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.unit.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? textDecoration, androidx.ui.graphics.Shadow? shadow, androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.unit.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
ctor public TextStyle();
diff --git a/ui/ui-text/api/public_plus_experimental_current.txt b/ui/ui-text/api/public_plus_experimental_current.txt
index 9463223..74a8877 100644
--- a/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/ui/ui-text/api/public_plus_experimental_current.txt
@@ -540,18 +540,6 @@
method public static String substring(CharSequence, androidx.ui.text.TextRange range);
}
- public final class TextSpan {
- ctor public TextSpan(androidx.ui.text.SpanStyle? style, String? text, java.util.List<androidx.ui.text.TextSpan> children);
- ctor public TextSpan();
- method public java.util.List<androidx.ui.text.TextSpan> getChildren();
- method public androidx.ui.text.SpanStyle? getStyle();
- method public String? getText();
- }
-
- public final class TextSpanKt {
- method public static androidx.ui.text.AnnotatedString toAnnotatedString(androidx.ui.text.TextSpan);
- }
-
public final class TextStyle {
ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.unit.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.unit.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? textDecoration, androidx.ui.graphics.Shadow? shadow, androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.unit.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
ctor public TextStyle();
diff --git a/ui/ui-text/api/restricted_0.1.0-dev04.txt b/ui/ui-text/api/restricted_0.1.0-dev04.txt
index 9463223..74a8877 100644
--- a/ui/ui-text/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-text/api/restricted_0.1.0-dev04.txt
@@ -540,18 +540,6 @@
method public static String substring(CharSequence, androidx.ui.text.TextRange range);
}
- public final class TextSpan {
- ctor public TextSpan(androidx.ui.text.SpanStyle? style, String? text, java.util.List<androidx.ui.text.TextSpan> children);
- ctor public TextSpan();
- method public java.util.List<androidx.ui.text.TextSpan> getChildren();
- method public androidx.ui.text.SpanStyle? getStyle();
- method public String? getText();
- }
-
- public final class TextSpanKt {
- method public static androidx.ui.text.AnnotatedString toAnnotatedString(androidx.ui.text.TextSpan);
- }
-
public final class TextStyle {
ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.unit.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.unit.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? textDecoration, androidx.ui.graphics.Shadow? shadow, androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.unit.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
ctor public TextStyle();
diff --git a/ui/ui-text/api/restricted_current.txt b/ui/ui-text/api/restricted_current.txt
index 9463223..74a8877 100644
--- a/ui/ui-text/api/restricted_current.txt
+++ b/ui/ui-text/api/restricted_current.txt
@@ -540,18 +540,6 @@
method public static String substring(CharSequence, androidx.ui.text.TextRange range);
}
- public final class TextSpan {
- ctor public TextSpan(androidx.ui.text.SpanStyle? style, String? text, java.util.List<androidx.ui.text.TextSpan> children);
- ctor public TextSpan();
- method public java.util.List<androidx.ui.text.TextSpan> getChildren();
- method public androidx.ui.text.SpanStyle? getStyle();
- method public String? getText();
- }
-
- public final class TextSpanKt {
- method public static androidx.ui.text.AnnotatedString toAnnotatedString(androidx.ui.text.TextSpan);
- }
-
public final class TextStyle {
ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.unit.TextUnit fontSize, androidx.ui.text.font.FontWeight? fontWeight, androidx.ui.text.font.FontStyle? fontStyle, androidx.ui.text.font.FontSynthesis? fontSynthesis, androidx.ui.text.font.FontFamily? fontFamily, String? fontFeatureSettings, androidx.ui.unit.TextUnit letterSpacing, androidx.ui.text.style.BaselineShift? baselineShift, androidx.ui.text.style.TextGeometricTransform? textGeometricTransform, androidx.ui.text.LocaleList? localeList, androidx.ui.graphics.Color? background, androidx.ui.text.style.TextDecoration? textDecoration, androidx.ui.graphics.Shadow? shadow, androidx.ui.text.style.TextAlign? textAlign, androidx.ui.text.style.TextDirectionAlgorithm? textDirectionAlgorithm, androidx.ui.unit.TextUnit lineHeight, androidx.ui.text.style.TextIndent? textIndent);
ctor public TextStyle();
diff --git a/ui/ui-text/integration-tests/samples/src/main/java/androidx/ui/text/samples/BaselineShiftSamples.kt b/ui/ui-text/integration-tests/samples/src/main/java/androidx/ui/text/samples/BaselineShiftSamples.kt
index a5133e5..2c0ca77 100644
--- a/ui/ui-text/integration-tests/samples/src/main/java/androidx/ui/text/samples/BaselineShiftSamples.kt
+++ b/ui/ui-text/integration-tests/samples/src/main/java/androidx/ui/text/samples/BaselineShiftSamples.kt
@@ -18,10 +18,10 @@
import androidx.annotation.Sampled
import androidx.compose.Composable
-import androidx.ui.core.Span
import androidx.ui.core.Text
import androidx.ui.text.AnnotatedString
import androidx.ui.text.SpanStyle
+import androidx.ui.text.TextStyle
import androidx.ui.text.style.BaselineShift
import androidx.ui.text.withStyle
import androidx.ui.unit.sp
@@ -29,25 +29,18 @@
@Sampled
@Composable
fun BaselineShiftSample() {
- Text {
- Span(text = "Hello", style = SpanStyle(fontSize = 20.sp)) {
- Span(
- text = "superscript",
- style = SpanStyle(
- baselineShift = BaselineShift.Superscript,
- fontSize = 16.sp
- )
- ) {
- Span(
- text = "subscript",
- style = SpanStyle(
- baselineShift = BaselineShift.Subscript,
- fontSize = 16.sp
- )
- )
+ Text(
+ style = TextStyle(fontSize = 20.sp),
+ text = AnnotatedString {
+ append(text = "Hello")
+ withStyle(SpanStyle(baselineShift = BaselineShift.Superscript, fontSize = 16.sp)) {
+ append("superscript")
+ withStyle(SpanStyle(baselineShift = BaselineShift.Subscript)) {
+ append("subscript")
+ }
}
}
- }
+ )
}
@Sampled
diff --git a/ui/ui-text/integration-tests/samples/src/main/java/androidx/ui/text/samples/SpanStyleSamples.kt b/ui/ui-text/integration-tests/samples/src/main/java/androidx/ui/text/samples/SpanStyleSamples.kt
index e9ec6c7..f0cb9d8 100644
--- a/ui/ui-text/integration-tests/samples/src/main/java/androidx/ui/text/samples/SpanStyleSamples.kt
+++ b/ui/ui-text/integration-tests/samples/src/main/java/androidx/ui/text/samples/SpanStyleSamples.kt
@@ -18,9 +18,9 @@
import androidx.annotation.Sampled
import androidx.compose.Composable
-import androidx.ui.core.Span
import androidx.ui.core.Text
import androidx.ui.graphics.Color
+import androidx.ui.text.AnnotatedString
import androidx.ui.text.SpanStyle
import androidx.ui.text.TextStyle
import androidx.ui.unit.sp
@@ -28,8 +28,15 @@
@Sampled
@Composable
fun SpanStyleSample() {
- Text(style = TextStyle(fontSize = 16.sp)) {
- Span("Hello", style = SpanStyle(color = Color.Red))
- Span(" World", style = SpanStyle(color = Color.Blue))
- }
+ Text(
+ style = TextStyle(fontSize = 16.sp),
+ text = AnnotatedString {
+ pushStyle(style = SpanStyle(color = Color.Red))
+ append("Hello")
+ popStyle()
+ pushStyle(SpanStyle(color = Color.Blue))
+ append(" World")
+ popStyle()
+ }
+ )
}
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
index fd2d283..0d5ae32 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
@@ -17,17 +17,16 @@
package androidx.ui.text.demos
import androidx.compose.Composable
-import androidx.ui.core.Span
import androidx.ui.core.Text
import androidx.ui.foundation.VerticalScroller
import androidx.ui.geometry.Offset
import androidx.ui.graphics.Color
import androidx.ui.graphics.Shadow
-import androidx.ui.graphics.lerp
import androidx.ui.layout.Column
import androidx.ui.layout.LayoutHeight
import androidx.ui.layout.LayoutWidth
import androidx.ui.layout.Row
+import androidx.ui.text.AnnotatedString
import androidx.ui.text.LocaleList
import androidx.ui.text.SpanStyle
import androidx.ui.text.TextStyle
@@ -95,8 +94,6 @@
TexDemoTextOverflowFade()
TagLine(tag = "shadow")
TextDemoShadowEffect()
- TagLine(tag = "composable textspan")
- TextDemoComposableTextSpan()
TagLine(tag = "fontSizeScale")
TextDemoFontSizeScale()
TagLine(tag = "complex paragraph styling")
@@ -107,66 +104,75 @@
@Composable
fun TagLine(tag: String) {
- Text {
- Span(text = "\n", style = SpanStyle(fontSize = fontSize8))
- Span(
- text = tag,
- style = SpanStyle(
- color = Color(0xFFAAAAAA),
- fontSize = fontSize6
+ Text(
+ style = TextStyle(fontSize = fontSize8),
+ text = AnnotatedString {
+ append("\n")
+ pushStyle(
+ style = SpanStyle(
+ color = Color(0xFFAAAAAA),
+ fontSize = fontSize6
+ )
)
- )
- }
+ append(tag)
+ }
+ )
}
@Composable
fun SecondTagLine(tag: String) {
- Text {
- Span(
- text = tag,
- style = SpanStyle(
- color = Color(0xFFAAAAAA),
- fontSize = fontSize4
+ Text(
+ text = AnnotatedString {
+ pushStyle(
+ style = SpanStyle(
+ color = Color(0xFFAAAAAA),
+ fontSize = fontSize4
+ )
)
- )
- }
+ append(tag)
+ popStyle()
+ }
+ )
}
@Composable
fun TextDemoBasic() {
// This group of text composables show different color, fontSize, fontWeight and fontStyle in
// English.
- Text {
- Span(
- text = "$displayText ",
- style = SpanStyle(
+ Text(text = AnnotatedString {
+ pushStyle(
+ SpanStyle(
color = Color(0xFFFF0000),
fontSize = fontSize6,
fontWeight = FontWeight.W200,
fontStyle = FontStyle.Italic
)
)
+ append("$displayText ")
+ popStyle()
- Span(
- text = "$displayText ",
- style = SpanStyle(
+ pushStyle(
+ SpanStyle(
color = Color(0xFF00FF00),
fontSize = fontSize8,
fontWeight = FontWeight.W500,
fontStyle = FontStyle.Normal
)
)
+ append("$displayText ")
+ popStyle()
- Span(
- text = displayText,
- style = SpanStyle(
+ pushStyle(
+ SpanStyle(
color = Color(0xFF0000FF),
fontSize = fontSize10,
fontWeight = FontWeight.W800,
fontStyle = FontStyle.Normal
)
)
- }
+ append(displayText)
+ popStyle()
+ })
}
@Composable
@@ -178,9 +184,8 @@
fun TextDemoLanguage() {
// This group of text composables show different color, fontSize, fontWeight and fontStyle in
// Chinese, Arabic, and Hindi.
- Text {
- Span(
- text = "$displayTextChinese ",
+ Text(text = AnnotatedString {
+ pushStyle(
style = SpanStyle(
color = Color(0xFFFF0000),
fontSize = fontSize6,
@@ -188,9 +193,10 @@
fontStyle = FontStyle.Italic
)
)
+ append("$displayTextChinese ")
+ popStyle()
- Span(
- text = "$displayTextArabic ",
+ pushStyle(
style = SpanStyle(
color = Color(0xFF00FF00),
fontSize = fontSize8,
@@ -198,9 +204,10 @@
fontStyle = FontStyle.Normal
)
)
+ append("$displayTextArabic ")
+ popStyle()
- Span(
- text = displayTextHindi,
+ pushStyle(
style = SpanStyle(
color = Color(0xFF0000FF),
fontSize = fontSize10,
@@ -208,34 +215,42 @@
fontStyle = FontStyle.Normal
)
)
- }
+ append(displayTextHindi)
+ popStyle()
+ })
}
@Composable
fun TextDemoFontFamily() {
// This group of text composables show different fontFamilies in English.
- Text {
- Span(
- text = "$displayText sans-serif\n", style = SpanStyle(
+ Text(AnnotatedString {
+ pushStyle(
+ style = SpanStyle(
fontSize = fontSize8,
fontFamily = FontFamily("sans-serif")
)
)
+ append("$displayText sans-serif\n")
+ popStyle()
- Span(
- text = "$displayText serif\n", style = SpanStyle(
+ pushStyle(
+ style = SpanStyle(
fontSize = fontSize8,
fontFamily = FontFamily("serif")
)
)
+ append("$displayText serif\n")
+ popStyle()
- Span(
- text = "$displayText monospace", style = SpanStyle(
+ pushStyle(
+ style = SpanStyle(
fontSize = fontSize8,
fontFamily = FontFamily("monospace")
)
)
- }
+ append("$displayText monospace")
+ popStyle()
+ })
}
@Composable
@@ -258,16 +273,19 @@
@Composable
fun TextDemoLetterSpacing() {
// This group of text composables show different letterSpacing.
- Text {
- Span(text = "$displayText ", style = SpanStyle(fontSize = fontSize8))
- Span(
- text = displayText,
+ Text(text = AnnotatedString {
+ pushStyle(style = SpanStyle(fontSize = fontSize8))
+ append("$displayText ")
+ popStyle()
+ pushStyle(
style = SpanStyle(
fontSize = fontSize8,
letterSpacing = 0.5.em
)
)
- }
+ append(displayText)
+ popStyle()
+ })
}
@Composable
@@ -279,82 +297,73 @@
fun TextDemoHeight() {
// This group of text composables show different height.
Row(LayoutWidth.Fill) {
- Text {
- Span(
- text = "$displayText\n$displayText ",
- style = SpanStyle(fontSize = fontSize8)
- )
- }
- Text(style = TextStyle(lineHeight = 50.sp)) {
- Span(
- text = "$displayText\n$displayText ",
- style = SpanStyle(
- fontSize = fontSize8
- )
- )
- }
+ Text(
+ text = "$displayText\n$displayText ",
+ style = TextStyle(fontSize = fontSize8)
+ )
+ Text(
+ text = "$displayText\n$displayText ",
+ style = TextStyle(fontSize = fontSize8, lineHeight = 50.sp)
+ )
}
}
@Composable
fun TextDemoBackground() {
// This group of text composables show different background.
- Text {
- Span(
- text = "$displayText ",
+ Text(text = AnnotatedString {
+ pushStyle(
style = SpanStyle(
fontSize = fontSize8,
background = Color(0xFFFF0000)
)
)
+ append("$displayText ")
+ popStyle()
- Span(
- text = "$displayText ",
+ pushStyle(
style = SpanStyle(
fontSize = fontSize8,
background = Color(0xFF00FF00)
)
)
+ append("$displayText ")
+ popStyle()
- Span(
- text = displayText,
+ pushStyle(
style = SpanStyle(
fontSize = fontSize8,
background = Color(0xFF0000FF)
)
)
- }
+ append(displayText)
+ popStyle()
+ })
}
@Composable
fun TextDemoLocale() {
// This group of text composables show different Locales of the same Unicode codepoint.
val text = "\u82B1"
- Text {
- Span(
- text = "$text ",
- style = SpanStyle(
- fontSize = fontSize8,
- localeList = LocaleList("ja-JP")
- )
+ Text(AnnotatedString {
+ pushStyle(
+ style = SpanStyle(fontSize = fontSize8, localeList = LocaleList("ja-JP"))
)
+ append("$text ")
+ popStyle()
- Span(
- text = "$text ",
- style = SpanStyle(
- fontSize = fontSize8,
- localeList = LocaleList("zh-CN")
- )
+ pushStyle(
+ style = SpanStyle(fontSize = fontSize8, localeList = LocaleList("zh-CN"))
)
+ append("$text ")
+ popStyle()
- Span(
- text = text,
- style = SpanStyle(
- fontSize = fontSize8,
- localeList = LocaleList("zh-TW")
- )
+ pushStyle(
+ style = SpanStyle(fontSize = fontSize8, localeList = LocaleList("zh-TW"))
)
- }
+ append(text)
+ popStyle()
+ })
}
@Composable
@@ -367,52 +376,69 @@
}
Column(LayoutHeight.Fill) {
SecondTagLine(tag = "textAlign = TextAlign.Left")
- Text(style = TextStyle(textAlign = TextAlign.Left)) {
- Span(text = displayText, style = SpanStyle(fontSize = fontSize8))
- }
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = displayText,
+ style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Left)
+ )
+
SecondTagLine(tag = "textAlign = TextAlign.Right")
- Text(style = TextStyle(textAlign = TextAlign.Right)) {
- Span(text = displayText, style = SpanStyle(fontSize = fontSize8))
- }
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = displayText,
+ style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Right)
+ )
+
SecondTagLine(tag = "textAlign = TextAlign.Center")
- Text(style = TextStyle(textAlign = TextAlign.Center)) {
- Span(text = displayText, style = SpanStyle(fontSize = fontSize8))
- }
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = displayText,
+ style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Center)
+ )
+
SecondTagLine(tag = "textAlign = default and TextAlign.Justify")
- Text {
- Span(
- text = text,
- style = SpanStyle(
- fontSize = fontSize8,
- color = Color(0xFFFF0000)
- )
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = text,
+ style = TextStyle(
+ fontSize = fontSize8,
+ color = Color(0xFFFF0000)
)
- }
- Text(style = TextStyle(textAlign = TextAlign.Justify)) {
- Span(
- text = text,
- style = SpanStyle(
- fontSize = fontSize8,
- color = Color(0xFF0000FF)
- )
+ )
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = text,
+ style = TextStyle(
+ fontSize = fontSize8,
+ color = Color(0xFF0000FF),
+ textAlign = TextAlign.Justify
)
- }
- SecondTagLine(tag = "textAlgin = TextAlign.Start for Ltr")
- Text(style = TextStyle(textAlign = TextAlign.Start)) {
- Span(text = displayText, style = SpanStyle(fontSize = fontSize8))
- }
- SecondTagLine(tag = "textAlgin = TextAlign.Start for Rtl")
- Text(style = TextStyle(textAlign = TextAlign.Start)) {
- Span(text = displayText, style = SpanStyle(fontSize = fontSize8))
- }
- SecondTagLine(tag = "textAlgin = TextAlign.End for Ltr")
- Text(style = TextStyle(textAlign = TextAlign.End)) {
- Span(text = displayText, style = SpanStyle(fontSize = fontSize8))
- }
- SecondTagLine(tag = "textAlgin = TextAlign.End for Rtl")
- Text(style = TextStyle(textAlign = TextAlign.End)) {
- Span(text = displayText, style = SpanStyle(fontSize = fontSize8))
- }
+ )
+
+ SecondTagLine(tag = "textAlign = TextAlign.Start for Ltr")
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = displayText,
+ style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Start)
+ )
+ SecondTagLine(tag = "textAlign = TextAlign.Start for Rtl")
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = displayTextArabic,
+ style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Start)
+ )
+ SecondTagLine(tag = "textAlign = TextAlign.End for Ltr")
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = displayText,
+ style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.End)
+ )
+ SecondTagLine(tag = "textAlign = TextAlign.End for Rtl")
+ Text(
+ modifier = LayoutWidth.Fill,
+ text = displayTextArabic,
+ style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.End)
+ )
}
}
@@ -423,15 +449,11 @@
for (i in 1..10) {
text = "$text$displayText"
}
- val spanStyle = SpanStyle(fontSize = fontSize8, color = Color(0xFFFF0000))
+ val textStyle = TextStyle(fontSize = fontSize8, color = Color(0xFFFF0000))
Column(LayoutHeight.Fill) {
- Text {
- Span(text = text, style = spanStyle)
- }
- Text(softWrap = false) {
- Span(text = text, style = spanStyle)
- }
+ Text(text = text, style = textStyle)
+ Text(text = text, style = textStyle, softWrap = false)
}
}
@@ -441,22 +463,22 @@
for (i in 1..15) {
text += displayText
}
- val spanStyle = SpanStyle(fontSize = fontSize8, color = Color(0xFFFF0000))
+ val textStyle = TextStyle(fontSize = fontSize8, color = Color(0xFFFF0000))
SecondTagLine(tag = "horizontally fading edge")
Text(
+ text = text,
+ style = textStyle,
maxLines = 1,
overflow = TextOverflow.Fade,
softWrap = false
- ) {
- Span(text = text, style = spanStyle)
- }
+ )
SecondTagLine(tag = "vertically fading edge")
Text(
+ text = text,
+ style = textStyle,
maxLines = 3,
overflow = TextOverflow.Fade
- ) {
- Span(text = text, style = spanStyle)
- }
+ )
}
@Composable
@@ -466,38 +488,30 @@
Offset(5f, 5f),
blurRadius = 5.px
)
- Text {
- Span(text = "text with ", style = SpanStyle(fontSize = fontSize8)) {
- Span(text = "shadow!", style = SpanStyle(shadow = shadow))
+ Text(
+ style = TextStyle(fontSize = fontSize8),
+ text = AnnotatedString {
+ append("text with ")
+ pushStyle(style = SpanStyle(shadow = shadow))
+ append("shadow!")
+ popStyle()
}
- }
-}
-
-@Composable
-fun TextDemoComposableTextSpan() {
- Text(style = TextStyle(fontSize = fontSize8)) {
- Span(text = "This is a ")
- Span(text = "composable ", style = SpanStyle(fontStyle = FontStyle.Italic))
- val color1 = Color(0xFFEF50AD)
- val color2 = Color(0xFF10AF52)
- val text = "TextSpan"
- text.forEachIndexed { index, ch ->
- val color = lerp(color1, color2, index.toFloat() / text.lastIndex)
- Span(text = "$ch", style = SpanStyle(color = color))
- }
- }
+ )
}
@Composable
fun TextDemoFontSizeScale() {
- Text {
- Span(style = SpanStyle(fontSize = fontSize8)) {
+ Text(
+ style = TextStyle(fontSize = fontSize8),
+ text = AnnotatedString {
for (i in 4..12 step 4) {
val scale = i * 0.1f
- Span("fontSizeScale=$scale\n", style = SpanStyle(fontSize = scale.em))
+ pushStyle(style = SpanStyle(fontSize = scale.em))
+ append("fontSizeScale=$scale\n")
+ popStyle()
}
}
- }
+ )
}
@Composable
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeTextSelection.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeTextSelection.kt
index ee72d28c..35b3e14 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeTextSelection.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeTextSelection.kt
@@ -18,7 +18,6 @@
import androidx.compose.Composable
import androidx.compose.state
-import androidx.ui.core.Span
import androidx.ui.core.Text
import androidx.ui.core.selection.Selection
import androidx.ui.core.selection.SelectionContainer
@@ -28,6 +27,7 @@
import androidx.ui.layout.LayoutHeight
import androidx.ui.layout.LayoutWidth
import androidx.ui.layout.Row
+import androidx.ui.text.AnnotatedString
import androidx.ui.text.LocaleList
import androidx.ui.text.SpanStyle
import androidx.ui.text.TextStyle
@@ -56,38 +56,40 @@
SelectionContainer(
selection = selection.value,
selection.value = it }) {
- Text {
- Span(
- style = SpanStyle(
- color = Color(0xFFFF0000),
- fontSize = fontSize6,
- fontWeight = FontWeight.W200,
- fontStyle = FontStyle.Italic
- )
- ) {
- Span(text = "$displayText ")
- Span(text = "$displayTextArabic ")
- Span(text = "$displayTextChinese ")
- Span(
- text = displayTextHindi,
- style = SpanStyle(
+ Text(
+ style = TextStyle(
+ color = Color(0xFFFF0000),
+ fontSize = fontSize6,
+ fontWeight = FontWeight.W200,
+ fontStyle = FontStyle.Italic
+ ),
+ text = AnnotatedString {
+ append(text = "$displayText ")
+ append(text = "$displayTextArabic ")
+ append(text = "$displayTextChinese ")
+
+ pushStyle(
+ SpanStyle(
color = Color(0xFF0000FF),
fontSize = fontSize10,
fontWeight = FontWeight.W800,
fontStyle = FontStyle.Normal
)
)
- Span(text = "$arabicSentence")
- Span(
- text = "\n先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。",
- style = SpanStyle(localeList = LocaleList("zh-CN"))
- )
- Span(
- text = "\nまず、現在天下が魏・呉・蜀に分れており、そのうち蜀は疲弊していることを指摘する。",
- style = SpanStyle(localeList = LocaleList("ja-JP"))
- )
+ append(displayTextHindi)
+ popStyle()
+
+ append(text = "$arabicSentence")
+
+ pushStyle(SpanStyle(localeList = LocaleList("zh-CN")))
+ append("\n先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。")
+ popStyle()
+
+ pushStyle(SpanStyle(localeList = LocaleList("ja-JP")))
+ append("\nまず、現在天下が魏・呉・蜀に分れており、そのうち蜀は疲弊していることを指摘する。")
+ popStyle()
}
- }
+ )
}
}
@@ -136,15 +138,14 @@
for (i in 0..2) {
Row(LayoutWidth.Fill) {
for (j in 0..2) {
- Text {
- Span(
- text = text,
- style = SpanStyle(
- color = colorList[i * 3 + j],
- fontSize = fontSize6
- )
+ Text(
+ text = text,
+ style = TextStyle(
+ color = colorList[i * 3 + j],
+ fontSize = fontSize6
)
- }
+
+ )
}
}
}
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/TextDelegate.kt b/ui/ui-text/src/main/java/androidx/ui/text/TextDelegate.kt
index e4081db..bc76472 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/TextDelegate.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/TextDelegate.kt
@@ -244,7 +244,7 @@
* the default black background color), so if you are writing an application with a white
* background, the text will not be visible by default.
*
- * To set the text style, specify a [SpanStyle] when creating the [TextSpan] that you pass to
+ * To set the text style, specify a [SpanStyle] when creating the [AnnotatedString] that you pass to
* the [TextDelegate] constructor or to the [text] property.
*/
fun paint(canvas: Canvas, textLayoutResult: TextLayoutResult) {
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/TextSpan.kt b/ui/ui-text/src/main/java/androidx/ui/text/TextSpan.kt
deleted file mode 100644
index 65362cf..0000000
--- a/ui/ui-text/src/main/java/androidx/ui/text/TextSpan.kt
+++ /dev/null
@@ -1,91 +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.text
-
-/**
- * A [TextSpan] object can be styled using its [style] property.
- * The style will be applied to the [text] and the [children].
- *
- * For the object to be useful, at least one of [text] or [children] should be set.
- *
- * @param style The style to apply to the [text] and the [children].
- *
- * @param text The text contained in the span. If both [text] and [children] are non-null, the text
- * will precede the children.
- *
- * @param children Additional spans to include as children. If both [text] and [children] are
- * non-null, the text will precede the children. The list must not contain any nulls.
- */
-class TextSpan(
- val style: SpanStyle? = null,
- val text: String? = null,
- val children: MutableList<TextSpan> = mutableListOf()
-) {
-
- /**
- * Walks this text span and its descendants in pre-order and calls [visitor]
- * for each span that has text.
- */
- internal fun visitTextSpan(visitor: (span: TextSpan) -> Boolean): Boolean {
- if (text != null) {
- if (!visitor(this)) {
- return false
- }
- }
- for (child in children) {
- if (!child.visitTextSpan(visitor)) {
- return false
- }
- }
- return true
- }
-
- override fun toString(): String {
- val buffer = StringBuilder()
- visitTextSpan { span: TextSpan ->
- buffer.append(span.text)
- true
- }
- return buffer.toString()
- }
-}
-
-private fun TextSpan.annotatedStringVisitor(builder: AnnotatedString.Builder) {
- style?.let {
- builder.pushStyle(style)
- }
-
- text?.let { builder.append(text) }
-
- for (child in children) {
- child.annotatedStringVisitor(builder)
- }
-
- style?.let {
- builder.popStyle()
- }
-}
-
-/**
- * Convert a [TextSpan] into an [AnnotatedString].
- */
-fun TextSpan.toAnnotatedString(): AnnotatedString {
- return with(AnnotatedString.Builder()) {
- annotatedStringVisitor(this)
- toAnnotatedString()
- }
-}
\ No newline at end of file
diff --git a/ui/ui-text/src/test/java/androidx/ui/text/TextLayoutHelperTest.kt b/ui/ui-text/src/test/java/androidx/ui/text/TextLayoutHelperTest.kt
index 7d83f0a..44254d1 100644
--- a/ui/ui-text/src/test/java/androidx/ui/text/TextLayoutHelperTest.kt
+++ b/ui/ui-text/src/test/java/androidx/ui/text/TextLayoutHelperTest.kt
@@ -52,7 +52,7 @@
density = Density(1.0f),
layoutDirection = LayoutDirection.Ltr,
resourceLoader = resourceLoader,
- constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ constraints = Constraints.fixedWidth(100.ipx)
),
multiParagraph = mock(),
size = IntPxSize(50.ipx, 50.ipx)
@@ -61,7 +61,7 @@
@Test
fun testCanResue_same() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
style = TextStyle(),
@@ -77,7 +77,7 @@
@Test
fun testCanResue_different_text() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, Android").toAnnotatedString(),
style = TextStyle(),
@@ -93,7 +93,7 @@
@Test
fun testCanResue_different_style() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
style = TextStyle(fontSize = 1.5.em),
@@ -109,7 +109,7 @@
@Test
fun testCanResue_different_maxLines() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
style = TextStyle(),
@@ -125,7 +125,7 @@
@Test
fun testCanResue_different_softWrap() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
style = TextStyle(),
@@ -141,7 +141,7 @@
@Test
fun testCanResue_different_overflow() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
style = TextStyle(),
@@ -157,7 +157,7 @@
@Test
fun testCanResue_different_density() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
style = TextStyle(),
@@ -173,7 +173,7 @@
@Test
fun testCanResue_different_layoutDirection() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
style = TextStyle(),
@@ -189,7 +189,7 @@
@Test
fun testCanResue_different_resourceLoader() {
- val constraints = Constraints.tightConstraintsForWidth(100.ipx)
+ val constraints = Constraints.fixedWidth(100.ipx)
assertThat(referenceResult.canReuse(
text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
style = TextStyle(),
@@ -214,7 +214,7 @@
density = Density(1.0f),
layoutDirection = LayoutDirection.Ltr,
resourceLoader = resourceLoader,
- constraints = Constraints.tightConstraintsForWidth(200.ipx)
+ constraints = Constraints.fixedWidth(200.ipx)
)).isFalse()
}
}
diff --git a/ui/ui-text/src/test/java/androidx/ui/text/TextSpanTest.kt b/ui/ui-text/src/test/java/androidx/ui/text/TextSpanTest.kt
deleted file mode 100644
index 53e5f7c..0000000
--- a/ui/ui-text/src/test/java/androidx/ui/text/TextSpanTest.kt
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
-* Copyright 2018 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.text
-
-import androidx.ui.graphics.Color
-import androidx.ui.unit.em
-import androidx.ui.unit.sp
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-@RunWith(JUnit4::class)
-class TextSpanTest {
- @Test
- fun `constructor with default values`() {
- val textSpan = TextSpan()
-
- assertThat(textSpan.style).isNull()
- assertThat(textSpan.text).isNull()
- assertThat(textSpan.children).isEqualTo(mutableListOf<TextSpan>())
- }
-
- @Test
- fun `constructor with customized style`() {
- val spanStyle = SpanStyle(fontSize = 10.sp, letterSpacing = 1.5.em)
- val textSpan = TextSpan(style = spanStyle)
-
- assertThat(textSpan.style).isEqualTo(spanStyle)
- }
-
- @Test
- fun `constructor with customized text`() {
- val string = "Hello"
- val textSpan = TextSpan(text = string)
-
- assertThat(textSpan.text).isEqualTo(string)
- }
-
- @Test
- fun `constructor with customized children`() {
- val string1 = "Hello"
- val string2 = "World"
- val textSpan1 = TextSpan(text = string1)
- val textSpan2 = TextSpan(text = string2)
- val textSpan = TextSpan(children = mutableListOf(textSpan1, textSpan2))
-
- assertThat(textSpan.children.size).isEqualTo(2)
- assertThat(textSpan.children.get(0).text).isEqualTo(string1)
- assertThat(textSpan.children.get(1).text).isEqualTo(string2)
- }
-
- @Test
- fun `visitTextSpan with neither text nor children should return true`() {
- val textSpan = TextSpan()
-
- val result = textSpan.visitTextSpan { _: TextSpan -> false }
-
- assertThat(result).isTrue()
- }
-
- @Test
- fun `visitTextSpan with text and visitor always returns true`() {
- val textSpan = TextSpan(text = "Hello")
-
- val result = textSpan.visitTextSpan { _: TextSpan -> true }
-
- assertThat(result).isTrue()
- }
-
- @Test
- fun `visitTextSpan with text and visitor always returns false`() {
- val textSpan = TextSpan(text = "Hello")
-
- val result = textSpan.visitTextSpan { _: TextSpan -> false }
-
- assertThat(result).isFalse()
- }
-
- @Test
- fun `visitTextSpan with children and visitor always returns true`() {
- val textSpan1 = spy(TextSpan(text = "Hello"))
- val textSpan2 = spy(TextSpan(text = "World"))
- val textSpan = spy(
- TextSpan(
- children = mutableListOf(
- textSpan1,
- textSpan2
- )
- )
- )
- val returnTrueFunction = { _: TextSpan -> true }
-
- val result = textSpan.visitTextSpan(returnTrueFunction)
-
- assertThat(result).isTrue()
- verify(textSpan1, times(1)).visitTextSpan(returnTrueFunction)
- verify(textSpan2, times(1)).visitTextSpan(returnTrueFunction)
- }
-
- @Test
- fun `visitTextSpan with children and visitor always returns false`() {
- val textSpan1 = spy(TextSpan(text = "Hello"))
- val textSpan2 = spy(TextSpan(text = "World"))
- val textSpan = TextSpan(children = mutableListOf(textSpan1, textSpan2))
- val returnFalseFunction = { _: TextSpan -> false }
-
- val result = textSpan.visitTextSpan(returnFalseFunction)
-
- assertThat(result).isFalse()
- verify(textSpan1, times(1)).visitTextSpan(returnFalseFunction)
- verify(textSpan2, times(0)).visitTextSpan(returnFalseFunction)
- }
-
- @Test
- fun `toString with neither text nor children`() {
- val textSpan = TextSpan()
-
- assertThat(textSpan.toString()).isEmpty()
- }
-
- @Test
- fun `toString with text`() {
- val string = "Hello"
- val textSpan = TextSpan(text = string)
-
- assertThat(textSpan.toString()).isEqualTo(string)
- }
-
- @Test
- fun `toString with children`() {
- val string1 = "Hello"
- val string2 = "World"
- val textSpan1 = TextSpan(text = string1)
- val textSpan2 = TextSpan(text = string2)
- val textSpan = TextSpan(children = mutableListOf(textSpan1, textSpan2))
-
- assertThat(textSpan.toString()).isEqualTo(string1 + string2)
- }
-
- @Test
- fun `toAnnotatedString with includeRootStyle default value`() {
- val spanStyle = SpanStyle(fontSize = 10.sp)
- val text = "Hello"
- val textSpan = TextSpan(style = spanStyle, text = text)
- val annotatedString = textSpan.toAnnotatedString()
-
- // By default includeRootStyle = true and SpanStyle on root node should be converted.
- assertThat(annotatedString.spanStyles.size).isEqualTo(1)
- assertThat(annotatedString.spanStyles[0].item).isEqualTo(spanStyle)
- assertThat(annotatedString.spanStyles[0].start).isEqualTo(0)
- assertThat(annotatedString.spanStyles[0].end).isEqualTo(text.length)
- }
-
- @Test
- fun `toAnnotatedString with nested TextSpan plain text`() {
- val text1 = "Hello"
- val text2 = "World"
-
- val textSpan = TextSpan(
- text = text1,
- children = mutableListOf(TextSpan(text = text2))
- )
- val annotatedString = textSpan.toAnnotatedString()
-
- assertThat(annotatedString.text).isEqualTo(text1 + text2)
- assertThat(annotatedString.spanStyles.size).isEqualTo(0)
- }
-
- @Test
- fun `toAnnotatedString with nested TextSpan with SpanStyle`() {
- val spanStyle1 = SpanStyle(fontSize = 10.sp)
- val text1 = "Hello"
-
- val spanStyle2 = SpanStyle(color = Color.Red)
- val text2 = "World"
-
- val textSpan = TextSpan(
- style = spanStyle1,
- text = text1,
- children = mutableListOf(TextSpan(style = spanStyle2, text = text2))
- )
- val annotatedString = textSpan.toAnnotatedString()
-
- assertThat(annotatedString.text).isEqualTo(text1 + text2)
- assertThat(annotatedString.spanStyles.size).isEqualTo(2)
-
- assertThat(annotatedString.spanStyles[0].item).isEqualTo(spanStyle1)
- assertThat(annotatedString.spanStyles[0].start).isEqualTo(0)
- assertThat(annotatedString.spanStyles[0].end).isEqualTo((text1 + text2).length)
-
- assertThat(annotatedString.spanStyles[1].item).isEqualTo(spanStyle2)
- assertThat(annotatedString.spanStyles[1].start).isEqualTo(text1.length)
- assertThat(annotatedString.spanStyles[1].end).isEqualTo((text1 + text2).length)
- }
-
- @Test
- fun `toAnnotatedString with complex nested TextSpan`() {
- /*
- Case
- _________Root_________
- / \
- Leaf1 ____Inner____
- / \ / \
- Lorem leaf2 leaf3
- / \ / \
- ipsum dolor
-
- Output
- AnnotatedString(
- text = "Lorem ipsum dolor"
- spanStyles = listOf(Root[0, 17], Leaf1[0, 6], Inner[6, 17], leaf2[6, 12], leaf3[12, 17])
- )
- */
- val spanStyleRoot = SpanStyle(fontSize = 10.sp)
- val spanStyleLeaf1 = SpanStyle(color = Color.Blue)
- val text1 = "Lorem "
-
- val spanStyleInner = SpanStyle(color = Color.Blue)
- val spanStyleLeaf2 = SpanStyle(color = Color.Red)
- val text2 = "ipsum "
-
- val spanStyleLeaf3 = SpanStyle(color = Color.Blue)
- val text3 = "dolor"
-
- val textSpan = TextSpan(
- style = spanStyleRoot,
- children = mutableListOf(
- TextSpan(text = text1, style = spanStyleLeaf1),
- TextSpan(
- style = spanStyleInner,
- children = mutableListOf(
- TextSpan(text = text2, style = spanStyleLeaf2),
- TextSpan(text = text3, style = spanStyleLeaf3)
- )
- )
- )
- )
- val annotatedString = textSpan.toAnnotatedString()
-
- assertThat(annotatedString.text).isEqualTo(text1 + text2 + text3)
- assertThat(annotatedString.spanStyles.size).isEqualTo(5)
-
- assertThat(annotatedString.spanStyles[0].item).isEqualTo(spanStyleRoot)
- assertThat(annotatedString.spanStyles[0].start).isEqualTo(0)
- assertThat(annotatedString.spanStyles[0].end)
- .isEqualTo((text1 + text2 + text3).length)
-
- assertThat(annotatedString.spanStyles[1].item).isEqualTo(spanStyleLeaf1)
- assertThat(annotatedString.spanStyles[1].start).isEqualTo(0)
- assertThat(annotatedString.spanStyles[1].end).isEqualTo(text1.length)
-
- assertThat(annotatedString.spanStyles[2].item).isEqualTo(spanStyleInner)
- assertThat(annotatedString.spanStyles[2].start).isEqualTo(text1.length)
- assertThat(annotatedString.spanStyles[2].end)
- .isEqualTo((text1 + text2 + text3).length)
-
- assertThat(annotatedString.spanStyles[3].item).isEqualTo(spanStyleLeaf2)
- assertThat(annotatedString.spanStyles[3].start).isEqualTo(text1.length)
- assertThat(annotatedString.spanStyles[3].end).isEqualTo((text1 + text2).length)
-
- assertThat(annotatedString.spanStyles[4].item).isEqualTo(spanStyleLeaf3)
- assertThat(annotatedString.spanStyles[4].start).isEqualTo((text1 + text2).length)
- assertThat(annotatedString.spanStyles[4].end)
- .isEqualTo((text1 + text2 + text3).length)
- }
-}
diff --git a/ui/ui-util/api/api_lint.ignore b/ui/ui-util/api/api_lint.ignore
new file mode 100644
index 0000000..cc585e2
--- /dev/null
+++ b/ui/ui-util/api/api_lint.ignore
@@ -0,0 +1,9 @@
+// Baseline format: 1.0
+NoByteOrShort: androidx.ui.util.Float16#Float16(short) parameter #0:
+ Should avoid odd sized primitives; use `int` instead of `short` in parameter halfValue in androidx.ui.util.Float16(short halfValue)
+NoByteOrShort: androidx.ui.util.Float16#getHalfValue():
+ Should avoid odd sized primitives; use `int` instead of `short` in method androidx.ui.util.Float16.getHalfValue()
+NoByteOrShort: androidx.ui.util.Float16#toByte():
+ Should avoid odd sized primitives; use `int` instead of `byte` in method androidx.ui.util.Float16.toByte()
+NoByteOrShort: androidx.ui.util.Float16#toShort():
+ Should avoid odd sized primitives; use `int` instead of `short` in method androidx.ui.util.Float16.toShort()
diff --git a/webkit/OWNERS b/webkit/OWNERS
index b33da57..e054e6f 100644
--- a/webkit/OWNERS
+++ b/webkit/OWNERS
@@ -1,4 +1,5 @@
changwan@google.com
+laisminchillo@google.com
ntfschr@google.com
tobiasjs@google.com
torne@google.com
diff --git a/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderAjaxActivityTestAppTest.java b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderAjaxActivityTestAppTest.java
new file mode 100644
index 0000000..bc07f31
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderAjaxActivityTestAppTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 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 com.example.androidx.webkit;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Integeration test for AssetLoaderAjaxActivity demo activity.
+ */
+@RunWith(JUnit4.class)
+@LargeTest
+public final class AssetLoaderAjaxActivityTestAppTest {
+
+ @Rule
+ public IntegrationActivityTestRule<AssetLoaderAjaxActivity> mRule =
+ new IntegrationActivityTestRule<>(AssetLoaderAjaxActivity.class,
+ R.id.webview_asset_loader_webview);
+
+ @Test
+ public void testAssetLoaderAjaxActivity() {
+ mRule.assertHtmlElementContainsText(R.id.webview_asset_loader_webview, "title",
+ "Loaded HTML should appear below on success");
+ mRule.assertHtmlElementContainsText(R.id.webview_asset_loader_webview, "assets_html",
+ "Successfully loaded html from assets!");
+ mRule.assertHtmlElementContainsText(R.id.webview_asset_loader_webview, "res_html",
+ "Successfully loaded html from resources!");
+ }
+}
diff --git a/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivityTestAppTest.java b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivityTestAppTest.java
new file mode 100644
index 0000000..82ba14b
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivityTestAppTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 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 com.example.androidx.webkit;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Integeration test for AssetLoaderInternalStorageActivity demo activity.
+ */
+@RunWith(JUnit4.class)
+@LargeTest
+public final class AssetLoaderInternalStorageActivityTestAppTest {
+
+ @Rule
+ public IntegrationActivityTestRule<AssetLoaderInternalStorageActivity> mRule =
+ new IntegrationActivityTestRule<>(AssetLoaderInternalStorageActivity.class,
+ R.id.webview_asset_loader_webview);
+
+ @Test
+ public void testAssetLoaderInternalStorageActivity() {
+ mRule.assertHtmlElementContainsText(R.id.webview_asset_loader_webview, "data_success_msg",
+ "Successfully loaded html from app files dir!");
+ }
+}
diff --git a/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderSimpleActivityTestAppTest.java b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderSimpleActivityTestAppTest.java
new file mode 100644
index 0000000..aeda465
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/AssetLoaderSimpleActivityTestAppTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 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 com.example.androidx.webkit;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Integeration test for AssetLoaderSimpleActivity demo activity.
+ */
+@RunWith(JUnit4.class)
+@LargeTest
+public final class AssetLoaderSimpleActivityTestAppTest {
+
+ @Rule
+ public IntegrationActivityTestRule<AssetLoaderSimpleActivity> mRule =
+ new IntegrationActivityTestRule<>(AssetLoaderSimpleActivity.class,
+ R.id.webview_asset_loader_webview);
+
+ @Test
+ public void testAssetLoaderSimpleActivity() {
+ mRule.assertHtmlElementContainsText(R.id.webview_asset_loader_webview, "assets_success_msg",
+ "Successfully loaded html from assets!");
+ }
+}
diff --git a/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/IntegrationActivityTestRule.java b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/IntegrationActivityTestRule.java
new file mode 100644
index 0000000..08e6806
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/IntegrationActivityTestRule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 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 com.example.androidx.webkit;
+
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.web.assertion.WebViewAssertions.webMatches;
+import static androidx.test.espresso.web.sugar.Web.onWebView;
+import static androidx.test.espresso.web.webdriver.DriverAtoms.findElement;
+import static androidx.test.espresso.web.webdriver.DriverAtoms.getText;
+
+import static org.hamcrest.Matchers.containsString;
+
+import androidx.annotation.IdRes;
+import androidx.test.espresso.web.webdriver.Locator;
+import androidx.test.rule.ActivityTestRule;
+
+/**
+ * Launch, interact, and verify conditions in an activity that has a WebView instance
+ * in the integration test app.
+ *
+ * <p>
+ * Note that this class relies on Espresso's wait for idle behavior to synchronize
+ * operations with UI updates from the test application. If there are any async
+ * UI updates, then the test activity must ensure that actions and assertions do not execute
+ * before pending they are completed. Implementing an
+ * {@link androidx.test.espresso.IdlingResource} in the test application is the standard way
+ * of doing this.
+ */
+public class IntegrationActivityTestRule<T extends android.app.Activity>
+ extends ActivityTestRule<T> {
+
+ private @IdRes int[] mWebViewIds;
+
+ /**
+ * @param activityClass the activity to start the test from.
+ * @param webViewIds IDs of all WebView objects in the launched activity to enable javascript
+ * in them.
+ */
+ public IntegrationActivityTestRule(Class<T> activityClass, @IdRes int... webViewIds) {
+ super(activityClass);
+ mWebViewIds = webViewIds;
+ }
+
+ @Override
+ public void afterActivityLaunched() {
+ // Javascript has to be enabled in order for espresso tests to work.
+ if (mWebViewIds == null) return;
+ for (@IdRes int webViewId : mWebViewIds) {
+ onWebView(withId(webViewId)).forceJavascriptEnabled();
+ }
+ }
+
+ /**
+ * Assert that an HTML element in the given WebView object contains the given text.
+ *
+ * @param webViewId ID of the WebView object that contains the HTML object.
+ * @param tagId the ID attribute of the HTML tag.
+ * @param text the expected text inside the HTML element.
+ */
+ public static void assertHtmlElementContainsText(@IdRes int webViewId,
+ String tagId, String text) {
+ onWebView(withId(webViewId))
+ .withElement(findElement(Locator.ID, tagId))
+ .check(webMatches(getText(),
+ containsString(text)));
+ }
+}
diff --git a/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/WebViewAssetLoaderTestAppTest.java b/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/WebViewAssetLoaderTestAppTest.java
deleted file mode 100644
index d7dd962..0000000
--- a/webkit/integration-tests/testapp/src/androidTest/java/com/example/androidx/webkit/WebViewAssetLoaderTestAppTest.java
+++ /dev/null
@@ -1,122 +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 com.example.androidx.webkit;
-
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.web.assertion.WebViewAssertions.webMatches;
-import static androidx.test.espresso.web.sugar.Web.onWebView;
-import static androidx.test.espresso.web.webdriver.DriverAtoms.findElement;
-import static androidx.test.espresso.web.webdriver.DriverAtoms.getText;
-
-import static org.hamcrest.Matchers.containsString;
-
-import androidx.annotation.IdRes;
-import androidx.test.espresso.web.webdriver.Locator;
-import androidx.test.filters.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Integeration tests for AssetLoader demo activities.
- *
- * Tests the following activities:
- * <ul>
- * <li>AssetLoaderDemoActivity.</li>
- * <li>AssetLoaderAjaxActivity.</li>
- * <li>AssetLoaderInternalStorageActivity.</li>
- * </ul>
- */
-@RunWith(JUnit4.class)
-@LargeTest
-public final class WebViewAssetLoaderTestAppTest {
-
- /**
- * A test rule to set up the tests for WebViewAssetLoader demo Activities.
- * It does the following:
- * <ol>
- * <li> Launch {@code MainActivity}. </li>
- * <li> Clicks on the "WebView Asset Loader Demos" in the main menu to open
- * {@code AssetLoaderListActivity}. </li>
- * <li> Clicks on the Activity to be tested in the menu.</li>
- * <li> Force enables Javascript since it's required by Espresso library to test WebView
- * contents.</li>
- * </ol>
- */
- public class IntegrationAppWebViewTestRule extends IntegrationAppTestRule {
- /**
- * Opens the activity to be tested from WebViewAssetLoader demos activity list and force
- * Javascript to be enabled in the WebView instance in that activity.
- * @param activityTitleId resource id for the title of the activity to be opened.
- */
- public void openByActivityTitle(@IdRes int activityTitleId) {
- clickMenuListItem(activityTitleId);
- // All the activities in "WebView Asset Loader Demos" uses the same
- // "layout/activity_asset_loader" so all the WebViews have the same ID:
- // R.id.webview_asset_loader_webview.
- // Not all WebViews in the app have javascript turned on, however since the only way
- // to automate WebViews is through javascript, it must be enabled.
- onWebView(withId(R.id.webview_asset_loader_webview)).forceJavascriptEnabled();
- }
-
- }
-
- @Rule
- public IntegrationAppWebViewTestRule mRule = new IntegrationAppWebViewTestRule();
-
- @Before
- public void setUp() {
- // Open the activity of WebViewAssetLoader demo activities from the main Activity list.
- mRule.clickMenuListItem(R.string.asset_loader_list_activity_title);
- }
-
- @Test
- public void testAssetLoaderSimpleActivity() {
- mRule.openByActivityTitle(R.string.asset_loader_simple_activity_title);
- onWebView()
- .withElement(findElement(Locator.TAG_NAME, "h3"))
- .check(webMatches(getText(),
- containsString("Successfully loaded html from assets!")));
- }
-
- @Test
- public void testAssetLoaderAjaxActivity() {
- mRule.openByActivityTitle(R.string.asset_loader_ajax_activity_title);
- onWebView()
- .withElement(findElement(Locator.TAG_NAME, "h1"))
- .check(webMatches(getText(),
- containsString("Loaded HTML should appear below on success")))
- .withElement(findElement(Locator.ID, "assets_html"))
- .check(webMatches(getText(),
- containsString("Successfully loaded html from assets!")))
- .withElement(findElement(Locator.ID, "res_html"))
- .check(webMatches(getText(),
- containsString("Successfully loaded html from resources!")));
- }
-
- @Test
- public void testAssetLoaderInternalStorageActivity() {
- mRule.openByActivityTitle(R.string.asset_loader_internal_storage_activity_title);
- onWebView()
- .withElement(findElement(Locator.TAG_NAME, "h3"))
- .check(webMatches(getText(),
- containsString("Successfully loaded html from app files dir!")));
- }
-}
diff --git a/webkit/integration-tests/testapp/src/main/assets/www/ajax_requests.html b/webkit/integration-tests/testapp/src/main/assets/www/ajax_requests.html
index 12299b1..95e92ec 100644
--- a/webkit/integration-tests/testapp/src/main/assets/www/ajax_requests.html
+++ b/webkit/integration-tests/testapp/src/main/assets/www/ajax_requests.html
@@ -54,7 +54,7 @@
</head>
<body>
- <h1>Loaded HTML should appear below on success</h1>
+ <h1 id="title">Loaded HTML should appear below on success</h1>
<hr>
<div id="assets_html"></div>
<div id="res_html"></div>
diff --git a/webkit/integration-tests/testapp/src/main/assets/www/some_text.html b/webkit/integration-tests/testapp/src/main/assets/www/some_text.html
index c7578bc..da7ec73 100644
--- a/webkit/integration-tests/testapp/src/main/assets/www/some_text.html
+++ b/webkit/integration-tests/testapp/src/main/assets/www/some_text.html
@@ -12,4 +12,4 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<h3>Successfully loaded html from assets!</h3>
+<h3 id="assets_success_msg">Successfully loaded html from assets!</h3>
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivity.java
index 88ae121..4a0373d 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivity.java
@@ -43,7 +43,7 @@
*/
public class AssetLoaderInternalStorageActivity extends AppCompatActivity {
private static final String DEMO_HTML_CONTENT =
- "<h3>Successfully loaded html from app files dir!</h3>";
+ "<h3 id=\"data_success_msg\">Successfully loaded html from app files dir!</h3>";
@NonNull private File mPublicDir;
@NonNull private File mDemoFile;
diff --git a/webkit/integration-tests/testapp/src/main/res/raw/some_text.html b/webkit/integration-tests/testapp/src/main/res/raw/some_text.html
index 502e241..3f1dcb1 100644
--- a/webkit/integration-tests/testapp/src/main/res/raw/some_text.html
+++ b/webkit/integration-tests/testapp/src/main/res/raw/some_text.html
@@ -12,4 +12,4 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<h3>Successfully loaded html from resources!</h3>
+<h3 id="res_success_msg">Successfully loaded html from resources!</h3>
diff --git a/work/workmanager/api/api_lint.ignore b/work/workmanager/api/api_lint.ignore
index 88176e0..6185dcc 100644
--- a/work/workmanager/api/api_lint.ignore
+++ b/work/workmanager/api/api_lint.ignore
@@ -17,6 +17,14 @@
Missing nullability on field `EMPTY` in class `class androidx.work.Data`
+NoByteOrShort: androidx.work.Data#getByte(String, byte):
+ Should avoid odd sized primitives; use `int` instead of `byte` in method androidx.work.Data.getByte(String,byte)
+NoByteOrShort: androidx.work.Data#getByte(String, byte) parameter #1:
+ Should avoid odd sized primitives; use `int` instead of `byte` in parameter defaultValue in androidx.work.Data.getByte(String key, byte defaultValue)
+NoByteOrShort: androidx.work.Data.Builder#putByte(String, byte) parameter #1:
+ Should avoid odd sized primitives; use `int` instead of `byte` in parameter value in androidx.work.Data.Builder.putByte(String key, byte value)
+
+
SetterReturnsThis: androidx.work.WorkRequest.Builder#setBackoffCriteria(androidx.work.BackoffPolicy, java.time.Duration):
Methods must return the builder object (return type androidx.work.WorkRequest.Builder<B,W> instead of B): method androidx.work.WorkRequest.Builder.setBackoffCriteria(androidx.work.BackoffPolicy,java.time.Duration)
SetterReturnsThis: androidx.work.WorkRequest.Builder#setBackoffCriteria(androidx.work.BackoffPolicy, long, java.util.concurrent.TimeUnit):