Merge "[WebView Support Library] Expose force dark API." into androidx-master-dev
diff --git a/.idea/misc.xml b/.idea/misc.xml
index f4c5c4f..fbe0221 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -15,7 +15,7 @@
<option name="myDefaultNotNull" value="androidx.annotation.NonNull" />
<option name="myNullables">
<value>
- <list size="10">
+ <list size="12">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
@@ -26,12 +26,14 @@
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
+ <item index="10" class="java.lang.String" itemvalue="android.annotation.Nullable" />
+ <item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
- <list size="9">
+ <list size="11">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
@@ -41,14 +43,16 @@
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
+ <item index="9" class="java.lang.String" itemvalue="android.annotation.NonNull" />
+ <item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
</list>
</value>
</option>
</component>
- <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
-</project>
+</project>
\ No newline at end of file
diff --git a/annotation/annotation-experimental/api/1.0.0-beta01.txt b/annotation/annotation-experimental/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/1.0.0-beta01.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+ method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+ }
+
+ public enum Experimental.Level {
+ enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+ enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+ method public abstract Class<?> markerClass();
+ }
+
+}
+
diff --git a/annotation/annotation-experimental/api/public_plus_experimental_1.0.0-beta01.txt b/annotation/annotation-experimental/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+ method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+ }
+
+ public enum Experimental.Level {
+ enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+ enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+ method public abstract Class<?> markerClass();
+ }
+
+}
+
diff --git a/annotation/annotation-experimental/api/res-1.0.0-beta01.txt b/annotation/annotation-experimental/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/annotation/annotation-experimental/api/res-1.0.0-beta01.txt
diff --git a/annotation/annotation-experimental/api/restricted_1.0.0-beta01.txt b/annotation/annotation-experimental/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+ method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+ }
+
+ public enum Experimental.Level {
+ enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+ enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+ method public abstract Class<?> markerClass();
+ }
+
+}
+
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java b/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
index d114519..831a8bc 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
@@ -36,6 +36,7 @@
*/
@RestrictTo(LIBRARY)
public class ThemeUtils {
+ private static final String TAG = "ThemeUtils";
private static final ThreadLocal<TypedValue> TL_TYPED_VALUE = new ThreadLocal<>();
@@ -162,9 +163,10 @@
try {
// Same check as in AppCompatDelegateImpl - do not allow using AppCompat widgets
- // without a top-level AppCompat theme (or its descendant).
+ // without a top-level AppCompat theme (or its descendant). For now flag this as
+ // an error-level log message.
if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
- throw new IllegalStateException("View " + view.getClass()
+ android.util.Log.e(TAG, "View " + view.getClass()
+ " is an AppCompat widget that can only be used with a "
+ "Theme.AppCompat theme (or descendant).");
}
diff --git a/benchmark/common/api/1.0.0-beta01.txt b/benchmark/common/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..d811783
--- /dev/null
+++ b/benchmark/common/api/1.0.0-beta01.txt
@@ -0,0 +1,19 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+ public final class ArgumentsKt {
+ ctor public ArgumentsKt();
+ }
+
+ public final class BenchmarkState {
+ method public boolean keepRunning();
+ method public void pauseTiming();
+ method public void resumeTiming();
+ field public static final androidx.benchmark.BenchmarkState.Companion! Companion;
+ }
+
+ public static final class BenchmarkState.Companion {
+ }
+
+}
+
diff --git a/benchmark/common/api/public_plus_experimental_1.0.0-beta01.txt b/benchmark/common/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..efa9c80
--- /dev/null
+++ b/benchmark/common/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+ public final class ArgumentsKt {
+ ctor public ArgumentsKt();
+ }
+
+ public final class BenchmarkState {
+ method public boolean keepRunning();
+ method public void pauseTiming();
+ method @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+ method public void resumeTiming();
+ field public static final androidx.benchmark.BenchmarkState.Companion! Companion;
+ }
+
+ public static final class BenchmarkState.Companion {
+ method @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+ }
+
+ @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
+ }
+
+}
+
diff --git a/benchmark/common/api/res-1.0.0-beta01.txt b/benchmark/common/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/common/api/res-1.0.0-beta01.txt
diff --git a/benchmark/common/api/restricted_1.0.0-beta01.txt b/benchmark/common/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..1ff8212
--- /dev/null
+++ b/benchmark/common/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,35 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+ public final class ArgumentsKt {
+ ctor public ArgumentsKt();
+ }
+
+ public final class BenchmarkState {
+ ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public BenchmarkState();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public long getMin();
+ method public boolean keepRunning();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public inline boolean keepRunningInline();
+ method public void pauseTiming();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void report(String fullClassName, String simpleClassName, String methodName);
+ method public void resumeTiming();
+ field public static final androidx.benchmark.BenchmarkState.Companion! Companion;
+ }
+
+ public static final class BenchmarkState.Companion {
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class IsolationActivity extends android.app.Activity {
+ method public void actuallyFinish();
+ field public static final androidx.benchmark.IsolationActivity.Companion! Companion;
+ }
+
+ public static final class IsolationActivity.Companion {
+ method @AnyThread public void finishSingleton();
+ method public boolean getResumed();
+ method @WorkerThread public void launchSingleton();
+ property public final boolean resumed;
+ }
+
+}
+
diff --git a/benchmark/junit4/api/1.0.0-beta01.txt b/benchmark/junit4/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/1.0.0-beta01.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+ public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+ ctor public AndroidBenchmarkRunner();
+ }
+
+ public final class BenchmarkRule implements org.junit.rules.TestRule {
+ ctor public BenchmarkRule();
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ method public androidx.benchmark.BenchmarkState getState();
+ }
+
+ public final class BenchmarkRule.Scope {
+ method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+ }
+
+ public final class BenchmarkRuleKt {
+ ctor public BenchmarkRuleKt();
+ method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+ }
+
+}
+
diff --git a/benchmark/junit4/api/public_plus_experimental_1.0.0-beta01.txt b/benchmark/junit4/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+ public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+ ctor public AndroidBenchmarkRunner();
+ }
+
+ public final class BenchmarkRule implements org.junit.rules.TestRule {
+ ctor public BenchmarkRule();
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ method public androidx.benchmark.BenchmarkState getState();
+ }
+
+ public final class BenchmarkRule.Scope {
+ method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+ }
+
+ public final class BenchmarkRuleKt {
+ ctor public BenchmarkRuleKt();
+ method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+ }
+
+}
+
diff --git a/benchmark/junit4/api/res-1.0.0-beta01.txt b/benchmark/junit4/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/junit4/api/res-1.0.0-beta01.txt
diff --git a/benchmark/junit4/api/restricted_1.0.0-beta01.txt b/benchmark/junit4/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+ public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+ ctor public AndroidBenchmarkRunner();
+ }
+
+ public final class BenchmarkRule implements org.junit.rules.TestRule {
+ ctor public BenchmarkRule();
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ method public androidx.benchmark.BenchmarkState getState();
+ }
+
+ public final class BenchmarkRule.Scope {
+ method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+ }
+
+ public final class BenchmarkRuleKt {
+ ctor public BenchmarkRuleKt();
+ method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+ }
+
+}
+
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index ca0fe3d..e69bfab 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -25,14 +25,14 @@
val ANIMATION = Version("1.0.0-alpha01")
val ANIMATION_TESTING = Version("1.1.0-alpha01")
val ANNOTATION = Version("1.2.0-alpha01")
- val ANNOTATION_EXPERIMENTAL = Version("1.0.0-alpha01")
+ val ANNOTATION_EXPERIMENTAL = Version("1.0.0-beta01")
val APPCOMPAT = Version("1.2.0-alpha01")
val ARCH_CORE = Version("2.2.0-alpha01")
val ARCH_CORE_TESTING = ARCH_CORE
val ARCH_RUNTIME = Version("2.2.0-alpha01")
val ASYNCLAYOUTINFLATER = Version("1.1.0-alpha01")
val AUTOFILL = Version("1.0.0-beta01")
- val BENCHMARK = Version("1.0.0-alpha06")
+ val BENCHMARK = Version("1.0.0-beta01")
val BIOMETRIC = Version("1.0.0-beta02")
val BROWSER = Version("1.2.0-alpha08")
val CAMERA = Version("1.0.0-alpha06")
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index 0870381..134c7f1 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -53,7 +53,7 @@
.addStubs("car/stubs/android.car.jar")
prebuilts(LibraryGroups.CARDVIEW, "1.0.0")
prebuilts(LibraryGroups.COLLECTION, "1.1.0")
- prebuilts(LibraryGroups.CONCURRENT, "1.0.0-rc01")
+ prebuilts(LibraryGroups.CONCURRENT, "1.0.0")
prebuilts(LibraryGroups.CONTENTPAGER, "1.0.0")
prebuilts(LibraryGroups.COORDINATORLAYOUT, "1.1.0-beta01")
prebuilts(LibraryGroups.CORE, "core", "1.2.0-alpha04")
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
index 89d64da..b7bbe88 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
@@ -77,7 +77,6 @@
"TopLevelBuilder",
"MissingBuild",
"BuilderSetStyle",
- "SetterReturnsThis",
"PackageLayering",
"OverlappingConstants",
"IllegalStateException",
@@ -98,7 +97,10 @@
"MissingJvmStatic"
).joinToString(),
"--error",
- "MinMaxConstant"
+ listOf(
+ "MinMaxConstant",
+ "SetterReturnsThis"
+ ).joinToString()
)
sealed class GenerateApiMode {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
index 387a960..3602ea9 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
@@ -43,6 +43,7 @@
import androidx.camera.core.ImageAnalysisConfig;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.UseCase.StateChangeListener;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.testing.CameraUtil;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
@@ -145,7 +146,7 @@
Analyzer initialAnalyzer = useCase.getAnalyzer();
- useCase.setAnalyzer(mMockAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
Analyzer retrievedAnalyzer = useCase.getAnalyzer();
@@ -163,7 +164,7 @@
useCase.updateSuggestedResolution(suggestedResolutionMap);
useCase.addStateChangeListener(mMockListener);
- useCase.setAnalyzer(mMockAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
verify(mMockListener, times(1)).onUseCaseActive(useCase);
}
@@ -176,7 +177,7 @@
suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
useCase.updateSuggestedResolution(suggestedResolutionMap);
useCase.addStateChangeListener(mMockListener);
- useCase.setAnalyzer(mMockAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
useCase.removeAnalyzer();
verify(mMockListener, times(1)).onUseCaseInactive(useCase);
@@ -197,9 +198,8 @@
private void analyzerAnalyzesImagesWithMode(ImageReaderMode imageReaderMode)
throws InterruptedException, CameraInfoUnavailableException {
final int imageFormat = ImageFormat.YUV_420_888;
- ImageAnalysisConfig config =
- new ImageAnalysisConfig.Builder().setImageReaderMode(
- imageReaderMode).setCallbackHandler(mHandler).build();
+ ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().setImageReaderMode(
+ imageReaderMode).build();
ImageAnalysis useCase = new ImageAnalysis(config);
Map<String, Size> suggestedResolutionMap = new HashMap<>();
suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
@@ -209,7 +209,7 @@
mInstrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
- useCase.setAnalyzer(mAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
}
});
@@ -229,13 +229,12 @@
@Test
@UiThreadTest
public void analyzerDoesNotAnalyzeImages_whenCameraIsNotOpen() throws InterruptedException {
- ImageAnalysisConfig config =
- new ImageAnalysisConfig.Builder().setCallbackHandler(mHandler).build();
+ ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().build();
ImageAnalysis useCase = new ImageAnalysis(config);
Map<String, Size> suggestedResolutionMap = new HashMap<>();
suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
useCase.updateSuggestedResolution(suggestedResolutionMap);
- useCase.setAnalyzer(mAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
// Keep the lifecycle in an inactive state.
// Wait a little while for frames to be analyzed.
mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS);
@@ -254,10 +253,9 @@
final int imageFormat = ImageFormat.YUV_420_888;
final Size[] sizes = {SECONDARY_RESOLUTION, DEFAULT_RESOLUTION};
- ImageAnalysisConfig config =
- new ImageAnalysisConfig.Builder().setCallbackHandler(mHandler).build();
+ ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().build();
ImageAnalysis useCase = new ImageAnalysis(config);
- useCase.setAnalyzer(mAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
for (Size size : sizes) {
Map<String, Size> suggestedResolutionMap = new HashMap<>();
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
index 70ff115..cd28c8b 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
@@ -20,12 +20,15 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.os.AsyncTask;
import android.os.Build;
import android.util.Size;
import android.view.Surface;
@@ -45,6 +48,7 @@
import androidx.camera.core.SessionConfig;
import androidx.camera.testing.CameraUtil;
import androidx.camera.testing.fakes.FakeCameraControl;
+import androidx.camera.testing.fakes.FakeLifecycleOwner;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -62,11 +66,10 @@
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -212,6 +215,7 @@
final SurfaceTextureCallable surfaceTextureCallable0 = new SurfaceTextureCallable();
final FutureTask<SurfaceTexture> future0 = new FutureTask<>(surfaceTextureCallable0);
useCase.setOnPreviewOutputUpdateListener(
+ AsyncTask.SERIAL_EXECUTOR,
new OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -228,6 +232,7 @@
final SurfaceTextureCallable surfaceTextureCallable1 = new SurfaceTextureCallable();
final FutureTask<SurfaceTexture> future1 = new FutureTask<>(surfaceTextureCallable1);
useCase.setOnPreviewOutputUpdateListener(
+ AsyncTask.SERIAL_EXECUTOR,
new OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -256,6 +261,7 @@
final FutureTask<SurfaceTexture> future = new FutureTask<>(surfaceTextureCallable);
useCase.setOnPreviewOutputUpdateListener(
+ AsyncTask.SERIAL_EXECUTOR,
new OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -282,6 +288,7 @@
final SurfaceTextureCallable surfaceTextureCallable0 = new SurfaceTextureCallable();
final FutureTask<SurfaceTexture> future0 = new FutureTask<>(surfaceTextureCallable0);
useCase.setOnPreviewOutputUpdateListener(
+ AsyncTask.SERIAL_EXECUTOR,
new OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(@NonNull PreviewOutput previewOutput) {
@@ -297,6 +304,7 @@
final SurfaceTextureCallable surfaceTextureCallable1 = new SurfaceTextureCallable();
final FutureTask<SurfaceTexture> future1 = new FutureTask<>(surfaceTextureCallable1);
useCase.setOnPreviewOutputUpdateListener(
+ AsyncTask.SERIAL_EXECUTOR,
new Preview.OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -367,24 +375,30 @@
Preview useCase = new Preview(mDefaultConfig);
useCase.updateSuggestedResolution(Collections.singletonMap(mCameraId, DEFAULT_RESOLUTION));
- final AtomicInteger calledCount = new AtomicInteger(0);
- useCase.setOnPreviewOutputUpdateListener(
- new Preview.OnPreviewOutputUpdateListener() {
- @Override
- public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
- calledCount.incrementAndGet();
- }
- });
-
- int initialCount = calledCount.get();
+ useCase.setOnPreviewOutputUpdateListener(AsyncTask.SERIAL_EXECUTOR, mMockListener);
+ verify(mMockListener, timeout(3000)).onUpdated(any(PreviewOutput.class));
useCase.updateSuggestedResolution(
Collections.singletonMap(mCameraId, SECONDARY_RESOLUTION));
- int countAfterUpdate = calledCount.get();
+ verify(mMockListener, timeout(3000).times(2)).onUpdated(any(PreviewOutput.class));
+ }
- assertThat(initialCount).isEqualTo(1);
- assertThat(countAfterUpdate).isEqualTo(2);
+ @Test
+ @UiThreadTest
+ public void previewOutput_invokedByExecutor() {
+ Executor mockExecutor = mock(Executor.class);
+
+ Preview useCase = new Preview(mDefaultConfig);
+
+ FakeLifecycleOwner lifecycleOwner = new FakeLifecycleOwner();
+ lifecycleOwner.startAndResume();
+ CameraX.bindToLifecycle(lifecycleOwner, useCase);
+
+ useCase.setOnPreviewOutputUpdateListener(mockExecutor,
+ mock(OnPreviewOutputUpdateListener.class));
+
+ verify(mockExecutor, timeout(1000)).execute(any(Runnable.class));
}
@Test
@@ -394,24 +408,21 @@
useCase.setTargetRotation(Surface.ROTATION_0);
useCase.updateSuggestedResolution(Collections.singletonMap(mCameraId, DEFAULT_RESOLUTION));
- final AtomicReference<PreviewOutput> latestPreviewOutput = new AtomicReference<>();
- useCase.setOnPreviewOutputUpdateListener(
- new OnPreviewOutputUpdateListener() {
- @Override
- public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
- latestPreviewOutput.set(previewOutput);
- }
- });
-
- Preview.PreviewOutput initialOutput = latestPreviewOutput.get();
+ ArgumentCaptor<PreviewOutput> previewOutput = ArgumentCaptor.forClass(PreviewOutput.class);
+ useCase.setOnPreviewOutputUpdateListener(AsyncTask.SERIAL_EXECUTOR, mMockListener);
useCase.setTargetRotation(Surface.ROTATION_90);
+ verify(mMockListener, timeout(3000).times(2)).onUpdated(previewOutput.capture());
+ assertThat(previewOutput.getAllValues()).hasSize(2);
+ Preview.PreviewOutput initialOutput = previewOutput.getAllValues().get(0);
+ Preview.PreviewOutput latestPreviewOutput = previewOutput.getAllValues().get(1);
+
assertThat(initialOutput).isNotNull();
assertThat(initialOutput.getSurfaceTexture())
- .isEqualTo(latestPreviewOutput.get().getSurfaceTexture());
+ .isEqualTo(latestPreviewOutput.getSurfaceTexture());
assertThat(initialOutput.getRotationDegrees())
- .isNotEqualTo(latestPreviewOutput.get().getRotationDegrees());
+ .isNotEqualTo(latestPreviewOutput.getRotationDegrees());
}
@Test
@@ -423,6 +434,7 @@
final SurfaceTextureCallable surfaceTextureCallable0 = new SurfaceTextureCallable();
final FutureTask<SurfaceTexture> future0 = new FutureTask<>(surfaceTextureCallable0);
useCase.setOnPreviewOutputUpdateListener(
+ AsyncTask.SERIAL_EXECUTOR,
new OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -449,6 +461,7 @@
useCase.updateSuggestedResolution(Collections.singletonMap(mCameraId, DEFAULT_RESOLUTION));
useCase.setOnPreviewOutputUpdateListener(
+ AsyncTask.SERIAL_EXECUTOR,
new Preview.OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(@NonNull PreviewOutput previewOutput) {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
index 253cb85..e472f28 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
@@ -22,11 +22,10 @@
import android.app.Instrumentation;
import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Rational;
import androidx.camera.core.AppConfig;
+import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraX;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageAnalysisConfig;
@@ -111,9 +110,8 @@
ImageAnalysisConfig imageAnalysisConfig =
new ImageAnalysisConfig.Builder()
.setLensFacing(CameraX.LensFacing.BACK)
- .setTargetAspectRatio(new Rational(3, 4))
+ .setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setTargetName("ImageAnalysis")
- .setCallbackHandler(new Handler(Looper.getMainLooper()))
.build();
ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
mInstrumentation.runOnMainSync(new Runnable() {
@@ -136,9 +134,8 @@
ImageAnalysisConfig imageAnalysisConfig =
new ImageAnalysisConfig.Builder()
.setLensFacing(CameraX.LensFacing.BACK)
- .setTargetAspectRatio(new Rational(3, 4))
+ .setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setTargetName("ImageAnalysis")
- .setCallbackHandler(new Handler(Looper.getMainLooper()))
.build();
ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
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 d1c2c3f..c27ca9d 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
@@ -23,9 +23,9 @@
import android.Manifest;
import android.app.Instrumentation;
import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
+import android.graphics.SurfaceTexture;
+import androidx.annotation.NonNull;
import androidx.camera.core.AppConfig;
import androidx.camera.core.CameraX;
import androidx.camera.core.CameraX.LensFacing;
@@ -36,7 +36,9 @@
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.core.PreviewConfig;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.GLUtil;
import androidx.camera.testing.fakes.FakeLifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
@@ -115,7 +117,7 @@
* Test Combination: Preview + ImageCapture
*/
@Test
- public void previewCombinesImageCapture() {
+ public void previewCombinesImageCapture() throws InterruptedException {
initPreview();
initImageCapture();
@@ -123,10 +125,30 @@
@Override
public void run() {
CameraX.bindToLifecycle(mLifecycle, mPreview, mImageCapture);
+ mPreview.setOnPreviewOutputUpdateListener(
+ new Preview.OnPreviewOutputUpdateListener() {
+ @Override
+ public void onUpdated(@NonNull Preview.PreviewOutput output) {
+ output.getSurfaceTexture().attachToGLContext(
+ GLUtil.getTexIdFromGLContext());
+ output.getSurfaceTexture().setOnFrameAvailableListener(
+ new SurfaceTexture.OnFrameAvailableListener() {
+ @Override
+ public void onFrameAvailable(
+ SurfaceTexture surfaceTexture) {
+ surfaceTexture.updateTexImage();
+ mSemaphore.release();
+ }
+ });
+ }
+ });
mLifecycle.startAndResume();
}
});
+ // Wait for the frame available update.
+ mSemaphore.acquire(10);
+
assertThat(mLifecycle.getObserverCount()).isEqualTo(2);
assertThat(CameraX.isBound(mPreview)).isTrue();
assertThat(CameraX.isBound(mImageCapture)).isTrue();
@@ -144,7 +166,7 @@
@Override
public void run() {
CameraX.bindToLifecycle(mLifecycle, mPreview, mImageAnalysis);
- mImageAnalysis.setAnalyzer(mImageAnalyzer);
+ mImageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
mAnalysisResult.observe(mLifecycle,
createCountIncrementingObserver());
mLifecycle.startAndResume();
@@ -171,7 +193,7 @@
@Override
public void run() {
CameraX.bindToLifecycle(mLifecycle, mPreview, mImageAnalysis, mImageCapture);
- mImageAnalysis.setAnalyzer(mImageAnalyzer);
+ mImageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
mAnalysisResult.observe(mLifecycle,
createCountIncrementingObserver());
mLifecycle.startAndResume();
@@ -192,7 +214,6 @@
new ImageAnalysisConfig.Builder()
.setLensFacing(DEFAULT_LENS_FACING)
.setTargetName("ImageAnalysis")
- .setCallbackHandler(new Handler(Looper.getMainLooper()))
.build();
mImageAnalyzer =
new ImageAnalysis.Analyzer() {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
index 0a7309f..a67fbe6 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
@@ -45,6 +45,7 @@
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureConfig;
import androidx.camera.core.ImageProxy;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.testing.CameraUtil;
import androidx.camera.testing.fakes.FakeLifecycleOwner;
import androidx.lifecycle.MutableLiveData;
@@ -143,7 +144,7 @@
ImageAnalysis useCase = new ImageAnalysis(builder.build());
CameraX.bindToLifecycle(mLifecycle, useCase);
- useCase.setAnalyzer(mImageAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
mAnalysisResult.observe(mLifecycle, mockObserver);
mLifecycle.startAndResume();
@@ -184,8 +185,8 @@
CameraX.bindToLifecycle(mLifecycle, useCase, useCase2);
- useCase.setAnalyzer(mImageAnalyzer);
- useCase2.setAnalyzer(mImageAnalyzer2);
+ useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
+ useCase2.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer2);
mAnalysisResult.observe(mLifecycle, mockObserver);
mAnalysisResult2.observe(mLifecycle, mockObserver2);
@@ -216,7 +217,7 @@
.setSessionCaptureCallback(sessionCaptureCallback);
ImageAnalysis useCase = new ImageAnalysis(configBuilder.build());
CameraX.bindToLifecycle(mLifecycle, useCase);
- useCase.setAnalyzer(mImageAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
mAnalysisResult.observe(mLifecycle, createCountIncrementingObserver(observedCount));
mLifecycle.startAndResume();
@@ -259,7 +260,7 @@
@Override
public void run() {
CameraX.bindToLifecycle(mLifecycle, useCase);
- useCase.setAnalyzer(mImageAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
mLifecycle.startAndResume();
}
});
@@ -362,7 +363,7 @@
@Override
public void run() {
CameraX.bindToLifecycle(mLifecycle, useCase);
- useCase.setAnalyzer(mImageAnalyzer);
+ useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
}
});
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
index 3719aab..2701a74 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
@@ -85,7 +85,7 @@
targetRotation);
boolean isRotateNeeded = (rotationDegrees == 90 || rotationDegrees == 270);
builder.setTargetRotation(targetRotation);
- builder.setTargetAspectRatio(
+ builder.setTargetAspectRatioCustom(
isRotateNeeded ? DEFAULT_ASPECT_RATIO_3_4 : DEFAULT_ASPECT_RATIO_4_3);
} catch (Exception e) {
Log.w(TAG, "Unable to determine default lens facing for ImageAnalysis.", e);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
index b8b2951..d6c8e574 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
@@ -85,7 +85,7 @@
targetRotation);
boolean isRotateNeeded = (rotationDegrees == 90 || rotationDegrees == 270);
builder.setTargetRotation(targetRotation);
- builder.setTargetAspectRatio(
+ builder.setTargetAspectRatioCustom(
isRotateNeeded ? DEFAULT_ASPECT_RATIO_3_4 : DEFAULT_ASPECT_RATIO_4_3);
} catch (Exception e) {
Log.w(TAG, "Unable to determine default lens facing for ImageCapture.", e);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
index e58ca95..3033be7 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
@@ -83,7 +83,7 @@
targetRotation);
boolean isRotateNeeded = (rotationDegrees == 90 || rotationDegrees == 270);
builder.setTargetRotation(targetRotation);
- builder.setTargetAspectRatio(
+ builder.setTargetAspectRatioCustom(
isRotateNeeded ? DEFAULT_ASPECT_RATIO_3_4 : DEFAULT_ASPECT_RATIO_4_3);
} catch (Exception e) {
Log.w(TAG, "Unable to determine default lens facing for Preview.", e);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/SupportedSurfaceCombination.java
index a226491..bc8463d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/SupportedSurfaceCombination.java
@@ -26,11 +26,14 @@
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
import android.os.Build;
+import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
import android.view.WindowManager;
+import androidx.annotation.Nullable;
+import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraDeviceConfig;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraX;
@@ -71,6 +74,10 @@
private static final Size QUALITY_720P_SIZE = new Size(1280, 720);
private static final Size QUALITY_480P_SIZE = new Size(720, 480);
private static final int ALIGN16 = 16;
+ private static final Rational ASPECT_RATIO_4_3 = new Rational(4, 3);
+ private static final Rational ASPECT_RATIO_3_4 = new Rational(3, 4);
+ private static final Rational ASPECT_RATIO_16_9 = new Rational(16, 9);
+ private static final Rational ASPECT_RATIO_9_16 = new Rational(9, 16);
private final List<SurfaceCombination> mSurfaceCombinations = new ArrayList<>();
private final Map<Integer, Size> mMaxSizeCache = new HashMap<>();
private String mCameraId;
@@ -321,7 +328,7 @@
private List<Size> getSupportedOutputSizes(UseCase useCase) {
int imageFormat = useCase.getImageFormat();
- Size[] outputSizes = getAllOutputSizesByFormat(imageFormat);
+ Size[] outputSizes = getAllOutputSizesByFormat(imageFormat, useCase);
List<Size> outputSizeCandidates = new ArrayList<>();
ImageOutputConfig config = (ImageOutputConfig) useCase.getUseCaseConfig();
Size maxSize = config.getMaxResolution(getMaxOutputSizeByFormat(imageFormat));
@@ -330,10 +337,17 @@
// result.
Arrays.sort(outputSizes, new CompareSizesByArea(true));
- // Filter out the ones that exceed the maximum size
+ // Get the minimum size according to min(DEFAULT_SIZE, TARGET_RESOLUTION).
+ Size targetSize = config.getTargetResolution(ZERO_SIZE);
+ Size minSize = DEFAULT_SIZE;
+ if (!targetSize.equals(ZERO_SIZE) && getArea(targetSize) < getArea(DEFAULT_SIZE)) {
+ minSize = targetSize;
+ }
+
+ // Filter out the ones that exceed the maximum size and the minimum size.
for (Size outputSize : outputSizes) {
- if (outputSize.getWidth() * outputSize.getHeight()
- <= maxSize.getWidth() * maxSize.getHeight()) {
+ if (getArea(outputSize) <= getArea(maxSize)
+ && getArea(outputSize) >= getArea(minSize)) {
outputSizeCandidates.add(outputSize);
}
}
@@ -350,9 +364,28 @@
List<Size> sizesMatchAspectRatio = new ArrayList<>();
List<Size> sizesNotMatchAspectRatio = new ArrayList<>();
- Rational aspectRatio = config.getTargetAspectRatio(null);
- int targetRotation = config.getTargetRotation(Surface.ROTATION_0);
- aspectRatio = rotateAspectRatioByRotation(aspectRatio, targetRotation);
+ Rational aspectRatio = null;
+ AspectRatio targetAspectRatio = config.getTargetAspectRatio(null);
+ if (targetAspectRatio != null) {
+ // Checks the sensor orientation.
+ boolean isSensorLandscapeOrientation = isRotationNeeded(Surface.ROTATION_0);
+ switch (targetAspectRatio) {
+ case RATIO_4_3:
+ aspectRatio =
+ isSensorLandscapeOrientation ? ASPECT_RATIO_4_3 : ASPECT_RATIO_3_4;
+ break;
+ case RATIO_16_9:
+ aspectRatio =
+ isSensorLandscapeOrientation ? ASPECT_RATIO_16_9 : ASPECT_RATIO_9_16;
+ break;
+ default:
+ // Unhandled event.
+ }
+ } else {
+ aspectRatio = config.getTargetAspectRatioCustom(null);
+ int targetRotation = config.getTargetRotation(Surface.ROTATION_0);
+ aspectRatio = rotateAspectRatioByRotation(aspectRatio, targetRotation);
+ }
for (Size outputSize : outputSizeCandidates) {
// If target aspect ratio is set, moves the matched results to the front of the list.
@@ -372,9 +405,11 @@
// Check whether the desired default resolution is included in the original supported list
boolean isDefaultResolutionSupported = outputSizeCandidates.contains(DEFAULT_SIZE);
- // If the target resolution is set, use it to find the minimum one from big enough items
- Size targetSize = config.getTargetResolution(ZERO_SIZE);
+ // Check the default resolution if the target resolution is not set
+ targetSize = (targetSize.equals(ZERO_SIZE)) ? config.getDefaultResolution(ZERO_SIZE)
+ : targetSize;
+ // If the target resolution is set, use it to find the minimum one from big enough items
if (!targetSize.equals(ZERO_SIZE)) {
removeSupportedSizesByTargetSize(sizesMatchAspectRatio, targetSize);
removeSupportedSizesByTargetSize(sizesNotMatchAspectRatio, targetSize);
@@ -405,6 +440,16 @@
// Use target rotation to calibrate the aspect ratio.
private Rational rotateAspectRatioByRotation(Rational aspectRatio, int targetRotation) {
Rational outputRatio = aspectRatio;
+ // Calibrates the aspect ratio with the display and sensor rotation degrees values.
+ // Otherwise, retrieves default aspect ratio for the target use case.
+ if (aspectRatio != null && isRotationNeeded(targetRotation)) {
+ outputRatio = new Rational(aspectRatio.getDenominator(),
+ aspectRatio.getNumerator());
+ }
+ return outputRatio;
+ }
+
+ private boolean isRotationNeeded(int targetRotation) {
int sensorRotationDegrees;
try {
sensorRotationDegrees = CameraX.getCameraInfo(mCameraId).getSensorRotationDegrees(
@@ -412,13 +457,7 @@
} catch (CameraInfoUnavailableException e) {
throw new IllegalArgumentException("Unable to retrieve camera sensor orientation.", e);
}
- // Calibrates the aspect ratio with the display and sensor rotation degrees values.
- // Otherwise, retrieves default aspect ratio for the target use case.
- if (aspectRatio != null && (sensorRotationDegrees == 90 || sensorRotationDegrees == 270)) {
- outputRatio = new Rational(aspectRatio.getDenominator(),
- aspectRatio.getNumerator());
- }
- return outputRatio;
+ return sensorRotationDegrees == 90 || sensorRotationDegrees == 270;
}
private boolean hasMatchingAspectRatio(Size resolution, Rational aspectRatio) {
@@ -451,6 +490,10 @@
return false;
}
+ private int getArea(Size size) {
+ return size.getWidth() * size.getHeight();
+ }
+
private boolean ratioIntersectsMod16Segment(int height, int mod16Width, Rational aspectRatio) {
Preconditions.checkArgument(mod16Width % 16 == 0);
double aspectRatioWidth =
@@ -466,8 +509,7 @@
// Get the index of the item that is big enough for the view size in matched list
for (int i = 0; i < supportedSizesList.size(); i++) {
Size outputSize = supportedSizesList.get(i);
- if (outputSize.getWidth() * outputSize.getHeight()
- >= targetSize.getWidth() * targetSize.getHeight()) {
+ if (getArea(outputSize) >= getArea(targetSize)) {
indexBigEnough = i;
} else {
break;
@@ -477,8 +519,7 @@
Size bigEnoughSize = supportedSizesList.get(indexBigEnough);
List<Size> removeSizes = new ArrayList<>();
for (Size size : supportedSizesList) {
- if (size.getWidth() * size.getHeight()
- > bigEnoughSize.getWidth() * bigEnoughSize.getHeight()) {
+ if (getArea(size) > getArea(bigEnoughSize)) {
removeSizes.add(size);
}
}
@@ -541,32 +582,58 @@
return allPossibleSizeArrangements;
}
+ @Nullable
private Size[] getAllOutputSizesByFormat(int imageFormat) {
- if (mCharacteristics == null) {
- throw new IllegalStateException("CameraCharacteristics is null.");
+ return getAllOutputSizesByFormat(imageFormat, null);
+ }
+
+ @Nullable
+ private Size[] getAllOutputSizesByFormat(int imageFormat, @Nullable UseCase useCase) {
+ Size[] outputSizes = null;
+
+ // Try to retrieve customized supported resolutions from config.
+ List<Pair<Integer, Size[]>> formatResolutionsPairList = null;
+
+ if (useCase != null) {
+ ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCase.getUseCaseConfig();
+
+ formatResolutionsPairList = imageOutputConfig.getSupportedResolutions(null);
}
- StreamConfigurationMap map =
- mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-
- if (map == null) {
- throw new IllegalArgumentException(
- "Can not get supported output size for the format: " + imageFormat);
+ if (formatResolutionsPairList != null) {
+ for (Pair<Integer, Size[]> formatResolutionPair : formatResolutionsPairList) {
+ if (formatResolutionPair.first == imageFormat) {
+ outputSizes = formatResolutionPair.second;
+ break;
+ }
+ }
}
- Size[] outputSizes;
- if (Build.VERSION.SDK_INT < 23
- && imageFormat == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
- // This is a little tricky that 0x22 that is internal defined in
- // StreamConfigurationMap.java
- // to be equal to ImageFormat.PRIVATE that is public after Android level 23 but not
- // public in
- // Android L. Use {@link SurfaceTexture} or {@link MediaCodec} will finally mapped to
- // 0x22 in
- // StreamConfigurationMap to retrieve the output sizes information.
- outputSizes = map.getOutputSizes(SurfaceTexture.class);
- } else {
- outputSizes = map.getOutputSizes(imageFormat);
+ // Try to retrieve supported resolutions if there is no customization.
+ if (outputSizes == null) {
+ if (mCharacteristics == null) {
+ throw new IllegalStateException("CameraCharacteristics is null.");
+ }
+
+ StreamConfigurationMap map =
+ mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ if (map == null) {
+ throw new IllegalArgumentException(
+ "Can not get supported output size for the format: " + imageFormat);
+ }
+
+ if (Build.VERSION.SDK_INT < 23
+ && imageFormat == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
+ // This is a little tricky that 0x22 that is internal defined in
+ // StreamConfigurationMap.java to be equal to ImageFormat.PRIVATE that is public
+ // after Android level 23 but not public in Android L. Use {@link SurfaceTexture}
+ // or {@link MediaCodec} will finally mapped to 0x22 in StreamConfigurationMap to
+ // retrieve the output sizes information.
+ outputSizes = map.getOutputSizes(SurfaceTexture.class);
+ } else {
+ outputSizes = map.getOutputSizes(imageFormat);
+ }
}
if (outputSizes == null) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
index 807ef4a..2a574db 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
@@ -85,7 +85,7 @@
targetRotation);
boolean isRotateNeeded = (rotationDegrees == 90 || rotationDegrees == 270);
builder.setTargetRotation(targetRotation);
- builder.setTargetAspectRatio(
+ builder.setTargetAspectRatioCustom(
isRotateNeeded ? DEFAULT_ASPECT_RATIO_9_16 : DEFAULT_ASPECT_RATIO_16_9);
} catch (Exception e) {
Log.w(TAG, "Unable to determine default lens facing for VideoCapture.", e);
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
index 0422452..af1dd0c 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
@@ -35,6 +35,7 @@
import android.view.WindowManager;
import androidx.camera.core.AppConfig;
+import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraDeviceSurfaceManager;
import androidx.camera.core.CameraX;
import androidx.camera.core.CameraX.LensFacing;
@@ -315,14 +316,13 @@
@Test
public void suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice() {
- Rational aspectRatio = new Rational(16, 9);
PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
- previewConfigBuilder.setTargetAspectRatio(aspectRatio);
- videoCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
- imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+ previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ videoCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
imageCaptureConfigBuilder.setLensFacing(LensFacing.FRONT);
ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
@@ -350,14 +350,13 @@
@Test
public void getSuggestedResolutionsForMixedUseCaseInLimitedDevice() {
- Rational aspectRatio = new Rational(9, 16);
PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
- previewConfigBuilder.setTargetAspectRatio(aspectRatio);
- videoCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
- imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+ previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ videoCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
imageCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
@@ -496,7 +495,7 @@
Rational targetAspectRatio = new Rational(9, 16);
PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
- previewConfigBuilder.setTargetAspectRatio(targetAspectRatio);
+ previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
previewConfigBuilder.setLensFacing(LensFacing.FRONT);
PreviewConfig previewConfig = previewConfigBuilder.build();
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
index 9861dc2..fa669bd 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
@@ -31,12 +31,14 @@
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.os.Build;
+import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.WindowManager;
import androidx.camera.camera2.Camera2AppConfig;
import androidx.camera.core.AppConfig;
+import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraX;
import androidx.camera.core.CameraX.LensFacing;
import androidx.camera.core.ImageAnalysis;
@@ -75,6 +77,7 @@
import org.robolectric.shadows.ShadowCameraManager;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -92,9 +95,10 @@
private static final String LEVEL3_CAMERA_ID = "4";
private static final int DEFAULT_SENSOR_ORIENTATION = 90;
private static final Rational ASPECT_RATIO_4_3 = new Rational(4, 3);
- private final Size mDisplaySize = new Size(1280, 720);
+ private static final Rational ASPECT_RATIO_16_9 = new Rational(16, 9);
+ private final Size mDisplaySize = new Size(720, 1280);
private final Size mAnalysisSize = new Size(640, 480);
- private final Size mPreviewSize = mDisplaySize;
+ private final Size mPreviewSize = new Size(1280, 720);
private final Size mRecordSize = new Size(3840, 2160);
private final Size mMaximumSize = new Size(4032, 3024);
private final Size mMaximumVideoSize = new Size(1920, 1080);
@@ -132,9 +136,11 @@
new Size(1280, 720),
new Size(1280, 720), // duplicate the size since Nexus 5X emulator has the case.
new Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
+ new Size(800, 450),
new Size(640, 480),
new Size(320, 240),
- new Size(320, 180)
+ new Size(320, 180),
+ new Size(256, 144) // For checkSmallSizesAreFilteredOut test.
};
private final Context mContext = RuntimeEnvironment.application.getApplicationContext();
@@ -463,33 +469,41 @@
Rational targetAspectRatio = new Rational(9, 16);
PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
- previewConfigBuilder.setTargetAspectRatio(targetAspectRatio);
+ previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
previewConfigBuilder.setLensFacing(LensFacing.FRONT);
Preview preview = new Preview(previewConfigBuilder.build());
PreviewConfig config = (PreviewConfig) preview.getUseCaseConfig();
- Rational previewAspectRatio = config.getTargetAspectRatio();
+ Rational previewAspectRatio = config.getTargetAspectRatioCustom();
- Rational resultAspectRatio = supportedSurfaceCombination.getCorrectedAspectRatio(config);
+ Rational correctedAspectRatio = supportedSurfaceCombination.getCorrectedAspectRatio(config);
Size maxJpegSize = supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG);
Rational maxJpegAspectRatio = new Rational(maxJpegSize.getHeight(), maxJpegSize.getWidth());
+ List<UseCase> useCases = new ArrayList<>();
+ useCases.add(preview);
+ Map<UseCase, Size> suggestedResolutionMap =
+ supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+ Size previewSize = suggestedResolutionMap.get(preview);
+ Rational resultAspectRatio = new Rational(previewSize.getHeight(), previewSize.getWidth());
+
if (Build.VERSION.SDK_INT == 21) {
// Checks targetAspectRatio and maxJpegAspectRatio, which is the ratio of maximum size
// in the mSupportedSizes, are not equal to make sure this test case is valid.
assertFalse(targetAspectRatio.equals(maxJpegAspectRatio));
assertTrue(previewAspectRatio.equals(maxJpegAspectRatio));
+ assertTrue(correctedAspectRatio.equals(maxJpegAspectRatio));
assertTrue(resultAspectRatio.equals(maxJpegAspectRatio));
} else {
// Checks no correction is needed.
- assertThat(resultAspectRatio).isNull();
- assertTrue(previewAspectRatio.equals(targetAspectRatio));
+ assertThat(correctedAspectRatio).isNull();
+ assertTrue(resultAspectRatio.equals(targetAspectRatio));
}
}
@Test
- public void checkDefaultAspectRatioForMixedUseCase() {
+ public void checkDefaultAspectRatioAndResolutionForMixedUseCase() {
setupCamera(/* supportsRaw= */ false);
SupportedSurfaceCombination supportedSurfaceCombination =
new SupportedSurfaceCombination(
@@ -524,9 +538,13 @@
Rational imageAnalysisAspectRatio = new Rational(imageAnalysisSize.getWidth(),
imageAnalysisSize.getHeight());
+ // Checks the default aspect ratio.
assertTrue(previewAspectRatio.equals(ASPECT_RATIO_4_3));
assertTrue(imageCaptureAspectRatio.equals(ASPECT_RATIO_4_3));
assertTrue(imageAnalysisAspectRatio.equals(ASPECT_RATIO_4_3));
+
+ // Checks the default resolution.
+ assertTrue(imageAnalysisSize.equals(mAnalysisSize));
}
@Test
@@ -558,20 +576,84 @@
}
@Test
+ public void checkSmallSizesAreFilteredOutByDefaultSize480p() {
+ setupCamera(/* supportsRaw= */ false);
+ SupportedSurfaceCombination supportedSurfaceCombination =
+ new SupportedSurfaceCombination(
+ mContext, LIMITED_CAMERA_ID, mMockCamcorderProfileHelper);
+ /* This test case is for b/139018208 that get small resolution 144x256 with below
+ conditions:
+ 1. The target aspect ratio is set to the screen size 1080 x 2220 (9:18.5).
+ 2. The camera doesn't provide any 9:18.5 resolution and the size 144x256(9:16)
+ is considered the 9:18.5 mod16 version.
+ 3. There is no other bigger resolution matched the target aspect ratio.
+ */
+ final int displayWidth = 1080;
+ final int displayHeight = 2220;
+ PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
+ previewConfigBuilder.setTargetResolution(new Size(displayHeight, displayWidth));
+ Preview preview = new Preview(previewConfigBuilder.build());
+
+ List<UseCase> useCases = new ArrayList<>();
+ useCases.add(preview);
+ Map<UseCase, Size> suggestedResolutionMap =
+ supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+
+ // Checks the preconditions.
+ final Size preconditionSize = new Size(256, 144);
+ final Rational targetRatio = new Rational(displayHeight, displayWidth);
+ ArrayList<Size> sizeList = new ArrayList<>(Arrays.asList(mSupportedSizes));
+ assertTrue(sizeList.contains(preconditionSize));
+ for (Size s : mSupportedSizes) {
+ Rational supportedRational = new Rational(s.getWidth(), s.getHeight());
+ assertFalse(supportedRational.equals(targetRatio));
+ }
+
+ // Checks the mechanism has filtered out the sizes which are smaller than default size 480p.
+ Size previewSize = suggestedResolutionMap.get(preview);
+ assertTrue(!previewSize.equals(preconditionSize));
+ }
+
+ @Test
+ public void checkSmallTargetResolutionIsNotFilteredOutBy480p() {
+ setupCamera(/* supportsRaw= */ false);
+ SupportedSurfaceCombination supportedSurfaceCombination =
+ new SupportedSurfaceCombination(
+ mContext, LIMITED_CAMERA_ID, mMockCamcorderProfileHelper);
+
+ // The resolution selection will filter out the sizes which are smaller than min(640x480,
+ // TARGET_RESOLUTION)
+ final Size targetResolution = new Size(240, 320);
+ PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
+ previewConfigBuilder.setTargetResolution(targetResolution);
+ Preview preview = new Preview(previewConfigBuilder.build());
+
+ List<UseCase> useCases = new ArrayList<>();
+ useCases.add(preview);
+ Map<UseCase, Size> suggestedResolutionMap =
+ supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+
+ // Checks the returned size is 320x240, if we set the target resolution which is smaller
+ // than 480p.
+ final Size resultSize = new Size(320, 240);
+ Size previewSize = suggestedResolutionMap.get(preview);
+ assertTrue(previewSize.equals(resultSize));
+ }
+
+ @Test
public void suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice() {
setupCamera(/* supportsRaw= */ false);
SupportedSurfaceCombination supportedSurfaceCombination =
new SupportedSurfaceCombination(
mContext, LEGACY_CAMERA_ID, mMockCamcorderProfileHelper);
- Rational aspectRatio = new Rational(16, 9);
PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
- previewConfigBuilder.setTargetAspectRatio(aspectRatio);
- videoCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
- imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+ previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ videoCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
imageCaptureConfigBuilder.setLensFacing(LensFacing.FRONT);
ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
@@ -597,14 +679,13 @@
new SupportedSurfaceCombination(
mContext, LIMITED_CAMERA_ID, mMockCamcorderProfileHelper);
- Rational aspectRatio = new Rational(9, 16);
PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
- previewConfigBuilder.setTargetAspectRatio(aspectRatio);
- videoCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
- imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+ previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ videoCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
imageCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
@@ -638,7 +719,6 @@
1. There are duplicated two 1280x720 supported sizes for ImageCapture and Preview.
2. supportedOutputSizes for ImageCapture and Preview in
SupportedSurfaceCombination#getAllPossibleSizeArrangements are the same.
- 3. Target aspect ratio is 3:4.
*/
ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
@@ -646,9 +726,9 @@
imageCaptureConfigBuilder.setTargetResolution(mDisplaySize);
ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
- previewConfigBuilder.setTargetAspectRatio(new Rational(3, 4)).setTargetResolution(
- mDisplaySize).setLensFacing(LensFacing.BACK);
+ previewConfigBuilder.setTargetResolution(mDisplaySize).setLensFacing(LensFacing.BACK);
Preview preview = new Preview(previewConfigBuilder.build());
+ imageAnalysisConfigBuilder.setTargetResolution(mDisplaySize).setLensFacing(LensFacing.BACK);
ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfigBuilder.build());
List<UseCase> useCases = new ArrayList<>();
@@ -658,9 +738,133 @@
Map<UseCase, Size> suggestedResolutionMap =
supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
- assertThat(suggestedResolutionMap).containsEntry(imageCapture, mMaximumSize);
+ assertThat(suggestedResolutionMap).containsEntry(imageCapture, mPreviewSize);
+ assertThat(suggestedResolutionMap).containsEntry(preview, mPreviewSize);
+ assertThat(suggestedResolutionMap).containsEntry(imageAnalysis, mPreviewSize);
+ }
+
+ @Test
+ public void setTargetAspectRatioForMixedUseCases() {
+ setupCamera(/* supportsRaw= */ false);
+ SupportedSurfaceCombination supportedSurfaceCombination =
+ new SupportedSurfaceCombination(
+ mContext, FULL_CAMERA_ID, mMockCamcorderProfileHelper);
+
+ PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
+ ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
+ ImageAnalysisConfig.Builder imageAnalysisConfigBuilder = new ImageAnalysisConfig.Builder();
+
+ previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ imageAnalysisConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+
+ Preview preview = new Preview(previewConfigBuilder.build());
+ ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
+ ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfigBuilder.build());
+
+ List<UseCase> useCases = new ArrayList<>();
+ useCases.add(imageCapture);
+ useCases.add(preview);
+ useCases.add(imageAnalysis);
+ Map<UseCase, Size> suggestedResolutionMap =
+ supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+
+ Size previewSize = suggestedResolutionMap.get(preview);
+ Size imageCaptureSize = suggestedResolutionMap.get(imageCapture);
+ Size imageAnalysisSize = suggestedResolutionMap.get(imageAnalysis);
+
+ Rational previewAspectRatio = new Rational(previewSize.getWidth(), previewSize.getHeight());
+ Rational imageCaptureAspectRatio = new Rational(imageCaptureSize.getWidth(),
+ imageCaptureSize.getHeight());
+ Rational imageAnalysisAspectRatio = new Rational(imageAnalysisSize.getWidth(),
+ imageAnalysisSize.getHeight());
+
+ assertTrue(previewAspectRatio.equals(ASPECT_RATIO_16_9));
+ assertTrue(imageCaptureAspectRatio.equals(ASPECT_RATIO_16_9));
+ assertTrue(imageAnalysisAspectRatio.equals(ASPECT_RATIO_16_9));
+ }
+
+ @Test
+ public void throwsWhenSetBothTargetResolutionAndAspectRatioForDifferentUseCases() {
+ setupCamera(/* supportsRaw= */ false);
+
+ boolean previewExceptionHappened = false;
+ PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder()
+ .setLensFacing(LensFacing.BACK)
+ .setTargetResolution(mDisplaySize)
+ .setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ try {
+ previewConfigBuilder.build();
+ } catch (IllegalArgumentException e) {
+ previewExceptionHappened = true;
+ }
+ assertTrue(previewExceptionHappened);
+
+ boolean imageCaptureExceptionHappened = false;
+ ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder()
+ .setLensFacing(LensFacing.BACK)
+ .setTargetResolution(mDisplaySize)
+ .setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ try {
+ imageCaptureConfigBuilder.build();
+ } catch (IllegalArgumentException e) {
+ imageCaptureExceptionHappened = true;
+ }
+ assertTrue(imageCaptureExceptionHappened);
+
+ boolean imageAnalysisExceptionHappened = false;
+ ImageAnalysisConfig.Builder imageAnalysisConfigBuilder = new ImageAnalysisConfig.Builder()
+ .setLensFacing(LensFacing.BACK)
+ .setTargetResolution(mDisplaySize)
+ .setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ try {
+ imageAnalysisConfigBuilder.build();
+ } catch (IllegalArgumentException e) {
+ imageAnalysisExceptionHappened = true;
+ }
+ assertTrue(imageAnalysisExceptionHappened);
+ }
+
+ @Test
+ public void getSuggestedResolutionsForCustomizedSupportedResolutions() {
+ setupCamera(/* supportsRaw= */ false);
+ SupportedSurfaceCombination supportedSurfaceCombination =
+ new SupportedSurfaceCombination(
+ mContext, LIMITED_CAMERA_ID, mMockCamcorderProfileHelper);
+
+ PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
+ VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
+ ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
+
+ List<Pair<Integer, Size[]>> formatResolutionsPairList = new ArrayList<>();
+ formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, new Size[]{mAnalysisSize}));
+ formatResolutionsPairList.add(
+ Pair.create(ImageFormat.YUV_420_888, new Size[]{mAnalysisSize}));
+ formatResolutionsPairList.add(Pair.create(ImageFormat.PRIVATE, new Size[]{mAnalysisSize}));
+
+ // Sets customized supported resolutions to 640x480 only.
+ imageCaptureConfigBuilder.setSupportedResolutions(formatResolutionsPairList);
+ videoCaptureConfigBuilder.setSupportedResolutions(formatResolutionsPairList);
+ previewConfigBuilder.setSupportedResolutions(formatResolutionsPairList);
+
+ imageCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
+ ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
+ videoCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
+ VideoCapture videoCapture = new VideoCapture(videoCaptureConfigBuilder.build());
+ previewConfigBuilder.setLensFacing(LensFacing.BACK);
+ Preview preview = new Preview(previewConfigBuilder.build());
+
+ List<UseCase> useCases = new ArrayList<>();
+ useCases.add(imageCapture);
+ useCases.add(videoCapture);
+ useCases.add(preview);
+ Map<UseCase, Size> suggestedResolutionMap =
+ supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+
+ // Checks all suggested resolutions will become 640x480.
+ assertThat(suggestedResolutionMap).containsEntry(imageCapture, mAnalysisSize);
+ assertThat(suggestedResolutionMap).containsEntry(videoCapture, mAnalysisSize);
assertThat(suggestedResolutionMap).containsEntry(preview, mAnalysisSize);
- assertThat(suggestedResolutionMap).containsEntry(imageAnalysis, mAnalysisSize);
}
@Test
@@ -826,18 +1030,17 @@
new SupportedSurfaceCombination(
mContext, LEGACY_CAMERA_ID, mMockCamcorderProfileHelper);
- Rational aspectRatio = new Rational(9, 16);
PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
previewConfigBuilder.setLensFacing(LensFacing.BACK);
imageCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
- previewConfigBuilder.setTargetAspectRatio(aspectRatio);
- imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+ previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
- previewConfigBuilder.setTargetResolution(mMod16Size);
- imageCaptureConfigBuilder.setTargetResolution(mMod16Size);
+ previewConfigBuilder.setDefaultResolution(mMod16Size);
+ imageCaptureConfigBuilder.setDefaultResolution(mMod16Size);
Preview preview = new Preview(previewConfigBuilder.build());
ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/AspectRatio.java b/camera/camera-core/src/main/java/androidx/camera/core/AspectRatio.java
new file mode 100644
index 0000000..ac49a42
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/AspectRatio.java
@@ -0,0 +1,25 @@
+/*
+ * 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.camera.core;
+
+/** The aspect ratio of the use case. */
+public enum AspectRatio {
+ /** 4:3 standard aspect ratio. */
+ RATIO_4_3,
+ /** 16:9 standard aspect ratio. */
+ RATIO_16_9
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index 483e17c..06f9ed9 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -17,8 +17,6 @@
package androidx.camera.core;
import android.media.ImageReader;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import android.util.Size;
import android.view.Display;
@@ -62,8 +60,8 @@
private static final int NON_BLOCKING_IMAGE_DEPTH = 4;
final AtomicReference<Analyzer> mSubscribedAnalyzer;
+ final AtomicReference<Executor> mAnalyzerExecutor;
final AtomicInteger mRelativeRotation = new AtomicInteger();
- final Handler mHandler;
private final ImageAnalysisConfig.Builder mUseCaseConfigBuilder;
@SuppressWarnings("WeakerAccess") /* synthetic access */
final ImageAnalysisBlockingAnalyzer mImageAnalysisBlockingAnalyzer;
@@ -86,18 +84,15 @@
// Get the combined configuration with defaults
ImageAnalysisConfig combinedConfig = (ImageAnalysisConfig) getUseCaseConfig();
mSubscribedAnalyzer = new AtomicReference<>();
- mHandler = combinedConfig.getCallbackHandler(null);
- if (mHandler == null) {
- throw new IllegalStateException("No default mHandler specified.");
- }
+ mAnalyzerExecutor = new AtomicReference<>();
setImageFormat(ImageReaderFormatRecommender.chooseCombo().imageAnalysisFormat());
// Init both instead of lazy loading to void synchronization.
mImageAnalysisBlockingAnalyzer = new ImageAnalysisBlockingAnalyzer(mSubscribedAnalyzer,
mRelativeRotation,
- mHandler);
+ mAnalyzerExecutor);
mImageAnalysisNonBlockingAnalyzer = new ImageAnalysisNonBlockingAnalyzer(
mSubscribedAnalyzer, mRelativeRotation,
- mHandler, config.getBackgroundExecutor(
+ mAnalyzerExecutor, config.getBackgroundExecutor(
CameraXExecutors.highPriorityExecutor()));
}
@@ -184,15 +179,17 @@
/**
* Removes a previously set analyzer.
*
- * <p>This is equivalent to calling {@code setAnalyzer(null)}. This will stop data from
- * streaming to the {@link ImageAnalysis}.
+ * <p>This will stop data from streaming to the {@link ImageAnalysis}.
*
* @throws IllegalStateException If not called on main thread.
*/
@UiThread
public void removeAnalyzer() {
Threads.checkMainThread();
- setAnalyzer(null);
+ mAnalyzerExecutor.set(null);
+ if (mSubscribedAnalyzer.getAndSet(null) != null) {
+ notifyInactive();
+ }
}
/**
@@ -266,8 +263,7 @@
* Sets an analyzer to receive and analyze images.
*
* <p>Setting an analyzer will signal to the camera that it should begin sending data. The
- * stream of data can be stopped by setting the analyzer to {@code null} or by calling {@link
- * #removeAnalyzer()}.
+ * stream of data can be stopped by calling {@link #removeAnalyzer()}.
*
* <p>Applications can process or copy the image by implementing the {@link Analyzer}. If
* frames should be skipped (no analysis), the analyzer function should return, instead of
@@ -276,18 +272,18 @@
* <p>Setting an analyzer function replaces any previous analyzer. Only one analyzer can be
* set at any time.
*
- * @param analyzer of the images or {@code null} to stop data streaming to
- * {@link ImageAnalysis}.
+ * @param executor The executor in which the
+ * {@link ImageAnalysis.Analyzer#analyze(ImageProxy, int)} will be run.
+ * @param analyzer of the images.
* @throws IllegalStateException If not called on main thread.
*/
@UiThread
- public void setAnalyzer(@Nullable Analyzer analyzer) {
+ public void setAnalyzer(@NonNull Executor executor, @NonNull Analyzer analyzer) {
Threads.checkMainThread();
+ mAnalyzerExecutor.set(executor);
Analyzer previousAnalyzer = mSubscribedAnalyzer.getAndSet(analyzer);
if (previousAnalyzer == null && analyzer != null) {
notifyActive();
- } else if (previousAnalyzer != null && analyzer == null) {
- notifyInactive();
}
}
@@ -385,8 +381,8 @@
/**
* Interface for analyzing images.
*
- * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Analyzer)} to receive
- * images and perform custom processing by implementing the
+ * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Executor, Analyzer)}
+ * to receive images and perform custom processing by implementing the
* {@link ImageAnalysis.Analyzer#analyze(ImageProxy, int)} function.
*/
public interface Analyzer {
@@ -428,7 +424,6 @@
public static final class Defaults implements ConfigProvider<ImageAnalysisConfig> {
private static final ImageReaderMode DEFAULT_IMAGE_READER_MODE =
ImageReaderMode.ACQUIRE_LATEST_IMAGE;
- private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
private static final int DEFAULT_IMAGE_QUEUE_DEPTH = 6;
private static final Size DEFAULT_TARGET_RESOLUTION = new Size(640, 480);
private static final Size DEFAULT_MAX_RESOLUTION = new Size(1920, 1080);
@@ -440,9 +435,8 @@
ImageAnalysisConfig.Builder builder =
new ImageAnalysisConfig.Builder()
.setImageReaderMode(DEFAULT_IMAGE_READER_MODE)
- .setCallbackHandler(DEFAULT_HANDLER)
.setImageQueueDepth(DEFAULT_IMAGE_QUEUE_DEPTH)
- .setTargetResolution(DEFAULT_TARGET_RESOLUTION)
+ .setDefaultResolution(DEFAULT_TARGET_RESOLUTION)
.setMaxResolution(DEFAULT_MAX_RESOLUTION)
.setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
index 74b18c4..6d1a3c3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
@@ -16,8 +16,7 @@
package androidx.camera.core;
-import android.os.Handler;
-
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -34,16 +33,16 @@
// Member variables from ImageAnalysis.
private final AtomicReference<ImageAnalysis.Analyzer> mSubscribedAnalyzer;
private final AtomicInteger mRelativeRotation;
- final Handler mUserHandler;
+ final AtomicReference<Executor> mUserExecutor;
// Flag that reflects the state of ImageAnalysis.
private AtomicBoolean mIsClosed;
ImageAnalysisAbstractAnalyzer(AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
- AtomicInteger relativeRotation, Handler userHandler) {
+ AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor) {
mSubscribedAnalyzer = subscribedAnalyzer;
mRelativeRotation = relativeRotation;
- mUserHandler = userHandler;
+ mUserExecutor = userExecutor;
mIsClosed = new AtomicBoolean(false);
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
index 1283f8d..43e3a65 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
@@ -16,8 +16,7 @@
package androidx.camera.core;
-import android.os.Handler;
-
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -30,8 +29,8 @@
ImageAnalysisBlockingAnalyzer(
AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
- AtomicInteger relativeRotation, Handler userHandler) {
- super(subscribedAnalyzer, relativeRotation, userHandler);
+ AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor) {
+ super(subscribedAnalyzer, relativeRotation, userExecutor);
}
@Override
@@ -40,18 +39,23 @@
if (image == null) {
return;
}
- try {
- mUserHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- analyzeImage(image);
- } finally {
- image.close();
+ Executor executor = mUserExecutor.get();
+ if (executor != null) {
+ try {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ analyzeImage(image);
+ } finally {
+ image.close();
+ }
}
- }
- });
- } catch (RuntimeException e) {
+ });
+ } catch (RuntimeException e) {
+ image.close();
+ }
+ } else {
image.close();
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
index 6018d40..310952c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
@@ -17,7 +17,7 @@
package androidx.camera.core;
import android.media.ImageReader;
-import android.os.Handler;
+import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
@@ -28,6 +28,7 @@
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.core.ImageAnalysis.ImageReaderMode;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -284,11 +285,13 @@
* @param valueIfMissing The value to return if this configuration option has not been set.
* @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
* configuration.
+ * @hide
*/
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@Nullable
- public Rational getTargetAspectRatio(@Nullable Rational valueIfMissing) {
- return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+ public Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing) {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, valueIfMissing);
}
/**
@@ -300,10 +303,37 @@
*
* @return The stored value, if it exists in this configuration.
* @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
*/
- @Override
@NonNull
- public Rational getTargetAspectRatio() {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Rational getTargetAspectRatioCustom() {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM);
+ }
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ */
+ @Nullable
+ @Override
+ public AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing) {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+ }
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ */
+ @NonNull
+ @Override
+ public AspectRatio getTargetAspectRatio() {
return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
}
@@ -365,6 +395,35 @@
return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
}
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ * @hide
+ */
+ @Nullable
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Size getDefaultResolution(@Nullable Size valueIfMissing) {
+ return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION, valueIfMissing);
+ }
+
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
+ */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Size getDefaultResolution() {
+ return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION);
+ }
+
/** @hide */
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
@@ -381,33 +440,25 @@
return retrieveOption(OPTION_MAX_RESOLUTION);
}
- // Implementations of ThreadConfig default methods
-
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @param valueIfMissing The value to return if this configuration option has not been set.
- * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
- * configuration.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@Nullable
- public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
- return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
+ public List<Pair<Integer, Size[]>> getSupportedResolutions(
+ @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
+ return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
}
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @return The stored value, if it exists in this configuration.
- * @throws IllegalArgumentException if the option does not exist in this configuration.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@NonNull
- public Handler getCallbackHandler() {
- return retrieveOption(OPTION_CALLBACK_HANDLER);
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
}
+ // Implementations of ThreadConfig default methods
+
/**
* Returns the executor that will be used for background tasks.
*
@@ -641,6 +692,14 @@
*/
@NonNull
public ImageAnalysisConfig build() {
+ // Error at runtime for using both setTargetResolution and setTargetAspectRatio on
+ // the same config.
+ if (getMutableConfig().retrieveOption(OPTION_TARGET_ASPECT_RATIO, null) != null
+ && getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, null) != null) {
+ throw new IllegalArgumentException(
+ "Cannot use both setTargetResolution and setTargetAspectRatio on the same"
+ + " config.");
+ }
return new ImageAnalysisConfig(OptionsBundle.from(mMutableConfig));
}
@@ -728,16 +787,45 @@
* ratio which may differ from the request, possibly due to device constraints.
* Application code should check the resulting output's resolution.
*
+ * <p>This method can be used to request an aspect ratio that is not from the standard set
+ * of aspect ratios defined in the {@link AspectRatio}.
+ *
+ * <p>This method will remove any value set by setTargetAspectRatio().
+ *
* <p>For ImageAnalysis, the output is the {@link ImageProxy} passed to the analyzer
* function.
*
* @param aspectRatio A {@link Rational} representing the ratio of the target's width and
* height.
* @return The current Builder.
+ * @hide
*/
- @Override
@NonNull
- public Builder setTargetAspectRatio(@NonNull Rational aspectRatio) {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Builder setTargetAspectRatioCustom(@NonNull Rational aspectRatio) {
+ getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, aspectRatio);
+ getMutableConfig().removeOption(OPTION_TARGET_ASPECT_RATIO);
+ return this;
+ }
+
+ /**
+ * Sets the aspect ratio of the intended target for images from this configuration.
+ *
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+ * ratio which may differ from the request, possibly due to device constraints.
+ * Application code should check the resulting output's resolution.
+ *
+ * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+ * target's width and height.
+ * @return The current Builder.
+ */
+ @NonNull
+ @Override
+ public Builder setTargetAspectRatio(@NonNull AspectRatio aspectRatio) {
getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
return this;
}
@@ -752,8 +840,8 @@
* @param rotation The rotation of the intended target.
* @return The current Builder.
*/
- @Override
@NonNull
+ @Override
public Builder setTargetRotation(@RotationValue int rotation) {
getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
return this;
@@ -768,14 +856,49 @@
* if no resolution exists that is equal to or larger than the target resolution, the
* nearest available resolution smaller than the target resolution will be chosen.
*
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+ * {@link Size}. Make sure to set the target resolution with the correct orientation.
+ *
* @param resolution The target resolution to choose from supported output sizes list.
* @return The current Builder.
*/
- @Override
@NonNull
+ @Override
public Builder setTargetResolution(@NonNull Size resolution) {
getMutableConfig()
.insertOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION, resolution);
+ if (resolution != null) {
+ getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM,
+ new Rational(resolution.getWidth(), resolution.getHeight()));
+ }
+ return this;
+ }
+
+ /**
+ * Sets the default resolution of the intended target from this configuration.
+ *
+ * @param resolution The default resolution to choose from supported output sizes list.
+ * @return The current Builder.
+ * @hide
+ */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Builder setDefaultResolution(@NonNull Size resolution) {
+ getMutableConfig().insertOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION,
+ resolution);
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Builder setMaxResolution(@NonNull Size resolution) {
+ getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
return this;
}
@@ -783,27 +906,14 @@
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
@NonNull
- public Builder setMaxResolution(@NonNull Size resolution) {
- getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
+ public Builder setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutions) {
+ getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
return this;
}
// Implementations of ThreadConfig.Builder default methods
/**
- * Sets the default handler that will be used for callbacks.
- *
- * @param handler The handler which will be used to post callbacks.
- * @return the current Builder.
- */
- @Override
- @NonNull
- public Builder setCallbackHandler(@NonNull Handler handler) {
- getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
- return this;
- }
-
- /**
* Sets the default executor that will be used for background tasks.
*
* @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
index 0583b38..71a6232 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
@@ -16,7 +16,6 @@
package androidx.camera.core;
-import android.os.Handler;
import android.util.Log;
import androidx.annotation.GuardedBy;
@@ -52,8 +51,9 @@
private final AtomicLong mFinishedImageTimestamp;
ImageAnalysisNonBlockingAnalyzer(AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
- AtomicInteger relativeRotation, Handler userHandler, Executor executor) {
- super(subscribedAnalyzer, relativeRotation, userHandler);
+ AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor,
+ Executor executor) {
+ super(subscribedAnalyzer, relativeRotation, userExecutor);
mBackgroundExecutor = executor;
mPostedImageTimestamp = new AtomicLong();
mFinishedImageTimestamp = new AtomicLong();
@@ -128,26 +128,31 @@
}
mPostedImageTimestamp.set(imageProxy.getTimestamp());
- try {
- mUserHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- analyzeImage(imageProxy);
- } finally {
- finishImage(imageProxy);
- mBackgroundExecutor.execute(new Runnable() {
- @Override
- public void run() {
- analyzeCachedImage();
- }
- });
+ Executor executor = mUserExecutor.get();
+ if (executor != null) {
+ try {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ analyzeImage(imageProxy);
+ } finally {
+ finishImage(imageProxy);
+ mBackgroundExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ analyzeCachedImage();
+ }
+ });
+ }
}
- }
- });
- } catch (RuntimeException e) {
- // Unblock if fails to post to user thread.
- Log.e(TAG, "Error calling user callback", e);
+ });
+ } catch (RuntimeException e) {
+ // Unblock if fails to post to user thread.
+ Log.e(TAG, "Error calling user callback", e);
+ finishImage(imageProxy);
+ }
+ } else {
finishImage(imageProxy);
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 5e775dbe..dd76501 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -22,6 +22,7 @@
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
@@ -55,12 +56,13 @@
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
-import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -101,10 +103,13 @@
private static final Metadata EMPTY_METADATA = new Metadata();
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ @Nullable
+ private HandlerThread mProcessingImageResultThread;
+ @Nullable
+ private Handler mProcessingImageResultHandler;
+
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
- final ArrayDeque<ImageCaptureRequest> mImageCaptureRequests = new ArrayDeque<>();
- @SuppressWarnings("WeakerAccess") /* synthetic accessor */
- final Handler mHandler;
+ final Deque<ImageCaptureRequest> mImageCaptureRequests = new ConcurrentLinkedDeque<>();
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
SessionConfig.Builder mSessionConfigBuilder;
private final CaptureConfig mCaptureConfig;
@@ -197,11 +202,6 @@
mEnableCheck3AConverged = false; // skip 3A convergence in MIN_LATENCY mode
}
- mHandler = mConfig.getCallbackHandler(null);
- if (mHandler == null) {
- throw new IllegalStateException("No default handler specified.");
- }
-
CaptureConfig.Builder captureBuilder = CaptureConfig.Builder.createFrom(mConfig);
mCaptureConfig = captureBuilder.build();
}
@@ -212,20 +212,27 @@
SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config);
sessionConfigBuilder.addRepeatingCameraCaptureCallback(mSessionCallbackChecker);
+ mProcessingImageResultThread = new HandlerThread("OnImageAvailableHandlerThread");
+ mProcessingImageResultThread.start();
+ mProcessingImageResultHandler = new Handler(mProcessingImageResultThread.getLooper());
+
// Setup the ImageReader to do processing
if (mCaptureProcessor != null) {
+ // TODO: To allow user to use an Executor for the image processing.
ProcessingImageReader processingImageReader =
new ProcessingImageReader(
resolution.getWidth(),
resolution.getHeight(),
getImageFormat(), mMaxCaptureStages,
- mHandler, getCaptureBundle(CaptureBundles.singleDefaultCaptureBundle()),
+ mProcessingImageResultHandler,
+ getCaptureBundle(CaptureBundles.singleDefaultCaptureBundle()),
mCaptureProcessor);
mMetadataMatchingCaptureCallback = processingImageReader.getCameraCaptureCallback();
mImageReader = processingImageReader;
} else {
MetadataImageReader metadataImageReader = new MetadataImageReader(resolution.getWidth(),
- resolution.getHeight(), getImageFormat(), MAX_IMAGES, mHandler);
+ resolution.getHeight(), getImageFormat(), MAX_IMAGES,
+ mProcessingImageResultHandler);
mMetadataMatchingCaptureCallback = metadataImageReader.getCameraCaptureCallback();
mImageReader = metadataImageReader;
}
@@ -256,7 +263,7 @@
}
}
},
- mMainHandler);
+ mProcessingImageResultHandler);
mDeferrableSurface = new ImmediateSurface(mImageReader.getSurface());
sessionConfigBuilder.addNonRepeatingSurface(mDeferrableSurface);
@@ -285,6 +292,7 @@
mDeferrableSurface = null;
ImageReaderProxy imageReaderProxy = mImageReader;
mImageReader = null;
+ HandlerThread handlerThread = mProcessingImageResultThread;
if (deferrableSurface != null) {
deferrableSurface.setOnSurfaceDetachedListener(
@@ -295,6 +303,9 @@
if (imageReaderProxy != null) {
imageReaderProxy.close();
}
+
+ // Close the handlerThread after the ImageReader was closed.
+ handlerThread.quitSafely();
}
});
}
@@ -363,11 +374,11 @@
*
* @param aspectRatio New target aspect ratio.
*/
- public void setTargetAspectRatio(Rational aspectRatio) {
+ public void setTargetAspectRatioCustom(Rational aspectRatio) {
ImageOutputConfig oldConfig = (ImageOutputConfig) getUseCaseConfig();
- Rational oldRatio = oldConfig.getTargetAspectRatio(null);
+ Rational oldRatio = oldConfig.getTargetAspectRatioCustom(null);
if (!aspectRatio.equals(oldRatio)) {
- mUseCaseConfigBuilder.setTargetAspectRatio(aspectRatio);
+ mUseCaseConfigBuilder.setTargetAspectRatioCustom(aspectRatio);
updateUseCaseConfig(mUseCaseConfigBuilder.build());
mConfig = (ImageCaptureConfig) getUseCaseConfig();
@@ -577,7 +588,7 @@
Log.e(TAG, "Unable to retrieve camera sensor orientation.", e);
}
- Rational targetRatio = mConfig.getTargetAspectRatio(null);
+ Rational targetRatio = mConfig.getTargetAspectRatioCustom(null);
targetRatio = ImageUtil.rotate(targetRatio, relativeRotation);
mImageCaptureRequests.offer(
@@ -1160,7 +1171,6 @@
implements ConfigProvider<ImageCaptureConfig> {
private static final CaptureMode DEFAULT_CAPTURE_MODE = CaptureMode.MIN_LATENCY;
private static final FlashMode DEFAULT_FLASH_MODE = FlashMode.OFF;
- private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 4;
private static final ImageCaptureConfig DEFAULT_CONFIG;
@@ -1170,7 +1180,6 @@
new ImageCaptureConfig.Builder()
.setCaptureMode(DEFAULT_CAPTURE_MODE)
.setFlashMode(DEFAULT_FLASH_MODE)
- .setCallbackHandler(DEFAULT_HANDLER)
.setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY);
DEFAULT_CONFIG = builder.build();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
index 140f23d..1192c7e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
@@ -17,7 +17,7 @@
package androidx.camera.core;
import android.graphics.ImageFormat;
-import android.os.Handler;
+import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
@@ -28,6 +28,7 @@
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.core.ImageCapture.CaptureMode;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -389,11 +390,13 @@
* @param valueIfMissing The value to return if this configuration option has not been set.
* @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
* configuration.
+ * @hide
*/
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@Nullable
- public Rational getTargetAspectRatio(@Nullable Rational valueIfMissing) {
- return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+ public Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing) {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, valueIfMissing);
}
/**
@@ -405,10 +408,37 @@
*
* @return The stored value, if it exists in this configuration.
* @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
*/
- @Override
@NonNull
- public Rational getTargetAspectRatio() {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Rational getTargetAspectRatioCustom() {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM);
+ }
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ */
+ @Nullable
+ @Override
+ public AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing) {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+ }
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ */
+ @NonNull
+ @Override
+ public AspectRatio getTargetAspectRatio() {
return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
}
@@ -470,6 +500,35 @@
return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
}
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ * @hide
+ */
+ @Nullable
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Size getDefaultResolution(@Nullable Size valueIfMissing) {
+ return retrieveOption(OPTION_DEFAULT_RESOLUTION, valueIfMissing);
+ }
+
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
+ */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Size getDefaultResolution() {
+ return retrieveOption(OPTION_DEFAULT_RESOLUTION);
+ }
+
/** @hide */
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
@@ -486,33 +545,25 @@
return retrieveOption(OPTION_MAX_RESOLUTION);
}
- // Implementations of ThreadConfig default methods
-
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @param valueIfMissing The value to return if this configuration option has not been set.
- * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
- * configuration.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@Nullable
- public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
- return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
+ public List<Pair<Integer, Size[]>> getSupportedResolutions(
+ @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
+ return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
}
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @return The stored value, if it exists in this configuration.
- * @throws IllegalArgumentException if the option does not exist in this configuration.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@NonNull
- public Handler getCallbackHandler() {
- return retrieveOption(OPTION_CALLBACK_HANDLER);
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
}
+ // Implementations of ThreadConfig default methods
+
/**
* Returns the executor that will be used for background tasks.
*
@@ -699,6 +750,14 @@
*/
@NonNull
public ImageCaptureConfig build() {
+ // Error at runtime for using both setTargetResolution and setTargetAspectRatio on
+ // the same config.
+ if (getMutableConfig().retrieveOption(OPTION_TARGET_ASPECT_RATIO, null) != null
+ && getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, null) != null) {
+ throw new IllegalArgumentException(
+ "Cannot use both setTargetResolution and setTargetAspectRatio on the same "
+ + "config.");
+ }
return new ImageCaptureConfig(OptionsBundle.from(mMutableConfig));
}
@@ -793,6 +852,15 @@
return this;
}
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ @NonNull
+ public Builder setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutions) {
+ getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
+ return this;
+ }
+
// Implementations of TargetConfig.Builder default methods
/** @hide */
@@ -877,6 +945,11 @@
* ratio which may differ from the request, possibly due to device constraints.
* Application code should check the resulting output's resolution.
*
+ * <p>This method can be used to request an aspect ratio that is not from the standard set
+ * of aspect ratios defined in the {@link AspectRatio}.
+ *
+ * <p>This method will remove any value set by setTargetAspectRatio().
+ *
* <p>For ImageCapture, the outputs are the {@link ImageProxy} or the File passed to image
* capture listeners.
*
@@ -884,9 +957,31 @@
* height.
* @return The current Builder.
*/
- @Override
@NonNull
- public Builder setTargetAspectRatio(@NonNull Rational aspectRatio) {
+ @Override
+ public Builder setTargetAspectRatioCustom(@NonNull Rational aspectRatio) {
+ getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, aspectRatio);
+ getMutableConfig().removeOption(OPTION_TARGET_ASPECT_RATIO);
+ return this;
+ }
+
+ /**
+ * Sets the aspect ratio of the intended target for images from this configuration.
+ *
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+ * ratio which may differ from the request, possibly due to device constraints.
+ * Application code should check the resulting output's resolution.
+ *
+ * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+ * target's width and height.
+ * @return The current Builder.
+ */
+ @NonNull
+ @Override
+ public Builder setTargetAspectRatio(@NonNull AspectRatio aspectRatio) {
getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
return this;
}
@@ -901,8 +996,8 @@
* @param rotation The rotation of the intended target.
* @return The current Builder.
*/
- @Override
@NonNull
+ @Override
public Builder setTargetRotation(@RotationValue int rotation) {
getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
return this;
@@ -917,20 +1012,46 @@
* if no resolution exists that is equal to or larger than the target resolution, the
* nearest available resolution smaller than the target resolution will be chosen.
*
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+ * {@link Size}. Make sure to set the target resolution with the correct orientation.
+ *
* @param resolution The target resolution to choose from supported output sizes list.
* @return The current Builder.
*/
- @Override
@NonNull
+ @Override
public Builder setTargetResolution(@NonNull Size resolution) {
getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION, resolution);
+ if (resolution != null) {
+ getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM,
+ new Rational(resolution.getWidth(), resolution.getHeight()));
+ }
+ return this;
+ }
+
+ /**
+ * Sets the default resolution of the intended target from this configuration.
+ *
+ * @param resolution The default resolution to choose from supported output sizes list.
+ * @return The current Builder.
+ * @hide
+ */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Builder setDefaultResolution(@NonNull Size resolution) {
+ getMutableConfig().insertOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION,
+ resolution);
return this;
}
/** @hide */
+ @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
- @NonNull
public Builder setMaxResolution(@NonNull Size resolution) {
getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
return this;
@@ -939,19 +1060,6 @@
// Implementations of ThreadConfig.Builder default methods
/**
- * Sets the default handler that will be used for callbacks.
- *
- * @param handler The handler which will be used to post callbacks.
- * @return the current Builder.
- */
- @Override
- @NonNull
- public Builder setCallbackHandler(@NonNull Handler handler) {
- getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
- return this;
- }
-
- /**
* Sets the default executor that will be used for background tasks.
*
* @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageOutputConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageOutputConfig.java
index 9e5f150..9067ee0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageOutputConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageOutputConfig.java
@@ -16,6 +16,8 @@
package androidx.camera.core;
+import android.graphics.ImageFormat;
+import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
@@ -29,6 +31,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* Configuration containing options for configuring the output image data of a pipeline.
@@ -49,13 +52,23 @@
// *********************************************************************************************
/**
+ * Option: camerax.core.imageOutput.targetAspectRatioCustom
+ *
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ Option<Rational> OPTION_TARGET_ASPECT_RATIO_CUSTOM =
+ Option.create("camerax.core.imageOutput.targetAspectRatioCustom", Rational.class);
+
+ /**
* Option: camerax.core.imageOutput.targetAspectRatio
*
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
- Option<Rational> OPTION_TARGET_ASPECT_RATIO =
- Option.create("camerax.core.imageOutput.targetAspectRatio", Rational.class);
+ Option<AspectRatio> OPTION_TARGET_ASPECT_RATIO =
+ Option.create("camerax.core.imageOutput.targetAspectRatio", AspectRatio.class);
+
/**
* Option: camerax.core.imageOutput.targetRotation
*
@@ -73,6 +86,14 @@
Option<Size> OPTION_TARGET_RESOLUTION =
Option.create("camerax.core.imageOutput.targetResolution", Size.class);
/**
+ * Option: camerax.core.imageOutput.defaultResolution
+ *
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ Option<Size> OPTION_DEFAULT_RESOLUTION =
+ Option.create("camerax.core.imageOutput.defaultResolution", Size.class);
+ /**
* Option: camerax.core.imageOutput.maxResolution
*
* @hide
@@ -80,6 +101,14 @@
@RestrictTo(Scope.LIBRARY_GROUP)
Option<Size> OPTION_MAX_RESOLUTION =
Option.create("camerax.core.imageOutput.maxResolution", Size.class);
+ /**
+ * Option: camerax.core.imageOutput.supportedResolutions
+ *
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ Option<List<Pair<Integer, Size[]>>> OPTION_SUPPORTED_RESOLUTIONS =
+ Option.create("camerax.core.imageOutput.supportedResolutions", List.class);
// *********************************************************************************************
@@ -93,9 +122,11 @@
* @param valueIfMissing The value to return if this configuration option has not been set.
* @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
* configuration.
+ * @hide
*/
@Nullable
- Rational getTargetAspectRatio(@Nullable Rational valueIfMissing);
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing);
/**
* Retrieves the aspect ratio of the target intending to use images from this configuration.
@@ -106,9 +137,30 @@
*
* @return The stored value, if it exists in this configuration.
* @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
*/
@NonNull
- Rational getTargetAspectRatio();
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ Rational getTargetAspectRatioCustom();
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ */
+ @Nullable
+ AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing);
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ */
+ @NonNull
+ AspectRatio getTargetAspectRatio();
/**
* Retrieves the rotation of the target intending to use images from this configuration.
@@ -161,6 +213,29 @@
Size getTargetResolution();
/**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Nullable
+ Size getDefaultResolution(@Nullable Size valueIfMissing);
+
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ Size getDefaultResolution();
+
+ /**
* Retrieves the max resolution limitation of the target intending to use from this
* configuration.
*
@@ -186,6 +261,38 @@
Size getMaxResolution();
/**
+ * Retrieves the supported resolutions can be used by the target from this configuration.
+ *
+ * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The returned
+ * {@link Size} array should be subset of the complete supported sizes list for the camera
+ * device.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Nullable
+ List<Pair<Integer, Size[]>> getSupportedResolutions(
+ @Nullable List<Pair<Integer, Size[]>> valueIfMissing);
+
+ /**
+ * Retrieves the supported resolutions can be used by the target from this configuration.
+ *
+ * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The returned
+ * {@link Size} array should be subset of the complete supported sizes list for the camera
+ * device.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ List<Pair<Integer, Size[]>> getSupportedResolutions();
+
+ /**
* Builder for a {@link ImageOutputConfig}.
*
* @param <B> The top level builder type for which this builder is composed with.
@@ -201,12 +308,32 @@
* the provided {@link Rational} corresponds to the width, and the denominator corresponds
* to the height.
*
+ * <p>This method can be used to request an aspect ratio that is not from the standard set
+ * of aspect ratios defined in the {@link AspectRatio}.
+ *
+ * <p>This method will remove any value set by setTargetAspectRatio().
+ *
* @param aspectRatio A {@link Rational} representing the ratio of the target's width and
* height.
* @return The current Builder.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ B setTargetAspectRatioCustom(@NonNull Rational aspectRatio);
+
+ /**
+ * Sets the aspect ratio of the intended target for images from this configuration.
+ *
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+ * target's width and height.
+ * @return The current Builder.
*/
@NonNull
- B setTargetAspectRatio(@NonNull Rational aspectRatio);
+ B setTargetAspectRatio(@NonNull AspectRatio aspectRatio);
/**
* Sets the rotation of the intended target for images from this configuration.
@@ -224,6 +351,12 @@
/**
* Sets the resolution of the intended target from this configuration.
*
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+ * {@link Size}. Make sure to set the target resolution with the correct orientation.
+ *
* @param resolution The target resolution to choose from supported output sizes list.
* @return The current Builder.
* @hide
@@ -233,6 +366,17 @@
B setTargetResolution(@NonNull Size resolution);
/**
+ * Sets the default resolution of the intended target from this configuration.
+ *
+ * @param resolution The default resolution to choose from supported output sizes list.
+ * @return The current Builder.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ B setDefaultResolution(@NonNull Size resolution);
+
+ /**
* Sets the max resolution limitation of the intended target from this configuration.
*
* @param resolution The max resolution limitation to choose from supported output sizes
@@ -243,6 +387,21 @@
@RestrictTo(Scope.LIBRARY_GROUP)
@NonNull
B setMaxResolution(@NonNull Size resolution);
+
+ /**
+ * Sets the supported resolutions can be used by target from this configuration.
+ *
+ * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The
+ * {@link Size} array should be subset of the complete supported sizes list for the camera
+ * device.
+ *
+ * @param resolutionsList The resolutions can be supported for image formats.
+ * @return The current Builder.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ B setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutionsList);
}
/**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 7296f54..82bce2f 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -20,7 +20,7 @@
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerThread;
import android.util.Log;
import android.util.Rational;
import android.util.Size;
@@ -37,6 +37,7 @@
import androidx.camera.core.CameraX.LensFacing;
import androidx.camera.core.ImageOutputConfig.RotationValue;
import androidx.camera.core.impl.utils.Threads;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.core.util.Preconditions;
import com.google.auto.value.AutoValue;
@@ -44,6 +45,8 @@
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
/**
* A use case that provides a camera preview stream for displaying on-screen.
@@ -82,7 +85,10 @@
private static final String TAG = "Preview";
private static final String CONFLICTING_SURFACE_API_ERROR_MESSAGE =
"PreviewSurfaceCallback cannot be used with OnPreviewOutputUpdateListener.";
- private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ @Nullable
+ private HandlerThread mProcessingPreviewThread;
+ @Nullable
+ private Handler mProcessingPreviewHandler;
private final PreviewConfig.Builder mUseCaseConfigBuilder;
@Nullable
@@ -97,6 +103,8 @@
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
SurfaceTextureHolder mSurfaceTextureHolder;
+ private Executor mOutputUpdateExecutor;
+
/**
* Creates a new preview use case from the given configuration.
*
@@ -116,13 +124,20 @@
final CaptureProcessor captureProcessor = config.getCaptureProcessor(null);
if (captureProcessor != null) {
CaptureStage captureStage = new CaptureStage.DefaultCaptureStage();
+ // TODO: To allow user to use an Executor for the processing.
+
+ if (mProcessingPreviewHandler == null) {
+ mProcessingPreviewThread = new HandlerThread("ProcessingSurfaceTexture");
+ mProcessingPreviewThread.start();
+ mProcessingPreviewHandler = new Handler(mProcessingPreviewThread.getLooper());
+ }
ProcessingSurfaceTexture processingSurfaceTexture =
new ProcessingSurfaceTexture(
resolution.getWidth(),
resolution.getHeight(),
ImageFormat.YUV_420_888,
- config.getCallbackHandler(),
+ mProcessingPreviewHandler,
captureStage,
captureProcessor);
@@ -182,19 +197,31 @@
if (surfaceTextureHolder != null) {
surfaceTextureHolder.release();
}
+
+ // The handler no longer used by the ProcessingSurfaceTexture, we can close the
+ // handlerTread after the ProcessingSurfaceTexture was released.
+ if (mProcessingPreviewHandler != null) {
+ mProcessingPreviewThread.quitSafely();
+ mProcessingPreviewThread = null;
+ mProcessingPreviewHandler = null;
+ }
}
/**
- * Removes previously PreviewOutput listener.
- *
- * <p>This is equivalent to calling {@code setOnPreviewOutputUpdateListener(null)}.
+ * Un-register a listener previously registered via
+ * {@link Preview#setOnPreviewOutputUpdateListener(OnPreviewOutputUpdateListener)}.
+ * It will signal to the camera that the camera should no longer stream data to the last
+ * {@link PreviewOutput}.
*
* @throws IllegalStateException If not called on main thread.
*/
@UiThread
public void removePreviewOutputListener() {
Threads.checkMainThread();
- setOnPreviewOutputUpdateListener(null);
+ if (mSubscribedPreviewOutputListener != null) {
+ mSubscribedPreviewOutputListener = null;
+ notifyInactive();
+ }
}
/**
@@ -214,8 +241,7 @@
* Sets a listener to get the {@link PreviewOutput} updates.
*
* <p>Setting this listener will signal to the camera that the use case is ready to receive
- * data. Setting the listener to {@code null} will signal to the camera that the camera should
- * no longer stream data to the last {@link PreviewOutput}.
+ * data.
*
* <p>Once {@link OnPreviewOutputUpdateListener#onUpdated(PreviewOutput)} is called,
* ownership of the {@link PreviewOutput} and its contents is transferred to the application. It
@@ -239,25 +265,44 @@
* with the output from the previous {@link PreviewOutput} to attach it to a new TextureView,
* such as on resuming the application.
*
+ * <p>The listener will run on the UI thread. See
+ * {@link Preview#setOnPreviewOutputUpdateListener(Executor, OnPreviewOutputUpdateListener)}
+ * to set the updates run on the given executor.
+ *
+ * @param newListener The listener which will receive {@link PreviewOutput} updates.
+ */
+ @UiThread
+ public void setOnPreviewOutputUpdateListener(
+ @NonNull OnPreviewOutputUpdateListener newListener) {
+ setOnPreviewOutputUpdateListener(CameraXExecutors.mainThreadExecutor(), newListener);
+ }
+
+ /**
+ * Sets a listener and its executor to get the {@link PreviewOutput} updates.
+ *
+ * <p>See {@link Preview#setOnPreviewOutputUpdateListener(OnPreviewOutputUpdateListener)} for
+ * more information.
+ *
+ * @param executor The executor on which the listener should be invoked.
* @param newListener The listener which will receive {@link PreviewOutput} updates.
* @throws IllegalStateException If not called on main thread.
*/
@UiThread
public void setOnPreviewOutputUpdateListener(
- @Nullable OnPreviewOutputUpdateListener newListener) {
+ @NonNull /* @CallbackExecutor */ Executor executor,
+ @NonNull OnPreviewOutputUpdateListener newListener) {
Threads.checkMainThread();
Preconditions.checkState(mPreviewSurfaceCallback == null,
CONFLICTING_SURFACE_API_ERROR_MESSAGE);
+ mOutputUpdateExecutor = executor;
OnPreviewOutputUpdateListener oldListener = mSubscribedPreviewOutputListener;
mSubscribedPreviewOutputListener = newListener;
if (oldListener == null && newListener != null) {
notifyActive();
if (mLatestPreviewOutput != null) {
mSurfaceDispatched = true;
- newListener.onUpdated(mLatestPreviewOutput);
+ updateListener(newListener, mLatestPreviewOutput);
}
- } else if (oldListener != null && newListener == null) {
- notifyInactive();
} else if (oldListener != null && oldListener != newListener) {
if (mLatestPreviewOutput != null) {
PreviewConfig config = (PreviewConfig) getUseCaseConfig();
@@ -318,6 +363,14 @@
updateOutput(mSurfaceTextureHolder.getSurfaceTexture(), resolution);
}
+ private void updateListener(OnPreviewOutputUpdateListener listener, PreviewOutput output) {
+ try {
+ mOutputUpdateExecutor.execute(() -> listener.onUpdated(output));
+ } catch (RejectedExecutionException e) {
+ Log.e(TAG, "Unable to post to the supplied executor.", e);
+ }
+ }
+
private CameraControlInternal getCurrentCameraControl() {
PreviewConfig config = (PreviewConfig) getUseCaseConfig();
String cameraId = getCameraIdUnchecked(config);
@@ -423,7 +476,7 @@
if (CameraX.getSurfaceManager().requiresCorrectedAspectRatio(config)) {
Rational resultRatio = CameraX.getSurfaceManager().getCorrectedAspectRatio(config);
PreviewConfig.Builder configBuilder = PreviewConfig.Builder.fromConfig(config);
- configBuilder.setTargetAspectRatio(resultRatio);
+ configBuilder.setTargetAspectRatioCustom(resultRatio);
config = configBuilder.build();
}
super.updateUseCaseConfig(config);
@@ -530,7 +583,7 @@
if (outputListener != null) {
mSurfaceDispatched = true;
- outputListener.onUpdated(newOutput);
+ updateListener(outputListener, newOutput);
}
}
}
@@ -584,7 +637,6 @@
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public static final class Defaults implements ConfigProvider<PreviewConfig> {
- private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
private static final Size DEFAULT_MAX_RESOLUTION =
CameraX.getSurfaceManager().getPreviewSize();
private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 2;
@@ -594,7 +646,6 @@
static {
PreviewConfig.Builder builder =
new PreviewConfig.Builder()
- .setCallbackHandler(DEFAULT_HANDLER)
.setMaxResolution(DEFAULT_MAX_RESOLUTION)
.setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY);
DEFAULT_CONFIG = builder.build();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
index 8ced57d..4f70179 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
@@ -16,7 +16,7 @@
package androidx.camera.core;
-import android.os.Handler;
+import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
@@ -26,6 +26,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -225,11 +226,13 @@
* @param valueIfMissing The value to return if this configuration option has not been set.
* @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
* configuration.
+ * @hide
*/
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@Nullable
- public Rational getTargetAspectRatio(@Nullable Rational valueIfMissing) {
- return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+ public Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing) {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, valueIfMissing);
}
/**
@@ -241,10 +244,37 @@
*
* @return The stored value, if it exists in this configuration.
* @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
*/
- @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
@NonNull
- public Rational getTargetAspectRatio() {
+ @Override
+ public Rational getTargetAspectRatioCustom() {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM);
+ }
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ */
+ @Nullable
+ @Override
+ public AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing) {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+ }
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ */
+ @NonNull
+ @Override
+ public AspectRatio getTargetAspectRatio() {
return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
}
@@ -306,6 +336,35 @@
return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
}
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ * @hide
+ */
+ @Nullable
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Size getDefaultResolution(@Nullable Size valueIfMissing) {
+ return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION, valueIfMissing);
+ }
+
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
+ */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Size getDefaultResolution() {
+ return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION);
+ }
+
/** @hide */
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
@@ -322,33 +381,25 @@
return retrieveOption(OPTION_MAX_RESOLUTION);
}
- // Implementations of ThreadConfig default methods
-
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @param valueIfMissing The value to return if this configuration option has not been set.
- * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
- * configuration.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@Nullable
- public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
- return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
+ public List<Pair<Integer, Size[]>> getSupportedResolutions(
+ @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
+ return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
}
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @return The stored value, if it exists in this configuration.
- * @throws IllegalArgumentException if the option does not exist in this configuration.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@NonNull
- public Handler getCallbackHandler() {
- return retrieveOption(OPTION_CALLBACK_HANDLER);
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
}
+ // Implementations of ThreadConfig default methods
+
/**
* Returns the executor that will be used for background tasks.
*
@@ -575,6 +626,14 @@
*/
@NonNull
public PreviewConfig build() {
+ // Error at runtime for using both setTargetResolution and setTargetAspectRatio on
+ // the same config.
+ if (getMutableConfig().retrieveOption(OPTION_TARGET_ASPECT_RATIO, null) != null
+ && getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, null) != null) {
+ throw new IllegalArgumentException(
+ "Cannot use both setTargetResolution and setTargetAspectRatio on the same "
+ + "config.");
+ }
return new PreviewConfig(OptionsBundle.from(mMutableConfig));
}
@@ -662,16 +721,48 @@
* ratio which may differ from the request, possibly due to device constraints.
* Application code should check the resulting output's resolution.
*
+ * <p>This method can be used to request an aspect ratio that is not from the standard set
+ * of aspect ratios defined in the {@link AspectRatio}.
+ *
+ * <p>This method will remove any value set by setTargetAspectRatio().
+ *
* <p>For Preview, the output is the SurfaceTexture of the
* {@link androidx.camera.core.Preview.PreviewOutput}.
*
* @param aspectRatio A {@link Rational} representing the ratio of the target's width and
* height.
* @return The current Builder.
+ * @hide
*/
- @Override
@NonNull
- public Builder setTargetAspectRatio(@NonNull Rational aspectRatio) {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Builder setTargetAspectRatioCustom(@NonNull Rational aspectRatio) {
+ getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, aspectRatio);
+ getMutableConfig().removeOption(OPTION_TARGET_ASPECT_RATIO);
+ return this;
+ }
+
+ /**
+ * Sets the aspect ratio of the intended target for images from this configuration.
+ *
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+ * ratio which may differ from the request, possibly due to device constraints.
+ * Application code should check the resulting output's resolution.
+ *
+ * <p>For Preview, the output is the SurfaceTexture of the
+ * {@link androidx.camera.core.Preview.PreviewOutput}.
+ *
+ * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+ * target's width and height.
+ * @return The current Builder.
+ */
+ @NonNull
+ @Override
+ public Builder setTargetAspectRatio(@NonNull AspectRatio aspectRatio) {
getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
return this;
}
@@ -686,8 +777,8 @@
* @param rotation The rotation of the intended target.
* @return The current Builder.
*/
- @Override
@NonNull
+ @Override
public Builder setTargetRotation(@RotationValue int rotation) {
getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
return this;
@@ -703,14 +794,48 @@
* target resolution, the nearest available resolution smaller than the target resolution
* will be chosen.
*
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+ * {@link Size}. Make sure to set the target resolution with the correct orientation.
+ *
* @param resolution The target resolution to choose from supported output sizes list.
* @return The current Builder.
*/
- @Override
@NonNull
+ @Override
public Builder setTargetResolution(@NonNull Size resolution) {
getMutableConfig()
.insertOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION, resolution);
+ if (resolution != null) {
+ getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM,
+ new Rational(resolution.getWidth(), resolution.getHeight()));
+ }
+ return this;
+ }
+
+ /**
+ * Sets the default resolution of the intended target from this configuration.
+ *
+ * @param resolution The default resolution to choose from supported output sizes list.
+ * @return The current Builder.
+ * @hide
+ */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Builder setDefaultResolution(@NonNull Size resolution) {
+ getMutableConfig().insertOption(OPTION_DEFAULT_RESOLUTION, resolution);
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Builder setMaxResolution(@NonNull Size resolution) {
+ getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
return this;
}
@@ -718,27 +843,14 @@
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
@NonNull
- public Builder setMaxResolution(@NonNull Size resolution) {
- getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
+ public Builder setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutions) {
+ getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
return this;
}
// Implementations of ThreadConfig.Builder default methods
/**
- * Sets the default handler that will be used for callbacks.
- *
- * @param handler The handler which will be used to post callbacks.
- * @return the current Builder.
- */
- @Override
- @NonNull
- public Builder setCallbackHandler(@NonNull Handler handler) {
- getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
- return this;
- }
-
- /**
* Sets the default executor that will be used for background tasks.
*
* @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
index 6744bd4..57b5262 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
@@ -18,6 +18,7 @@
import android.graphics.PixelFormat;
import android.graphics.SurfaceTexture;
+import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
@@ -219,7 +220,7 @@
}
}
},
- mImageReaderHandler
+ AsyncTask.THREAD_POOL_EXECUTOR
);
// Need to wait for Surface has been detached before closing it
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
index c8814ec..679d446 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
@@ -16,8 +16,6 @@
package androidx.camera.core;
-import android.os.Handler;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
@@ -38,16 +36,6 @@
// *********************************************************************************************
/**
- * Option: camerax.core.thread.callbackHandler
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY_GROUP)
- Option<Handler> OPTION_CALLBACK_HANDLER =
- Option.create("camerax.core.thread.callbackHandler", Handler.class);
-
-
- /**
* Option: camerax.core.thread.backgroundExecutor
*
* @hide
@@ -59,25 +47,6 @@
// *********************************************************************************************
/**
- * Returns the default handler that will be used for callbacks.
- *
- * @param valueIfMissing The value to return if this configuration option has not been set.
- * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
- * configuration.
- */
- @Nullable
- Handler getCallbackHandler(@Nullable Handler valueIfMissing);
-
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @return The stored value, if it exists in this configuration.
- * @throws IllegalArgumentException if the option does not exist in this configuration.
- */
- @NonNull
- Handler getCallbackHandler();
-
- /**
* Returns the executor that will be used for background tasks.
*
* @param valueIfMissing The value to return if this configuration option has not been set.
@@ -108,15 +77,6 @@
interface Builder<B> {
/**
- * Sets the default handler that will be used for callbacks.
- *
- * @param handler The handler which will be used to post callbacks.
- * @return the current Builder.
- */
- @NonNull
- B setCallbackHandler(@NonNull Handler handler);
-
- /**
* Sets the default executor that will be used for background tasks.
*
* @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
index 136e04f..e30326c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
@@ -921,7 +921,6 @@
static {
VideoCaptureConfig.Builder builder =
new VideoCaptureConfig.Builder()
- .setCallbackHandler(DEFAULT_HANDLER)
.setVideoFrameRate(DEFAULT_VIDEO_FRAME_RATE)
.setBitRate(DEFAULT_BIT_RATE)
.setIFrameInterval(DEFAULT_INTRA_FRAME_INTERVAL)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
index 03acc0b..9a35ee8 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
@@ -16,7 +16,7 @@
package androidx.camera.core;
-import android.os.Handler;
+import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
@@ -26,6 +26,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -428,11 +429,13 @@
* @param valueIfMissing The value to return if this configuration option has not been set.
* @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
* configuration.
+ * @hide
*/
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@Nullable
- public Rational getTargetAspectRatio(@Nullable Rational valueIfMissing) {
- return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+ public Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing) {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, valueIfMissing);
}
/**
@@ -444,10 +447,37 @@
*
* @return The stored value, if it exists in this configuration.
* @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
*/
- @Override
@NonNull
- public Rational getTargetAspectRatio() {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Rational getTargetAspectRatioCustom() {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM);
+ }
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ */
+ @Nullable
+ @Override
+ public AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing) {
+ return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+ }
+
+ /**
+ * Retrieves the aspect ratio of the target intending to use images from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ */
+ @NonNull
+ @Override
+ public AspectRatio getTargetAspectRatio() {
return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
}
@@ -500,6 +530,35 @@
return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
}
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @param valueIfMissing The value to return if this configuration option has not been set.
+ * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+ * configuration.
+ * @hide
+ */
+ @Nullable
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Size getDefaultResolution(@Nullable Size valueIfMissing) {
+ return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION, valueIfMissing);
+ }
+
+ /**
+ * Retrieves the default resolution of the target intending to use from this configuration.
+ *
+ * @return The stored value, if it exists in this configuration.
+ * @throws IllegalArgumentException if the option does not exist in this configuration.
+ * @hide
+ */
+ @NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Size getDefaultResolution() {
+ return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION);
+ }
+
/** @hide */
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
@@ -516,33 +575,25 @@
return retrieveOption(OPTION_MAX_RESOLUTION);
}
- // Implementations of ThreadConfig default methods
-
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @param valueIfMissing The value to return if this configuration option has not been set.
- * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
- * configuration.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@Nullable
- public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
- return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
+ public List<Pair<Integer, Size[]>> getSupportedResolutions(
+ @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
+ return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
}
- /**
- * Returns the default handler that will be used for callbacks.
- *
- * @return The stored value, if it exists in this configuration.
- * @throws IllegalArgumentException if the option does not exist in this configuration.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@NonNull
- public Handler getCallbackHandler() {
- return retrieveOption(OPTION_CALLBACK_HANDLER);
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
}
+ // Implementations of ThreadConfig default methods
+
/**
* Returns the executor that will be used for background tasks.
*
@@ -729,6 +780,14 @@
*/
@NonNull
public VideoCaptureConfig build() {
+ // Error at runtime for using both setTargetResolution and setTargetAspectRatio on
+ // the same config.
+ if (getMutableConfig().retrieveOption(OPTION_TARGET_ASPECT_RATIO, null) != null
+ && getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, null) != null) {
+ throw new IllegalArgumentException(
+ "Cannot use both setTargetResolution and setTargetAspectRatio on the same "
+ + "config.");
+ }
return new VideoCaptureConfig(OptionsBundle.from(mMutableConfig));
}
@@ -922,15 +981,44 @@
* ratio which may differ from the request, possibly due to device constraints.
* Application code should check the resulting output's resolution.
*
+ * <p>This method can be used to request an aspect ratio that is not from the standard set
+ * of aspect ratios defined in the {@link AspectRatio}.
+ *
+ * <p>This method will remove any value set by setTargetAspectRatio().
+ *
* <p>For VideoCapture the output is the output video file.
*
* @param aspectRatio A {@link Rational} representing the ratio of the target's width and
* height.
* @return The current Builder.
+ * @hide
*/
- @Override
@NonNull
- public Builder setTargetAspectRatio(@NonNull Rational aspectRatio) {
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
+ public Builder setTargetAspectRatioCustom(@NonNull Rational aspectRatio) {
+ getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, aspectRatio);
+ getMutableConfig().removeOption(OPTION_TARGET_ASPECT_RATIO);
+ return this;
+ }
+
+ /**
+ * Sets the aspect ratio of the intended target for images from this configuration.
+ *
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+ * ratio which may differ from the request, possibly due to device constraints.
+ * Application code should check the resulting output's resolution.
+ *
+ * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+ * target's width and height.
+ * @return The current Builder.
+ */
+ @NonNull
+ @Override
+ public Builder setTargetAspectRatio(@NonNull AspectRatio aspectRatio) {
getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
return this;
}
@@ -945,46 +1033,79 @@
* @param rotation The rotation of the intended target.
* @return The current Builder.
*/
- @Override
@NonNull
+ @Override
public Builder setTargetRotation(@RotationValue int rotation) {
getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
return this;
}
- /** @hide */
+ /**
+ * Sets the resolution of the intended target from this configuration.
+ *
+ * <p>The target resolution attempts to establish a minimum bound for the image resolution.
+ * The actual image resolution will be the closest available resolution in size that is not
+ * smaller than the target resolution, as determined by the Camera implementation. However,
+ * if no resolution exists that is equal to or larger than the target resolution, the
+ * nearest available resolution smaller than the target resolution will be chosen.
+ *
+ * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+ * use case.
+ *
+ * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+ * {@link Size}. Make sure to set the target resolution with the correct orientation.
+ *
+ * @param resolution The target resolution to choose from supported output sizes list.
+ * @return The current Builder.
+ * @hide
+ */
+ @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
- @NonNull
public Builder setTargetResolution(@NonNull Size resolution) {
getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION, resolution);
+ if (resolution != null) {
+ getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM,
+ new Rational(resolution.getWidth(), resolution.getHeight()));
+ }
return this;
}
- /** @hide */
+ /**
+ * Sets the default resolution of the intended target from this configuration.
+ *
+ * @param resolution The default resolution to choose from supported output sizes list.
+ * @return The current Builder.
+ * @hide
+ */
+ @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
@Override
+ public Builder setDefaultResolution(@NonNull Size resolution) {
+ getMutableConfig().insertOption(OPTION_DEFAULT_RESOLUTION, resolution);
+ return null;
+ }
+
+ /** @hide */
@NonNull
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @Override
public Builder setMaxResolution(@NonNull Size resolution) {
getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
return this;
}
- // Implementations of ThreadConfig.Builder default methods
-
- /**
- * Sets the default handler that will be used for callbacks.
- *
- * @param handler The handler which will be used to post callbacks.
- * @return the current Builder.
- */
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
@Override
@NonNull
- public Builder setCallbackHandler(@NonNull Handler handler) {
- getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
+ public Builder setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutions) {
+ getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
return this;
}
+ // Implementations of ThreadConfig.Builder default methods
+
/**
* Sets the default executor that will be used for background tasks.
*
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
index b2d6555..1acb499 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
@@ -23,8 +23,6 @@
import static org.mockito.Mockito.when;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.test.filters.SmallTest;
@@ -62,7 +60,7 @@
mImageAnalysisNonBlockingAnalyzer = new ImageAnalysisNonBlockingAnalyzer(
new AtomicReference<ImageAnalysis.Analyzer>(mAnalyzer),
ROTATION,
- new Handler(Looper.getMainLooper()),
+ new AtomicReference<>(CameraXExecutors.mainThreadExecutor()),
CameraXExecutors.directExecutor()
);
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
index cec54b0..0c29520 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -197,18 +197,18 @@
private void setUpImageAnalysisWithMode(ImageAnalysis.ImageReaderMode imageReaderMode) {
mImageAnalysis = new ImageAnalysis(new ImageAnalysisConfig.Builder()
- .setCallbackHandler(mCallbackHandler)
.setBackgroundExecutor(mBackgroundExecutor)
.setImageQueueDepth(QUEUE_DEPTH)
.setImageReaderMode(imageReaderMode)
.build());
- mImageAnalysis.setAnalyzer(new ImageAnalysis.Analyzer() {
- @Override
- public void analyze(ImageProxy image, int rotationDegrees) {
- mImagesReceived.add(image.getImage());
- }
- });
+ mImageAnalysis.setAnalyzer(CameraXExecutors.newHandlerExecutor(mCallbackHandler),
+ new ImageAnalysis.Analyzer() {
+ @Override
+ public void analyze(ImageProxy image, int rotationDegrees) {
+ mImagesReceived.add(image.getImage());
+ }
+ });
Map<String, Size> suggestedResolutionMap = new HashMap<>();
suggestedResolutionMap.put(ShadowCameraX.DEFAULT_CAMERA_ID, DEFAULT_RESOLUTION);
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
index 2bdb75a..1a85283 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
index cddbb31..222c617 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Stub implementation for auto preview use case.
*
@@ -82,4 +86,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
index b08a2a3..e1cac6c 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
index cc04bec..e8afee2 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Stub implementation for beauty preview use case.
*
@@ -82,4 +86,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
index 49cb42c..a76253f 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
index 85b6f00..172ff90 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -17,10 +17,14 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Stub implementation for bokeh preview use case.
*
@@ -80,4 +84,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index 0d7434d..907cb72 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
index 536d51c..f5c2532 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Stub implementation for HDR preview use case.
*
@@ -82,4 +86,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 6c5c124..702e6c0 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -16,7 +16,12 @@
package androidx.camera.extensions.impl;
+import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
import java.util.List;
@@ -57,4 +62,20 @@
* @return the maximum count.
*/
int getMaxCaptureStage();
+
+ /**
+ * Returns the customized supported resolutions.
+ *
+ * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+ *
+ * <p>The returned resolutions should be subset of the supported sizes retrieved from
+ * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
+ * returned list is not null, it will be used to find the best resolutions combination for
+ * the bound use cases.
+ *
+ * @return the customized supported resolutions.
+ * @since 1.1
+ */
+ @Nullable
+ List<Pair<Integer, Size[]>> getSupportedResolutions();
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index f857a48..861a4fd 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
index 5281f65..4d34852 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Stub implementation for night preview use case.
*
@@ -82,4 +86,9 @@
public CaptureStageImpl onDisableSession() {
throw new RuntimeException("Stub, replace with implementation.");
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
index cfe2995..d690da3 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -16,8 +16,15 @@
package androidx.camera.extensions.impl;
+import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
+
+import java.util.List;
/**
* Provides abstract methods that the OEM needs to implement to enable extensions in the preview.
@@ -76,4 +83,20 @@
*
*/
ProcessorImpl getProcessor();
+
+ /**
+ * Returns the customized supported resolutions.
+ *
+ * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+ *
+ * <p>The returned resolutions should be subset of the supported sizes retrieved from
+ * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
+ * returned list is not null, it will be used to find the best resolutions combination for
+ * the bound use cases.
+ *
+ * @return the customized supported resolutions.
+ * @since 1.1
+ */
+ @Nullable
+ List<Pair<Integer, Size[]>> getSupportedResolutions();
}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
index 38ea33d..9977b2c 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
@@ -16,6 +16,8 @@
package androidx.camera.extensions;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
@@ -29,17 +31,22 @@
import android.Manifest;
import android.app.Instrumentation;
import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.util.Pair;
+import android.util.Size;
import androidx.camera.camera2.Camera2AppConfig;
import androidx.camera.camera2.Camera2Config;
import androidx.camera.camera2.impl.CameraEventCallbacks;
+import androidx.camera.core.CameraDeviceConfig;
+import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraX;
import androidx.camera.core.CaptureProcessor;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureConfig;
+import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
import androidx.camera.extensions.impl.CaptureStageImpl;
import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
import androidx.camera.testing.CameraUtil;
@@ -47,6 +54,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;
@@ -58,6 +66,7 @@
import org.mockito.InOrder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -194,6 +203,58 @@
verifyNoMoreInteractions(mMockImageCaptureExtenderImpl);
}
+ @Test
+ @SmallTest
+ public void canSetSupportedResolutionsToConfigTest()
+ throws CameraInfoUnavailableException, CameraAccessException {
+ CameraX.LensFacing lensFacing = CameraX.LensFacing.BACK;
+ assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing));
+ ImageCaptureConfig.Builder configBuilder = new ImageCaptureConfig.Builder().setLensFacing(
+ lensFacing);
+
+ String cameraId = androidx.camera.extensions.CameraUtil.getCameraId(
+ ((CameraDeviceConfig) configBuilder.build()));
+ CameraCharacteristics cameraCharacteristics =
+ CameraUtil.getCameraManager().getCameraCharacteristics(
+ CameraX.getCameraWithLensFacing(lensFacing));
+
+ // Only BeautyImageCaptureExtenderImpl has sample implementation for ImageCapture.
+ // Retrieves the target format/resolutions pair list directly from
+ // BeautyImageCaptureExtenderImpl.
+ BeautyImageCaptureExtenderImpl impl = new BeautyImageCaptureExtenderImpl();
+
+ impl.init(cameraId, cameraCharacteristics);
+ List<Pair<Integer, Size[]>> targetFormatResolutionsPairList =
+ impl.getSupportedResolutions();
+
+ assertThat(targetFormatResolutionsPairList).isNotNull();
+
+ // Retrieves the target format/resolutions pair list from builder after applying beauty
+ // mode.
+ BeautyImageCaptureExtender extender = BeautyImageCaptureExtender.create(configBuilder);
+ assertThat(configBuilder.build().getSupportedResolutions(null)).isNull();
+ extender.enableExtension();
+
+ List<Pair<Integer, Size[]>> resultFormatResolutionsPairList =
+ configBuilder.build().getSupportedResolutions(null);
+
+ assertThat(resultFormatResolutionsPairList).isNotNull();
+
+ // Checks the result and target pair lists are the same
+ for (Pair<Integer, Size[]> resultPair : resultFormatResolutionsPairList) {
+ Size[] targetSizes = null;
+ for (Pair<Integer, Size[]> targetPair : targetFormatResolutionsPairList) {
+ if (targetPair.first.equals(resultPair.first)) {
+ targetSizes = targetPair.second;
+ break;
+ }
+ }
+
+ assertThat(Arrays.asList(resultPair.second).equals(
+ Arrays.asList(targetSizes))).isTrue();
+ }
+ }
+
final class FakeCaptureStage implements CaptureStageImpl {
@Override
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
index 8915480..caa620e 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
@@ -31,16 +31,21 @@
import android.Manifest;
import android.app.Instrumentation;
import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.util.Pair;
+import android.util.Size;
import androidx.camera.camera2.Camera2AppConfig;
+import androidx.camera.core.CameraDeviceConfig;
+import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraX;
import androidx.camera.core.Preview;
import androidx.camera.core.PreviewConfig;
+import androidx.camera.extensions.impl.BeautyPreviewExtenderImpl;
import androidx.camera.extensions.impl.CaptureStageImpl;
import androidx.camera.extensions.impl.PreviewExtenderImpl;
import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
@@ -50,6 +55,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;
@@ -62,6 +68,7 @@
import org.mockito.InOrder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -139,6 +146,7 @@
any(CameraCharacteristics.class));
verify(mockPreviewExtenderImpl, timeout(3000)).getProcessorType();
verify(mockPreviewExtenderImpl, timeout(3000)).getProcessor();
+ verify(mockPreviewExtenderImpl, timeout(3000)).getSupportedResolutions();
InOrder inOrder = inOrder(ignoreStubs(mockPreviewExtenderImpl));
@@ -258,6 +266,57 @@
any(TotalCaptureResult.class));
}
+ @Test
+ @SmallTest
+ public void canSetSupportedResolutionsToConfigTest()
+ throws CameraInfoUnavailableException, CameraAccessException {
+ CameraX.LensFacing lensFacing = CameraX.LensFacing.BACK;
+ assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing));
+ PreviewConfig.Builder configBuilder = new PreviewConfig.Builder().setLensFacing(lensFacing);
+
+ String cameraId = androidx.camera.extensions.CameraUtil.getCameraId(
+ ((CameraDeviceConfig) configBuilder.build()));
+ CameraCharacteristics cameraCharacteristics =
+ CameraUtil.getCameraManager().getCameraCharacteristics(
+ CameraX.getCameraWithLensFacing(lensFacing));
+
+ // Only BeautyPreviewExtenderImpl has sample implementation for Preview.
+ // Retrieves the target format/resolutions pair list directly from
+ // BeautyPreviewExtenderImpl.
+ BeautyPreviewExtenderImpl impl = new BeautyPreviewExtenderImpl();
+
+ impl.init(cameraId, cameraCharacteristics);
+ List<Pair<Integer, Size[]>> targetFormatResolutionsPairList =
+ impl.getSupportedResolutions();
+
+ assertThat(targetFormatResolutionsPairList).isNotNull();
+
+ // Retrieves the target format/resolutions pair list from builder after applying beauty
+ // mode.
+ BeautyPreviewExtender extender = BeautyPreviewExtender.create(configBuilder);
+ assertThat(configBuilder.build().getSupportedResolutions(null)).isNull();
+ extender.enableExtension();
+
+ List<Pair<Integer, Size[]>> resultFormatResolutionsPairList =
+ configBuilder.build().getSupportedResolutions(null);
+
+ assertThat(resultFormatResolutionsPairList).isNotNull();
+
+ // Checks the result and target pair lists are the same
+ for (Pair<Integer, Size[]> resultPair : resultFormatResolutionsPairList) {
+ Size[] targetSizes = null;
+ for (Pair<Integer, Size[]> targetPair : targetFormatResolutionsPairList) {
+ if (targetPair.first.equals(resultPair.first)) {
+ targetSizes = targetPair.second;
+ break;
+ }
+ }
+
+ assertThat(Arrays.asList(resultPair.second).equals(
+ Arrays.asList(targetSizes))).isTrue();
+ }
+ }
+
private class FakePreviewExtender extends PreviewExtender {
FakePreviewExtender(PreviewConfig.Builder builder, PreviewExtenderImpl impl) {
init(builder, impl, ExtensionsManager.EffectMode.NORMAL);
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
index 0880084..7cc2b91 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
@@ -19,6 +19,9 @@
import android.hardware.camera2.CameraCharacteristics;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -51,6 +54,7 @@
* Class for using an OEM provided extension on image capture.
*/
public abstract class ImageCaptureExtender {
+ private static final String TAG = "ImageCaptureExtender";
static final Config.Option<EffectMode> OPTION_IMAGE_CAPTURE_EXTENDER_MODE =
Config.Option.create("camerax.extensions.imageCaptureExtender.mode", EffectMode.class);
@@ -130,6 +134,21 @@
mBuilder.setUseCaseEventListener(imageCaptureAdapter);
mBuilder.setCaptureBundle(imageCaptureAdapter);
mBuilder.getMutableConfig().insertOption(OPTION_IMAGE_CAPTURE_EXTENDER_MODE, mEffectMode);
+ setSupportedResolutions();
+ }
+
+ private void setSupportedResolutions() {
+ List<Pair<Integer, Size[]>> supportedResolutions = null;
+
+ try {
+ supportedResolutions = mImpl.getSupportedResolutions();
+ } catch (NoSuchMethodError e) {
+ Log.e(TAG, "getSupportedResolution interface is not implemented in vendor library.");
+ }
+
+ if (supportedResolutions != null) {
+ mBuilder.setSupportedResolutions(supportedResolutions);
+ }
}
static void checkPreviewEnabled(EffectMode effectMode, Collection<UseCase> activeUseCases) {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
index 3590207..4c1eab1 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
@@ -19,6 +19,9 @@
import android.hardware.camera2.CameraCharacteristics;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -40,12 +43,14 @@
import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
import java.util.Collection;
+import java.util.List;
import java.util.Set;
/**
* Class for using an OEM provided extension on preview.
*/
public abstract class PreviewExtender {
+ private static final String TAG = "PreviewExtender";
static final Config.Option<EffectMode> OPTION_PREVIEW_EXTENDER_MODE = Config.Option.create(
"camerax.extensions.previewExtender.mode", EffectMode.class);
@@ -135,6 +140,21 @@
new CameraEventCallbacks(previewExtenderAdapter));
mBuilder.setUseCaseEventListener(previewExtenderAdapter);
mBuilder.getMutableConfig().insertOption(OPTION_PREVIEW_EXTENDER_MODE, mEffectMode);
+ setSupportedResolutions();
+ }
+
+ private void setSupportedResolutions() {
+ List<Pair<Integer, Size[]>> supportedResolutions = null;
+
+ try {
+ supportedResolutions = mImpl.getSupportedResolutions();
+ } catch (NoSuchMethodError e) {
+ Log.e(TAG, "getSupportedResolution interface is not implemented in vendor library.");
+ }
+
+ if (supportedResolutions != null) {
+ mBuilder.setSupportedResolutions(supportedResolutions);
+ }
}
static void checkImageCaptureEnabled(EffectMode effectMode,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
index 814b924..0216939 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
@@ -104,7 +104,7 @@
@Override
public Rational getCorrectedAspectRatio(@NonNull UseCaseConfig<?> useCaseConfig) {
ImageOutputConfig config = (ImageOutputConfig) useCaseConfig;
- Rational aspectRatio = config.getTargetAspectRatio(null);
+ Rational aspectRatio = config.getTargetAspectRatioCustom(null);
return aspectRatio;
}
}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraXModule.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraXModule.java
index 27f6356..fac0352 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraXModule.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraXModule.java
@@ -34,6 +34,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
import androidx.annotation.UiThread;
+import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraOrientationUtil;
@@ -216,22 +217,18 @@
// Set the preferred aspect ratio as 4:3 if it is IMAGE only mode. Set the preferred aspect
// ratio as 16:9 if it is VIDEO or MIXED mode. Then, it will be WYSIWYG when the view finder
- // is
- // in CENTER_INSIDE mode.
+ // is in CENTER_INSIDE mode.
boolean isDisplayPortrait = getDisplayRotationDegrees() == 0
|| getDisplayRotationDegrees() == 180;
+ Rational targetAspectRatio;
if (getCaptureMode() == CaptureMode.IMAGE) {
- mImageCaptureConfigBuilder.setTargetAspectRatio(
- isDisplayPortrait ? ASPECT_RATIO_3_4 : ASPECT_RATIO_4_3);
- mPreviewConfigBuilder.setTargetAspectRatio(
- isDisplayPortrait ? ASPECT_RATIO_3_4 : ASPECT_RATIO_4_3);
+ mImageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_4_3);
+ targetAspectRatio = isDisplayPortrait ? ASPECT_RATIO_3_4 : ASPECT_RATIO_4_3;
} else {
- mImageCaptureConfigBuilder.setTargetAspectRatio(
- isDisplayPortrait ? ASPECT_RATIO_9_16 : ASPECT_RATIO_16_9);
- mPreviewConfigBuilder.setTargetAspectRatio(
- isDisplayPortrait ? ASPECT_RATIO_9_16 : ASPECT_RATIO_16_9);
+ mImageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+ targetAspectRatio = isDisplayPortrait ? ASPECT_RATIO_9_16 : ASPECT_RATIO_16_9;
}
mImageCaptureConfigBuilder.setTargetRotation(getDisplaySurfaceRotation());
@@ -243,15 +240,9 @@
mVideoCapture = new VideoCapture(mVideoCaptureConfigBuilder.build());
mPreviewConfigBuilder.setLensFacing(mCameraLensFacing);
- int relativeCameraOrientation = getRelativeCameraOrientation(false);
-
- if (relativeCameraOrientation == 90 || relativeCameraOrientation == 270) {
- mPreviewConfigBuilder.setTargetResolution(
- new Size(getMeasuredHeight(), getMeasuredWidth()));
- } else {
- mPreviewConfigBuilder.setTargetResolution(
- new Size(getMeasuredWidth(), getMeasuredHeight()));
- }
+ // Adjusts the preview resolution according to the view size and the target aspect ratio.
+ int height = (int) (getMeasuredWidth() / targetAspectRatio.floatValue());
+ mPreviewConfigBuilder.setTargetResolution(new Size(getMeasuredWidth(), height));
mPreview = new Preview(mPreviewConfigBuilder.build());
mPreview.setOnPreviewOutputUpdateListener(
@@ -605,7 +596,7 @@
// Update view related information used in use cases
private void updateViewInfo() {
if (mImageCapture != null) {
- mImageCapture.setTargetAspectRatio(new Rational(getWidth(), getHeight()));
+ mImageCapture.setTargetAspectRatioCustom(new Rational(getWidth(), getHeight()));
mImageCapture.setTargetRotation(getDisplaySurfaceRotation());
}
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index 00d3d30..e8f9ecc 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -24,8 +24,6 @@
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Log;
@@ -396,7 +394,6 @@
new ImageAnalysisConfig.Builder()
.setLensFacing(mCurrentCameraLensFacing)
.setTargetName("ImageAnalysis")
- .setCallbackHandler(new Handler(Looper.getMainLooper()))
.build();
mImageAnalysis = new ImageAnalysis(config);
@@ -409,6 +406,7 @@
}
mImageAnalysis.setAnalyzer(
+ CameraXExecutors.mainThreadExecutor(),
new ImageAnalysis.Analyzer() {
@Override
public void analyze(ImageProxy image, int rotationDegrees) {
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index aac9f9e..52d9f26 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -23,8 +23,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.StrictMode;
import android.util.Log;
import android.view.TextureView;
@@ -89,8 +87,6 @@
private ImageCapture mImageCapture;
private ImageCaptureType mCurrentImageCaptureType = ImageCaptureType.IMAGE_CAPTURE_TYPE_HDR;
- private HandlerThread mHandlerThread = new HandlerThread("CameraExtensionsActivityHandler");
-
// Espresso testing variables
@VisibleForTesting
CountingIdlingResource mTakePictureIdlingResource = new CountingIdlingResource("TakePicture");
@@ -249,7 +245,6 @@
builder);
if (hdrImageCaptureExtender.isExtensionAvailable()) {
hdrImageCaptureExtender.enableExtension();
- builder.setCallbackHandler(new Handler(mHandlerThread.getLooper()));
}
break;
case IMAGE_CAPTURE_TYPE_BOKEH:
@@ -394,8 +389,6 @@
new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build();
StrictMode.setVmPolicy(policy);
- mHandlerThread.start();
-
// Get params from adb extra string
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
@@ -416,12 +409,6 @@
setupPermissions();
}
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mHandlerThread.quitSafely();
- }
-
private void setupCamera() {
try {
// Wait for permissions before proceeding.
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
index 3d08d35..90b1f20 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -176,4 +176,9 @@
public int getMaxCaptureStage() {
return 3;
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
}
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
index 6992fab..03e4331 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Implementation for auto preview use case.
*
@@ -68,6 +72,11 @@
}
@Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
+
+ @Override
public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
Context context) {
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
index 2c5bbdf..fb33c00 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -16,9 +16,11 @@
package androidx.camera.extensions.impl;
import android.content.Context;
+import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageWriter;
import android.os.Build;
@@ -45,12 +47,14 @@
private static final String TAG = "BeautyICExtender";
private static final int DEFAULT_STAGE_ID = 0;
private static final int SESSION_STAGE_ID = 101;
+ private CameraCharacteristics mCameraCharacteristics;
public BeautyImageCaptureExtenderImpl() {
}
@Override
public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+ mCameraCharacteristics = cameraCharacteristics;
}
@Override
@@ -176,4 +180,30 @@
public int getMaxCaptureStage() {
return 3;
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ List<Pair<Integer, Size[]>> formatResolutionsPairList = new ArrayList<>();
+
+ StreamConfigurationMap map =
+ mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ if (map != null) {
+ // The sample implementation only retrieves originally supported resolutions from
+ // CameraCharacteristics for JPEG and YUV_420_888 formats to return.
+ Size[] outputSizes = map.getOutputSizes(ImageFormat.JPEG);
+
+ if (outputSizes != null) {
+ formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, outputSizes));
+ }
+
+ outputSizes = map.getOutputSizes(ImageFormat.YUV_420_888);
+
+ if (outputSizes != null) {
+ formatResolutionsPairList.add(Pair.create(ImageFormat.YUV_420_888, outputSizes));
+ }
+ }
+
+ return formatResolutionsPairList;
+ }
}
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
index a5c511e..99a57f9 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -16,12 +16,19 @@
package androidx.camera.extensions.impl;
import android.content.Context;
+import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Implementation for beauty preview use case.
*
@@ -31,12 +38,14 @@
public final class BeautyPreviewExtenderImpl implements PreviewExtenderImpl {
private static final int DEFAULT_STAGE_ID = 0;
private static final int SESSION_STAGE_ID = 101;
+ private CameraCharacteristics mCameraCharacteristics;
public BeautyPreviewExtenderImpl() {
}
@Override
public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+ mCameraCharacteristics = cameraCharacteristics;
}
@Override
@@ -68,6 +77,26 @@
}
@Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ List<Pair<Integer, Size[]>> formatResolutionsPairList = new ArrayList<>();
+
+ StreamConfigurationMap map =
+ mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ if (map != null) {
+ // The sample implementation only retrieves originally supported resolutions from
+ // CameraCharacteristics for PRIVATE format to return.
+ Size[] outputSizes = map.getOutputSizes(ImageFormat.PRIVATE);
+
+ if (outputSizes != null) {
+ formatResolutionsPairList.add(Pair.create(ImageFormat.PRIVATE, outputSizes));
+ }
+ }
+
+ return formatResolutionsPairList;
+ }
+
+ @Override
public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
Context context) {
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
index 2da14ff..d6dd45f 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -178,4 +178,9 @@
return 3;
}
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
+
}
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
index 4fa72b7..94a142e 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -19,12 +19,15 @@
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Implementation for bokeh preview use case.
*
@@ -106,6 +109,11 @@
}
@Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
+
+ @Override
public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
Context context) {
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index 39f961a..8a2d01b 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -201,4 +201,9 @@
return 4;
}
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
+
}
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
index f85518b..acf0f7e 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -20,12 +20,15 @@
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
+import android.util.Pair;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Implementation for HDR preview use case.
*
@@ -69,6 +72,11 @@
return mProcessor;
}
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
+
private PreviewImageProcessorImpl mProcessor = new PreviewImageProcessorImpl() {
Surface mSurface;
Size mSize;
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 8bf39ae..a844969 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -16,7 +16,12 @@
package androidx.camera.extensions.impl;
+import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
import java.util.List;
@@ -57,5 +62,21 @@
* @return the maximum count.
*/
int getMaxCaptureStage();
+
+ /**
+ * Returns the customized supported resolutions.
+ *
+ * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+ *
+ * <p>The returned resolutions should be subset of the supported sizes retrieved from
+ * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
+ * returned list is not null, it will be used to find the best resolutions combination for
+ * the bound use cases.
+ *
+ * @return the customized supported resolutions.
+ * @since 1.1
+ */
+ @Nullable
+ List<Pair<Integer, Size[]>> getSupportedResolutions();
}
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index 83cc4f1..fe6daea 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -176,4 +176,9 @@
public int getMaxCaptureStage() {
return 3;
}
+
+ @Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
}
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
index 3b85e7a..7c32035 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.List;
+
/**
* Implementation for night preview use case.
*
@@ -68,6 +72,11 @@
}
@Override
+ public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+ return null;
+ }
+
+ @Override
public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
Context context) {
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
index cfe2995..d690da3 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -16,8 +16,15 @@
package androidx.camera.extensions.impl;
+import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
+
+import java.util.List;
/**
* Provides abstract methods that the OEM needs to implement to enable extensions in the preview.
@@ -76,4 +83,20 @@
*
*/
ProcessorImpl getProcessor();
+
+ /**
+ * Returns the customized supported resolutions.
+ *
+ * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+ *
+ * <p>The returned resolutions should be subset of the supported sizes retrieved from
+ * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
+ * returned list is not null, it will be used to find the best resolutions combination for
+ * the bound use cases.
+ *
+ * @return the customized supported resolutions.
+ * @since 1.1
+ */
+ @Nullable
+ List<Pair<Integer, Size[]>> getSupportedResolutions();
}
diff --git a/car/core/api/1.0.0-alpha8.txt b/car/core/api/1.0.0-alpha8.txt
index 7966547..e6852f0 100644
--- a/car/core/api/1.0.0-alpha8.txt
+++ b/car/core/api/1.0.0-alpha8.txt
@@ -586,6 +586,7 @@
method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
method public void setDownEnabled(boolean);
method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+ method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
method public void setScrollbarThumbColor(@ColorRes int);
@@ -598,6 +599,10 @@
method public void onAlphaJump();
}
+ public static interface PagedScrollBarView.OnVisibilityChangedListener {
+ method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+ }
+
public static interface PagedScrollBarView.PaginationListener {
method @Deprecated public default void onAlphaJump();
method public void onPaginate(int);
@@ -605,6 +610,9 @@
field public static final int PAGE_UP = 0; // 0x0
}
+ @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+ }
+
public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
ctor public RadioButtonListItem(android.content.Context);
method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/current.txt b/car/core/api/current.txt
index 7966547..e6852f0 100644
--- a/car/core/api/current.txt
+++ b/car/core/api/current.txt
@@ -586,6 +586,7 @@
method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
method public void setDownEnabled(boolean);
method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+ method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
method public void setScrollbarThumbColor(@ColorRes int);
@@ -598,6 +599,10 @@
method public void onAlphaJump();
}
+ public static interface PagedScrollBarView.OnVisibilityChangedListener {
+ method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+ }
+
public static interface PagedScrollBarView.PaginationListener {
method @Deprecated public default void onAlphaJump();
method public void onPaginate(int);
@@ -605,6 +610,9 @@
field public static final int PAGE_UP = 0; // 0x0
}
+ @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+ }
+
public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
ctor public RadioButtonListItem(android.content.Context);
method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/public_plus_experimental_1.0.0-alpha8.txt b/car/core/api/public_plus_experimental_1.0.0-alpha8.txt
index 7966547..e6852f0 100644
--- a/car/core/api/public_plus_experimental_1.0.0-alpha8.txt
+++ b/car/core/api/public_plus_experimental_1.0.0-alpha8.txt
@@ -586,6 +586,7 @@
method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
method public void setDownEnabled(boolean);
method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+ method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
method public void setScrollbarThumbColor(@ColorRes int);
@@ -598,6 +599,10 @@
method public void onAlphaJump();
}
+ public static interface PagedScrollBarView.OnVisibilityChangedListener {
+ method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+ }
+
public static interface PagedScrollBarView.PaginationListener {
method @Deprecated public default void onAlphaJump();
method public void onPaginate(int);
@@ -605,6 +610,9 @@
field public static final int PAGE_UP = 0; // 0x0
}
+ @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+ }
+
public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
ctor public RadioButtonListItem(android.content.Context);
method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/public_plus_experimental_current.txt b/car/core/api/public_plus_experimental_current.txt
index 7966547..e6852f0 100644
--- a/car/core/api/public_plus_experimental_current.txt
+++ b/car/core/api/public_plus_experimental_current.txt
@@ -586,6 +586,7 @@
method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
method public void setDownEnabled(boolean);
method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+ method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
method public void setScrollbarThumbColor(@ColorRes int);
@@ -598,6 +599,10 @@
method public void onAlphaJump();
}
+ public static interface PagedScrollBarView.OnVisibilityChangedListener {
+ method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+ }
+
public static interface PagedScrollBarView.PaginationListener {
method @Deprecated public default void onAlphaJump();
method public void onPaginate(int);
@@ -605,6 +610,9 @@
field public static final int PAGE_UP = 0; // 0x0
}
+ @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+ }
+
public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
ctor public RadioButtonListItem(android.content.Context);
method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/restricted_1.0.0-alpha8.txt b/car/core/api/restricted_1.0.0-alpha8.txt
index 05c3757..ee527c2 100644
--- a/car/core/api/restricted_1.0.0-alpha8.txt
+++ b/car/core/api/restricted_1.0.0-alpha8.txt
@@ -611,6 +611,7 @@
method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
method public void setDownEnabled(boolean);
method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+ method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
method public void setScrollbarThumbColor(@ColorRes int);
@@ -623,6 +624,10 @@
method public void onAlphaJump();
}
+ public static interface PagedScrollBarView.OnVisibilityChangedListener {
+ method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+ }
+
public static interface PagedScrollBarView.PaginationListener {
method @Deprecated public default void onAlphaJump();
method public void onPaginate(int);
@@ -630,6 +635,9 @@
field public static final int PAGE_UP = 0; // 0x0
}
+ @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+ }
+
public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
ctor public RadioButtonListItem(android.content.Context);
method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/restricted_current.txt b/car/core/api/restricted_current.txt
index 05c3757..ee527c2 100644
--- a/car/core/api/restricted_current.txt
+++ b/car/core/api/restricted_current.txt
@@ -611,6 +611,7 @@
method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
method public void setDownEnabled(boolean);
method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+ method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
method public void setScrollbarThumbColor(@ColorRes int);
@@ -623,6 +624,10 @@
method public void onAlphaJump();
}
+ public static interface PagedScrollBarView.OnVisibilityChangedListener {
+ method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+ }
+
public static interface PagedScrollBarView.PaginationListener {
method @Deprecated public default void onAlphaJump();
method public void onPaginate(int);
@@ -630,6 +635,9 @@
field public static final int PAGE_UP = 0; // 0x0
}
+ @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+ }
+
public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
ctor public RadioButtonListItem(android.content.Context);
method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/src/main/java/androidx/car/widget/PagedListView.java b/car/core/src/main/java/androidx/car/widget/PagedListView.java
index bacdb30..1aa0efa 100644
--- a/car/core/src/main/java/androidx/car/widget/PagedListView.java
+++ b/car/core/src/main/java/androidx/car/widget/PagedListView.java
@@ -373,7 +373,8 @@
a.getInt(R.styleable.PagedListView_scrollBarGravity, Gravity.LEFT);
}
- mScrollBarView.setVisibility(mIsScrollBarVisibleIfNeeded ? VISIBLE : GONE);
+ // Initially hide scrollbar view and make it visible when required.
+ mScrollBarView.setVisibility(GONE);
if (mIsScrollBarVisibleIfNeeded) {
// Use the top margin that is defined in the layout as the default value.
@@ -412,6 +413,9 @@
setGutter(Gutter.BOTH);
}
+ // Upon scrollbar visibility changes, margins for gutters may need to be updated.
+ mScrollBarView.setOnVisibilityChangedListener(
+ (scrollbar, visibility) -> setGutter(mGutter));
a.recycle();
}
@@ -467,16 +471,18 @@
boolean isLTR = ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR;
boolean isScrollBarGravityStart =
(scrollBarGravity == Gravity.START)
- || (scrollBarGravity == Gravity.LEFT && isLTR)
- || (scrollBarGravity == Gravity.RIGHT && !isLTR);
+ || ((scrollBarGravity == Gravity.LEFT) && isLTR)
+ || ((scrollBarGravity == Gravity.RIGHT) && !isLTR);
boolean isScrollBarGravityEnd =
(scrollBarGravity == Gravity.END)
- || (scrollBarGravity == Gravity.RIGHT && isLTR)
- || (scrollBarGravity == Gravity.LEFT && !isLTR);
+ || ((scrollBarGravity == Gravity.RIGHT) && isLTR)
+ || ((scrollBarGravity == Gravity.LEFT) && !isLTR);
+
+ boolean isScrollbarVisible = mScrollBarView.getVisibility() == View.VISIBLE;
// Default starting margin is either the width of the scroll bar, if present at the start,
// or just flush to the edge.
- int startMargin = mIsScrollBarVisibleIfNeeded && isScrollBarGravityStart
+ int startMargin = isScrollbarVisible && isScrollBarGravityStart
? mScrollBarView.getLayoutParams().width : 0;
if ((mGutter & Gutter.START) != 0) {
// Ensure that the gutter value is large enough so that the RecyclerView does not
@@ -486,7 +492,7 @@
// Default end margin is either the width of the scroll bar, if present at the end, or
// just flush to the edge.
- int endMargin = mIsScrollBarVisibleIfNeeded && isScrollBarGravityEnd
+ int endMargin = isScrollbarVisible && isScrollBarGravityEnd
? mScrollBarView.getLayoutParams().width : 0;
if ((mGutter & Gutter.END) != 0) {
// Ensure that the gutter value is large enough so that the RecyclerView does not
@@ -504,6 +510,12 @@
// If there's a gutter, set ClipToPadding to false so that CardView's shadow will still
// appear outside of the padding.
mRecyclerView.setClipToPadding(startMargin == 0 && endMargin == 0);
+
+ // If currently performing a layout pass, updated margins may not get applied until
+ // the next layout pass. Schedule another layout pass to ensure margins are updated.
+ if (isInLayout()) {
+ post(this::requestLayout);
+ }
}
/**
@@ -1262,7 +1274,7 @@
boolean isAtEnd = isAtEnd();
if ((isAtStart && isAtEnd) || layoutManager.getItemCount() == 0) {
- mScrollBarView.setVisibility(View.INVISIBLE);
+ mScrollBarView.setVisibility(View.GONE);
return;
}
@@ -1304,7 +1316,7 @@
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if ((isAtStart && isAtEnd) || layoutManager == null || layoutManager.getItemCount() == 0) {
- mScrollBarView.setVisibility(View.INVISIBLE);
+ mScrollBarView.setVisibility(View.GONE);
} else {
mScrollBarView.setVisibility(VISIBLE);
}
diff --git a/car/core/src/main/java/androidx/car/widget/PagedScrollBarView.java b/car/core/src/main/java/androidx/car/widget/PagedScrollBarView.java
index fa47350..ac4e393 100644
--- a/car/core/src/main/java/androidx/car/widget/PagedScrollBarView.java
+++ b/car/core/src/main/java/androidx/car/widget/PagedScrollBarView.java
@@ -33,11 +33,16 @@
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.R;
import androidx.core.content.ContextCompat;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/** A custom view to provide list scroll behaviour -- up/down buttons and scroll indicator. */
public class PagedScrollBarView extends ViewGroup {
private static final float BUTTON_DISABLED_ALPHA = 0.2f;
@@ -63,7 +68,8 @@
* @deprecated Use {@link OnAlphaJumpListener#onAlphaJump()} instead.
*/
@Deprecated
- default void onAlphaJump() {}
+ default void onAlphaJump() {
+ }
}
public interface OnAlphaJumpListener {
@@ -79,6 +85,24 @@
void onAlphaJump();
}
+ @IntDef({View.VISIBLE, View.INVISIBLE, View.GONE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Visibility {
+ }
+
+ /** Listener for changes to the visibility of the scrollbar view. */
+ public interface OnVisibilityChangedListener {
+
+ /**
+ * Called when the visibility of the scrollbar view changes.
+ *
+ * @param view A reference to the scrollbar for which the visibility changed.
+ * @param visibility The new visibility, one of {@code View.VISIBLE}, {@code View
+ * .INVISIBLE} or {@code View.GONE}
+ */
+ void onVisibilityChanged(@NonNull View view, @Visibility int visibility);
+ }
+
private final ImageView mUpButton;
private final PaginateButtonClickListener mUpButtonClickListener;
private final ImageView mDownButton;
@@ -86,6 +110,7 @@
private final TextView mAlphaJumpButton;
private final AlphaJumpButtonClickListener mAlphaJumpButtonClickListener;
private final View mScrollThumb;
+ private OnVisibilityChangedListener mOnVisibilityChangedListener;
private final int mSeparatingMargin;
private final int mScrollBarThumbWidth;
@@ -198,6 +223,22 @@
mAlphaJumpButtonClickListener.setOnAlphaJumpListener(listener);
}
+ /**
+ * Sets the listener that will be notified when the visibility of the scrollbar changes.
+ *
+ * @param listener The listener to set.
+ */
+ public void setOnVisibilityChangedListener(@Nullable OnVisibilityChangedListener listener) {
+ mOnVisibilityChangedListener = listener;
+ }
+
+ @Override
+ protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
+ if (mOnVisibilityChangedListener != null) {
+ mOnVisibilityChangedListener.onVisibilityChanged(this, visibility);
+ }
+ }
+
/** Returns {@code true} if the "up" button is pressed */
public boolean isUpPressed() {
return mUpButton.isPressed();
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
index 186259f..2a91d2a 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
@@ -2361,14 +2361,6 @@
super.setUp()
}
- override fun setupEnvironment(environment: KotlinCoreEnvironment) {
- environment.configuration.put(
- ComposeConfigurationKeys.COMPOSABLE_CHECKER_MODE_KEY,
- ComposableAnnotationChecker.Mode.FCS
- )
- super.setupEnvironment(environment)
- }
-
private var isSetup = false
private inline fun <T> ensureSetup(block: () -> T): T {
if (!isSetup) setUp()
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsModelCodeGenTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsModelCodeGenTests.kt
index 8ce6c9e..5597c44 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsModelCodeGenTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsModelCodeGenTests.kt
@@ -335,14 +335,6 @@
advanceMethod.invoke(instanceOfClass, *arguments)
}
}
-
- override fun setupEnvironment(environment: KotlinCoreEnvironment) {
- environment.configuration.put(
- ComposeConfigurationKeys.COMPOSABLE_CHECKER_MODE_KEY,
- ComposableAnnotationChecker.Mode.FCS
- )
- super.setupEnvironment(environment)
- }
}
private val Activity.root get() = findViewById(ROOT_ID) as ViewGroup
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ComposableCheckerTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ComposableCheckerTests.kt
index e40e8fd..b951372 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ComposableCheckerTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ComposableCheckerTests.kt
@@ -4,8 +4,6 @@
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import androidx.compose.plugins.kotlin.AbstractComposeDiagnosticsTest
-import androidx.compose.plugins.kotlin.ComposableAnnotationChecker.Mode
-import androidx.compose.plugins.kotlin.ComposeConfigurationKeys
import androidx.compose.plugins.kotlin.newConfiguration
import com.intellij.openapi.util.Disposer
@@ -26,12 +24,11 @@
)
}
- fun doTest(mode: Mode, text: String, expectPass: Boolean) {
+ fun doTest(text: String, expectPass: Boolean) {
val disposable = TestDisposable()
val classPath = createClasspath()
val configuration = newConfiguration()
configuration.addJvmClasspathRoots(classPath)
- configuration.put(ComposeConfigurationKeys.COMPOSABLE_CHECKER_MODE_KEY, mode)
val environment =
KotlinCoreEnvironment.createForTests(
@@ -45,26 +42,26 @@
doTest(text, environment)
if (!expectPass) {
throw Exception(
- "Test unexpectedly passed when running in $mode mode, but SHOULD FAIL"
+ "Test unexpectedly passed, but SHOULD FAIL"
)
}
} catch (e: Exception) {
- if (expectPass) throw Exception("Unexpected failure while running in $mode mode", e)
+ if (expectPass) throw Exception(e)
} finally {
Disposer.dispose(disposable)
}
}
- fun doTest(modes: Int, expectedText: String) {
- doTest(Mode.KTX_CHECKED, expectedText, (modes and MODE_KTX_CHECKED) != 0)
- doTest(Mode.KTX_STRICT, expectedText, (modes and MODE_KTX_STRICT) != 0)
- doTest(Mode.KTX_PEDANTIC, expectedText, (modes and MODE_KTX_PEDANTIC) != 0)
- doTest(Mode.FCS, expectedText, (modes and MODE_FCS) != 0)
+ fun check(expectedText: String) {
+ doTest(expectedText, true)
+ }
+
+ fun checkFail(expectedText: String) {
+ doTest(expectedText, false)
}
fun testComposableReporting001() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -76,8 +73,7 @@
<myStatelessFunctionalComponent />
}
""")
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -94,16 +90,14 @@
}
fun testComposableReporting002() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
val myLambda1 = { <TextView text="Hello World!" /> }
val myLambda2: ()->Unit = { <TextView text="Hello World!" /> }
""")
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -113,8 +107,7 @@
}
fun testComposableReporting003() {
- doTest(
- MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -125,8 +118,7 @@
}
fun testComposableReporting004() {
- doTest(
- MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -139,8 +131,7 @@
}
fun testComposableReporting005() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -153,8 +144,7 @@
}
fun testComposableReporting006() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -166,8 +156,7 @@
System.out.println(bar)
}
""")
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -183,8 +172,7 @@
}
fun testComposableReporting007() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -195,8 +183,7 @@
}
fun testComposableReporting008() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -211,8 +198,7 @@
}
fun testComposableReporting009() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -224,8 +210,7 @@
<!SVC_INVOCATION!>myStatelessFunctionalComponent<!>()
}
""")
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -241,8 +226,7 @@
}
fun testComposableReporting010() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -258,8 +242,7 @@
}
fun testComposableReporting011() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -273,8 +256,7 @@
}
fun testComposableReporting012() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -288,8 +270,7 @@
}
fun testComposableReporting013() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -303,8 +284,7 @@
}
fun testComposableReporting014() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -320,8 +300,7 @@
}
fun testComposableReporting015() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -341,8 +320,7 @@
}
}
""")
- doTest(
- MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -365,16 +343,14 @@
}
fun testComposableReporting016() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
<!WRONG_ANNOTATION_TARGET!>@Composable<!>
class Noise() {}
""")
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
val adHoc = <!WRONG_ANNOTATION_TARGET!>@Composable()<!> object {
@@ -383,8 +359,7 @@
}
""")
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
open class Noise() {}
@@ -397,8 +372,7 @@
}
fun testComposableReporting017() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -413,8 +387,7 @@
<Foo><TextView text="Hello" /></Foo>
}
""")
- doTest(
- MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -432,8 +405,7 @@
}
fun testComposableReporting018() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -442,8 +414,7 @@
System.out.println(myVariable)
}
""")
- doTest(
- MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -455,8 +426,7 @@
}
fun testComposableReporting019() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -467,8 +437,7 @@
System.out.println(myVariable)
}
""")
- doTest(
- MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -482,8 +451,7 @@
}
fun testComposableReporting020() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -497,8 +465,7 @@
}
fun testComposableReporting021() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -511,8 +478,7 @@
}
fun testComposableReporting022() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -521,8 +487,7 @@
myList.forEach { value: Int -> <TextView text=value.toString() />; System.out.println(value); }
}
""")
- doTest(
- MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -534,8 +499,7 @@
}
fun testComposableReporting023() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -549,8 +513,7 @@
}
fun testComposableReporting024() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
import android.widget.LinearLayout;
@@ -562,8 +525,7 @@
}
fun testComposableReporting025() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -575,8 +537,7 @@
}
fun testComposableReporting026() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -591,8 +552,7 @@
}
fun testComposableReporting027() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -609,8 +569,7 @@
}
fun testComposableReporting028() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -619,8 +578,7 @@
myVariable()
}
""")
- doTest(
- MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -632,8 +590,7 @@
}
fun testComposableReporting029() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -643,8 +600,7 @@
<myVariable />
}
""")
- doTest(
- MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -657,8 +613,7 @@
}
fun testComposableReporting030() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -671,8 +626,7 @@
}
fun testComposableReporting031() {
- doTest(
- MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -684,8 +638,7 @@
}
fun testComposableReporting032() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import androidx.compose.Children;
import android.widget.TextView;
@@ -701,8 +654,7 @@
}
fun testComposableReporting033() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import androidx.compose.Children;
import android.widget.TextView;
@@ -718,8 +670,7 @@
}
fun testComposableReporting034() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+ checkFail("""
import androidx.compose.*;
import android.widget.TextView;
@@ -731,8 +682,7 @@
<f2 />
}
""")
- doTest(
- MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*;
import android.widget.TextView;
@@ -747,8 +697,7 @@
}
fun testComposableReporting035() {
- doTest(
- MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*
@Composable
@@ -760,8 +709,7 @@
}
fun testComposableReporting036() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*
import android.widget.TextView;
@@ -775,8 +723,7 @@
<!SVC_INVOCATION!>Foo<!>()
}
""")
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*
import android.widget.TextView;
@@ -793,8 +740,7 @@
}
fun testComposableReporting037() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*
import android.widget.TextView;
@@ -811,8 +757,7 @@
}
fun testComposableReporting038() {
- doTest(
- MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*
import android.widget.TextView;
@@ -828,8 +773,7 @@
}
fun testComposableReporting039() {
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*
import android.widget.TextView;
@@ -845,8 +789,7 @@
Foo()
}
""")
- doTest(
- MODE_KTX_CHECKED, """
+ checkFail("""
import androidx.compose.*
import android.widget.TextView;
@@ -865,8 +808,7 @@
}
fun testComposableReporting040() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*
import android.widget.TextView;
@@ -884,8 +826,7 @@
}
fun testComposableReporting041() {
- doTest(
- MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+ check("""
import androidx.compose.*
import android.widget.TextView;
@@ -905,8 +846,7 @@
}
fun testComposableReporting042() {
- doTest(
- MODE_FCS, """
+ check("""
import androidx.compose.*
import android.widget.TextView;
@@ -925,8 +865,7 @@
Foo()
}
""")
- doTest(
- MODE_FCS, """
+ check("""
import androidx.compose.*
import android.widget.TextView;
@@ -944,8 +883,7 @@
}
fun testComposableReporting043() {
- doTest(
- MODE_FCS, """
+ check("""
import androidx.compose.*
import android.widget.TextView;
@@ -961,8 +899,7 @@
}
fun testComposableReporting044() {
- doTest(
- MODE_FCS, """
+ check("""
import androidx.compose.*
import android.widget.TextView;
@@ -979,8 +916,7 @@
}
fun testComposableReporting045() {
- doTest(
- MODE_FCS, """
+ check("""
import androidx.compose.*;
@Composable
@@ -993,8 +929,7 @@
}
fun testComposableReporting046() {
- doTest(
- MODE_FCS, """
+ check( """
import androidx.compose.*;
import android.widget.TextView;
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
index 198d33a..aa1d342 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
@@ -81,9 +81,7 @@
import org.jetbrains.kotlin.types.upperIfFlexible
import org.jetbrains.kotlin.util.OperatorNameConventions
-open class ComposableAnnotationChecker(
- val nullableMode: Mode? = null
-) : CallChecker, DeclarationChecker,
+open class ComposableAnnotationChecker : CallChecker, DeclarationChecker,
AdditionalTypeChecker, AdditionalAnnotationChecker, StorageComponentContainerContributor {
enum class Mode {
@@ -98,9 +96,6 @@
}
companion object {
- val DEFAULT_MODE =
- Mode.FCS
-
fun get(project: Project): ComposableAnnotationChecker {
return StorageComponentContainerContributor.getInstances(project).single {
it is ComposableAnnotationChecker
@@ -149,11 +144,6 @@
return candidateDescriptor.hasComposableAnnotation()
}
- open fun getMode(psi: PsiElement): Mode {
- if (nullableMode != null) return nullableMode
- return DEFAULT_MODE
- }
-
fun analyze(trace: BindingTrace, descriptor: FunctionDescriptor): Composability {
val unwrappedDescriptor =
if (descriptor is
@@ -203,7 +193,6 @@
signatureComposability: Composability
): Composability {
var composability = signatureComposability
- val mode = getMode(element)
var localKtx = false
var localFcs = false
var isInlineLambda = false
@@ -230,20 +219,18 @@
}
override fun visitCallExpression(expression: KtCallExpression) {
- if (mode == Mode.FCS) {
- // Can be null in cases where the call isn't resolvable (eg. due to a bug/typo in the user's code)
- val isCallComposable = trace.get(FCS_RESOLVEDCALL_COMPOSABLE, expression)
- if (isCallComposable == true) {
- localFcs = true
- if (!isInlineLambda && composability != Composability.MARKED) {
- // Report error on composable element to make it obvious which invocation is offensive
- val reportElement = expression.calleeExpression ?: expression
- trace.reportFromPlugin(
- ComposeErrors.COMPOSABLE_INVOCATION_IN_NON_COMPOSABLE
- .on(reportElement),
- ComposeDefaultErrorMessages
- )
- }
+ // Can be null in cases where the call isn't resolvable (eg. due to a bug/typo in the user's code)
+ val isCallComposable = trace.get(FCS_RESOLVEDCALL_COMPOSABLE, expression)
+ if (isCallComposable == true) {
+ localFcs = true
+ if (!isInlineLambda && composability != Composability.MARKED) {
+ // Report error on composable element to make it obvious which invocation is offensive
+ val reportElement = expression.calleeExpression ?: expression
+ trace.reportFromPlugin(
+ ComposeErrors.COMPOSABLE_INVOCATION_IN_NON_COMPOSABLE
+ .on(reportElement),
+ ComposeDefaultErrorMessages
+ )
}
}
super.visitCallExpression(expression)
@@ -251,8 +238,7 @@
}, null)
if (
(localKtx || localFcs) &&
- !isInlineLambda && composability != Composability.MARKED &&
- (mode == Mode.KTX_STRICT || mode == Mode.KTX_PEDANTIC || mode == Mode.FCS)
+ !isInlineLambda && composability != Composability.MARKED
) {
val reportElement = when (element) {
is KtNamedFunction -> element.nameIdentifier ?: element
@@ -438,33 +424,12 @@
reportOn: PsiElement,
context: CallCheckerContext
) {
- val mode = getMode(reportOn)
val shouldBeTag = shouldInvokeAsTag(context.trace, resolvedCall)
- if (mode == Mode.FCS) {
- context.trace.record(
- FCS_RESOLVEDCALL_COMPOSABLE,
- resolvedCall.call.callElement,
- shouldBeTag
- )
- }
- // NOTE: we return early here because the KtxCallResolver does its own composable checking (by delegating to this class) and has
- // more context to make the right call.
- if (reportOn.parent is KtxElement) return
- if (
- mode != Mode.FCS && context.resolutionContext is CallResolutionContext &&
- resolvedCall is ResolvedCallImpl &&
- (context.resolutionContext as CallResolutionContext).call.callElement is
- KtCallExpression
- ) {
- if (shouldBeTag) {
- context.trace.reportFromPlugin(
- ComposeErrors.SVC_INVOCATION.on(
- reportOn as KtElement,
- resolvedCall.candidateDescriptor.name.asString()
- ), ComposeDefaultErrorMessages
- )
- }
- }
+ context.trace.record(
+ FCS_RESOLVEDCALL_COMPOSABLE,
+ resolvedCall.call.callElement,
+ shouldBeTag
+ )
}
override fun checkType(
@@ -473,8 +438,6 @@
expressionTypeWithSmartCast: KotlinType,
c: ResolutionContext<*>
) {
- val mode = getMode(expression)
- if (mode != Mode.KTX_PEDANTIC && mode != Mode.FCS) return
if (expression is KtLambdaExpression) {
val expectedType = c.expectedType
if (expectedType === TypeUtils.NO_EXPECTED_TYPE) return
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
index 807cdb4..b6e21a3 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
@@ -33,33 +33,19 @@
import org.jetbrains.kotlin.extensions.TypeResolutionInterceptorExtension
import org.jetbrains.kotlin.parsing.KtxParsingExtension
import org.jetbrains.kotlin.psi2ir.extensions.SyntheticIrExtension
-import androidx.compose.plugins.kotlin.ComposeConfigurationKeys.COMPOSABLE_CHECKER_MODE_KEY
import androidx.compose.plugins.kotlin.frames.analysis.FrameModelChecker
import androidx.compose.plugins.kotlin.frames.analysis.FramePackageAnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
-object ComposeConfigurationKeys {
- val COMPOSABLE_CHECKER_MODE_KEY = CompilerConfigurationKey<ComposableAnnotationChecker.Mode>(
- "@composable checker mode"
- )
-}
-
class ComposeCommandLineProcessor : CommandLineProcessor {
companion object {
val PLUGIN_ID = "androidx.compose.plugins.kotlin"
- val SYNTAX_OPTION = CliOption(
- "syntax",
- "<ktx_checked|ktx_strict|ktx_pedantic|fcs>",
- "@composable syntax checker mode",
- required = false,
- allowMultipleOccurrences = false
- )
}
override val pluginId =
PLUGIN_ID
- override val pluginOptions = listOf(SYNTAX_OPTION)
+ override val pluginOptions = emptyList<CliOption>()
@Suppress("OverridingDeprecatedMember")
override fun processOption(
@@ -73,13 +59,7 @@
option: AbstractCliOption,
value: String,
configuration: CompilerConfiguration
- ) = when (option) {
- SYNTAX_OPTION -> configuration.put(
- COMPOSABLE_CHECKER_MODE_KEY,
- ComposableAnnotationChecker.Mode.valueOf(value.toUpperCase())
- )
- else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
- }
+ ) = throw CliOptionProcessingException("Unknown option: ${option.optionName}")
}
class ComposeComponentRegistrar : ComponentRegistrar {
@@ -95,6 +75,7 @@
companion object {
+ @Suppress("UNUSED_PARAMETER")
fun registerProjectExtensions(project: Project, configuration: CompilerConfiguration) {
StorageComponentContainerContributor.registerExtension(
project,
@@ -102,12 +83,7 @@
)
StorageComponentContainerContributor.registerExtension(
project,
- ComposableAnnotationChecker(
- configuration.get(
- COMPOSABLE_CHECKER_MODE_KEY,
- ComposableAnnotationChecker.DEFAULT_MODE
- )
- )
+ ComposableAnnotationChecker()
)
StorageComponentContainerContributor.registerExtension(
project,
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposeObservePatcher.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposeObservePatcher.kt
index 1a608ed..339a001 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposeObservePatcher.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposeObservePatcher.kt
@@ -413,7 +413,7 @@
context.state.bindingContext, "tmp for composable analysis"
)
val composability =
- ComposableAnnotationChecker(ComposableAnnotationChecker.Mode.KTX_CHECKED)
+ ComposableAnnotationChecker()
.analyze(
tmpTrace,
declaration.descriptor
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/ComposableAnnotationChecker.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/ComposableAnnotationChecker.kt
deleted file mode 100644
index 3814444..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/ComposableAnnotationChecker.kt
+++ /dev/null
@@ -1,37 +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.compose.plugins.idea
-
-import androidx.compose.plugins.kotlin.ComposableAnnotationChecker
-import com.intellij.openapi.module.ModuleUtilCore
-import com.intellij.psi.PsiElement
-
-class ComposableAnnotationChecker() : ComposableAnnotationChecker() {
- override fun getMode(psi: PsiElement): Mode {
- if (nullableMode != null) return nullableMode
- val module = ModuleUtilCore.findModuleForPsiElement(psi) ?: return DEFAULT_MODE
- val kotlinFacet = org.jetbrains.kotlin.idea.facet.KotlinFacet.get(module)
- ?: return DEFAULT_MODE
- val commonArgs = kotlinFacet.configuration.settings.compilerArguments ?: return DEFAULT_MODE
- val modeOption = commonArgs.pluginOptions?.firstOrNull {
- it.startsWith("plugin:androidx.compose.plugins.kotlin:syntax=")
- } ?: return DEFAULT_MODE
- return Mode.valueOf(
- modeOption.substring(modeOption.indexOf("=") + 1).toUpperCase()
- )
- }
-}
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/AndroidConversion.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/AndroidConversion.kt
deleted file mode 100644
index b8aa00d9..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/AndroidConversion.kt
+++ /dev/null
@@ -1,129 +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.compose.plugins.idea.conversion
-
-import org.jetbrains.kotlin.builtins.DefaultBuiltIns
-import org.jetbrains.kotlin.j2k.ast.Expression
-import org.jetbrains.kotlin.j2k.ast.LiteralExpression
-import org.jetbrains.kotlin.j2k.ast.QualifiedExpression
-import org.jetbrains.kotlin.name.FqName
-import androidx.compose.plugins.kotlin.ComposeUtils
-
-internal val ANDROID_CONVERSION = conversion {
- DefaultBuiltIns.Instance.booleanType
- anyClass {
- anyAttribute {
- "true" { success<Boolean>(LiteralExpression("true")) }
- "false" { success<Boolean>(LiteralExpression("false")) }
- }
- }
- "android.view.View" {
- "focusable" {
- anyValue {
- val enumValue = when (xmlValue) {
- "auto" -> "FOCUSABLE_AUTO"
- "true" -> "FOCUSABLE"
- "false" -> "NOT_FOCUSABLE"
- else -> null
- }
-
- if (enumValue != null) {
- success<Int>(enumValue.asIdentifier("android.view.View.$enumValue"))
- } else {
- failure()
- }
- }
- }
- anyOf("layout_width", "layout_height") {
- anyValue {
- val enumValue = when (xmlValue) {
- "match_parent", "fill_parent" -> "MATCH_PARENT"
- "wrap_content" -> "WRAP_CONTENT"
- else -> null
- }
-
- if (enumValue != null) {
- success<Int>(
- enumValue.asIdentifier(
- FqName("android.view.ViewGroup.LayoutParams.$enumValue")
- )
- )
- } else {
- failure()
- }
- }
- }
- anyAttribute {
- // Resource.
- val resourceTypes = listOf(
- "anim", "animator", "array", "attr", "bool", "color", "dimen", "drawable", "font",
- "id", "integer", "layout", "menu", "mipmap", "string", "style", "styleable"
- ).joinToString("|")
- pattern("^@(([^:]+):)?($resourceTypes)/(.+)$") { matcher ->
- success<Int>(with(matcher) {
- resourceExpression(
- group(2),
- group(3),
- group(4)
- )
- })
- }
-
- // Dimension.
- pattern("^(\\d+(\\.\\d+)?)(px|dip|dp|sp|pt|in|mm)$") { matcher ->
- // Returns expression: <value>.<unit>
- val unit = matcher.group(3)
- success(
- QualifiedExpression(
- qualifier = LiteralExpression(matcher.group(1)),
- identifier = unit.asIdentifier(
- ComposeUtils.composeFqName("adapters.$unit")
- ),
- dotPrototype = null
- ), ComposeUtils.composeFqName("adapters.Dimension")
- )
- }
- }
- }
-}
-
-// Returns expression: [<package>.]R.<type>.<name>
-private fun resourceExpression(packageName: String?, type: String, name: String): Expression {
- // TODO(jdemeulenaere): We directly return the resource ID
- return if (packageName == null) {
- resourceExpression(type, name)
- } else {
- QualifiedExpression(
- qualifier = packageName.asIdentifier(),
- identifier = resourceExpression(type, name),
- dotPrototype = null
- )
- }
-}
-
-// Returns expression: R.<type>.<name>
-private fun resourceExpression(type: String, name: String): Expression {
- return QualifiedExpression(
- qualifier = QualifiedExpression(
- qualifier = "R".asIdentifier(),
- identifier = type.asIdentifier(),
- dotPrototype = null
- ),
- identifier = name.asIdentifier(),
- dotPrototype = null
- )
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/Conversion.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/Conversion.kt
deleted file mode 100644
index 6b575fdbb..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/Conversion.kt
+++ /dev/null
@@ -1,187 +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.compose.plugins.idea.conversion
-
-import org.jetbrains.kotlin.j2k.ast.Expression
-import org.jetbrains.kotlin.j2k.ast.Identifier
-import org.jetbrains.kotlin.name.FqName
-import java.util.regex.Matcher
-import java.util.regex.Pattern
-
-internal fun conversion(builder: ConversionConfiguration.() -> Unit): ConversionConfiguration {
- return ConversionConfiguration().apply(builder)
-}
-
-@DslMarker
-annotation class ConversionDsl
-
-@ConversionDsl
-class ConversionConfiguration {
- val classConversions = arrayListOf<ClassConversion>()
-
- fun anyClass(builder: AnyClassConversion.() -> Unit) {
- classConversions.add(AnyClassConversion().apply(builder))
- }
-
- operator fun String.invoke(builder: ExactClassConversion.() -> Unit) {
- classConversions.add(ExactClassConversion(this).apply(builder))
- }
-}
-
-@ConversionDsl
-sealed class ClassConversion {
- val attributeConversions = arrayListOf<AttributeConversion>()
-
- abstract fun matchesAny(classNames: Set<String>): Boolean
-
- fun anyAttribute(builder: AnyAttributeConversion.() -> Unit) {
- attributeConversions.add(AnyAttributeConversion().apply(builder))
- }
-
- fun anyOf(vararg attributes: String, builder: ExactXmlAttributeConversion.() -> Unit) {
- attributes.forEach { attribute ->
- attributeConversions.add(
- ExactXmlAttributeConversion(
- attribute
- ).apply(builder))
- }
- }
-
- operator fun String.invoke(builder: ExactXmlAttributeConversion.() -> Unit) {
- attributeConversions.add(
- ExactXmlAttributeConversion(
- this
- ).apply(builder))
- }
-}
-class AnyClassConversion : ClassConversion() {
- override fun matchesAny(classNames: Set<String>): Boolean = true
-}
-class ExactClassConversion(val className: String) : ClassConversion() {
- override fun matchesAny(classNames: Set<String>): Boolean = classNames.contains(className)
-}
-
-interface ConversionContext {
- // TODO(jdemeulenaere): Add tagClass here if a converter needs it.
- val xmlName: String
- val xmlValue: String
-}
-
-sealed class ConversionResult {
- class Success(
- val expression: Expression,
- val kotlinType: FqName
- ) : ConversionResult()
-
- object Failure : ConversionResult()
-}
-
-inline fun <reified T> success(expression: Expression) =
- ConversionResult.Success(
- expression,
- FqName(T::class.qualifiedName!!)
- )
-fun success(expression: Expression, kotlinType: FqName) =
- ConversionResult.Success(expression, kotlinType)
-fun failure() = ConversionResult.Failure
-
-typealias Conversion = ConversionContext.() -> ConversionResult
-typealias ConversionWithMatcher = ConversionContext.(Matcher) -> ConversionResult
-
-@ConversionDsl
-sealed class AttributeConversion {
- val valueConversions = arrayListOf<ValueConversion>()
-
- abstract fun matches(xmlName: String): Boolean
-
- fun anyValue(conversion: Conversion) {
- valueConversions.add(AnyValueConversion(conversion))
- }
-
- fun anyOf(vararg values: String, conversion: Conversion) {
- values.forEach { value ->
- valueConversions.add(
- ExactValueConversion(
- value,
- conversion
- )
- )
- }
- }
-
- operator fun String.invoke(conversion: Conversion) {
- valueConversions.add(
- ExactValueConversion(
- this,
- conversion
- )
- )
- }
-
- fun pattern(pattern: String, conversion: ConversionWithMatcher) {
- valueConversions.add(
- PatternValueConversion(
- pattern,
- conversion
- )
- )
- }
-}
-class AnyAttributeConversion : AttributeConversion() {
- override fun matches(xmlName: String) = true
-}
-class ExactXmlAttributeConversion(private val attributeName: String) : AttributeConversion() {
- override fun matches(xmlName: String): Boolean = this.attributeName == xmlName
-}
-
-abstract class ValueConversion {
- abstract fun matches(attributeValue: String): Boolean
-
- abstract fun convert(context: ConversionContext): ConversionResult
-}
-
-abstract class BaseValueConversion(private val conversion: Conversion) : ValueConversion() {
- override fun convert(context: ConversionContext): ConversionResult = conversion(context)
-}
-
-class AnyValueConversion(conversion: Conversion) : BaseValueConversion(conversion) {
- override fun matches(attributeValue: String): Boolean = true
-}
-class ExactValueConversion(
- private val value: String,
- conversion: Conversion
-) : BaseValueConversion(conversion) {
- override fun matches(attributeValue: String): Boolean = this.value == attributeValue
-}
-
-class PatternValueConversion(
- pattern: String,
- private val conversion: ConversionWithMatcher
-) : ValueConversion() {
- private val pattern = Pattern.compile(pattern)
- override fun matches(attributeValue: String): Boolean =
- pattern.matcher(attributeValue).matches()
-
- override fun convert(context: ConversionContext): ConversionResult {
- return context.conversion(pattern.matcher(context.xmlValue).also { assert(it.matches()) })
- }
-}
-
-internal fun String.asIdentifier(import: FqName? = null) =
- Identifier(this, isNullable = false, imports = import?.let(::listOf) ?: emptyList())
-internal fun String.asIdentifier(import: String): Identifier =
- Identifier(this, isNullable = false, imports = listOf(FqName(import)))
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConversionUtils.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConversionUtils.kt
deleted file mode 100644
index 581f6ea..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConversionUtils.kt
+++ /dev/null
@@ -1,55 +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.compose.plugins.idea.conversion
-
-import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
-import org.jetbrains.kotlin.idea.util.ImportInsertHelper
-import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtFile
-import androidx.compose.plugins.kotlin.ComposeUtils
-
-internal fun createFunctionalComponent(
- name: String,
- composeBody: String,
- imports: MutableSet<FqName>
-): String {
- // TODO(jdemeulenaere): Allow to specify package.
- // We don't use org.jetbrains.kotlin.j2k.ast.Function because it requires a j2k.Converter instance to create a DeferredElement
- // (the type of the Function body).
-
- imports.add(ComposeUtils.composeFqName("Composable"))
- return """
- |@Composable
- |fun $name() {
- | $composeBody
- |}""".trimMargin()
-}
-
-internal fun addComposeStarImport(targetFile: KtFile) {
- runWriteAction {
- targetFile.resolveImportReference(
- ComposeUtils.composeFqName("Composable")
- ).firstOrNull()?.let {
- ImportInsertHelper.getInstance(targetFile.project).importDescriptor(
- targetFile,
- it,
- forceAllUnderImport = true
- )
- }
- }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConvertXmlCopyPasteProcessor.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConvertXmlCopyPasteProcessor.kt
deleted file mode 100644
index 208c016..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConvertXmlCopyPasteProcessor.kt
+++ /dev/null
@@ -1,422 +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.compose.plugins.idea.conversion
-
-import com.intellij.codeInsight.editorActions.CopyPastePostProcessor
-import com.intellij.codeInsight.editorActions.TextBlockTransferableData
-import com.intellij.lang.xml.XMLLanguage
-import com.intellij.openapi.diagnostic.Logger
-import com.intellij.openapi.editor.Editor
-import com.intellij.openapi.editor.RangeMarker
-import com.intellij.openapi.project.DumbService
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.util.Ref
-import com.intellij.openapi.util.TextRange
-import com.intellij.psi.PsiDocumentManager
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiErrorElement
-import com.intellij.psi.PsiFile
-import com.intellij.psi.PsiFileFactory
-import com.intellij.psi.impl.source.tree.TreeElement
-import com.intellij.psi.xml.XmlFile
-import com.intellij.psi.xml.XmlTag
-import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
-import org.jetbrains.kotlin.idea.conversion.copy.CopiedKotlinCode
-import org.jetbrains.kotlin.idea.conversion.copy.ElementAndTextList
-import org.jetbrains.kotlin.idea.conversion.copy.ElementsAndTextsProcessor
-import org.jetbrains.kotlin.idea.j2k.EmptyDocCommentConverter
-import org.jetbrains.kotlin.idea.util.ImportInsertHelper
-import org.jetbrains.kotlin.idea.util.addAnnotation
-import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.jetbrains.kotlin.idea.util.findAnnotation
-import org.jetbrains.kotlin.j2k.CodeBuilder
-import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtCallableDeclaration
-import org.jetbrains.kotlin.psi.KtFile
-import org.jetbrains.kotlin.psi.KtNamedFunction
-import org.jetbrains.kotlin.psi.KtxElement
-import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
-import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
-import org.jetbrains.kotlin.psi.psiUtil.startOffset
-import androidx.compose.plugins.kotlin.ComposeUtils
-import androidx.compose.plugins.idea.editor.KtxEditorOptions
-import androidx.compose.plugins.idea.parentOfType
-import org.jetbrains.kotlin.idea.core.util.end
-import org.jetbrains.kotlin.idea.core.util.range
-import org.jetbrains.kotlin.idea.core.util.start
-import java.awt.datatransfer.DataFlavor
-import java.awt.datatransfer.Transferable
-
-class ConvertXmlCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferableData>() {
- private val LOG = Logger.getInstance(ConvertXmlCopyPasteProcessor::class.java)
-
- private class CopiedXmlCode(
- val fileText: String,
- val startOffsets: IntArray,
- val endOffsets: IntArray
- ) : TextBlockTransferableData {
- override fun getFlavor() =
- DATA_FLAVOR
- override fun getOffsetCount() = 0
-
- override fun getOffsets(offsets: IntArray?, index: Int) = index
- override fun setOffsets(offsets: IntArray?, index: Int) = index
-
- companion object {
- val DATA_FLAVOR: DataFlavor = DataFlavor(
- CopiedXmlCode::class.java,
- "class: ConvertXmlCopyPasteProcessor.CopiedXmlCode"
- )
- }
- }
-
- private class CopiedText(val text: String) : TextBlockTransferableData {
- override fun getFlavor() =
- DATA_FLAVOR
- override fun getOffsetCount() = 0
-
- override fun getOffsets(offsets: IntArray?, index: Int) = index
- override fun setOffsets(offsets: IntArray?, index: Int) = index
-
- companion object {
- val DATA_FLAVOR: DataFlavor = DataFlavor(
- CopiedText::class.java,
- "class: ConvertXmlCopyPasteProcessor.CopiedText"
- )
- }
- }
-
- override fun collectTransferableData(
- file: PsiFile,
- editor: Editor,
- startOffsets: IntArray,
- endOffsets: IntArray
- ): List<TextBlockTransferableData> {
- if (file !is XmlFile) return emptyList()
- return listOf(
- CopiedXmlCode(
- file.text,
- startOffsets,
- endOffsets
- )
- )
- }
-
- override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
- try {
- when {
- // Do not try to convert text copied from Kotlin file.
- content.isDataFlavorSupported(CopiedKotlinCode.DATA_FLAVOR) -> return emptyList()
- content.isDataFlavorSupported(CopiedXmlCode.DATA_FLAVOR) -> {
- return listOf(content.getTransferData(CopiedXmlCode.DATA_FLAVOR)
- as TextBlockTransferableData)
- }
- content.isDataFlavorSupported(DataFlavor.stringFlavor) -> {
- val text = content.getTransferData(DataFlavor.stringFlavor) as String
- return listOf(
- CopiedText(
- text
- )
- )
- }
- }
- } catch (e: Throwable) {
- LOG.error(e)
- }
- return emptyList()
- }
-
- override fun processTransferableData(
- project: Project,
- editor: Editor,
- bounds: RangeMarker,
- caretOffset: Int,
- indented: Ref<Boolean>,
- values: List<TextBlockTransferableData>
- ) {
- if (DumbService.getInstance(project).isDumb) return
-
- val editorOptions = KtxEditorOptions.getInstance()
- if (!editorOptions.enableXmlToKtxConversion) return
-
- val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) as?
- KtFile ?: return
- val transferableData = values.single()
- var (fileText, startOffsets, endOffsets) = when (transferableData) {
- is CopiedXmlCode ->
- Triple(
- transferableData.fileText,
- transferableData.startOffsets,
- transferableData.endOffsets
- )
- is CopiedText ->
- Triple(
- transferableData.text,
- intArrayOf(0),
- intArrayOf(transferableData.text.length)
- )
- else ->
- throw IllegalStateException("Unsupported transferable data: $transferableData")
- }
- var xmlFile = createXmlFile(project, fileText)
-
- if (transferableData is CopiedText) {
- if (hasErrors(xmlFile)) {
- // Try to parse as attributes if we are inside a KTX tag.
- val closestKtxTag =
- targetFile.findElementAt(caretOffset)?.parentOfType<KtxElement>() ?: return
- val tagName =
- closestKtxTag.qualifiedTagName?.text ?: closestKtxTag.simpleTagName?.text
- ?: return
-
- // Check that the caret is inside that tag attributes list.
- val bracketsElements = closestKtxTag.bracketsElements
- val firstLTPosition = bracketsElements
- .filter { it is TreeElement && it.elementType == KtTokens.LT }
- .map { it.startOffset }
- .min() ?: return
- val firstGTPosition = bracketsElements
- .filter { it is TreeElement && it.elementType == KtTokens.GT }
- .map { it.startOffset }
- .min() ?: return
-
- if (caretOffset < firstLTPosition || caretOffset > firstGTPosition) {
- return
- }
-
- // Create dummy XML tag and file.
- val filePrefix = "<$tagName "
- val newStartOffset = filePrefix.length
- startOffsets = intArrayOf(newStartOffset)
- endOffsets = intArrayOf(newStartOffset + fileText.length)
- xmlFile = createXmlFile(project, "$filePrefix$fileText />")
-
- if (hasErrors(xmlFile)) {
- return
- }
- }
-
- // Make sure we don't try to convert copied KTX code.
- if (!hasAttributeWithNamespace(xmlFile)) {
- return
- }
- }
-
- val ranges = startOffsets.mapIndexed { i, startOffset ->
- TextRange(startOffset, endOffsets[i])
- }
- val elementAndTextList = ElementAndTextList().apply {
- ranges.forEach { this.collectElementsToConvert(xmlFile, it) }
- }
-
- // Convert PsiElement's to AST Element's.
- val conversionResult = StringBuilder()
- val imports = hashSetOf<FqName>()
- val converter = XmlToKtxConverter(targetFile)
- var skipConversion = true
- elementAndTextList.process(object : ElementsAndTextsProcessor {
- override fun processElement(element: PsiElement) {
- // TODO(jdemeulenaere): Better comment converter.
- val codeBuilder = CodeBuilder(null, EmptyDocCommentConverter)
- val resultKtx = converter.convertElement(element)
- if (resultKtx != null) {
- skipConversion = false
- codeBuilder.append(resultKtx)
- imports.addAll(codeBuilder.importsToAdd)
- conversionResult.append(codeBuilder.resultText)
- } else {
- conversionResult.append(element.text)
- }
- }
-
- override fun processText(string: String) {
- conversionResult.append(string)
- }
- })
-
- if (skipConversion || !confirmConversion(project)) {
- return
- }
-
- // Replace text.
- val resultKtx = conversionResult.toString()
- val enclosingCallable = targetFile.findElementAt(bounds.startOffset)?.let {
- enclosingCallable(it)
- }
- val conversionResultText = if (enclosingCallable != null) {
- resultKtx
- } else {
- // TODO(jdemeulenaere): Find better function name. If we are pasting in a class that has the same name as the file (common
- // case), we will clash with the constructor. Maybe override invoke operator if it's not already overridden in that class or
- // its subclasses ?
- val functionName = targetFile.name.takeWhile { it != '.' }
- createFunctionalComponent(
- functionName,
- resultKtx,
- imports
- )
- }
- val rangeAfterReplace = replaceText(editor, conversionResultText, bounds)
- PsiDocumentManager.getInstance(project).commitAllDocuments()
-
- // Suggest to add @Composable annotation and/or androidx.compose.* import if necessary.
- if (editorOptions.enableAddComposableAnnotation) {
- val fixes = arrayListOf<() -> Unit>()
-
- // Add @Composable annotation.
- if (enclosingCallable != null &&
- // TODO(jdemeulenaere): For some reason, checking for annotations on lambdas doesn't work. Fix that and remove this check.
- enclosingCallable is KtNamedFunction) {
- val annotationFqName = ComposeUtils.composeFqName("Composable")
- if (enclosingCallable.findAnnotation(annotationFqName) == null) {
- fixes.add {
- runWriteAction {
- enclosingCallable.addAnnotation(annotationFqName)
- }
- }
- }
- }
-
- // Add androidx.compose.* import.
- val composeStarFqName = ComposeUtils.composeFqName("*")
- if (targetFile.importDirectives.none { it.importedFqName == composeStarFqName }) {
- fixes.add { addComposeStarImport(targetFile) }
- }
-
- if (fixes.isNotEmpty()) {
- // Ask for approval if necessary.
- val shouldApplyFixes = if (editorOptions.donTShowAddComposableAnnotationDialog) {
- true
- } else {
- val dialog =
- KtxAddComposableAnnotationDialog(
- project
- )
- dialog.show()
- dialog.isOK
- }
-
- // Apply fixes.
- if (shouldApplyFixes) {
- fixes.forEach { it() }
- }
- }
- }
-
- // TODO(jdemeulenaere): If enclosingCallable == null (i.e. if we generated the enclosing function), put the cursor at the function
- // name position and select it to easily change it right after pasting.
-
- // Import classes.
- if (imports.isNotEmpty()) {
- runWriteAction {
- imports.forEach { fqName ->
- targetFile.resolveImportReference(fqName).firstOrNull()?.let {
- ImportInsertHelper.getInstance(project).importDescriptor(targetFile, it)
- }
- }
- }
-
- PsiDocumentManager.getInstance(project).commitAllDocuments()
- }
-
- // Format code.
- XmlToKtxConverter.formatCode(
- targetFile,
- rangeAfterReplace
- )
- return
- }
-
- private fun createXmlFile(project: Project, fileText: String): XmlFile {
- return PsiFileFactory.getInstance(project).createFileFromText(
- XMLLanguage.INSTANCE, fileText
- ) as XmlFile
- }
-
- private fun hasErrors(xmlFile: XmlFile) = xmlFile.anyDescendantOfType<PsiErrorElement>()
-
- private fun enclosingCallable(element: PsiElement): KtCallableDeclaration? {
- var current = element
- while (current !is KtFile) {
- if (current is KtCallableDeclaration) {
- return current
- }
- current = current.parent
- }
- return null
- }
-
- private fun hasAttributeWithNamespace(file: XmlFile): Boolean {
- val rootTag = file.rootTag ?: return false
- return hasAttributeWithNamespace(rootTag)
- }
-
- private fun hasAttributeWithNamespace(tag: XmlTag): Boolean {
- for (attribute in tag.attributes) {
- if (attribute.namespacePrefix.isNotEmpty()) {
- return true
- }
- }
-
- for (subTag in tag.subTags) {
- if (hasAttributeWithNamespace(subTag)) {
- return true
- }
- }
-
- return false
- }
-
- private fun replaceText(
- editor: Editor,
- conversionResultText: String,
- bounds: RangeMarker
- ): RangeMarker {
- return runWriteAction {
- val startOffset = bounds.startOffset
- editor.document.replaceString(startOffset, bounds.endOffset, conversionResultText)
-
- val endOffset = startOffset + conversionResultText.length
- editor.caretModel.moveToOffset(endOffset)
- editor.document.createRangeMarker(startOffset, endOffset).apply {
- isGreedyToLeft = true
- isGreedyToRight = true
- }
- }
- }
-
- private fun ElementAndTextList.collectElementsToConvert(file: PsiFile, range: TextRange) {
- val elements = file.elementsInRange(range)
- val fileText = file.text
- if (elements.isEmpty()) {
- add(fileText.substring(range.start, range.end))
- } else {
- add(fileText.substring(range.start, elements.first().range.start))
- this += elements
- add(fileText.substring(elements.last().range.end, range.end))
- }
- }
-
- private fun confirmConversion(project: Project): Boolean {
- if (KtxEditorOptions.getInstance().donTShowKtxConversionDialog) return true
-
- val dialog = KtxPasteFromXmlDialog(project)
- dialog.show()
- return dialog.isOK
- }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/EnumReflection.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/EnumReflection.kt
deleted file mode 100644
index 42357a7..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/EnumReflection.kt
+++ /dev/null
@@ -1,190 +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.compose.plugins.idea.conversion
-
-import com.intellij.codeInsight.AnnotationUtil
-import com.intellij.openapi.project.Project
-import com.intellij.psi.JavaPsiFacade
-import com.intellij.psi.PsiAnnotation
-import com.intellij.psi.PsiAnnotationMemberValue
-import com.intellij.psi.PsiArrayInitializerMemberValue
-import com.intellij.psi.PsiClass
-import com.intellij.psi.PsiModifierListOwner
-import com.intellij.psi.PsiReference
-import org.jetbrains.kotlin.asJava.elements.KtLightPsiLiteral
-import org.jetbrains.kotlin.descriptors.ClassDescriptor
-import org.jetbrains.kotlin.descriptors.ClassKind
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.descriptors.FunctionDescriptor
-import org.jetbrains.kotlin.descriptors.PropertyDescriptor
-import org.jetbrains.kotlin.descriptors.ValueDescriptor
-import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
-import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
-import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
-import org.jetbrains.kotlin.idea.search.allScope
-import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
-import org.jetbrains.kotlin.name.FqName
-import androidx.compose.plugins.idea.AttributeInfo
-import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
-import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
-import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
-import org.jetbrains.kotlin.types.KotlinType
-import org.jetbrains.kotlin.types.typeUtil.isEnum
-import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
-import java.util.ArrayList
-
-private val MAGIC_CONSTANT_ANNOTATIONS = listOf("IntDef", "LongDef", "StringDef").flatMap {
- listOf("android.annotation.$it", "android.support.annotation.$it")
-}
-
-fun AttributeInfo.getPossibleValues(project: Project): List<FqName> {
- return getPossibleValues(
- project,
- descriptor ?: return emptyList()
- )
-}
-
-internal fun getPossibleValues(project: Project, descriptor: DeclarationDescriptor): List<FqName> {
- val valueDescriptor: ValueDescriptor = when (descriptor) {
- is FunctionDescriptor -> descriptor.valueParameters.takeIf { it.size == 1 }?.first()
- is PropertyDescriptor -> descriptor
- is ValueParameterDescriptor -> descriptor
- else -> null
- } ?: return emptyList()
-
- if (valueDescriptor.type.isEnum()) {
- return getPossibleValuesFromEnum(valueDescriptor.type)
- }
-
- return getPossibleValuesFromAnnotations(
- project,
- valueDescriptor
- )
-}
-
-internal fun getPossibleValuesFromEnum(type: KotlinType): List<FqName> {
- return (type.constructor.declarationDescriptor as ClassDescriptor)
- .unsubstitutedMemberScope
- .getDescriptorsFiltered()
- .filter {
- it is EnumEntrySyntheticClassDescriptor ||
- (it is LazyClassDescriptor && it.kind == ClassKind.ENUM_ENTRY)
- }
- .mapNotNull { it.fqNameOrNull() }
-}
-
-private fun getPossibleValuesFromAnnotations(
- project: Project,
- descriptor: DeclarationDescriptor
-): List<FqName> {
- val fqNames = arrayListOf<FqName>()
-
- // When possible, use the overload taking PsiModifierListOwner as parameter as it has support for external annotations.
- val psiElement = descriptor.findPsi() ?: descriptor.original.findPsi()
- if (psiElement != null && psiElement is PsiModifierListOwner) {
- fillPossibleEnumValues(
- project,
- psiElement,
- fqNames,
- hashSetOf()
- )
- } else {
- val visited = hashSetOf<PsiClass>()
- descriptor.annotations.forEach {
- val annotationFqName = it.fqName?.asString() ?: return@forEach
- fillPossibleEnumValues(
- project,
- annotationFqName,
- fqNames,
- visited
- )
- }
- }
-
- return fqNames
-}
-
-private fun fillPossibleEnumValues(
- project: Project,
- annotationFqName: String,
- enumFqNames: ArrayList<FqName>,
- visited: MutableSet<PsiClass>
-) {
- val annotationClass = JavaPsiFacade.getInstance(project).findClass(
- annotationFqName,
- project.allScope()
- ) ?: return
- if (!visited.add(annotationClass)) return
-
- // We use the navigationElement if possible such that we also get annotations with SOURCE retention.
- val annotated = (annotationClass.navigationElement as? PsiModifierListOwner) ?: annotationClass
- fillPossibleEnumValues(
- project,
- annotated,
- enumFqNames,
- visited
- )
-}
-
-private fun fillPossibleEnumValues(
- project: Project,
- annotated: PsiModifierListOwner,
- enumFqNames: ArrayList<FqName>,
- visited: MutableSet<PsiClass>
-) {
- val annotations = AnnotationUtil.getAllAnnotations(
- annotated,
- /* inHierarchy= */ true,
- /* visited= */ null
- )
- for (annotation in annotations) {
- val qualifiedName = annotation.qualifiedName ?: continue
- if (qualifiedName in MAGIC_CONSTANT_ANNOTATIONS) {
- enumFqNames.addAll(
- getMagicConstantEnumValues(
- annotation
- )
- )
- } else {
- fillPossibleEnumValues(
- project,
- qualifiedName,
- enumFqNames,
- visited
- )
- }
- }
-}
-
-private fun getMagicConstantEnumValues(annotation: PsiAnnotation): List<FqName> {
- val valueAttribute = annotation.findAttributeValue("value")
- val allowedValues = (valueAttribute as? PsiArrayInitializerMemberValue)?.initializers
- ?: PsiAnnotationMemberValue.EMPTY_ARRAY
- return allowedValues
- .map<Any, Array<out PsiReference>> {
- when (it) {
- is PsiReference -> arrayOf(it)
- // TODO(jdemeulenaere): Fix the case when the value is expressed as a KtDotQualifiedExpression (i.e. 'View.VISIBLE' instead
- // of 'VISIBLE').
- is KtLightPsiLiteral -> it.references
- else -> emptyArray()
- }
- }
- .mapNotNull {
- references -> references.firstNotNullResult { it.resolve()?.getKotlinFqName() }
- }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.form b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.form
deleted file mode 100644
index 52a86f4..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.form
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="androidx.compose.plugins.idea.conversion.KtxAddComposableAnnotationDialog">
- <grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
- <margin top="0" left="0" bottom="0" right="0"/>
- <constraints>
- <xy x="20" y="20" width="640" height="400"/>
- </constraints>
- <properties/>
- <border type="none"/>
- <children>
- <component id="746fc" class="javax.swing.JLabel">
- <constraints>
- <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
- </constraints>
- <properties>
- <text value="Looks like your code is missing the @Composable annotation and/or the androidx.compose.* import. Do you want to fix that?"/>
- </properties>
- </component>
- <vspacer id="b8512">
- <constraints>
- <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
- </constraints>
- </vspacer>
- <component id="98483" class="javax.swing.JCheckBox" binding="donTShowThisCheckBox" default-binding="true">
- <constraints>
- <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
- </constraints>
- <properties>
- <text value="&Don't show this dialog next time"/>
- </properties>
- </component>
- </children>
- </grid>
-</form>
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.kt
deleted file mode 100644
index d12df35..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.kt
+++ /dev/null
@@ -1,65 +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.compose.plugins.idea.conversion
-
-import com.intellij.CommonBundle
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.ui.DialogWrapper
-import androidx.compose.plugins.idea.editor.KtxEditorOptions
-import java.awt.Container
-import javax.swing.Action
-import javax.swing.JCheckBox
-import javax.swing.JComponent
-import javax.swing.JPanel
-
-class KtxAddComposableAnnotationDialog(
- project: Project
-) : DialogWrapper(project, false) {
- private lateinit var panel: JPanel
- private lateinit var donTShowThisCheckBox: JCheckBox
-
- init {
- isModal = true
- title = "Add @Composable annotation"
- init()
- }
-
- override fun createCenterPanel(): JComponent = panel
-
- override fun getContentPane(): Container = panel
-
- override fun createActions(): Array<Action> {
- setOKButtonText(CommonBundle.getYesButtonText())
- setCancelButtonText(CommonBundle.getNoButtonText())
- return arrayOf(okAction, cancelAction)
- }
-
- override fun doOKAction() {
- if (donTShowThisCheckBox.isSelected) {
- KtxEditorOptions.getInstance().donTShowAddComposableAnnotationDialog = true
- }
- super.doOKAction()
- }
-
- override fun doCancelAction() {
- if (donTShowThisCheckBox.isSelected) {
- KtxEditorOptions.getInstance().enableAddComposableAnnotation = false
- KtxEditorOptions.getInstance().donTShowAddComposableAnnotationDialog = true
- }
- super.doCancelAction()
- }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.form b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.form
deleted file mode 100644
index 8a4f6d3..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.form
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="androidx.compose.plugins.idea.conversion.KtxPasteFromXmlDialog">
- <grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
- <margin top="0" left="0" bottom="0" right="0"/>
- <constraints>
- <xy x="20" y="20" width="515" height="400"/>
- </constraints>
- <properties/>
- <border type="none"/>
- <children>
- <component id="746fc" class="javax.swing.JLabel">
- <constraints>
- <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
- </constraints>
- <properties>
- <text value="Clipboard content copied from XML file. Do you want to convert it to Kotlin KTX code?"/>
- </properties>
- </component>
- <vspacer id="b8512">
- <constraints>
- <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
- </constraints>
- </vspacer>
- <component id="98483" class="javax.swing.JCheckBox" binding="donTShowThisCheckBox" default-binding="true">
- <constraints>
- <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
- </constraints>
- <properties>
- <text value="&Don't show this dialog next time"/>
- </properties>
- </component>
- </children>
- </grid>
-</form>
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.kt
deleted file mode 100644
index 573e5a8..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.kt
+++ /dev/null
@@ -1,55 +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.compose.plugins.idea.conversion
-
-import com.intellij.CommonBundle
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.ui.DialogWrapper
-import androidx.compose.plugins.idea.editor.KtxEditorOptions
-import java.awt.Container
-import javax.swing.Action
-import javax.swing.JCheckBox
-import javax.swing.JComponent
-import javax.swing.JPanel
-
-class KtxPasteFromXmlDialog(project: Project) : DialogWrapper(project, false) {
- private lateinit var panel: JPanel
- private lateinit var donTShowThisCheckBox: JCheckBox
-
- init {
- isModal = true
- title = "Convert Code From XML"
- init()
- }
-
- override fun createCenterPanel(): JComponent = panel
-
- override fun getContentPane(): Container = panel
-
- override fun createActions(): Array<Action> {
- setOKButtonText(CommonBundle.getYesButtonText())
- setCancelButtonText(CommonBundle.getNoButtonText())
- return arrayOf(okAction, cancelAction)
- }
-
- override fun doOKAction() {
- if (donTShowThisCheckBox.isSelected) {
- KtxEditorOptions.getInstance().donTShowKtxConversionDialog = true
- }
- super.doOKAction()
- }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToComponentAction.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToComponentAction.kt
deleted file mode 100644
index 7bb065e..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToComponentAction.kt
+++ /dev/null
@@ -1,236 +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.compose.plugins.idea.conversion
-
-import com.google.common.base.CaseFormat
-import com.intellij.openapi.actionSystem.AnAction
-import com.intellij.openapi.actionSystem.AnActionEvent
-import com.intellij.openapi.actionSystem.CommonDataKeys
-import com.intellij.openapi.fileEditor.FileDocumentManager
-import com.intellij.openapi.fileEditor.FileEditorManager
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.vfs.VfsUtilCore
-import com.intellij.openapi.vfs.VirtualFile
-import com.intellij.openapi.vfs.VirtualFileVisitor
-import com.intellij.psi.PsiDocumentManager
-import com.intellij.psi.PsiManager
-import com.intellij.psi.xml.XmlFile
-import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
-import org.jetbrains.kotlin.idea.j2k.EmptyDocCommentConverter
-import org.jetbrains.kotlin.idea.core.util.toPsiFile
-import org.jetbrains.kotlin.idea.util.ImportInsertHelper
-import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
-import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.jetbrains.kotlin.j2k.CodeBuilder
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtFile
-import java.io.File
-
-class XmlToComponentAction : AnAction() {
- companion object {
- fun convertFiles(xmlFiles: List<XmlFile>) {
- if (xmlFiles.isEmpty()) {
- return
- }
-
- // TODO(jdemeulenaere): Check if there are syntax errors in any file, in which case either abort or warn that result might be incorrect.
-
- val project = xmlFiles.first().project
- project.executeWriteCommand("Convert XML Files to Component") {
- val convertedFiles = xmlFiles.map {
- convertFile(
- it
- )
- }
- PsiDocumentManager.getInstance(project).commitAllDocuments()
-
- // Open first converted file.
- FileEditorManager.getInstance(project).openFile(
- convertedFiles.first().virtualFile,
- true
- )
- }
- }
-
- private fun convertFile(sourceFile: XmlFile): KtFile {
- val copy = sourceFile.copy() as XmlFile
- val targetFile =
- renameFile(
- sourceFile
- )
-
- val resultKtx = XmlToKtxConverter(targetFile).convertElement(copy)
- val (content, imports) = if (resultKtx != null) {
- // TODO(jdemeulenaere): Better comment converter.
- val codeBuilder = CodeBuilder(null, EmptyDocCommentConverter)
- codeBuilder.append(resultKtx)
- val functionName =
- functionName(
- copy
- )
- val imports = codeBuilder.importsToAdd.toMutableSet()
- val content = createFunctionalComponent(
- functionName,
- codeBuilder.resultText,
- imports
- )
- Pair(content, imports)
- } else {
- Pair(copy.text, emptySet<FqName>())
- }
-
- replaceContent(
- targetFile,
- content
- )
- addImports(
- targetFile,
- imports
- )
- XmlToKtxConverter.formatCode(
- targetFile
- )
- return targetFile
- }
-
- private fun functionName(file: XmlFile): String {
- // TODO(jdemeulenaere): Ask the user which file/function name to use (and pre-fill name with this algorithm) if converting only one file.
- var functionName = file.name
-
- // Remove extension.
- val dotIndex = functionName.lastIndexOf('.')
- if (dotIndex != -1) {
- functionName = functionName.substring(0, dotIndex)
- }
-
- // Remove special characters.
- functionName = functionName.replace(Regex("[^A-Za-z0-9_]"), "")
-
- // Strip activity_* prefix away.
- if (functionName.startsWith("activity_")) {
- functionName = functionName.substring("activity_".length)
- }
-
- // Convert case.
- return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, functionName)
- }
-
- private fun replaceContent(file: KtFile, content: String) {
- // Replace content.
- val documentManager = PsiDocumentManager.getInstance(file.project)
- val document = documentManager.getDocument(file)!!
- document.replaceString(0, document.textLength, content)
- FileDocumentManager.getInstance().saveDocument(document)
- documentManager.commitDocument(document)
- }
-
- private fun renameFile(sourceFile: XmlFile): KtFile {
- // TODO(jdemeulenaere): If we are converting only one file, ask the user in which folder we should move that file.
- // TODO(jdemeulenaere): Handle scratch files (change language mapping).
- val virtualFile = sourceFile.virtualFile
- val ioFile = File(virtualFile.path.replace('/', File.separatorChar))
- val className =
- functionName(
- sourceFile
- )
- var kotlinFileName = "$className.kt"
- var i = 1
- while (true) {
- if (!ioFile.resolveSibling(kotlinFileName).exists()) break
- kotlinFileName = "$className${i++}.kt"
- }
- virtualFile.rename(this, kotlinFileName)
- return virtualFile.toPsiFile(sourceFile.project)!! as KtFile
- }
-
- private fun addImports(ktFile: KtFile, imports: Collection<FqName>) {
- runWriteAction {
- imports.forEach { fqName ->
- ktFile.resolveImportReference(fqName).firstOrNull()?.let {
- ImportInsertHelper.getInstance(ktFile.project).importDescriptor(ktFile, it)
- }
- }
- }
- addComposeStarImport(ktFile)
- }
-
- private fun isAnyXmlFileSelected(project: Project, files: Array<VirtualFile>): Boolean {
- val manager = PsiManager.getInstance(project)
-
- if (files.any { manager.findFile(it) is XmlFile && it.isWritable }) return true
- return files.any { it.isDirectory && isAnyXmlFileSelected(
- project,
- it.children
- )
- }
- }
-
- private fun selectedXmlFiles(event: AnActionEvent): Sequence<XmlFile> {
- val virtualFiles = event.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY)
- ?: return sequenceOf()
- val project = event.project ?: return sequenceOf()
- return allXmlFiles(
- virtualFiles,
- project
- )
- }
-
- private fun allXmlFiles(
- filesOrDirs: Array<VirtualFile>,
- project: Project
- ): Sequence<XmlFile> {
- val manager = PsiManager.getInstance(project)
- return allFiles(
- filesOrDirs
- )
- .asSequence()
- .mapNotNull { manager.findFile(it) as? XmlFile }
- }
-
- private fun allFiles(filesOrDirs: Array<VirtualFile>): Collection<VirtualFile> {
- val result = ArrayList<VirtualFile>()
- for (file in filesOrDirs) {
- VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor<Unit>() {
- override fun visitFile(file: VirtualFile): Boolean {
- result.add(file)
- return true
- }
- })
- }
- return result
- }
- }
-
- override fun actionPerformed(e: AnActionEvent) {
- val xmlFiles = selectedXmlFiles(
- e
- ).filter { it.isWritable }.toList()
- convertFiles(
- xmlFiles
- )
- }
-
- override fun update(e: AnActionEvent) {
- val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return
- val project = e.project ?: return
- e.presentation.isVisible =
- isAnyXmlFileSelected(
- project,
- virtualFiles
- )
- }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToKtxConverter.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToKtxConverter.kt
deleted file mode 100644
index 490dfec..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToKtxConverter.kt
+++ /dev/null
@@ -1,381 +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.compose.plugins.idea.conversion
-
-import com.intellij.openapi.editor.RangeMarker
-import com.intellij.psi.JavaPsiFacade
-import com.intellij.psi.PsiClass
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiFile
-import com.intellij.psi.codeStyle.CodeStyleManager
-import com.intellij.psi.xml.XmlAttribute
-import com.intellij.psi.xml.XmlFile
-import com.intellij.psi.xml.XmlTag
-import org.jetbrains.kotlin.builtins.DefaultBuiltIns
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
-import org.jetbrains.kotlin.descriptors.resolveClassByFqName
-import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
-import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaClassDescriptor
-import org.jetbrains.kotlin.idea.core.isExcludedFromAutoImport
-import org.jetbrains.kotlin.idea.core.isVisible
-import org.jetbrains.kotlin.idea.imports.importableFqName
-import org.jetbrains.kotlin.idea.search.allScope
-import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.jetbrains.kotlin.incremental.components.NoLookupLocation
-import org.jetbrains.kotlin.j2k.ast.DeclarationStatement
-import org.jetbrains.kotlin.j2k.ast.Element
-import org.jetbrains.kotlin.j2k.ast.Expression
-import org.jetbrains.kotlin.j2k.ast.KtxAttribute
-import org.jetbrains.kotlin.j2k.ast.KtxElement
-import org.jetbrains.kotlin.j2k.ast.LiteralExpression
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtFile
-import androidx.compose.plugins.idea.AttributeInfo
-import androidx.compose.plugins.idea.AttributeInfoExtractor
-import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
-import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
-import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
-import org.jetbrains.kotlin.types.KotlinType
-import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
-import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
-
-class XmlToKtxConverter(private val targetFile: KtFile) {
- companion object {
- private const val NAMESPACE_PREFIX = "xmlns"
- private val NON_ALPHANUMERIC_PATTERN = Regex("[^A-Za-z0-9]")
-
- private val CLASS_PREFIX_LIST = listOf(
- "android.widget.",
- "android.webkit.",
- "android.app.",
- "android.view."
- )
-
- private enum class SpecialTags(val tagName: String) {
- VIEW("view"),
- BLINK("blink"),
- MERGE("merge"),
- INCLUDE("include"),
- REQUEST_FOCUS("requestFocus"),
- TAG("tag")
- }
-
- enum class XmlNamespace(val uri: String, val defaultPrefix: String) {
- ANDROID("http://schemas.android.com/apk/res/android", "android"),
- ANDROID_TOOLS("http://schemas.android.com/tools", "tools"),
- ANDROID_APP("http://schemas.android.com/apk/res-auto", "app");
- }
-
- fun formatCode(file: PsiFile, range: RangeMarker? = null) {
- runWriteAction {
- val codeStyleManager = CodeStyleManager.getInstance(file.project)
- if (range != null && range.isValid) {
- codeStyleManager.reformatRange(file, range.startOffset, range.endOffset)
- } else {
- codeStyleManager.reformat(file)
- }
- }
- }
- }
-
- private val project = targetFile.project
- // TODO(jdemeulenaere): Compare with `isVisibleDescriptor` in (BaseCompose)CompletionSession to add more checks here.
- private val visibilityFilter: (DeclarationDescriptor) -> Boolean = lambda@{ descriptor ->
- if (descriptor is DeclarationDescriptorWithVisibility) {
- return@lambda descriptor.isVisible(targetFile.findModuleDescriptor())
- }
-
- return@lambda descriptor.isExcludedFromAutoImport(project, targetFile)
- }
- private val attributeInfoExtractor =
- AttributeInfoExtractor(targetFile, visibilityFilter)
-
- fun convertElement(element: PsiElement): Element? = when (element) {
- is XmlFile -> convertFile(element)
- is XmlTag -> convertTag(element)
- // We return an empty element as `maybeConvertAttribute` returns null if the attribute should be skipped.
- is XmlAttribute -> maybeConvertAttribute(element) ?: Element.Empty
- else -> null
- }
-
- private fun convertFile(element: XmlFile): Element {
- return element.rootTag?.let { convertTag(it) } ?: Element.Empty
- }
-
- private val valueConversionsCache = hashMapOf<Pair<String, String>, List<ValueConversion>>()
- private fun getValueConversions(
- tagQualifiedName: String,
- xmlAttributeName: String
- ): List<ValueConversion> {
- return valueConversionsCache.computeIfAbsent(Pair(tagQualifiedName, xmlAttributeName)) {
- val descriptor = findClass(tagQualifiedName)?.getJavaClassDescriptor()
- ?: return@computeIfAbsent emptyList()
- val classNames = descriptor.getAllSuperClassifiers().toList().mapNotNull {
- it.fqNameOrNull()?.asString()
- }.toSet()
- ANDROID_CONVERSION.classConversions
- .filter { it.matchesAny(classNames) }
- .flatMap { it.attributeConversions }
- .filter { it.matches(xmlAttributeName) }
- .flatMap { it.valueConversions }
- }
- }
-
- private val attributeInfosCache = hashMapOf<String, Map<String, List<AttributeInfo>>>()
- private fun getAttributeInfos(
- tagQualifiedName: String,
- ktxAttributeName: String
- ): List<AttributeInfo> {
- return attributeInfosCache.computeIfAbsent(tagQualifiedName) {
- val descriptor = findClass(tagQualifiedName)?.getJavaClassDescriptor()
- ?: return@computeIfAbsent emptyMap()
- val attributeInfos = arrayListOf<AttributeInfo>()
- // TODO(jdemeulenaere): Allow to filter with ktxAttributeName at the AttributeInfoExtractor level instead.
- attributeInfoExtractor.extract(descriptor) { attributeInfos.addAll(it) }
- attributeInfos.groupBy { it.name }
- }[ktxAttributeName] ?: emptyList()
- }
-
- private fun findClass(qualifiedName: String): PsiClass? {
- return JavaPsiFacade.getInstance(project).findClass(qualifiedName, project.allScope())
- }
-
- private data class EnumValue(
- val attributeInfo: AttributeInfo,
- val fqNames: List<FqName>
- )
-
- private val enumValuesCache = hashMapOf<Pair<String, String>, List<EnumValue>>()
- private fun getEnumValues(tagQualifiedName: String, ktxAttributeName: String): List<EnumValue> {
- // TODO(jdemeulenaere): Precompute those for Android views or download android SDK & external annotations jars.
- // TODO(jdemeulenaere): We might want to somehow filter those, as some incorrect attribute conversions will be returned (e.g.
- // "notFocusable" in XML will be mapped to android.view.View.NOT_FOCUSABLE, which does not exist).
- return enumValuesCache.computeIfAbsent(Pair(tagQualifiedName, ktxAttributeName)) {
- getAttributeInfos(tagQualifiedName, ktxAttributeName).mapNotNull { attributeInfo ->
- attributeInfo.getPossibleValues(project)
- .takeIf { it.isNotEmpty() }
- ?.let {
- EnumValue(
- attributeInfo,
- it
- )
- }
- }
- }
- }
-
- /**
- * Returns the common prefix length of the possible values, e.g: :
- * getCommonPrefixLength(["TEXT_ALIGNMENT_TEXT_START", "TEXT_ALIGNMENT_CENTER"]) == "TEXT_ALIGNMENT_".length
- */
- private fun getCommonPrefixLength(possibleValues: List<String>): Int {
- // TODO(jdemeulenaere): In the latest android sources, the @IntDef attribute has a prefix value that gives exactly the prefix we
- // want to remove. Use that when generating the conversion configuration.
-
- var lastUnderscoreIndex = -1
- val minLength = possibleValues.map(String::length).min()!!
- for (i in 0 until minLength) {
- val char = possibleValues[0][i]
- if (!possibleValues.all { it[i] == char }) {
- break
- }
- if (char == '_') {
- lastUnderscoreIndex = i
- }
- }
- return lastUnderscoreIndex + 1
- }
-
- private fun convertTag(tag: XmlTag): Element {
- when (tag.name) {
- Companion.SpecialTags.MERGE.tagName -> return DeclarationStatement(tag.subTags.map {
- convertTag(it)
- })
- // TODO(jdemeulenaere): Instead of ignoring the <include> tag, we might want to suggest the user to convert the included layout
- // file into a component, or inline and convert its content.
- Companion.SpecialTags.INCLUDE.tagName,
- // TODO(jdemeulenaere): Make sure we want to ignore <tag> and <requestFocus/> tags.
- Companion.SpecialTags.TAG.tagName,
- Companion.SpecialTags.REQUEST_FOCUS.tagName -> return Element.Empty
- }
-
- val attributes = tag.attributes.mapNotNull(::maybeConvertAttribute)
- val body = tag.subTags.map(::convertTag)
- val (simpleName, qualifiedName) = getTagSimpleAndQualifiedName(tag)
- return KtxElement(simpleName.asIdentifier(qualifiedName), attributes, body)
- }
-
- private fun getTagSimpleAndQualifiedName(tag: XmlTag): Pair<String, String> {
- val fullName = when (tag.name) {
- Companion.SpecialTags.VIEW.tagName -> tag.getAttributeValue("class")
- ?: "android.view.View"
- // The <blink></blink> tag will be inflated as a android.view.LayoutInflater.BlinkLayout, which is a private class that extends
- // FrameLayout. User will lose the blinking but this is the closest we can get.
- Companion.SpecialTags.BLINK.tagName -> "android.widget.FrameLayout"
- else -> tag.name
- }
-
- val dotIndex = fullName.lastIndexOf('.')
- if (dotIndex != -1) {
- // TODO(jdemeulenaere): Check that import doesn't conflict with another (existing or future) import (in which case use the fully qualified name).
- return fullName.substring(dotIndex + 1) to fullName
- }
-
- val qualifiedName = CLASS_PREFIX_LIST
- .map { "$it$fullName" }
- .firstOrNull {
- JavaPsiFacade.getInstance(project).findClass(it, project.allScope()) != null
- } ?: "android.widget.$fullName"
- return fullName to qualifiedName
- }
-
- private fun XmlAttribute.isInNamespace(ns: XmlNamespace): Boolean {
- return if (namespace.isNotEmpty()) {
- namespace == ns.uri
- } else {
- namespacePrefix == ns.defaultPrefix
- }
- }
-
- private fun shouldConvertAttribute(attribute: XmlAttribute): Boolean {
- return attribute.namespacePrefix != NAMESPACE_PREFIX &&
- !attribute.isInNamespace(Companion.XmlNamespace.ANDROID_TOOLS) &&
- !(attribute.value ?: "").startsWith("@+id/") &&
- (attribute.parent.name != Companion.SpecialTags.VIEW.tagName ||
- attribute.name != "class")
- }
-
- private fun maybeConvertAttribute(attribute: XmlAttribute): KtxAttribute? {
- if (!shouldConvertAttribute(attribute)) {
- return null
- }
-
- return convertAttribute(attribute)
- }
-
- private fun convertAttribute(attribute: XmlAttribute): KtxAttribute {
- val xmlName = attribute.localName // Strips away the namespace.
- val ktxName = getKtxAttributeName(xmlName)
- val xmlValue = attribute.value ?: ""
-
- val tag: XmlTag? = attribute.parent
-
- // Convert the value.
- val tagQualifiedName = tag?.let { getTagSimpleAndQualifiedName(it).second }
- val valueConversions = tagQualifiedName?.let {
- getValueConversions(it, xmlName)
- } ?: emptyList()
- val conversionResult = valueConversions
- .asSequence()
- .filter { it.matches(xmlValue) }
- .mapNotNull {
- it.convert(object : ConversionContext {
- override val xmlName = xmlName
- override val xmlValue = xmlValue
- })
- }
- .firstIsInstanceOrNull<ConversionResult.Success>()
-
- var valueType: KotlinType? = null
- var valueExpression: Expression? = null
- if (conversionResult != null) {
- // If conversion from the ConversionConfiguration succeeded, we end up with an Expression and the fully qualified name of this
- // expression type. We compare the fully qualified name with the KotlinType of the AttributeInfos corresponding to this attribute
- // to try to guess if we should import any extension function/property.
- // The current logic is that if there is one and only one AttributeInfo with that type, and that AttributeInfo is associated to
- // an extension, then we import it, otherwise we don't.
-
- // TODO(jdemeulenaere): Make sure that the comparison with f.q. name from converter and KotlinType from AttributeInfo works well
- // for all cases (e.g. generics).
-
- valueExpression = conversionResult.expression
-
- // TODO(jdemeulenaere/lelandr): There is certainly a better way to map FqName => KotlinType.
- val fqName = conversionResult.kotlinType
- val classDescriptor = DefaultBuiltIns.Instance.builtInsModule.resolveClassByFqName(
- fqName,
- NoLookupLocation.FROM_BUILTINS
- ) ?: findClass(fqName.asString())?.getJavaClassDescriptor()
- valueType = classDescriptor?.defaultType
- } else {
- // If conversion from ConversionConfiguration failed, then we try to match the XML value to an enum using reflection.
-
- val enumValues = tagQualifiedName?.let { getEnumValues(it, ktxName) }
- enumValues?.forEach { (attributeInfo, possibleValues) ->
- assert(possibleValues.isNotEmpty())
-
- // TODO(jdemeulenaere): We certainly want to remove common prefix only for values coming from magic annotations and not
- // actual enum classes.
- val commonPrefixLength =
- if (possibleValues.size == 1) 0 else getCommonPrefixLength(possibleValues.map {
- it.shortName().asString()
- })
-
- // We remove all non alpha numeric characters and lower case when matching values.
- fun simplify(s: String) = s.replace(NON_ALPHANUMERIC_PATTERN, "").toLowerCase()
-
- possibleValues.forEach { fqName ->
- val shortName = fqName.shortName().asString()
- val upperUnderscoreValue = shortName.substring(commonPrefixLength)
-
- if (simplify(upperUnderscoreValue) == simplify(xmlValue)) {
- val nameImport = attributeInfo.descriptor?.takeIf {
- it.isExtension
- }?.importableFqName
- return KtxAttribute(
- ktxName.asIdentifier(nameImport),
- shortName.asIdentifier(fqName)
- )
- }
- }
- }
- }
-
- if (valueExpression == null) {
- valueExpression = LiteralExpression(xmlValue.quoted())
- valueType = DefaultBuiltIns.Instance.stringType
- }
-
- // Add import if we are using an extension function.
- val attributeImport = valueType?.let { theValueType ->
- val attributeInfos = tagQualifiedName?.let { getAttributeInfos(it, ktxName) }
- ?: emptyList()
- attributeInfos
- .filter { theValueType.isSubtypeOf(it.type) }
- // Only import extension function if it's the only one matching that KotlinType.
- .takeIf { it.size == 1 }
- ?.firstOrNull { it.isExtension }
- ?.let { it.descriptor?.importableFqName }
- }
-
- return KtxAttribute(ktxName.asIdentifier(attributeImport), valueExpression)
- }
-
- private fun getKtxAttributeName(
- xmlName: String
- ) = xmlName.replace(Regex("_([a-zA-Z\\d])")) { it.groupValues[1].toUpperCase() }
-
- private fun String.quoted() = "\"${escaped()}\""
-
- private fun String.escaped(): String {
- return this
- .replace("\\", "\\\\") // Replaces all \ by \\
- .replace("\"", "\\\"") // Replaces all " by \"
- .replace("\$", "\\\$") // Replaces all $ by \$
- }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/resources/META-INF/plugin.xml b/compose/compose-ide-plugin/src/main/resources/META-INF/plugin.xml
index a85a310..3e5bfc9 100644
--- a/compose/compose-ide-plugin/src/main/resources/META-INF/plugin.xml
+++ b/compose/compose-ide-plugin/src/main/resources/META-INF/plugin.xml
@@ -37,7 +37,7 @@
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<ktxParsingExtension implementation="androidx.compose.plugins.kotlin.ComposeKtxParsingExtension" />
<storageComponentContainerContributor implementation="androidx.compose.plugins.kotlin.ComponentsClosedDeclarationChecker" />
- <storageComponentContainerContributor implementation="androidx.compose.plugins.idea.ComposableAnnotationChecker" />
+ <storageComponentContainerContributor implementation="androidx.compose.plugins.kotlin.ComposableAnnotationChecker" />
<storageComponentContainerContributor implementation="androidx.compose.plugins.kotlin.UnionAnnotationCheckerProvider" />
<ktxTypeResolutionExtension implementation="androidx.compose.plugins.kotlin.ComposeKtxTypeResolutionExtension"/>
<callResolutionInterceptorExtension implementation="androidx.compose.plugins.kotlin.ComposeCallResolutionInterceptorExtension"/>
@@ -47,12 +47,6 @@
<diagnosticSuppressor implementation="androidx.compose.plugins.kotlin.ComposeDiagnosticSuppressor"/>
<quickFixContributor implementation="androidx.compose.plugins.idea.quickfix.ComposeQuickFixRegistry" />
</extensions>
- <actions>
- <action id="ConvertXmlToComponent" class="androidx.compose.plugins.idea.conversion.XmlToComponentAction"
- text="Convert XML File to Component">
- <add-to-group group-id="ConvertJavaToKotlinGroup" anchor="last"/>
- </action>
- </actions>
<application-components>
<component>
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
index da720b8..6e183dd 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
@@ -22,6 +22,7 @@
import androidx.compose.composer
import androidx.ui.core.Text
import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Row
import kotlin.random.Random
@@ -74,13 +75,13 @@
@Composable
fun Table(children: @Composable() () -> Unit) {
- Column { children() }
+ Column(mainAxisSize = LayoutSize.Expand) { children() }
}
@Composable
fun QueryColumn(query: Query) {
// TODO: we could do some conditional styling here which would make the test better
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Text(text="${query.elapsed}")
Text(text=query.query)
}
@@ -91,9 +92,9 @@
println(db)
val columns = 5
val topQueries = db.topQueries(columns)
- Row {
- Column { Text(text=db.name) }
- Column { Text(text="${db.queries.size}") }
+ Row(mainAxisSize = LayoutSize.Expand) {
+ Column(mainAxisSize = LayoutSize.Expand) { Text(text=db.name) }
+ Column(mainAxisSize = LayoutSize.Expand) { Text(text="${db.queries.size}") }
topQueries.forEach { query ->
QueryColumn(query = query)
}
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
index 6347a30..c121e31 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
@@ -23,6 +23,7 @@
import androidx.ui.foundation.ColoredRect
import androidx.ui.graphics.Color
import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Row
@Composable
@@ -38,9 +39,9 @@
@Composable
fun Stack(vertical: Boolean, children: @Composable() () -> Unit) {
if (vertical) {
- Column { children() }
+ Column(mainAxisSize = LayoutSize.Expand) { children() }
} else {
- Row { children() }
+ Row(mainAxisSize = LayoutSize.Expand) { children() }
}
}
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
index 169994b..040d0b3 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
@@ -26,13 +26,14 @@
import androidx.ui.foundation.ColoredRect
import androidx.ui.graphics.Color
import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Row
import androidx.ui.text.TextStyle
import kotlin.random.Random
@Composable
fun Stack(children: @Composable() () -> Unit) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
children()
}
}
@@ -44,7 +45,7 @@
1 -> Color.Black
else -> Color.Magenta
}
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
ColoredRect(color=color, width = 16.dp, height = 16.dp)
Text(text="${item.id}", style = TextStyle(color=color))
}
@@ -60,7 +61,7 @@
1 -> Color.Black
else -> Color.Magenta
}
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
ColoredRect(color=color, width = 16.dp, height = 16.dp)
Text(text="${item.id}", style = TextStyle(color=color))
}
diff --git a/core/core/api/1.2.0-alpha05.txt b/core/core/api/1.2.0-alpha05.txt
index 334f937..19f1ef5 100644
--- a/core/core/api/1.2.0-alpha05.txt
+++ b/core/core/api/1.2.0-alpha05.txt
@@ -277,11 +277,13 @@
public static class NotificationCompat.Action {
ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+ ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
method public android.app.PendingIntent! getActionIntent();
method public boolean getAllowGeneratedReplies();
method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
method public android.os.Bundle! getExtras();
- method public int getIcon();
+ method @Deprecated public int getIcon();
+ method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
method public androidx.core.app.RemoteInput![]! getRemoteInputs();
method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
method public boolean getShowsUserInterface();
@@ -299,11 +301,12 @@
field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
field public android.app.PendingIntent! actionIntent;
- field public int icon;
+ field @Deprecated public int icon;
field public CharSequence! title;
}
public static final class NotificationCompat.Action.Builder {
+ ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 334f937..19f1ef5 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -277,11 +277,13 @@
public static class NotificationCompat.Action {
ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+ ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
method public android.app.PendingIntent! getActionIntent();
method public boolean getAllowGeneratedReplies();
method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
method public android.os.Bundle! getExtras();
- method public int getIcon();
+ method @Deprecated public int getIcon();
+ method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
method public androidx.core.app.RemoteInput![]! getRemoteInputs();
method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
method public boolean getShowsUserInterface();
@@ -299,11 +301,12 @@
field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
field public android.app.PendingIntent! actionIntent;
- field public int icon;
+ field @Deprecated public int icon;
field public CharSequence! title;
}
public static final class NotificationCompat.Action.Builder {
+ ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/public_plus_experimental_1.2.0-alpha05.txt b/core/core/api/public_plus_experimental_1.2.0-alpha05.txt
index 334f937..19f1ef5 100644
--- a/core/core/api/public_plus_experimental_1.2.0-alpha05.txt
+++ b/core/core/api/public_plus_experimental_1.2.0-alpha05.txt
@@ -277,11 +277,13 @@
public static class NotificationCompat.Action {
ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+ ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
method public android.app.PendingIntent! getActionIntent();
method public boolean getAllowGeneratedReplies();
method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
method public android.os.Bundle! getExtras();
- method public int getIcon();
+ method @Deprecated public int getIcon();
+ method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
method public androidx.core.app.RemoteInput![]! getRemoteInputs();
method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
method public boolean getShowsUserInterface();
@@ -299,11 +301,12 @@
field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
field public android.app.PendingIntent! actionIntent;
- field public int icon;
+ field @Deprecated public int icon;
field public CharSequence! title;
}
public static final class NotificationCompat.Action.Builder {
+ ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 334f937..19f1ef5 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -277,11 +277,13 @@
public static class NotificationCompat.Action {
ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+ ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
method public android.app.PendingIntent! getActionIntent();
method public boolean getAllowGeneratedReplies();
method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
method public android.os.Bundle! getExtras();
- method public int getIcon();
+ method @Deprecated public int getIcon();
+ method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
method public androidx.core.app.RemoteInput![]! getRemoteInputs();
method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
method public boolean getShowsUserInterface();
@@ -299,11 +301,12 @@
field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
field public android.app.PendingIntent! actionIntent;
- field public int icon;
+ field @Deprecated public int icon;
field public CharSequence! title;
}
public static final class NotificationCompat.Action.Builder {
+ ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/restricted_1.2.0-alpha05.txt b/core/core/api/restricted_1.2.0-alpha05.txt
index 2108b6ba..00ed3ab 100644
--- a/core/core/api/restricted_1.2.0-alpha05.txt
+++ b/core/core/api/restricted_1.2.0-alpha05.txt
@@ -322,11 +322,13 @@
public static class NotificationCompat.Action {
ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+ ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
method public android.app.PendingIntent! getActionIntent();
method public boolean getAllowGeneratedReplies();
method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
method public android.os.Bundle! getExtras();
- method public int getIcon();
+ method @Deprecated public int getIcon();
+ method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
method public androidx.core.app.RemoteInput![]! getRemoteInputs();
method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
method public boolean getShowsUserInterface();
@@ -344,11 +346,12 @@
field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
field public android.app.PendingIntent! actionIntent;
- field public int icon;
+ field @Deprecated public int icon;
field public CharSequence! title;
}
public static final class NotificationCompat.Action.Builder {
+ ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 2108b6ba..00ed3ab 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -322,11 +322,13 @@
public static class NotificationCompat.Action {
ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+ ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
method public android.app.PendingIntent! getActionIntent();
method public boolean getAllowGeneratedReplies();
method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
method public android.os.Bundle! getExtras();
- method public int getIcon();
+ method @Deprecated public int getIcon();
+ method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
method public androidx.core.app.RemoteInput![]! getRemoteInputs();
method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
method public boolean getShowsUserInterface();
@@ -344,11 +346,12 @@
field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
field public android.app.PendingIntent! actionIntent;
- field public int icon;
+ field @Deprecated public int icon;
field public CharSequence! title;
}
public static final class NotificationCompat.Action.Builder {
+ ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java b/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
index f818a303..a79e27d 100644
--- a/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
@@ -173,6 +173,17 @@
}
@Test
+ public void testNotificationActionBuilder_copiesIcon() {
+ NotificationCompat.Action a = new NotificationCompat.Action.Builder(
+ R.drawable.notification_action_background, "title", null).build();
+ assertEquals(R.drawable.notification_action_background, a.getIconCompat().getResId());
+
+ NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build();
+
+ assertEquals(R.drawable.notification_action_background, aCopy.getIconCompat().getResId());
+ }
+
+ @Test
public void testNotificationActionBuilder_copiesRemoteInputs() throws Throwable {
NotificationCompat.Action a = newActionBuilder()
.addRemoteInput(new RemoteInput("a", "b", null, false,
@@ -250,6 +261,20 @@
@SdkSuppress(minSdkVersion = 20)
@Test
+ public void testGetActionCompatFromAction_sameIconResourceId() {
+ NotificationCompat.Action action = new NotificationCompat.Action.Builder(
+ R.drawable.notification_bg, "title", null).build();
+ assertEquals(R.drawable.notification_bg, action.getIconCompat().getResId());
+ Notification notification = newNotificationBuilder().addAction(action).build();
+
+ NotificationCompat.Action result =
+ NotificationCompat.getActionCompatFromAction(notification.actions[0]);
+
+ assertEquals(R.drawable.notification_bg, result.getIconCompat().getResId());
+ }
+
+ @SdkSuppress(minSdkVersion = 20)
+ @Test
public void testGetActionCompatFromAction_showsUserInterface() {
NotificationCompat.Action action = newActionBuilder()
.setShowsUserInterface(false).build();
@@ -906,14 +931,14 @@
@Test
public void action_builder_hasDefault() {
NotificationCompat.Action action =
- new NotificationCompat.Action.Builder(0, "Test Title", null).build();
+ newActionBuilder().build();
assertEquals(NotificationCompat.Action.SEMANTIC_ACTION_NONE, action.getSemanticAction());
}
@Test
public void action_builder_setSemanticAction() {
NotificationCompat.Action action =
- new NotificationCompat.Action.Builder(0, "Test Title", null)
+ newActionBuilder()
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
.build();
assertEquals(NotificationCompat.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction());
@@ -923,7 +948,7 @@
@SdkSuppress(minSdkVersion = 20)
public void action_semanticAction_toAndFromNotification() {
NotificationCompat.Action action =
- new NotificationCompat.Action.Builder(0, "Test Title", null)
+ newActionBuilder()
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
.build();
Notification notification = newNotificationBuilder().addAction(action).build();
@@ -933,7 +958,7 @@
}
private static final NotificationCompat.Action TEST_INVISIBLE_ACTION =
- new NotificationCompat.Action.Builder(0, "Test Title", null)
+ newActionBuilder()
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MUTE)
.setShowsUserInterface(false)
.build();
@@ -969,9 +994,7 @@
@Test
public void action_builder_defaultNotContextual() {
- NotificationCompat.Action action =
- new NotificationCompat.Action.Builder(0, "Test Title", null)
- .build();
+ NotificationCompat.Action action = newActionBuilder().build();
assertFalse(action.isContextual());
}
@@ -989,9 +1012,7 @@
@Test
public void action_builder_contextual_invalidIntentCausesNpe() {
- NotificationCompat.Action.Builder builder =
- new NotificationCompat.Action.Builder(0, "Test Title", null)
- .setContextual(true);
+ NotificationCompat.Action.Builder builder = newActionBuilder().setContextual(true);
try {
builder.build();
fail("Creating a contextual Action with a null PendingIntent should cause a "
@@ -1008,8 +1029,8 @@
// Without a PendingIntent the Action.Builder class throws an NPE when building a contextual
// action.
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- NotificationCompat.Action action =
- new NotificationCompat.Action.Builder(0, "Test Title", pendingIntent)
+ NotificationCompat.Action action = new NotificationCompat.Action.Builder(
+ R.drawable.notification_bg, "Test Title", pendingIntent)
.setContextual(true)
.build();
Notification notification = newNotificationBuilder().addAction(action).build();
diff --git a/core/core/src/main/java/androidx/core/app/NotificationCompat.java b/core/core/src/main/java/androidx/core/app/NotificationCompat.java
index 9b5b35d..8eaf28a 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationCompat.java
@@ -36,6 +36,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.net.Uri;
@@ -1970,15 +1971,31 @@
}
/**
+ * Create a bitmap using the given icon together with a color filter created from the given
+ * color.
+ *
* @hide
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public Bitmap createColoredBitmap(int iconId, int color) {
- return createColoredBitmap(iconId, color, 0);
+ return createColoredBitmap(iconId, color, 0 /* size */);
+ }
+
+ /**
+ * Create a bitmap using the given icon together with a color filter created from the given
+ * color.
+ */
+ Bitmap createColoredBitmap(IconCompat icon, int color) {
+ return createColoredBitmap(icon, color, 0 /* size */);
}
private Bitmap createColoredBitmap(int iconId, int color, int size) {
- Drawable drawable = mBuilder.mContext.getResources().getDrawable(iconId);
+ return createColoredBitmap(IconCompat.createWithResource(mBuilder.mContext, iconId),
+ color, size);
+ }
+
+ private Bitmap createColoredBitmap(IconCompat icon, int color, int size) {
+ Drawable drawable = icon.loadDrawable(mBuilder.mContext);
int width = size == 0 ? drawable.getIntrinsicWidth() : size;
int height = size == 0 ? drawable.getIntrinsicHeight() : size;
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
@@ -3138,7 +3155,7 @@
tombstone ? R.layout.notification_action_tombstone
: R.layout.notification_action);
button.setImageViewBitmap(R.id.action_image,
- createColoredBitmap(action.getIcon(), mBuilder.mContext.getResources()
+ createColoredBitmap(action.getIconCompat(), mBuilder.mContext.getResources()
.getColor(R.color.notification_action_color_filter)));
button.setTextViewText(R.id.action_text, action.title);
if (!tombstone) {
@@ -3227,6 +3244,7 @@
static final String EXTRA_SEMANTIC_ACTION = "android.support.action.semanticAction";
final Bundle mExtras;
+ @Nullable private IconCompat mIcon;
private final RemoteInput[] mRemoteInputs;
/**
@@ -3249,7 +3267,10 @@
/**
* Small icon representing the action.
+ *
+ * @deprecated Use {@link #getIconCompat()} instead.
*/
+ @Deprecated
public int icon;
/**
* Title of the action.
@@ -3262,15 +3283,38 @@
public PendingIntent actionIntent;
public Action(int icon, CharSequence title, PendingIntent intent) {
+ this(icon == 0 ? null : IconCompat.createWithResource(null, "", icon), title, intent);
+ }
+
+ /**
+ * <strong>Note:</strong> For devices running an Android version strictly lower than API
+ * level 23 this constructor only supports resource-ID based IconCompat objects.
+ */
+ public Action(@Nullable IconCompat icon, @Nullable CharSequence title,
+ @Nullable PendingIntent intent) {
this(icon, title, intent, new Bundle(), null, null, true, SEMANTIC_ACTION_NONE, true,
- false /* isContextual */);
+ false /* isContextual */);
}
Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
RemoteInput[] remoteInputs, RemoteInput[] dataOnlyRemoteInputs,
boolean allowGeneratedReplies, @SemanticAction int semanticAction,
boolean showsUserInterface, boolean isContextual) {
- this.icon = icon;
+ this(icon == 0 ? null : IconCompat.createWithResource(null, "", icon), title,
+ intent, extras, remoteInputs, dataOnlyRemoteInputs, allowGeneratedReplies,
+ semanticAction, showsUserInterface, isContextual);
+ }
+
+ // Package private access to avoid adding a SyntheticAccessor for the Action.Builder class.
+ Action(@Nullable IconCompat icon, CharSequence title, PendingIntent intent,
+ Bundle extras,
+ RemoteInput[] remoteInputs, RemoteInput[] dataOnlyRemoteInputs,
+ boolean allowGeneratedReplies, @SemanticAction int semanticAction,
+ boolean showsUserInterface, boolean isContextual) {
+ this.mIcon = icon;
+ if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
+ this.icon = icon.getResId();
+ }
this.title = NotificationCompat.Builder.limitCharSequenceLength(title);
this.actionIntent = intent;
this.mExtras = extras != null ? extras : new Bundle();
@@ -3282,10 +3326,24 @@
this.mIsContextual = isContextual;
}
+ /**
+ * @deprecated use {@link #getIconCompat()} instead.
+ */
+ @Deprecated
public int getIcon() {
return icon;
}
+ /**
+ * Return the icon associated with this Action.
+ */
+ public @Nullable IconCompat getIconCompat() {
+ if (mIcon == null && icon != 0) {
+ mIcon = IconCompat.createWithResource(null, "", icon);
+ }
+ return mIcon;
+ }
+
public CharSequence getTitle() {
return title;
}
@@ -3364,7 +3422,7 @@
* Builder class for {@link Action} objects.
*/
public static final class Builder {
- private final int mIcon;
+ private final IconCompat mIcon;
private final CharSequence mTitle;
private final PendingIntent mIntent;
private boolean mAllowGeneratedReplies = true;
@@ -3376,12 +3434,33 @@
/**
* Construct a new builder for {@link Action} object.
+ *
+ * <p><strong>Note:</strong> For devices running an Android version strictly lower than
+ * API level 23 this constructor only supports resource-ID based IconCompat objects.
+ * @param icon icon to show for this action
+ * @param title the title of the action
+ * @param intent the {@link PendingIntent} to fire when users trigger this action
+ */
+ public Builder(@Nullable IconCompat icon, @Nullable CharSequence title,
+ @Nullable PendingIntent intent) {
+ this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, true,
+ false /* isContextual */);
+ }
+
+ /**
+ * Construct a new builder for {@link Action} object.
* @param icon icon to show for this action
* @param title the title of the action
* @param intent the {@link PendingIntent} to fire when users trigger this action
*/
public Builder(int icon, CharSequence title, PendingIntent intent) {
- this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, true,
+ this(icon == 0 ? null : IconCompat.createWithResource(null, "", icon), title,
+ intent,
+ new Bundle(),
+ null,
+ true,
+ SEMANTIC_ACTION_NONE,
+ true,
false /* isContextual */);
}
@@ -3391,13 +3470,15 @@
* @param action the action to read fields from.
*/
public Builder(Action action) {
- this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
+ this(action.getIconCompat(), action.title, action.actionIntent,
+ new Bundle(action.mExtras),
action.getRemoteInputs(), action.getAllowGeneratedReplies(),
action.getSemanticAction(), action.mShowsUserInterface,
action.isContextual());
}
- private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
+ private Builder(@Nullable IconCompat icon, CharSequence title, PendingIntent intent,
+ Bundle extras,
RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
@SemanticAction int semanticAction, boolean showsUserInterface,
boolean isContextual) {
@@ -4172,9 +4253,17 @@
@RequiresApi(20)
private static Notification.Action getActionFromActionCompat(Action actionCompat) {
- Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
- actionCompat.getIcon(), actionCompat.getTitle(),
- actionCompat.getActionIntent());
+ Notification.Action.Builder actionBuilder;
+ if (Build.VERSION.SDK_INT >= 23) {
+ IconCompat iconCompat = actionCompat.getIconCompat();
+ actionBuilder = new Notification.Action.Builder(
+ iconCompat == null ? null : iconCompat.toIcon(), actionCompat.getTitle(),
+ actionCompat.getActionIntent());
+ } else {
+ actionBuilder = new Notification.Action.Builder(
+ actionCompat.getIcon(), actionCompat.getTitle(),
+ actionCompat.getActionIntent());
+ }
Bundle actionExtras;
if (actionCompat.getExtras() != null) {
actionExtras = new Bundle(actionCompat.getExtras());
diff --git a/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java b/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
index 1642870..3711994 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
@@ -32,6 +32,7 @@
import android.widget.RemoteViews;
import androidx.annotation.RestrictTo;
+import androidx.core.graphics.drawable.IconCompat;
import java.util.ArrayList;
import java.util.List;
@@ -260,8 +261,16 @@
private void addAction(NotificationCompat.Action action) {
if (Build.VERSION.SDK_INT >= 20) {
- Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
- action.getIcon(), action.getTitle(), action.getActionIntent());
+ Notification.Action.Builder actionBuilder;
+ if (Build.VERSION.SDK_INT >= 23) {
+ IconCompat iconCompat = action.getIconCompat();
+ actionBuilder = new Notification.Action.Builder(
+ iconCompat == null ? null : iconCompat.toIcon(), action.getTitle(),
+ action.getActionIntent());
+ } else {
+ actionBuilder = new Notification.Action.Builder(
+ action.getIcon(), action.getTitle(), action.getActionIntent());
+ }
if (action.getRemoteInputs() != null) {
for (android.app.RemoteInput remoteInput : RemoteInput.fromCompat(
action.getRemoteInputs())) {
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/UserVisibleHintTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/UserVisibleHintTest.kt
new file mode 100644
index 0000000..1504176
--- /dev/null
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/UserVisibleHintTest.kt
@@ -0,0 +1,114 @@
+/*
+ * 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 androidx.fragment.app.test.FragmentTestActivity
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.rule.ActivityTestRule
+import androidx.testutils.runOnUiThreadRethrow
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@Suppress("DEPRECATION")
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class UserVisibleHintTest {
+
+ @get:Rule
+ var activityRule = ActivityTestRule(FragmentTestActivity::class.java)
+
+ @UiThreadTest
+ @Test
+ fun startOrdering() {
+ var firstStartedFragment: Fragment? = null
+ val callback: (fragment: Fragment) -> Unit = {
+ if (firstStartedFragment == null) {
+ firstStartedFragment = it
+ }
+ }
+ val deferredFragment = ReportStartFragment(callback).apply {
+ userVisibleHint = false
+ }
+ val fragment = ReportStartFragment(callback)
+ val fm = activityRule.activity.supportFragmentManager
+ fm.beginTransaction()
+ .add(deferredFragment, "deferred")
+ .commit()
+ fm.beginTransaction()
+ .add(fragment, "fragment")
+ .commit()
+ activityRule.executePendingTransactions()
+
+ assertWithMessage("userVisibleHint=false Fragments should start after other Fragments")
+ .that(firstStartedFragment)
+ .isSameInstanceAs(fragment)
+ }
+
+ @Test
+ fun startOrderingAfterSave() {
+ var firstStartedFragment: Fragment? = null
+ val callback: (fragment: Fragment) -> Unit = {
+ if (firstStartedFragment == null) {
+ firstStartedFragment = it
+ }
+ }
+ val fm = activityRule.activity.supportFragmentManager
+
+ // Add the fragment, save its state, then remove it.
+ var deferredFragment = ReportStartFragment().apply {
+ userVisibleHint = false
+ }
+ fm.beginTransaction().add(deferredFragment, "tag").commit()
+ activityRule.executePendingTransactions()
+ var state: Fragment.SavedState? = null
+ activityRule.runOnUiThreadRethrow {
+ state = fm.saveFragmentInstanceState(deferredFragment)
+ }
+ fm.beginTransaction().remove(deferredFragment).commit()
+ activityRule.executePendingTransactions()
+
+ // Create a new instance, calling setInitialSavedState
+ deferredFragment = ReportStartFragment(callback)
+ deferredFragment.setInitialSavedState(state)
+
+ val fragment = ReportStartFragment(callback)
+ fm.beginTransaction()
+ .add(deferredFragment, "deferred")
+ .commit()
+ fm.beginTransaction()
+ .add(fragment, "fragment")
+ .commit()
+ activityRule.executePendingTransactions()
+
+ assertWithMessage("userVisibleHint=false Fragments should start after other Fragments")
+ .that(firstStartedFragment)
+ .isSameInstanceAs(fragment)
+ }
+
+ class ReportStartFragment(
+ val callback: ((fragment: Fragment) -> Unit) = {}
+ ) : StrictFragment() {
+ override fun onStart() {
+ super.onStart()
+ callback.invoke(this)
+ }
+ }
+}
diff --git a/mediarouter/src/main/res/layout/mr_cast_media_metadata.xml b/mediarouter/src/main/res/layout/mr_cast_media_metadata.xml
index a97fad4..fa4ac44 100644
--- a/mediarouter/src/main/res/layout/mr_cast_media_metadata.xml
+++ b/mediarouter/src/main/res/layout/mr_cast_media_metadata.xml
@@ -38,6 +38,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
+ android:contentDescription="@string/mr_controller_close_description"
android:src="?attr/mediaRouteCloseDrawable"
android:background="?attr/selectableItemBackgroundBorderless" />
<LinearLayout android:layout_width="match_parent"
diff --git a/mediarouter/src/main/res/layout/mr_controller_material_dialog_b.xml b/mediarouter/src/main/res/layout/mr_controller_material_dialog_b.xml
index b7f1cfc..bf5756f 100644
--- a/mediarouter/src/main/res/layout/mr_controller_material_dialog_b.xml
+++ b/mediarouter/src/main/res/layout/mr_controller_material_dialog_b.xml
@@ -40,6 +40,7 @@
android:singleLine="true"
android:ellipsize="end"
android:textAlignment="viewStart"
+ android:importantForAccessibility="no"
android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
<ImageButton android:id="@+id/mr_close"
android:layout_width="48dp"
diff --git a/mediarouter/src/main/res/layout/mr_picker_dialog.xml b/mediarouter/src/main/res/layout/mr_picker_dialog.xml
index 640c96d..b988df7 100644
--- a/mediarouter/src/main/res/layout/mr_picker_dialog.xml
+++ b/mediarouter/src/main/res/layout/mr_picker_dialog.xml
@@ -29,6 +29,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
+ android:contentDescription="@string/mr_controller_close_description"
android:src="?attr/mediaRouteCloseDrawable"
android:background="?attr/selectableItemBackgroundBorderless" />
</LinearLayout>
diff --git a/navigation/navigation-common-ktx/api/api_lint.ignore b/navigation/navigation-common-ktx/api/api_lint.ignore
index 6984790..83b4fd0 100644
--- a/navigation/navigation-common-ktx/api/api_lint.ignore
+++ b/navigation/navigation-common-ktx/api/api_lint.ignore
@@ -3,3 +3,17 @@
Method NavArgumentBuilder.getType appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
DocumentExceptions: androidx.navigation.NavGraphBuilder#build():
Method NavGraphBuilder.build appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+SetterReturnsThis: androidx.navigation.AnimBuilder:
+ Methods must return the builder object (return type AnimBuilder instead of void): method androidx.navigation.AnimBuilder.setEnter(int)
+SetterReturnsThis: androidx.navigation.NavActionBuilder:
+ Methods must return the builder object (return type NavActionBuilder instead of void): method androidx.navigation.NavActionBuilder.setDestinationId(int)
+SetterReturnsThis: androidx.navigation.NavArgumentBuilder:
+ Methods must return the builder object (return type NavArgumentBuilder instead of void): method androidx.navigation.NavArgumentBuilder.setType(androidx.navigation.NavType<?>)
+SetterReturnsThis: androidx.navigation.NavDestinationBuilder:
+ Methods must return the builder object (return type NavDestinationBuilder instead of void): method androidx.navigation.NavDestinationBuilder.setLabel(CharSequence)
+SetterReturnsThis: androidx.navigation.NavOptionsBuilder:
+ Methods must return the builder object (return type NavOptionsBuilder instead of void): method androidx.navigation.NavOptionsBuilder.setLaunchSingleTop(boolean)
+SetterReturnsThis: androidx.navigation.PopUpToBuilder:
+ Methods must return the builder object (return type PopUpToBuilder instead of void): method androidx.navigation.PopUpToBuilder.setInclusive(boolean)
diff --git a/paging/common/api/3.0.0-alpha01.txt b/paging/common/api/3.0.0-alpha01.txt
index ed2cabf..943ab14 100644
--- a/paging/common/api/3.0.0-alpha01.txt
+++ b/paging/common/api/3.0.0-alpha01.txt
@@ -4,7 +4,6 @@
public abstract class DataSource<Key, Value> {
method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- method protected final java.util.concurrent.Executor getExecutor();
method @AnyThread public void invalidate();
method @WorkerThread public boolean isInvalid();
method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -14,20 +13,9 @@
method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- property protected final java.util.concurrent.Executor executor;
property @WorkerThread public boolean isInvalid;
}
- public static class DataSource.BaseResult<Value> {
- method public final boolean getCounted();
- method public final int getLeadingNulls();
- method public final Object? getNextKey();
- method public final int getOffset();
- method public final Object? getPrevKey();
- method public final int getTrailingNulls();
- field public final java.util.List<Value> data;
- }
-
public abstract static class DataSource.Factory<Key, Value> {
ctor public DataSource.Factory();
method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -41,13 +29,6 @@
method @AnyThread public void onInvalidated();
}
- public static final class DataSource.Params<K> {
- method public int getInitialLoadSize();
- method public K? getKey();
- method public int getPageSize();
- method public boolean getPlaceholdersEnabled();
- }
-
public final class DataSourceKt {
ctor public DataSourceKt();
}
@@ -192,18 +173,17 @@
@MainThread public abstract static class PagedList.BoundaryCallback<T> {
ctor public PagedList.BoundaryCallback();
- method public void onItemAtEndLoaded(T? itemAtEnd);
- method public void onItemAtFrontLoaded(T? itemAtFront);
+ method public void onItemAtEndLoaded(T itemAtEnd);
+ method public void onItemAtFrontLoaded(T itemAtFront);
method public void onZeroItemsLoaded();
}
public static final class PagedList.Builder<Key, Value> {
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
- method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
- method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+ method public androidx.paging.PagedList<Value> build();
method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -292,24 +272,34 @@
method public boolean getPlaceholdersEnabled();
}
- public static final class PagedSource.LoadResult<Key, Value> {
- ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+ public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+ }
+
+ public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Error(Throwable throwable);
+ method public Throwable component1();
+ method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> component1();
method public int component2();
method public int component3();
method public Key? component4();
method public Key? component5();
- method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+ method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> getData();
method public int getItemsAfter();
method public int getItemsBefore();
method public Key? getNextKey();
method public Key? getPrevKey();
field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
- field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+ field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
}
- public static final class PagedSource.LoadResult.Companion {
+ public static final class PagedSource.LoadResult.Page.Companion {
}
public final class PagedSourceKt {
diff --git a/paging/common/api/api_lint.ignore b/paging/common/api/api_lint.ignore
index 2d2bc0a2..41ae48a 100644
--- a/paging/common/api/api_lint.ignore
+++ b/paging/common/api/api_lint.ignore
@@ -35,8 +35,8 @@
Missing nullability on field `INSTANCE` in class `class androidx.paging.LoadState.Loading`
MissingNullability: androidx.paging.PagedSource.KeyProvider.Positional#INSTANCE:
Missing nullability on field `INSTANCE` in class `class androidx.paging.PagedSource.KeyProvider.Positional`
-MissingNullability: androidx.paging.PagedSource.LoadResult#Companion:
- Missing nullability on field `Companion` in class `class androidx.paging.PagedSource.LoadResult`
+MissingNullability: androidx.paging.PagedSource.LoadResult.Page#Companion:
+ Missing nullability on field `Companion` in class `class androidx.paging.PagedSource.LoadResult.Page`
RegistrationName: androidx.paging.DataSource#addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback):
@@ -51,3 +51,7 @@
Callback methods should be named register/unregister; was addWeakCallback
RegistrationName: androidx.paging.PagedList#removeWeakCallback(androidx.paging.PagedList.Callback):
Callback methods should be named register/unregister; was removeWeakCallback
+
+
+SetterReturnsThis: androidx.paging.PagedList.Builder:
+ Methods must return the builder object (return type Builder instead of androidx.paging.PagedList.Builder<Key,Value>): method androidx.paging.PagedList.Builder.setCoroutineScope(kotlinx.coroutines.CoroutineScope)
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index ed2cabf..943ab14 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -4,7 +4,6 @@
public abstract class DataSource<Key, Value> {
method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- method protected final java.util.concurrent.Executor getExecutor();
method @AnyThread public void invalidate();
method @WorkerThread public boolean isInvalid();
method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -14,20 +13,9 @@
method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- property protected final java.util.concurrent.Executor executor;
property @WorkerThread public boolean isInvalid;
}
- public static class DataSource.BaseResult<Value> {
- method public final boolean getCounted();
- method public final int getLeadingNulls();
- method public final Object? getNextKey();
- method public final int getOffset();
- method public final Object? getPrevKey();
- method public final int getTrailingNulls();
- field public final java.util.List<Value> data;
- }
-
public abstract static class DataSource.Factory<Key, Value> {
ctor public DataSource.Factory();
method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -41,13 +29,6 @@
method @AnyThread public void onInvalidated();
}
- public static final class DataSource.Params<K> {
- method public int getInitialLoadSize();
- method public K? getKey();
- method public int getPageSize();
- method public boolean getPlaceholdersEnabled();
- }
-
public final class DataSourceKt {
ctor public DataSourceKt();
}
@@ -192,18 +173,17 @@
@MainThread public abstract static class PagedList.BoundaryCallback<T> {
ctor public PagedList.BoundaryCallback();
- method public void onItemAtEndLoaded(T? itemAtEnd);
- method public void onItemAtFrontLoaded(T? itemAtFront);
+ method public void onItemAtEndLoaded(T itemAtEnd);
+ method public void onItemAtFrontLoaded(T itemAtFront);
method public void onZeroItemsLoaded();
}
public static final class PagedList.Builder<Key, Value> {
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
- method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
- method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+ method public androidx.paging.PagedList<Value> build();
method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -292,24 +272,34 @@
method public boolean getPlaceholdersEnabled();
}
- public static final class PagedSource.LoadResult<Key, Value> {
- ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+ public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+ }
+
+ public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Error(Throwable throwable);
+ method public Throwable component1();
+ method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> component1();
method public int component2();
method public int component3();
method public Key? component4();
method public Key? component5();
- method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+ method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> getData();
method public int getItemsAfter();
method public int getItemsBefore();
method public Key? getNextKey();
method public Key? getPrevKey();
field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
- field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+ field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
}
- public static final class PagedSource.LoadResult.Companion {
+ public static final class PagedSource.LoadResult.Page.Companion {
}
public final class PagedSourceKt {
diff --git a/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt b/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
index ed2cabf..943ab14 100644
--- a/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
+++ b/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
@@ -4,7 +4,6 @@
public abstract class DataSource<Key, Value> {
method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- method protected final java.util.concurrent.Executor getExecutor();
method @AnyThread public void invalidate();
method @WorkerThread public boolean isInvalid();
method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -14,20 +13,9 @@
method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- property protected final java.util.concurrent.Executor executor;
property @WorkerThread public boolean isInvalid;
}
- public static class DataSource.BaseResult<Value> {
- method public final boolean getCounted();
- method public final int getLeadingNulls();
- method public final Object? getNextKey();
- method public final int getOffset();
- method public final Object? getPrevKey();
- method public final int getTrailingNulls();
- field public final java.util.List<Value> data;
- }
-
public abstract static class DataSource.Factory<Key, Value> {
ctor public DataSource.Factory();
method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -41,13 +29,6 @@
method @AnyThread public void onInvalidated();
}
- public static final class DataSource.Params<K> {
- method public int getInitialLoadSize();
- method public K? getKey();
- method public int getPageSize();
- method public boolean getPlaceholdersEnabled();
- }
-
public final class DataSourceKt {
ctor public DataSourceKt();
}
@@ -192,18 +173,17 @@
@MainThread public abstract static class PagedList.BoundaryCallback<T> {
ctor public PagedList.BoundaryCallback();
- method public void onItemAtEndLoaded(T? itemAtEnd);
- method public void onItemAtFrontLoaded(T? itemAtFront);
+ method public void onItemAtEndLoaded(T itemAtEnd);
+ method public void onItemAtFrontLoaded(T itemAtFront);
method public void onZeroItemsLoaded();
}
public static final class PagedList.Builder<Key, Value> {
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
- method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
- method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+ method public androidx.paging.PagedList<Value> build();
method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -292,24 +272,34 @@
method public boolean getPlaceholdersEnabled();
}
- public static final class PagedSource.LoadResult<Key, Value> {
- ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+ public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+ }
+
+ public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Error(Throwable throwable);
+ method public Throwable component1();
+ method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> component1();
method public int component2();
method public int component3();
method public Key? component4();
method public Key? component5();
- method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+ method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> getData();
method public int getItemsAfter();
method public int getItemsBefore();
method public Key? getNextKey();
method public Key? getPrevKey();
field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
- field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+ field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
}
- public static final class PagedSource.LoadResult.Companion {
+ public static final class PagedSource.LoadResult.Page.Companion {
}
public final class PagedSourceKt {
diff --git a/paging/common/api/public_plus_experimental_current.txt b/paging/common/api/public_plus_experimental_current.txt
index ed2cabf..943ab14 100644
--- a/paging/common/api/public_plus_experimental_current.txt
+++ b/paging/common/api/public_plus_experimental_current.txt
@@ -4,7 +4,6 @@
public abstract class DataSource<Key, Value> {
method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- method protected final java.util.concurrent.Executor getExecutor();
method @AnyThread public void invalidate();
method @WorkerThread public boolean isInvalid();
method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -14,20 +13,9 @@
method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- property protected final java.util.concurrent.Executor executor;
property @WorkerThread public boolean isInvalid;
}
- public static class DataSource.BaseResult<Value> {
- method public final boolean getCounted();
- method public final int getLeadingNulls();
- method public final Object? getNextKey();
- method public final int getOffset();
- method public final Object? getPrevKey();
- method public final int getTrailingNulls();
- field public final java.util.List<Value> data;
- }
-
public abstract static class DataSource.Factory<Key, Value> {
ctor public DataSource.Factory();
method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -41,13 +29,6 @@
method @AnyThread public void onInvalidated();
}
- public static final class DataSource.Params<K> {
- method public int getInitialLoadSize();
- method public K? getKey();
- method public int getPageSize();
- method public boolean getPlaceholdersEnabled();
- }
-
public final class DataSourceKt {
ctor public DataSourceKt();
}
@@ -192,18 +173,17 @@
@MainThread public abstract static class PagedList.BoundaryCallback<T> {
ctor public PagedList.BoundaryCallback();
- method public void onItemAtEndLoaded(T? itemAtEnd);
- method public void onItemAtFrontLoaded(T? itemAtFront);
+ method public void onItemAtEndLoaded(T itemAtEnd);
+ method public void onItemAtFrontLoaded(T itemAtFront);
method public void onZeroItemsLoaded();
}
public static final class PagedList.Builder<Key, Value> {
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
- method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
- method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+ method public androidx.paging.PagedList<Value> build();
method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -292,24 +272,34 @@
method public boolean getPlaceholdersEnabled();
}
- public static final class PagedSource.LoadResult<Key, Value> {
- ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+ public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+ }
+
+ public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Error(Throwable throwable);
+ method public Throwable component1();
+ method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> component1();
method public int component2();
method public int component3();
method public Key? component4();
method public Key? component5();
- method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+ method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> getData();
method public int getItemsAfter();
method public int getItemsBefore();
method public Key? getNextKey();
method public Key? getPrevKey();
field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
- field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+ field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
}
- public static final class PagedSource.LoadResult.Companion {
+ public static final class PagedSource.LoadResult.Page.Companion {
}
public final class PagedSourceKt {
diff --git a/paging/common/api/restricted_3.0.0-alpha01.txt b/paging/common/api/restricted_3.0.0-alpha01.txt
index 8df36e0..f47b396 100644
--- a/paging/common/api/restricted_3.0.0-alpha01.txt
+++ b/paging/common/api/restricted_3.0.0-alpha01.txt
@@ -5,8 +5,6 @@
public abstract class DataSource<Key, Value> {
method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- method protected final java.util.concurrent.Executor getExecutor();
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final void initExecutor(java.util.concurrent.Executor executor);
method @AnyThread public void invalidate();
method @WorkerThread public boolean isInvalid();
method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -16,20 +14,9 @@
method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- property protected final java.util.concurrent.Executor executor;
property @WorkerThread public boolean isInvalid;
}
- public static class DataSource.BaseResult<Value> {
- method public final boolean getCounted();
- method public final int getLeadingNulls();
- method public final Object? getNextKey();
- method public final int getOffset();
- method public final Object? getPrevKey();
- method public final int getTrailingNulls();
- field public final java.util.List<Value> data;
- }
-
public abstract static class DataSource.Factory<Key, Value> {
ctor public DataSource.Factory();
method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -43,13 +30,6 @@
method @AnyThread public void onInvalidated();
}
- public static final class DataSource.Params<K> {
- method public int getInitialLoadSize();
- method public K? getKey();
- method public int getPageSize();
- method public boolean getPlaceholdersEnabled();
- }
-
public final class DataSourceKt {
ctor public DataSourceKt();
}
@@ -181,7 +161,7 @@
method @Deprecated public final void addWeakCallback(java.util.List<? extends T>? previousSnapshot, androidx.paging.PagedList.Callback callback);
method public final void addWeakCallback(androidx.paging.PagedList.Callback callback);
method public final void addWeakLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final suspend <K, T> Object create(androidx.paging.PagedSource<K,T> p, kotlinx.coroutines.CoroutineScope pagedSource, kotlinx.coroutines.CoroutineDispatcher coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? initialFetchDispatcher, androidx.paging.PagedList.Config boundaryCallback, K? config, kotlin.coroutines.Continuation<? super androidx.paging.PagedList<T>> key);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final <K, T> androidx.paging.PagedList<T> create(androidx.paging.PagedSource<K,T> pagedSource, androidx.paging.PagedSource.LoadResult.Page<K,T>? initialPage, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key);
method public abstract void detach();
method public T? get(int index);
method public final androidx.paging.PagedList.Config getConfig();
@@ -210,18 +190,17 @@
@MainThread public abstract static class PagedList.BoundaryCallback<T> {
ctor public PagedList.BoundaryCallback();
- method public void onItemAtEndLoaded(T? itemAtEnd);
- method public void onItemAtFrontLoaded(T? itemAtFront);
+ method public void onItemAtEndLoaded(T itemAtEnd);
+ method public void onItemAtFrontLoaded(T itemAtFront);
method public void onZeroItemsLoaded();
}
public static final class PagedList.Builder<Key, Value> {
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
- method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
- method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+ method public androidx.paging.PagedList<Value> build();
method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -239,7 +218,7 @@
}
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class PagedList.Companion {
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public suspend <K, T> Object create(androidx.paging.PagedSource<K,T> pagedSource, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, kotlinx.coroutines.CoroutineDispatcher initialFetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key, kotlin.coroutines.Continuation<? super androidx.paging.PagedList<T>> p);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public <K, T> androidx.paging.PagedList<T> create(androidx.paging.PagedSource<K,T> pagedSource, androidx.paging.PagedSource.LoadResult.Page<K,T>? initialPage, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key);
}
public static final class PagedList.Config {
@@ -329,28 +308,39 @@
method public boolean getPlaceholdersEnabled();
}
- public static final class PagedSource.LoadResult<Key, Value> {
- ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+ public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+ }
+
+ public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Error(Throwable throwable);
+ method public Throwable component1();
+ method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> component1();
method public int component2();
method public int component3();
method public Key? component4();
method public Key? component5();
- method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+ method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> getData();
method public int getItemsAfter();
method public int getItemsBefore();
method public Key? getNextKey();
method public Key? getPrevKey();
field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
- field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+ field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
}
- public static final class PagedSource.LoadResult.Companion {
+ public static final class PagedSource.LoadResult.Page.Companion {
}
public final class PagedSourceKt {
ctor public PagedSourceKt();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <Key> androidx.paging.PagedSource.LoadParams<Key> toRefreshLoadParams(androidx.paging.PagedList.Config, Key? key);
}
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class PagedSourceWrapper<Key, Value> extends androidx.paging.PagedSource<Key,Value> {
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index 8df36e0..f47b396 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -5,8 +5,6 @@
public abstract class DataSource<Key, Value> {
method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- method protected final java.util.concurrent.Executor getExecutor();
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final void initExecutor(java.util.concurrent.Executor executor);
method @AnyThread public void invalidate();
method @WorkerThread public boolean isInvalid();
method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -16,20 +14,9 @@
method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
- property protected final java.util.concurrent.Executor executor;
property @WorkerThread public boolean isInvalid;
}
- public static class DataSource.BaseResult<Value> {
- method public final boolean getCounted();
- method public final int getLeadingNulls();
- method public final Object? getNextKey();
- method public final int getOffset();
- method public final Object? getPrevKey();
- method public final int getTrailingNulls();
- field public final java.util.List<Value> data;
- }
-
public abstract static class DataSource.Factory<Key, Value> {
ctor public DataSource.Factory();
method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -43,13 +30,6 @@
method @AnyThread public void onInvalidated();
}
- public static final class DataSource.Params<K> {
- method public int getInitialLoadSize();
- method public K? getKey();
- method public int getPageSize();
- method public boolean getPlaceholdersEnabled();
- }
-
public final class DataSourceKt {
ctor public DataSourceKt();
}
@@ -181,7 +161,7 @@
method @Deprecated public final void addWeakCallback(java.util.List<? extends T>? previousSnapshot, androidx.paging.PagedList.Callback callback);
method public final void addWeakCallback(androidx.paging.PagedList.Callback callback);
method public final void addWeakLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final suspend <K, T> Object create(androidx.paging.PagedSource<K,T> p, kotlinx.coroutines.CoroutineScope pagedSource, kotlinx.coroutines.CoroutineDispatcher coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? initialFetchDispatcher, androidx.paging.PagedList.Config boundaryCallback, K? config, kotlin.coroutines.Continuation<? super androidx.paging.PagedList<T>> key);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final <K, T> androidx.paging.PagedList<T> create(androidx.paging.PagedSource<K,T> pagedSource, androidx.paging.PagedSource.LoadResult.Page<K,T>? initialPage, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key);
method public abstract void detach();
method public T? get(int index);
method public final androidx.paging.PagedList.Config getConfig();
@@ -210,18 +190,17 @@
@MainThread public abstract static class PagedList.BoundaryCallback<T> {
ctor public PagedList.BoundaryCallback();
- method public void onItemAtEndLoaded(T? itemAtEnd);
- method public void onItemAtFrontLoaded(T? itemAtFront);
+ method public void onItemAtEndLoaded(T itemAtEnd);
+ method public void onItemAtFrontLoaded(T itemAtFront);
method public void onZeroItemsLoaded();
}
public static final class PagedList.Builder<Key, Value> {
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
- ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
- method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
- method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+ ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+ method public androidx.paging.PagedList<Value> build();
method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -239,7 +218,7 @@
}
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class PagedList.Companion {
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public suspend <K, T> Object create(androidx.paging.PagedSource<K,T> pagedSource, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, kotlinx.coroutines.CoroutineDispatcher initialFetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key, kotlin.coroutines.Continuation<? super androidx.paging.PagedList<T>> p);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public <K, T> androidx.paging.PagedList<T> create(androidx.paging.PagedSource<K,T> pagedSource, androidx.paging.PagedSource.LoadResult.Page<K,T>? initialPage, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key);
}
public static final class PagedList.Config {
@@ -329,28 +308,39 @@
method public boolean getPlaceholdersEnabled();
}
- public static final class PagedSource.LoadResult<Key, Value> {
- ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+ public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+ }
+
+ public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Error(Throwable throwable);
+ method public Throwable component1();
+ method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+ ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> component1();
method public int component2();
method public int component3();
method public Key? component4();
method public Key? component5();
- method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+ method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
method public java.util.List<Value> getData();
method public int getItemsAfter();
method public int getItemsBefore();
method public Key? getNextKey();
method public Key? getPrevKey();
field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
- field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+ field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
}
- public static final class PagedSource.LoadResult.Companion {
+ public static final class PagedSource.LoadResult.Page.Companion {
}
public final class PagedSourceKt {
ctor public PagedSourceKt();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <Key> androidx.paging.PagedSource.LoadParams<Key> toRefreshLoadParams(androidx.paging.PagedList.Config, Key? key);
}
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class PagedSourceWrapper<Key, Value> extends androidx.paging.PagedSource<Key,Value> {
diff --git a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
index 1ddfd22..6397e13 100644
--- a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
@@ -25,7 +25,7 @@
import androidx.paging.LoadState.Idle
import androidx.paging.LoadState.Loading
import androidx.paging.PagedSource.KeyProvider
-import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Page.Companion.COUNT_UNDEFINED
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -41,7 +41,7 @@
internal val backgroundDispatcher: CoroutineDispatcher,
internal val boundaryCallback: BoundaryCallback<V>?,
config: Config,
- initialResult: PagedSource.LoadResult<K, V>,
+ initialPage: PagedSource.LoadResult.Page<K, V>,
lastLoad: Int
) : PagedList<V>(
pagedSource,
@@ -90,7 +90,7 @@
notifyDispatcher,
backgroundDispatcher,
this,
- initialResult,
+ initialPage,
storage
)
@@ -113,10 +113,10 @@
*/
override fun onPageResult(
type: LoadType,
- pageResult: PagedSource.LoadResult<*, V>
+ page: PagedSource.LoadResult.Page<*, V>
): Boolean {
var continueLoading = false
- val page = pageResult.data
+ val list = page.data
// if we end up trimming, we trim from side that's furthest from most recent access
val trimFromFront = lastLoad > storage.middleOfLoadedRange
@@ -125,7 +125,7 @@
val skipNewPage = shouldTrim && storage.shouldPreTrimNewPage(
config.maxSize,
requiredRemainder,
- page.size
+ list.size
)
if (type == END) {
@@ -133,9 +133,9 @@
// don't append this data, drop it
appendItemsRequested = 0
} else {
- storage.appendPage(page, this@ContiguousPagedList)
- appendItemsRequested -= page.size
- if (appendItemsRequested > 0 && page.isNotEmpty()) {
+ storage.appendPage(list, this@ContiguousPagedList)
+ appendItemsRequested -= list.size
+ if (appendItemsRequested > 0 && list.isNotEmpty()) {
continueLoading = true
}
}
@@ -144,9 +144,9 @@
// don't append this data, drop it
prependItemsRequested = 0
} else {
- storage.prependPage(page, this@ContiguousPagedList)
- prependItemsRequested -= page.size
- if (prependItemsRequested > 0 && page.isNotEmpty()) {
+ storage.prependPage(list, this@ContiguousPagedList)
+ prependItemsRequested -= list.size
+ if (prependItemsRequested > 0 && list.isNotEmpty()) {
continueLoading = true
}
}
@@ -186,7 +186,7 @@
}
}
- triggerBoundaryCallback(type, page)
+ triggerBoundaryCallback(type, list)
return continueLoading
}
@@ -305,9 +305,9 @@
if (config.enablePlaceholders) {
// Placeholders enabled, pass raw data to storage init
storage.init(
- if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0,
- initialResult.data,
- if (initialResult.itemsAfter != COUNT_UNDEFINED) initialResult.itemsAfter else 0,
+ if (initialPage.itemsBefore != COUNT_UNDEFINED) initialPage.itemsBefore else 0,
+ initialPage.data,
+ if (initialPage.itemsAfter != COUNT_UNDEFINED) initialPage.itemsAfter else 0,
0,
this
)
@@ -316,9 +316,9 @@
// may have passed them anyway.
storage.init(
0,
- initialResult.data,
+ initialPage.data,
0,
- if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0,
+ if (initialPage.itemsBefore != COUNT_UNDEFINED) initialPage.itemsBefore else 0,
this
)
}
@@ -327,10 +327,10 @@
// Because the ContiguousPagedList wasn't initialized with a last load position,
// initialize it to the middle of the initial load
val itemsBefore =
- if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0
- this.lastLoad = itemsBefore + initialResult.data.size / 2
+ if (initialPage.itemsBefore != COUNT_UNDEFINED) initialPage.itemsBefore else 0
+ this.lastLoad = itemsBefore + initialPage.data.size / 2
}
- triggerBoundaryCallback(REFRESH, initialResult.data)
+ triggerBoundaryCallback(REFRESH, initialPage.data)
}
override fun dispatchCurrentLoadState(callback: LoadStateListener) {
diff --git a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
index d9a49b1..1ba3a80 100644
--- a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
@@ -17,13 +17,11 @@
package androidx.paging
import androidx.annotation.AnyThread
-import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
import androidx.arch.core.util.Function
-import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Page.Companion.COUNT_UNDEFINED
import java.util.concurrent.CopyOnWriteArrayList
-import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicBoolean
typealias -> Unit
@@ -114,26 +112,6 @@
@WorkerThread
get() = _invalid.get()
- private var _executor: Executor? = null
- /**
- * `null` until `loadInitial` is called by [PagedList] construction.
- *
- * This backing variable is necessary for back-compatibility with paging-common:2.1.0 Java API,
- * while still providing synthetic accessors for Kotlin API.
- */
- protected val executor: Executor
- get() = _executor ?: throw IllegalStateException(
- "This DataSource has not been passed to a PagedList, has no executor yet."
- )
-
- /**
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- fun initExecutor(executor: Executor) {
- _executor = executor
- }
-
/**
* Factory for DataSources.
*
@@ -434,7 +412,7 @@
* @param K Type of the key used to query the [DataSource].
* @property key Can be `null` for init, otherwise non-null
*/
- class Params<K : Any> internal constructor(
+ internal class Params<K : Any> internal constructor(
internal val type: LoadType,
val key: K?,
val initialLoadSize: Int,
@@ -446,7 +424,7 @@
* @param Value Type of the data produced by a [DataSource].
* @property counted Set to true if the result is an initial load that is passed totalCount
*/
- open class BaseResult<Value : Any> internal constructor(
+ internal open class BaseResult<Value : Any> internal constructor(
@JvmField
val data: List<Value>,
val prevKey: Any?,
@@ -511,7 +489,7 @@
* in [PagedSource.LoadResult].
*/
@Suppress("UNCHECKED_CAST") // Guaranteed to be the correct Key type.
- internal fun <Key : Any> toLoadResult() = PagedSource.LoadResult(
+ internal fun <Key : Any> toLoadResult() = PagedSource.LoadResult.Page(
data,
if (counted) leadingNulls else offset,
if (counted) trailingNulls else COUNT_UNDEFINED,
diff --git a/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
index f10bf15..32da108 100644
--- a/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
@@ -41,7 +41,7 @@
DirectDispatcher,
null,
config,
- PagedSource.LoadResult.empty(),
+ PagedSource.LoadResult.Page.empty(),
0 // no previous load, so pass 0
) {
override val lastKey = initialKey
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
index afc7038..98d6828 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
@@ -19,7 +19,6 @@
import androidx.annotation.IntRange
import androidx.annotation.MainThread
import androidx.annotation.RestrictTo
-import androidx.annotation.WorkerThread
import androidx.paging.PagedList.Callback
import androidx.paging.PagedList.Config
import androidx.paging.PagedList.Config.Builder
@@ -167,7 +166,7 @@
companion object {
/**
* Create a [PagedList] which loads data from the provided data source on a background
- * thread,posting updates to the main thread.
+ * thread, posting updates to the main thread.
*
* @param pagedSource [PagedSource] providing data to the [PagedList]
* @param notifyDispatcher [CoroutineDispatcher] that will use and consume data from the
@@ -186,12 +185,12 @@
*/
@JvmStatic
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- suspend fun <K : Any, T : Any> create(
+ fun <K : Any, T : Any> create(
pagedSource: PagedSource<K, T>,
+ initialPage: PagedSource.LoadResult.Page<K, T>?,
coroutineScope: CoroutineScope,
notifyDispatcher: CoroutineDispatcher,
fetchDispatcher: CoroutineDispatcher,
- initialFetchDispatcher: CoroutineDispatcher,
boundaryCallback: BoundaryCallback<T>?,
config: Config,
key: K?
@@ -201,18 +200,29 @@
else -> ContiguousPagedList.LAST_LOAD_UNSPECIFIED
}
- val params = PagedSource.LoadParams(
- LoadType.REFRESH,
- key,
- config.initialLoadSizeHint,
- config.enablePlaceholders,
- config.pageSize
- )
-
- val initialResult = withContext(initialFetchDispatcher) {
- pagedSource.load(params)
+ val resolvedInitialPage = when (initialPage) {
+ null -> {
+ // Compatibility codepath - perform the initial load immediately, since caller
+ // hasn't done it. We block in this case, but it's only used in the legacy path.
+ val params = PagedSource.LoadParams(
+ LoadType.REFRESH,
+ key,
+ config.initialLoadSizeHint,
+ config.enablePlaceholders,
+ config.pageSize
+ )
+ runBlocking {
+ val initialResult = withContext(DirectDispatcher) {
+ pagedSource.load(params)
+ }
+ when (initialResult) {
+ is PagedSource.LoadResult.Page -> initialResult
+ is PagedSource.LoadResult.Error -> throw initialResult.throwable
+ }
+ }
+ }
+ else -> initialPage
}
-
return ContiguousPagedList(
pagedSource,
coroutineScope,
@@ -220,7 +230,7 @@
fetchDispatcher,
boundaryCallback,
config,
- initialResult,
+ resolvedInitialPage,
lastLoad
)
}
@@ -275,6 +285,7 @@
*/
class Builder<Key : Any, Value : Any> {
private val pagedSource: PagedSource<Key, Value>
+ private val initialPage: PagedSource.LoadResult.Page<Key, Value>?
private val config: Config
private var coroutineScope: CoroutineScope = GlobalScope
private var notifyDispatcher: CoroutineDispatcher? = null
@@ -291,6 +302,7 @@
@Deprecated("DataSource is deprecated and has been replaced by PagedSource")
constructor(dataSource: DataSource<Key, Value>, config: Config) {
this.pagedSource = PagedSourceWrapper(dataSource)
+ this.initialPage = null
this.config = config
}
@@ -315,31 +327,49 @@
)
/**
- * Create a [PagedList.Builder] with the provided [PagedSource] and [Config].
+ * Create a [PagedList.Builder] with the provided [PagedSource],
+ * initial [PagedSource.LoadResult.Page], and [Config].
*
* @param pagedSource [PagedSource] the [PagedList] will load from.
+ * @param initialPage Initial page loaded from the [PagedSource].
* @param config [Config] that defines how the [PagedList] loads data from its
* [PagedSource].
*/
- constructor(pagedSource: PagedSource<Key, Value>, config: Config) {
+ constructor(
+ pagedSource: PagedSource<Key, Value>,
+ initialPage: PagedSource.LoadResult.Page<Key, Value>,
+ config: Config
+ ) {
this.pagedSource = pagedSource
+ this.initialPage = initialPage
this.config = config
}
/**
- * Create a [PagedList.Builder] with the provided [PagedSource] and page size.
+ * Create a [PagedList.Builder] with the provided [PagedSource],
+ * initial [PagedSource.LoadResult.Page], and page size.
*
* This method is a convenience for:
* ```
- * PagedList.Builder(pagedSource, PagedList.Config.Builder().setPageSize(pageSize).build())
+ * PagedList.Builder(
+ * pagedSource,
+ * page,
+ * PagedList.Config.Builder().setPageSize(pageSize).build()
+ * )
* ```
*
* @param pagedSource [PagedSource] the [PagedList] will load from.
+ * @param initialPage Initial page loaded from the [PagedSource].
* @param pageSize [Config] that defines how the [PagedList] loads data from its
* [PagedSource].
*/
- constructor(pagedSource: PagedSource<Key, Value>, pageSize: Int) : this(
+ constructor(
+ pagedSource: PagedSource<Key, Value>,
+ initialPage: PagedSource.LoadResult.Page<Key, Value>,
+ pageSize: Int
+ ) : this(
pagedSource,
+ initialPage,
PagedList.Config.Builder().setPageSize(pageSize).build()
)
@@ -450,6 +480,8 @@
/**
* Creates a [PagedList] with the given parameters.
*
+ * TODO DOCS
+ *
* This call will dispatch the [androidx.paging.PagedSource]'s loadInitial method
* immediately on the current thread, and block the current on the result. This method
* should always be called on a worker thread to prevent blocking the main thread.
@@ -471,39 +503,13 @@
*
* @return The newly constructed [PagedList]
*/
- @WorkerThread
- @Deprecated(
- message = "This method has no means of handling errors encountered during initial " +
- "load, and blocks on the initial load result.",
- replaceWith = ReplaceWith("buildAsync()")
- )
- fun build(): PagedList<Value> = runBlocking {
- create(DirectDispatcher)
- }
-
- /**
- * Creates a [PagedList] asynchronously with the given parameters.
- *
- * This call will dispatch [PagedSource.load] immediately with
- * [LoadType.REFRESH], and return a [PagedList] once it completes, triggering
- * [loadStateListeners].
- *
- * @throws IllegalArgumentException if [notifyDispatcher] or [fetchDispatcher] are not set.
- *
- * @return The newly constructed [PagedList]
- */
- @Suppress("unused") // Public API
- suspend fun buildAsync(): PagedList<Value> {
- return create(fetchDispatcher ?: Dispatchers.IO)
- }
-
- private suspend fun create(initialFetchDispatcher: CoroutineDispatcher): PagedList<Value> {
+ fun build(): PagedList<Value> {
return create(
pagedSource,
+ initialPage,
coroutineScope,
notifyDispatcher ?: Dispatchers.Main.immediate,
fetchDispatcher ?: Dispatchers.IO,
- initialFetchDispatcher,
boundaryCallback,
config,
initialKey
@@ -846,7 +852,7 @@
* @param T Type loaded by the [PagedList].
*/
@MainThread
- abstract class BoundaryCallback<T> {
+ abstract class BoundaryCallback<T : Any> {
/**
* Called when zero items are returned from an initial load of the PagedList's data source.
*/
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt b/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
index abf0046..98ee045 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
@@ -17,6 +17,7 @@
package androidx.paging
import androidx.annotation.IntRange
+import androidx.annotation.RestrictTo
import androidx.paging.PagedSource.KeyProvider
import androidx.paging.PagedSource.KeyProvider.ItemKey
import androidx.paging.PagedSource.KeyProvider.PageKey
@@ -36,6 +37,19 @@
typealias PagedSourceFactory<Key, Value> = () -> PagedSource<Key, Value>
/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun <Key : Any> PagedList.Config.toRefreshLoadParams(key: Key?): PagedSource.LoadParams<Key> =
+ PagedSource.LoadParams(
+ LoadType.REFRESH,
+ key,
+ initialLoadSizeHint,
+ enablePlaceholders,
+ pageSize
+ )
+
+/**
* Base class for an abstraction of pageable static data from some source, where loading pages data
* is typically an expensive operation. Some examples of common [PagedSource]s might be from network
* or DB.
@@ -124,45 +138,51 @@
val pageSize: Int
)
- /**
- * Result object for a generic load request on a [PagedSource].
- *
- * TODO: Builder for Java (also consider @JvmOverloads)
- */
- data class LoadResult<Key : Any, Value : Any>(
- /**
- * Loaded data
- */
- val data: List<Value>,
- /**
- * Optional count of items before the loaded data.
- */
- @IntRange(from = COUNT_UNDEFINED.toLong())
- val itemsBefore: Int = COUNT_UNDEFINED,
- /**
- * Optional count of items after the loaded data.
- */
- @IntRange(from = COUNT_UNDEFINED.toLong())
- val itemsAfter: Int = COUNT_UNDEFINED,
- /**
- * Key for next page - ignored unless you're using [KeyProvider.PageKey]
- */
- val nextKey: Key? = null,
- /**
- * Key for previous page - ignored unless you're using [KeyProvider.PageKey]
- */
- val prevKey: Key? = null
- ) {
- internal val counted = itemsBefore != COUNT_UNDEFINED && itemsAfter != COUNT_UNDEFINED
+ sealed class LoadResult<Key : Any, Value : Any> {
+ data class Error<Key : Any, Value : Any>(
+ val throwable: Throwable
+ ) : LoadResult<Key, Value>()
- companion object {
- const val COUNT_UNDEFINED = -1
+ /**
+ * Success result object for [PagedSource.load]
+ *
+ * TODO: Builder for Java (also consider @JvmOverloads)
+ */
+ data class Page<Key : Any, Value : Any>(
+ /**
+ * Loaded data
+ */
+ val data: List<Value>,
+ /**
+ * Optional count of items before the loaded data.
+ */
+ @IntRange(from = COUNT_UNDEFINED.toLong())
+ val itemsBefore: Int = COUNT_UNDEFINED,
+ /**
+ * Optional count of items after the loaded data.
+ */
+ @IntRange(from = COUNT_UNDEFINED.toLong())
+ val itemsAfter: Int = COUNT_UNDEFINED,
+ /**
+ * Key for next page - ignored unless you're using [KeyProvider.PageKey]
+ */
+ val nextKey: Key? = null,
+ /**
+ * Key for previous page - ignored unless you're using [KeyProvider.PageKey]
+ */
+ val prevKey: Key? = null
+ ) : LoadResult<Key, Value>() {
+ internal val counted = itemsBefore != COUNT_UNDEFINED && itemsAfter != COUNT_UNDEFINED
- @Suppress("MemberVisibilityCanBePrivate") // Prevent synthetic accessor generation.
- internal val EMPTY = LoadResult(emptyList(), 0, 0, null, null)
+ companion object {
+ const val COUNT_UNDEFINED = -1
- @Suppress("UNCHECKED_CAST") // Can safely ignore, since the list is empty.
- internal fun <Key : Any, Value : Any> empty() = EMPTY as LoadResult<Key, Value>
+ @Suppress("MemberVisibilityCanBePrivate") // Prevent synthetic accessor generation.
+ internal val EMPTY = Page(emptyList(), 0, 0, null, null)
+
+ @Suppress("UNCHECKED_CAST") // Can safely ignore, since the list is empty.
+ internal fun <Key : Any, Value : Any> empty() = EMPTY as Page<Key, Value>
+ }
}
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt b/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt
index 2e6f819..b9e1ee0 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt
@@ -299,7 +299,7 @@
)
}
- override fun onPageResultResolution(type: LoadType, result: LoadResult<*, T>) {
+ override fun onPageResultResolution(type: LoadType, result: LoadResult.Page<*, T>) {
// ignored
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/Pager.kt b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
index ab10031..7f8bbac 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Pager.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
@@ -18,8 +18,7 @@
import androidx.paging.PagedSource.KeyProvider
import androidx.paging.PagedSource.LoadParams
-import androidx.paging.PagedSource.LoadResult
-import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Page.Companion.COUNT_UNDEFINED
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -32,7 +31,7 @@
private val notifyDispatcher: CoroutineDispatcher,
private val fetchDispatcher: CoroutineDispatcher,
val pageConsumer: PageConsumer<V>,
- result: LoadResult<K, V>,
+ result: PagedSource.LoadResult.Page<K, V>,
private val adjacentProvider: AdjacentProvider<V> = SimpleAdjacentProvider()
) {
private val totalCount: Int
@@ -63,28 +62,25 @@
private fun scheduleLoad(type: LoadType, params: LoadParams<K>) {
// Listen on the BG thread if the paged source is invalid, since it can be expensive.
pagedListScope.launch(fetchDispatcher) {
- try {
- val value = source.load(params)
+ val value = source.load(params)
- // if invalid, drop result on the floor
- if (source.invalid) {
- detach()
- return@launch
- }
+ // if invalid, drop result on the floor
+ if (source.invalid) {
+ detach()
+ return@launch
+ }
- // Source has been verified to be valid after producing data, so sent data to UI
- launch(notifyDispatcher) {
- onLoadSuccess(type, value)
- }
- } catch (throwable: Throwable) {
- launch(notifyDispatcher) {
- onLoadError(type, throwable)
+ // Source has been verified to be valid after producing data, so sent data to UI
+ launch(notifyDispatcher) {
+ when (value) {
+ is PagedSource.LoadResult.Page -> onLoadSuccess(type, value)
+ is PagedSource.LoadResult.Error -> onLoadError(type, value.throwable)
}
}
}
}
- private fun onLoadSuccess(type: LoadType, value: LoadResult<K, V>) {
+ private fun onLoadSuccess(type: LoadType, value: PagedSource.LoadResult.Page<K, V>) {
if (isDetached) return // abort!
adjacentProvider.onPageResultResolution(type, value)
@@ -113,7 +109,6 @@
private fun onLoadError(type: LoadType, throwable: Throwable) {
if (isDetached) return // abort!
- // TODO: handle nesting
val state = LoadState.Error(throwable)
loadStateManager.setState(type, state)
}
@@ -142,7 +137,7 @@
private fun schedulePrepend() {
if (!canPrepend()) {
- onLoadSuccess(LoadType.START, LoadResult.empty())
+ onLoadSuccess(LoadType.START, PagedSource.LoadResult.Page.empty())
return
}
@@ -169,7 +164,7 @@
private fun scheduleAppend() {
if (!canAppend()) {
- onLoadSuccess(LoadType.END, LoadResult.empty())
+ onLoadSuccess(LoadType.END, PagedSource.LoadResult.Page.empty())
return
}
@@ -210,7 +205,7 @@
/**
* @return `true` if we need to fetch more
*/
- fun onPageResult(type: LoadType, pageResult: LoadResult<*, V>): Boolean
+ fun onPageResult(type: LoadType, page: PagedSource.LoadResult.Page<*, V>): Boolean
fun onStateChanged(type: LoadType, state: LoadState)
}
@@ -228,7 +223,7 @@
* implementation of the AdjacentProvider to handle this (generally by ignoring this call if
* dropping is supported).
*/
- fun onPageResultResolution(type: LoadType, result: LoadResult<*, V>)
+ fun onPageResultResolution(type: LoadType, result: PagedSource.LoadResult.Page<*, V>)
}
internal class SimpleAdjacentProvider<V : Any> : AdjacentProvider<V> {
@@ -245,7 +240,10 @@
private var leadingUnloadedCount: Int = 0
private var trailingUnloadedCount: Int = 0
- override fun onPageResultResolution(type: LoadType, result: LoadResult<*, V>) {
+ override fun onPageResultResolution(
+ type: LoadType,
+ result: PagedSource.LoadResult.Page<*, V>
+ ) {
if (result.data.isEmpty()) return
if (type == LoadType.START) {
diff --git a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
index cbe938e..f211bd5 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
@@ -365,7 +365,7 @@
*
* This method is called to load the initial page(s) from the DataSource.
*
- * Result list must be a multiple of pageSize to enable efficient tiling.
+ * LoadResult list must be a multiple of pageSize to enable efficient tiling.
*
* @param params Parameters for initial load, including requested start position, load size, and
* page size.
@@ -434,7 +434,7 @@
*
* This method is called to load the initial page(s) from the [DataSource].
*
- * Result list must be a multiple of pageSize to enable efficient tiling.
+ * LoadResult list must be a multiple of pageSize to enable efficient tiling.
*
* @param params Parameters for initial load, including requested start position, load size, and
* page size.
diff --git a/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
index f6b6fdf..6983251 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
@@ -28,7 +28,6 @@
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
@@ -36,6 +35,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import kotlin.test.assertFailsWith
+import kotlin.test.assertNotSame
@RunWith(Parameterized::class)
class ContiguousPagedListTest(private val placeholdersEnabled: Boolean) {
@@ -80,26 +80,26 @@
val result = getClampedRange(start, start + params.loadSize)
return when {
- result == null -> throw EXCEPTION
- placeholdersEnabled -> LoadResult(
+ result == null -> LoadResult.Error(EXCEPTION)
+ placeholdersEnabled -> LoadResult.Page(
data = result,
itemsBefore = start,
itemsAfter = listData.size - result.size - start
)
- else -> LoadResult(result)
+ else -> LoadResult.Page(result)
}
}
private fun loadAfter(params: LoadParams<Int>): LoadResult<Int, Item> {
val result = getClampedRange(params.key!! + 1, params.key!! + 1 + params.loadSize)
- ?: throw EXCEPTION
- return LoadResult(result)
+ ?: return LoadResult.Error(EXCEPTION)
+ return LoadResult.Page(result)
}
private fun loadBefore(params: LoadParams<Int>): LoadResult<Int, Item> {
val result = getClampedRange(params.key!! - params.loadSize, params.key!!)
- ?: throw EXCEPTION
- return LoadResult(result)
+ ?: return LoadResult.Error(EXCEPTION)
+ return LoadResult.Page(result)
}
private fun getClampedRange(startInc: Int, endExc: Int): List<Item>? {
@@ -119,6 +119,7 @@
private fun <E> MutableList<E>.getAllAndClear(): List<E> {
val data = this.toList()
+ assertNotSame(data, this)
this.clear()
return data
}
@@ -170,28 +171,22 @@
boundaryCallback: BoundaryCallback<Item>? = null,
maxSize: Int = Config.MAX_SIZE_UNBOUNDED,
pagedSource: PagedSource<Int, Item> = TestPagedSource(listData)
- ): ContiguousPagedList<Int, Item> {
- val ret = runBlocking {
- PagedList.create(
- pagedSource,
- GlobalScope,
- mainThread,
- backgroundThread,
- DirectDispatcher,
- boundaryCallback,
- Config.Builder()
- .setPageSize(pageSize)
- .setInitialLoadSizeHint(initLoadSize)
- .setPrefetchDistance(prefetchDistance)
- .setMaxSize(maxSize)
- .setEnablePlaceholders(placeholdersEnabled)
- .build(),
- initialPosition
- )
- }
- @Suppress("UNCHECKED_CAST")
- return ret as ContiguousPagedList<Int, Item>
- }
+ ): PagedList<Item> = PagedList.create(
+ pagedSource,
+ null,
+ GlobalScope,
+ mainThread,
+ backgroundThread,
+ boundaryCallback,
+ Config.Builder()
+ .setPageSize(pageSize)
+ .setInitialLoadSizeHint(initLoadSize)
+ .setPrefetchDistance(prefetchDistance)
+ .setMaxSize(maxSize)
+ .setEnablePlaceholders(placeholdersEnabled)
+ .build(),
+ initialPosition
+ )
@Test
fun construct() {
@@ -206,18 +201,16 @@
@Suppress("DEPRECATION")
assertFailsWith<IllegalStateException> { pagedListWithPagedSource.dataSource }
- val pagedListWithDataSource = runBlocking {
- PagedList.create(
- PagedSourceWrapper(ItemDataSource()),
- GlobalScope,
- FailDispatcher(),
- DirectDispatcher,
- DirectDispatcher,
- null,
- Config.Builder().setPageSize(10).build(),
- null
- )
- }
+ val pagedListWithDataSource = PagedList.create(
+ PagedSourceWrapper(ItemDataSource()),
+ null,
+ GlobalScope,
+ FailDispatcher(),
+ DirectDispatcher,
+ null,
+ Config.Builder().setPageSize(10).build(),
+ null
+ )
@Suppress("DEPRECATION")
assertTrue(pagedListWithDataSource.dataSource is ItemDataSource)
diff --git a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
index 36a6381..cd9c21b 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
@@ -16,7 +16,6 @@
package androidx.paging
-import androidx.paging.futures.DirectExecutor
import androidx.paging.futures.DirectDispatcher
import com.nhaarman.mockitokotlin2.capture
import com.nhaarman.mockitokotlin2.mock
@@ -44,7 +43,6 @@
initialLoadSize: Int,
enablePlaceholders: Boolean
): DataSource.BaseResult<Item> {
- dataSource.initExecutor(DirectExecutor)
return dataSource.loadInitial(
ItemKeyedDataSource.LoadInitialParams(key, initialLoadSize, enablePlaceholders)
)
@@ -288,20 +286,18 @@
}
}
- runBlocking {
- PagedList.create(
- PagedSourceWrapper(dataSource),
- GlobalScope,
- FailDispatcher(),
- DirectDispatcher,
- DirectDispatcher,
- null,
- PagedList.Config.Builder()
- .setPageSize(10)
- .build(),
- ""
- )
- }
+ PagedList.create(
+ PagedSourceWrapper(dataSource),
+ null,
+ GlobalScope,
+ FailDispatcher(),
+ DirectDispatcher,
+ null,
+ PagedList.Config.Builder()
+ .setPageSize(10)
+ .build(),
+ ""
+ )
}
@Test
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
index 11b7cda..dd397c2 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
@@ -20,8 +20,6 @@
import androidx.testutils.TestDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.async
-import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -93,21 +91,19 @@
fun loadFullVerify() {
// validate paging entire ItemDataSource results in full, correctly ordered data
val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
- val pagedListJob = testCoroutineScope.async(backgroundThread) {
- PagedList.create(
- PagedSourceWrapper(ItemDataSource()),
- testCoroutineScope,
- mainThread,
- backgroundThread,
- backgroundThread,
- null,
- PagedList.Config.Builder().setPageSize(100).build(),
- null
- )
- }
+ val pagedList = PagedList.create(
+ PagedSourceWrapper(ItemDataSource()),
+ null,
+ testCoroutineScope,
+ mainThread,
+ DirectDispatcher,
+ null,
+ PagedList.Config.Builder().setPageSize(100).build(),
+ null
+ )
- backgroundThread.executeAll()
- val pagedList = runBlocking { pagedListJob.await() }
+ // backgroundThread.executeAll()
+ // val pagedList = runBlocking { pagedListJob.await() }
// validate initial load
assertEquals(PAGE_MAP[INIT_KEY]!!.data, pagedList)
@@ -155,20 +151,18 @@
}
}
- runBlocking {
- PagedList.create(
- PagedSourceWrapper(dataSource),
- GlobalScope,
- FailDispatcher(),
- DirectDispatcher,
- DirectDispatcher,
- null,
- PagedList.Config.Builder()
- .setPageSize(10)
- .build(),
- ""
- )
- }
+ PagedList.create(
+ PagedSourceWrapper(dataSource),
+ null,
+ GlobalScope,
+ FailDispatcher(),
+ DirectDispatcher,
+ null,
+ PagedList.Config.Builder()
+ .setPageSize(10)
+ .build(),
+ ""
+ )
}
@Test
@@ -262,24 +256,18 @@
val dispatcher = TestDispatcher()
val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
- val pagedListJob = testCoroutineScope.async(dispatcher) {
- PagedList.create(
- PagedSourceWrapper(dataSource),
- testCoroutineScope,
- dispatcher,
- dispatcher,
- dispatcher,
- boundaryCallback,
- PagedList.Config.Builder()
- .setPageSize(10)
- .build(),
- ""
- )
- }
-
- dispatcher.executeAll()
-
- val pagedList = runBlocking { pagedListJob.await() }
+ val pagedList = PagedList.create(
+ PagedSourceWrapper(dataSource),
+ null,
+ testCoroutineScope,
+ dispatcher,
+ dispatcher,
+ boundaryCallback,
+ PagedList.Config.Builder()
+ .setPageSize(10)
+ .build(),
+ ""
+ )
pagedList.loadAround(0)
verifyZeroInteractions(boundaryCallback)
@@ -324,23 +312,18 @@
val dispatcher = TestDispatcher()
val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
- val pagedListJob = testCoroutineScope.async(dispatcher) {
- PagedList.create(
- PagedSourceWrapper(dataSource),
- testCoroutineScope,
- dispatcher,
- dispatcher,
- dispatcher,
- boundaryCallback,
- PagedList.Config.Builder()
- .setPageSize(10)
- .build(),
- ""
- )
- }
- dispatcher.executeAll()
- val pagedList = runBlocking { pagedListJob.await() }
-
+ val pagedList = PagedList.create(
+ PagedSourceWrapper(dataSource),
+ null,
+ testCoroutineScope,
+ dispatcher,
+ dispatcher,
+ boundaryCallback,
+ PagedList.Config.Builder()
+ .setPageSize(10)
+ .build(),
+ ""
+ )
pagedList.loadAround(0)
verifyZeroInteractions(boundaryCallback)
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
index 5415c8c..a8b7600 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
@@ -25,14 +25,13 @@
import androidx.testutils.TestDispatcher
import androidx.testutils.TestExecutor
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.test.assertFails
+import kotlin.test.assertFailsWith
@RunWith(JUnit4::class)
class PagedListTest {
@@ -45,7 +44,7 @@
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> =
when (params.loadType) {
- REFRESH -> LoadResult(
+ REFRESH -> LoadResult.Page(
data = listOf("a"),
itemsBefore = 0,
itemsAfter = 0
@@ -71,78 +70,70 @@
}
@Test
- fun createAsync() {
- val config = Config.Builder()
- .setPageSize(10)
- .setEnablePlaceholders(false)
- .build()
- var success = false
-
- val job = testCoroutineScope.async(backgroundThread) {
- val pagedList = PagedList.create(
- PagedSourceWrapper(ListDataSource(ITEMS)),
- testCoroutineScope,
- mainThread,
- backgroundThread,
- backgroundThread,
- null,
- config,
- 0
- )
-
- assertEquals(ITEMS.subList(0, 30), pagedList)
- success = true
+ fun createNoInitialPageThrow() {
+ runBlocking {
+ val pagedSource = object : PagedSource<Int, String>() {
+ override val keyProvider = KeyProvider.Positional
+ override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
+ throw IllegalStateException()
+ }
+ }
+ assertFailsWith<IllegalStateException> {
+ PagedList.create(
+ pagedSource,
+ null,
+ testCoroutineScope,
+ DirectDispatcher,
+ DirectDispatcher,
+ null,
+ Config(10),
+ 0
+ )
+ }
}
-
- backgroundThread.executeAll()
- runBlocking { job.await() }
-
- assert(success)
}
@Test
- fun createAsyncThrow() {
- val pagedSource = object : PagedSource<Int, String>() {
- override val keyProvider = KeyProvider.Positional
-
- override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
- throw Exception()
+ fun createNoInitialPageError() {
+ runBlocking {
+ val exception = IllegalStateException()
+ val pagedSource = object : PagedSource<Int, String>() {
+ override val keyProvider = KeyProvider.Positional
+ override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
+ return LoadResult.Error(exception)
+ }
}
- }
- val config = Config.Builder()
- .setPageSize(10)
- .setEnablePlaceholders(false)
- .build()
- var success = false
- assertFails {
- val job = testCoroutineScope.async(backgroundThread) {
+ // create doesn't differentiate between throw vs error runnable, which is why
+ // PagedList.Builder without the initial page is deprecated
+ assertFailsWith<IllegalStateException> {
PagedList.create(
pagedSource,
- testCoroutineScope,
- mainThread,
- backgroundThread,
- backgroundThread,
null,
- config,
+ testCoroutineScope,
+ DirectDispatcher,
+ DirectDispatcher,
+ null,
+ Config(10),
0
)
-
- success = true
}
-
- backgroundThread.executeAll()
- runBlocking { job.await() }
}
- assert(!success)
}
@Test
fun defaults() = runBlocking {
- val pagedList = Builder(pagedSource, config)
+ val initialPage = pagedSource.load(PagedSource.LoadParams(
+ REFRESH,
+ key = null,
+ loadSize = 10,
+ placeholdersEnabled = false,
+ pageSize = 10
+ )) as PagedSource.LoadResult.Page
+ val pagedList = Builder(pagedSource, initialPage, config)
.setNotifyDispatcher(DirectDispatcher)
.setFetchDispatcher(DirectDispatcher)
- .buildAsync()
+ .build()
assertEquals(pagedSource, pagedList.pagedSource)
assertEquals(config, pagedList.config)
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
index b8bf816..4c9a098 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
@@ -18,7 +18,7 @@
import androidx.paging.PagedSource.LoadParams
import androidx.paging.PagedSource.LoadResult
-import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Page.Companion.COUNT_UNDEFINED
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertSame
@@ -56,7 +56,7 @@
runBlocking {
val pagedSource = ItemDataSource()
val key = pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
- val result = loadInitial(pagedSource, key, 10, true)
+ val result = loadInitial(pagedSource, key, 10, true) as LoadResult.Page
assertEquals(45, result.itemsBefore)
assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
@@ -76,8 +76,9 @@
val pagedSource = ItemDataSource(items = ITEMS_BY_NAME_ID.subList(0, 1))
// this is tricky, since load after and load before with the passed key will fail
- val result =
- loadInitial(pagedSource, pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[0]), 20, true)
+ val result = loadInitial(
+ pagedSource, pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[0]), 20, true
+ ) as LoadResult.Page
assertEquals(0, result.itemsBefore)
assertEquals(ITEMS_BY_NAME_ID.subList(0, 1), result.data)
@@ -90,7 +91,7 @@
// tricky, because load after key is empty, so another load before and load after required
val key = pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID.last())
- val result = loadInitial(pagedSource, key, 20, true)
+ val result = loadInitial(pagedSource, key, 20, true) as LoadResult.Page
assertEquals(90, result.itemsBefore)
assertEquals(ITEMS_BY_NAME_ID.subList(90, 100), result.data)
@@ -101,7 +102,7 @@
fun loadInitial_nullKey() = runBlocking {
val dataSource = ItemDataSource()
- val result = loadInitial(dataSource, null, 10, true)
+ val result = loadInitial(dataSource, null, 10, true) as LoadResult.Page
assertEquals(0, result.itemsBefore)
assertEquals(ITEMS_BY_NAME_ID.subList(0, 10), result.data)
@@ -114,7 +115,7 @@
// if key is past entire data set, should return last items in data set
val key = Key("fz", 0)
- val result = loadInitial(dataSource, key, 10, true)
+ val result = loadInitial(dataSource, key, 10, true) as LoadResult.Page
// NOTE: ideally we'd load 10 items here, but it adds complexity and unpredictability to
// do: load after was empty, so pass full size to load before, since this can incur larger
@@ -132,7 +133,7 @@
// dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
- val result = loadInitial(dataSource, key, 10, false)
+ val result = loadInitial(dataSource, key, 10, false) as LoadResult.Page
assertEquals(COUNT_UNDEFINED, result.itemsBefore)
assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
@@ -145,7 +146,7 @@
// dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
- val result = loadInitial(dataSource, key, 10, true)
+ val result = loadInitial(dataSource, key, 10, true) as LoadResult.Page
assertEquals(COUNT_UNDEFINED, result.itemsBefore)
assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
@@ -156,7 +157,7 @@
fun loadInitial_nullKey_uncounted() = runBlocking {
val dataSource = ItemDataSource(counted = false)
- val result = loadInitial(dataSource, null, 10, true)
+ val result = loadInitial(dataSource, null, 10, true) as LoadResult.Page
assertEquals(COUNT_UNDEFINED, result.itemsBefore)
assertEquals(ITEMS_BY_NAME_ID.subList(0, 10), result.data)
@@ -171,7 +172,7 @@
// dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
- val result = loadInitial(dataSource, key, 10, true)
+ val result = loadInitial(dataSource, key, 10, true) as LoadResult.Page
assertEquals(0, result.itemsBefore)
assertTrue(result.data.isEmpty())
@@ -181,7 +182,7 @@
@Test
fun loadInitial_nullKey_empty() = runBlocking {
val dataSource = ItemDataSource(items = ArrayList())
- val result = loadInitial(dataSource, null, 10, true)
+ val result = loadInitial(dataSource, null, 10, true) as LoadResult.Page
assertEquals(0, result.itemsBefore)
assertTrue(result.data.isEmpty())
@@ -197,7 +198,7 @@
runBlocking {
val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[5])
val params = LoadParams(LoadType.START, key, 5, false, 5)
- val observed = dataSource.load(params).data
+ val observed = (dataSource.load(params) as LoadResult.Page).data
assertEquals(ITEMS_BY_NAME_ID.subList(0, 5), observed)
@@ -217,7 +218,7 @@
runBlocking {
val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[5])
val params = LoadParams(LoadType.END, key, 5, false, 5)
- val observed = dataSource.load(params).data
+ val observed = (dataSource.load(params) as LoadResult.Page).data
assertEquals(ITEMS_BY_NAME_ID.subList(6, 11), observed)
@@ -286,13 +287,13 @@
return if (params.placeholdersEnabled && counted) {
val data = items.subList(start, endExclusive)
- LoadResult(
+ LoadResult.Page(
data = data,
itemsBefore = start,
itemsAfter = items.size - data.size - start
)
} else {
- LoadResult(items.subList(start, endExclusive))
+ LoadResult.Page(items.subList(start, endExclusive))
}
}
@@ -305,7 +306,7 @@
val start = findFirstIndexAfter(params.key!!)
val endExclusive = minOf(start + params.loadSize, items.size)
- return LoadResult(items.subList(start, endExclusive))
+ return LoadResult.Page(items.subList(start, endExclusive))
}
private fun loadBefore(params: LoadParams<Key>): LoadResult<Key, Item> {
@@ -318,7 +319,7 @@
val endExclusive = maxOf(0, firstIndexBefore + 1)
val start = maxOf(0, firstIndexBefore - params.loadSize + 1)
- return LoadResult(items.subList(start, endExclusive))
+ return LoadResult.Page(items.subList(start, endExclusive))
}
private fun findFirstIndexAfter(key: Key): Int {
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
index 6de3920..bbfdca2 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
@@ -34,12 +34,8 @@
inner class ImmediateListDataSource(private val data: List<String>) :
PositionalDataSource<String>() {
- init {
- initExecutor(testExecutor)
- }
-
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<String>) {
- executor.execute {
+ testExecutor.execute {
val totalCount = data.size
val position = computeInitialLoadPosition(params, totalCount)
@@ -51,7 +47,7 @@
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
- executor.execute {
+ testExecutor.execute {
val position = params.startPosition
val end = minOf(position + params.loadSize, data.size)
callback.onResult(data.subList(position, end))
@@ -85,11 +81,8 @@
return ret
}
- override fun onPageResult(
- type: LoadType,
- pageResult: LoadResult<*, String>
- ): Boolean {
- results.add(Result(type, pageResult))
+ override fun onPageResult(type: LoadType, page: LoadResult.Page<*, String>): Boolean {
+ results.add(Result(type, page))
return false
}
@@ -104,7 +97,7 @@
end: Int = 10
): Pager<Int, String> {
val initialData = data.subList(start, end)
- val initialResult = LoadResult<Int, String>(
+ val initialResult = LoadResult.Page<Int, String>(
data = initialData,
itemsBefore = start,
itemsAfter = data.size - initialData.size - start
@@ -231,7 +224,7 @@
// Pager triggers an immediate empty response here, so we don't need to flush the executor
assertEquals(
- listOf(Result(LoadType.END, LoadResult.empty<Int, String>())),
+ listOf(Result(LoadType.END, LoadResult.Page.empty<Int, String>())),
consumer.takeResults()
)
assertEquals(
@@ -249,7 +242,7 @@
// Pager triggers an immediate empty response here, so we don't need to flush the executor
assertEquals(
- listOf(Result(LoadType.START, LoadResult.empty<Int, String>())),
+ listOf(Result(LoadType.START, LoadResult.Page.empty<Int, String>())),
consumer.takeResults()
)
assertEquals(
diff --git a/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
index 475fc4f..706da3b 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
@@ -16,7 +16,6 @@
package androidx.paging
-import androidx.paging.futures.DirectExecutor
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -177,8 +176,6 @@
.setEnablePlaceholders(enablePlaceholders)
.build()
- dataSource.initExecutor(DirectExecutor)
-
val params = PositionalDataSource.LoadInitialParams(
0,
config.initialLoadSizeHint,
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
index ef6be8c..6671aba 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
@@ -101,16 +101,20 @@
private fun loadInitial(params: LoadParams<Int>): LoadResult<Int, Item> {
val position = computeStartPosition(params)
val loadSize = minOf(COUNT - position, params.loadSize)
- val data = loadRangeInternal(position, loadSize)
- return LoadResult(
- data = data,
- itemsBefore = position,
- itemsAfter = COUNT - data.size - position
- )
+ return try {
+ val data = loadRangeInternal(position, loadSize)
+ LoadResult.Page(
+ data = data,
+ itemsBefore = position,
+ itemsAfter = COUNT - data.size - position
+ )
+ } catch (e: RetryableItemError) {
+ LoadResult.Error(e)
+ }
}
private fun loadRange(params: LoadParams<Int>): LoadResult<Int, Item> {
val data = loadRangeInternal(params.key ?: 0, params.loadSize)
- return LoadResult(data)
+ return LoadResult.Page(data)
}
}
diff --git a/paging/runtime/api/api_lint.ignore b/paging/runtime/api/api_lint.ignore
index 24e84aa..117293a 100644
--- a/paging/runtime/api/api_lint.ignore
+++ b/paging/runtime/api/api_lint.ignore
@@ -3,3 +3,7 @@
Method AsyncPagedListDiffer.getItem 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
DocumentExceptions: androidx.paging.AsyncPagedListDiffer#submitList(androidx.paging.PagedList<T>, Runnable):
Method AsyncPagedListDiffer.submitList appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+SetterReturnsThis: androidx.paging.LivePagedListBuilder:
+ Methods must return the builder object (return type LivePagedListBuilder instead of androidx.paging.LivePagedListBuilder<Key,Value>): method androidx.paging.LivePagedListBuilder.setCoroutineScope(kotlinx.coroutines.CoroutineScope)
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
index d5e1e68..d239ec8 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
@@ -120,11 +120,11 @@
throwable?.let { error ->
throwable = null
- throw error
+ return LoadResult.Error(error)
}
val data = listOf("a", "b")
- return LoadResult(
+ return LoadResult.Page(
data = data,
itemsBefore = 0,
itemsAfter = 4 - data.size
@@ -132,7 +132,7 @@
}
private fun loadRange(): LoadResult<Int, String> {
- return LoadResult(listOf("c", "d"))
+ return LoadResult.Page(listOf("c", "d"))
}
}
}
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt b/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
index 7de2f6d..5791bb8 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
@@ -27,12 +27,15 @@
) : PagedSource<Any, Value>() {
override suspend fun load(params: LoadParams<Any>): LoadResult<Any, Value> {
if (params.loadType == LoadType.REFRESH) {
- return LoadResult(
+ return LoadResult.Page(
data = data,
itemsBefore = leadingNulls,
itemsAfter = trailingNulls)
}
- throw IllegalArgumentException("This test source only supports initial load")
+ // TODO: prevent null-key load start/end
+ return LoadResult.Error(
+ IllegalArgumentException("This test source only supports initial load")
+ )
}
}
@@ -44,10 +47,10 @@
): PagedList<String> = runBlocking {
PagedList.create(
pagedSource = FakeSource(leadingNulls, trailingNulls, items.toList()),
+ initialPage = null,
coroutineScope = GlobalScope,
notifyDispatcher = DirectDispatcher,
fetchDispatcher = DirectDispatcher,
- initialFetchDispatcher = DirectDispatcher,
boundaryCallback = null,
config = Config(1, prefetchDistance = 0),
key = null
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt b/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
index a79a9af..0c4e4d7 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
@@ -77,14 +77,30 @@
currentJob?.cancel()
currentJob = coroutineScope.launch(fetchDispatcher) {
- try {
- val pagedList = createPagedList()
- withContext(notifyDispatcher) {
- onSuccess(pagedList)
- }
- } catch (throwable: Throwable) {
- withContext(notifyDispatcher) {
- onError(throwable)
+ val pagedSource = pagedSourceFactory()
+ currentData.pagedSource.unregisterInvalidatedCallback(callback)
+ pagedSource.registerInvalidatedCallback(callback)
+
+ withContext(notifyDispatcher) {
+ currentData.setInitialLoadState(REFRESH, Loading)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ val lastKey = currentData.lastKey as Key?
+ val params = config.toRefreshLoadParams(lastKey)
+ when (val initialResult = pagedSource.load(params)) {
+ is PagedSource.LoadResult.Error -> onError(initialResult.throwable)
+ is PagedSource.LoadResult.Page -> {
+ onSuccess(PagedList.create(
+ pagedSource,
+ initialResult,
+ coroutineScope,
+ notifyDispatcher,
+ fetchDispatcher,
+ boundaryCallback,
+ config,
+ lastKey
+ ))
}
}
}
@@ -94,29 +110,6 @@
previous.setRetryCallback(null)
next.setRetryCallback(refreshRetryCallback)
}
-
- private suspend fun createPagedList(): PagedList<Value> {
- val pagedSource = pagedSourceFactory()
- currentData.pagedSource.unregisterInvalidatedCallback(callback)
- pagedSource.registerInvalidatedCallback(callback)
-
- withContext(notifyDispatcher) {
- currentData.setInitialLoadState(REFRESH, Loading)
- }
-
- @Suppress("UNCHECKED_CAST") // getLastKey guaranteed to be of 'Key' type
- val lastKey = currentData.lastKey as Key?
- return PagedList.create(
- pagedSource,
- coroutineScope,
- notifyDispatcher,
- fetchDispatcher,
- fetchDispatcher,
- boundaryCallback,
- config,
- lastKey
- )
- }
}
/**
diff --git a/paging/rxjava2/api/api_lint.ignore b/paging/rxjava2/api/api_lint.ignore
new file mode 100644
index 0000000..9ed9c00
--- /dev/null
+++ b/paging/rxjava2/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+SetterReturnsThis: androidx.paging.RxPagedListBuilder:
+ Methods must return the builder object (return type RxPagedListBuilder instead of androidx.paging.RxPagedListBuilder<Key,Value>): method androidx.paging.RxPagedListBuilder.setInitialLoadKey(Key)
diff --git a/recyclerview/recyclerview-selection/api/1.1.0-alpha07.txt b/recyclerview/recyclerview-selection/api/1.1.0-alpha07.txt
index cea7610..573c952 100644
--- a/recyclerview/recyclerview-selection/api/1.1.0-alpha07.txt
+++ b/recyclerview/recyclerview-selection/api/1.1.0-alpha07.txt
@@ -94,7 +94,7 @@
public abstract class SelectionTracker<K> {
ctor public SelectionTracker();
- method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+ method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
method public abstract boolean clearSelection();
method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/current.txt b/recyclerview/recyclerview-selection/api/current.txt
index cea7610..573c952 100644
--- a/recyclerview/recyclerview-selection/api/current.txt
+++ b/recyclerview/recyclerview-selection/api/current.txt
@@ -94,7 +94,7 @@
public abstract class SelectionTracker<K> {
ctor public SelectionTracker();
- method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+ method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
method public abstract boolean clearSelection();
method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/public_plus_experimental_1.1.0-alpha07.txt b/recyclerview/recyclerview-selection/api/public_plus_experimental_1.1.0-alpha07.txt
index cea7610..573c952 100644
--- a/recyclerview/recyclerview-selection/api/public_plus_experimental_1.1.0-alpha07.txt
+++ b/recyclerview/recyclerview-selection/api/public_plus_experimental_1.1.0-alpha07.txt
@@ -94,7 +94,7 @@
public abstract class SelectionTracker<K> {
ctor public SelectionTracker();
- method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+ method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
method public abstract boolean clearSelection();
method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/public_plus_experimental_current.txt b/recyclerview/recyclerview-selection/api/public_plus_experimental_current.txt
index cea7610..573c952 100644
--- a/recyclerview/recyclerview-selection/api/public_plus_experimental_current.txt
+++ b/recyclerview/recyclerview-selection/api/public_plus_experimental_current.txt
@@ -94,7 +94,7 @@
public abstract class SelectionTracker<K> {
ctor public SelectionTracker();
- method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+ method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
method public abstract boolean clearSelection();
method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.txt b/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.txt
index adebbf3..4c24b9b 100644
--- a/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.txt
+++ b/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.txt
@@ -95,7 +95,7 @@
public abstract class SelectionTracker<K> {
ctor public SelectionTracker();
- method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+ method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
method public abstract boolean clearSelection();
method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/restricted_current.txt b/recyclerview/recyclerview-selection/api/restricted_current.txt
index adebbf3..4c24b9b 100644
--- a/recyclerview/recyclerview-selection/api/restricted_current.txt
+++ b/recyclerview/recyclerview-selection/api/restricted_current.txt
@@ -95,7 +95,7 @@
public abstract class SelectionTracker<K> {
ctor public SelectionTracker();
- method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+ method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
method public abstract boolean clearSelection();
method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/BandSelectionHelperTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/BandSelectionHelperTest.java
index 98224d0..d1939cf 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/BandSelectionHelperTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/BandSelectionHelperTest.java
@@ -58,7 +58,7 @@
public void setup() throws Exception {
mItems = TestData.createStringData(10);
mIsActive = false;
- mAdapter = new TestAdapter<String>();
+ mAdapter = new TestAdapter<>();
mAdapter.updateTestModelIds(mItems);
mBandHost = new TestBandHost();
mBandPredicate = new TestBandPredicate();
@@ -66,7 +66,7 @@
new TestItemKeyProvider<>(ItemKeyProvider.SCOPE_MAPPED, mAdapter);
OperationMonitor operationMonitor = new OperationMonitor();
- SelectionTracker<String> helper = new DefaultSelectionTracker<String>(
+ SelectionTracker<String> helper = new DefaultSelectionTracker<>(
"band-selection-test",
keyProvider,
SelectionPredicates.createSelectAnything(),
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
index d9f79d5..9940556 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
@@ -50,8 +50,8 @@
private List<String> mItems;
private Set<String> mIgnored;
- private TestAdapter mAdapter;
- private SelectionPredicate mSelectionPredicate;
+ private TestAdapter<String> mAdapter;
+ private SelectionPredicate<String> mSelectionPredicate;
private ItemKeyProvider<String> mKeyProvider;
private DefaultSelectionTracker<String> mTracker;
private TestSelectionObserver<String> mListener;
@@ -83,7 +83,7 @@
}
};
- mKeyProvider = new TestItemKeyProvider<String>(ItemKeyProvider.SCOPE_MAPPED, mAdapter);
+ mKeyProvider = new TestItemKeyProvider<>(ItemKeyProvider.SCOPE_MAPPED, mAdapter);
mTracker = new DefaultSelectionTracker<>(
SELECTION_ID,
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTracker_SingleSelectTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTracker_SingleSelectTest.java
index f34e952..a5730a2 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTracker_SingleSelectTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTracker_SingleSelectTest.java
@@ -44,7 +44,7 @@
public void setUp() throws Exception {
mItems = TestAdapter.createItemList(100);
mListener = new TestSelectionObserver<>();
- TestAdapter adapter = new TestAdapter();
+ TestAdapter<String> adapter = new TestAdapter<>();
adapter.updateTestModelIds(mItems);
ItemKeyProvider<String> keyProvider =
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
index 7d414c14..7a6e171 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
@@ -51,7 +51,7 @@
private GridModel<String> mModel;
private TestHost mHost;
- private TestAdapter mAdapter;
+ private TestAdapter<String> mAdapter;
private Set<String> mLastSelection;
private int mViewWidth;
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
index eff4173..425bcc6 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
@@ -51,15 +51,15 @@
@SmallTest
public final class MouseInputHandlerTest {
- private MouseInputHandler mInputDelegate;
+ private MouseInputHandler<String> mInputDelegate;
private TestOnContextClickListener mMouseCallbacks;
- private TestOnItemActivatedListener mActivationCallbacks;
- private TestFocusDelegate mFocusCallbacks;
+ private TestOnItemActivatedListener<String> mActivationCallbacks;
+ private TestFocusDelegate<String> mFocusCallbacks;
private TestItemDetailsLookup mDetailsLookup;
private SelectionProbe mSelection;
- private SelectionTracker mSelectionMgr;
+ private SelectionTracker<String> mSelectionMgr;
private TestEvents.Builder mEvent;
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/ToolHandlerRegistryTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/ToolHandlerRegistryTest.java
index 96bb216..3e2a6a6 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/ToolHandlerRegistryTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/ToolHandlerRegistryTest.java
@@ -59,11 +59,11 @@
private static final String UNKNOWN = "unknown";
}
- private ToolHandlerRegistry mRegistry;
+ private ToolHandlerRegistry<String> mRegistry;
@Before
public void setUp() {
- mRegistry = new ToolHandlerRegistry(Handlers.DEFAULT);
+ mRegistry = new ToolHandlerRegistry<>(Handlers.DEFAULT);
}
@Test
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
index 15fe7e3..a17e159 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
@@ -48,12 +48,12 @@
private static final List<String> ITEMS = TestData.createStringData(100);
- private TouchInputHandler mInputDelegate;
- private SelectionTracker mSelectionMgr;
- private TestSelectionPredicate mSelectionPredicate;
+ private TouchInputHandler<String> mInputDelegate;
+ private SelectionTracker<String> mSelectionMgr;
+ private TestSelectionPredicate<String> mSelectionPredicate;
private TestRunnable mGestureStarted;
private TestRunnable mHapticPerformer;
- private TestOnItemActivatedListener mActivationCallbacks;
+ private TestOnItemActivatedListener<String> mActivationCallbacks;
private TestItemDetailsLookup mDetailsLookup;
private SelectionProbe mSelection;
private TestDragListener mDragInitiatedListener;
@@ -85,7 +85,7 @@
@Test
public void testTap_ActivatesWhenNoExistingSelection() {
- ItemDetails doc = mDetailsLookup.initAt(11);
+ ItemDetails<String> doc = mDetailsLookup.initAt(11);
mInputDelegate.onSingleTapUp(TAP);
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/SelectionProbe.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/SelectionProbe.java
index 8ac4baf..c424522 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/SelectionProbe.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/SelectionProbe.java
@@ -36,7 +36,7 @@
public SelectionProbe(SelectionTracker<String> mgr) {
mMgr = mgr;
- mSelectionListener = new TestSelectionObserver<String>();
+ mSelectionListener = new TestSelectionObserver<>();
mMgr.addObserver(mSelectionListener);
}
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandPredicate.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandPredicate.java
index 1ee3d73..77fc1598 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandPredicate.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandPredicate.java
@@ -107,14 +107,15 @@
public static final class NonDraggableArea extends BandPredicate {
private final RecyclerView mRecyclerView;
- private final ItemDetailsLookup mDetailsLookup;
+ private final ItemDetailsLookup<?> mDetailsLookup;
/**
* Creates a new instance.
*
- * @param recyclerView the owner RecyclerView
+ * @param recyclerView the owner RecyclerView
* @param detailsLookup provides access to item details.
*/
+ @SuppressWarnings("rawtypes")
public NonDraggableArea(
@NonNull RecyclerView recyclerView, @NonNull ItemDetailsLookup detailsLookup) {
@@ -132,7 +133,8 @@
return false;
}
- @Nullable ItemDetailsLookup.ItemDetails details = mDetailsLookup.getItemDetails(e);
+ @Nullable ItemDetailsLookup.ItemDetails<?> details =
+ mDetailsLookup.getItemDetails(e);
return (details == null) || !details.inDragRegion(e);
}
}
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandSelectionHelper.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandSelectionHelper.java
index 495b63f..fa6c31d 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandSelectionHelper.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandSelectionHelper.java
@@ -58,7 +58,7 @@
static final String TAG = "BandSelectionHelper";
static final boolean DEBUG = false;
- private final BandHost mHost;
+ private final BandHost<K> mHost;
private final ItemKeyProvider<K> mKeyProvider;
@SuppressWarnings("WeakerAccess") /* synthetic access */
final SelectionTracker<K> mSelectionTracker;
@@ -66,17 +66,17 @@
private final FocusDelegate<K> mFocusDelegate;
private final OperationMonitor mLock;
private final AutoScroller mScroller;
- private final GridModel.SelectionObserver mGridObserver;
+ private final GridModel.SelectionObserver<K> mGridObserver;
private @Nullable Point mCurrentPosition;
private @Nullable Point mOrigin;
- private @Nullable GridModel mModel;
+ private @Nullable GridModel<K> mModel;
/**
* See {@link BandSelectionHelper#create}.
*/
BandSelectionHelper(
- @NonNull BandHost host,
+ @NonNull BandHost<K> host,
@NonNull AutoScroller scroller,
@NonNull ItemKeyProvider<K> keyProvider,
@NonNull SelectionTracker<K> selectionTracker,
@@ -122,7 +122,7 @@
*
* @return new BandSelectionHelper instance.
*/
- static <K> BandSelectionHelper create(
+ static <K> BandSelectionHelper<K> create(
@NonNull RecyclerView recyclerView,
@NonNull AutoScroller scroller,
@DrawableRes int bandOverlayId,
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DefaultSelectionTracker.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DefaultSelectionTracker.java
index e8540ce..6534aeb 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DefaultSelectionTracker.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DefaultSelectionTracker.java
@@ -57,7 +57,7 @@
private static final String EXTRA_SELECTION_PREFIX = "androidx.recyclerview.selection";
private final Selection<K> mSelection = new Selection<>();
- private final List<SelectionObserver> mObservers = new ArrayList<>(1);
+ private final List<SelectionObserver<K>> mObservers = new ArrayList<>(1);
private final ItemKeyProvider<K> mKeyProvider;
private final SelectionPredicate<K> mSelectionPredicate;
private final StorageStrategy<K> mStorage;
@@ -79,8 +79,8 @@
*/
public DefaultSelectionTracker(
@NonNull String selectionId,
- @NonNull ItemKeyProvider keyProvider,
- @NonNull SelectionPredicate selectionPredicate,
+ @NonNull ItemKeyProvider<K> keyProvider,
+ @NonNull SelectionPredicate<K> selectionPredicate,
@NonNull StorageStrategy<K> storage) {
checkArgument(selectionId != null);
@@ -102,7 +102,7 @@
}
@Override
- public void addObserver(@NonNull SelectionObserver callback) {
+ public void addObserver(@NonNull SelectionObserver<K> callback) {
checkArgument(callback != null);
mObservers.add(callback);
}
@@ -113,12 +113,12 @@
}
@Override
- public Selection getSelection() {
+ public Selection<K> getSelection() {
return mSelection;
}
@Override
- public void copySelection(@NonNull MutableSelection dest) {
+ public void copySelection(@NonNull MutableSelection<K> dest) {
dest.copyFrom(mSelection);
}
@@ -128,7 +128,7 @@
}
@Override
- protected void restoreSelection(@NonNull Selection other) {
+ protected void restoreSelection(@NonNull Selection<K> other) {
checkArgument(other != null);
setItemsSelectedQuietly(other.mSelection, true);
// NOTE: We intentionally don't restore provisional selection. It's provisional.
@@ -172,7 +172,7 @@
return;
}
- Selection prev = clearSelectionQuietly();
+ Selection<K> prev = clearSelectionQuietly();
notifySelectionCleared(prev);
notifySelectionChanged();
}
@@ -182,10 +182,10 @@
* Returns items in previous selection. Callers are responsible for notifying
* listeners about changes.
*/
- private Selection clearSelectionQuietly() {
+ private Selection<K> clearSelectionQuietly() {
mRange = null;
- MutableSelection prevSelection = new MutableSelection();
+ MutableSelection<K> prevSelection = new MutableSelection();
if (hasSelection()) {
copySelection(prevSelection);
mSelection.clear();
@@ -209,7 +209,7 @@
// Enforce single selection policy.
if (mSingleSelect && hasSelection()) {
- Selection prev = clearSelectionQuietly();
+ Selection<K> prev = clearSelectionQuietly();
notifySelectionCleared(prev);
}
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventRouter.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventRouter.java
index 2ef4f8a..54d6657 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventRouter.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventRouter.java
@@ -40,7 +40,7 @@
private final ToolHandlerRegistry<OnItemTouchListener> mDelegates;
EventRouter() {
- mDelegates = new ToolHandlerRegistry<OnItemTouchListener>(new DummyOnItemTouchListener());
+ mDelegates = new ToolHandlerRegistry<>(new DummyOnItemTouchListener());
}
/**
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
index 993727b3..8453b41 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
@@ -63,7 +63,7 @@
private final ItemKeyProvider<K> mKeyProvider;
private final SelectionPredicate<K> mSelectionPredicate;
- private final List<SelectionObserver> mOnSelectionChangedListeners = new ArrayList<>();
+ private final List<SelectionObserver<K>> mOnSelectionChangedListeners = new ArrayList<>();
// Map from the x-value of the left side of a SparseBooleanArray of adapter positions, keyed
// by their y-offset. For example, if the first column of the view starts at an x-value of 5,
@@ -103,7 +103,7 @@
@SuppressWarnings("unchecked")
GridModel(
- GridHost host,
+ GridHost<K> host,
ItemKeyProvider<K> keyProvider,
SelectionPredicate<K> selectionPredicate) {
@@ -287,7 +287,7 @@
*/
@SuppressWarnings("unchecked")
private void notifySelectionChanged() {
- for (SelectionObserver listener : mOnSelectionChangedListeners) {
+ for (SelectionObserver<K> listener : mOnSelectionChangedListeners) {
listener.onSelectionChanged(mSelection);
}
}
@@ -403,7 +403,7 @@
abstract void onSelectionChanged(Set<K> updatedSelection);
}
- void addOnSelectionChangedListener(SelectionObserver listener) {
+ void addOnSelectionChangedListener(SelectionObserver<K> listener) {
mOnSelectionChangedListeners.add(listener);
}
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/ItemDetailsLookup.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/ItemDetailsLookup.java
index 9955d7b..3251d19 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/ItemDetailsLookup.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/ItemDetailsLookup.java
@@ -241,10 +241,10 @@
@Override
public boolean equals(@Nullable Object obj) {
return (obj instanceof ItemDetails)
- && isEqualTo((ItemDetails) obj);
+ && isEqualTo((ItemDetails<?>) obj);
}
- private boolean isEqualTo(@NonNull ItemDetails other) {
+ private boolean isEqualTo(@NonNull ItemDetails<?> other) {
K key = getSelectionKey();
boolean sameKeys = false;
if (key == null) {
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/PointerDragEventInterceptor.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/PointerDragEventInterceptor.java
index ea8936f..ef0f077 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/PointerDragEventInterceptor.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/PointerDragEventInterceptor.java
@@ -32,12 +32,12 @@
*/
final class PointerDragEventInterceptor implements OnItemTouchListener {
- private final ItemDetailsLookup mEventDetailsLookup;
+ private final ItemDetailsLookup<?> mEventDetailsLookup;
private final OnDragInitiatedListener mDragListener;
private OnItemTouchListener mDelegate;
PointerDragEventInterceptor(
- ItemDetailsLookup eventDetailsLookup,
+ ItemDetailsLookup<?> eventDetailsLookup,
OnDragInitiatedListener dragListener,
@Nullable OnItemTouchListener delegate) {
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/Selection.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/Selection.java
index 155789d..6c49015 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/Selection.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/Selection.java
@@ -54,9 +54,8 @@
* (which can be initiated by long pressing an unselected item while there is an
* existing selection).
*
- * @see MutableSelection
- *
* @param <K> Selection key type. @see {@link StorageStrategy} for supported types.
+ * @see MutableSelection
*/
public class Selection<K> implements Iterable<K> {
@@ -78,7 +77,6 @@
}
/**
- * @param key
* @return true if the position is currently selected.
*/
public boolean contains(@Nullable K key) {
@@ -114,12 +112,13 @@
* Sets the provisional selection, which is a temporary selection that can be saved,
* canceled, or adjusted at a later time. When a new provision selection is applied, the old
* one (if it exists) is abandoned.
+ *
* @return Map of ids added or removed. Added ids have a value of true, removed are false.
*/
Map<K, Boolean> setProvisionalSelection(@NonNull Set<K> newSelection) {
Map<K, Boolean> delta = new LinkedHashMap<>();
- for (K key: mProvisionalSelection) {
+ for (K key : mProvisionalSelection) {
// Mark each item that used to be in the provisional selection
// but is not in the new provisional selection.
if (!newSelection.contains(key) && !mSelection.contains(key)) {
@@ -127,7 +126,7 @@
}
}
- for (K key: mSelection) {
+ for (K key : mSelection) {
// Mark each item that in the selection but is not in the new
// provisional selection.
if (!newSelection.contains(key)) {
@@ -135,7 +134,7 @@
}
}
- for (K key: newSelection) {
+ for (K key : newSelection) {
// Mark each item that was not previously in the selection but is in the new
// provisional selection.
if (!mSelection.contains(key) && !mProvisionalSelection.contains(key)) {
@@ -146,7 +145,7 @@
// Now, iterate through the changes and actually add/remove them to/from the current
// selection. This could not be done in the previous loops because changing the size of
// the selection mid-iteration changes iteration order erroneously.
- for (Map.Entry<K, Boolean> entry: delta.entrySet()) {
+ for (Map.Entry<K, Boolean> entry : delta.entrySet()) {
K key = entry.getKey();
if (entry.getValue()) {
mProvisionalSelection.add(key);
@@ -221,11 +220,11 @@
StringBuilder buffer = new StringBuilder(size() * 28);
buffer.append("Selection{")
- .append("primary{size=" + mSelection.size())
- .append(", entries=" + mSelection)
- .append("}, provisional{size=" + mProvisionalSelection.size())
- .append(", entries=" + mProvisionalSelection)
- .append("}}");
+ .append("primary{size=" + mSelection.size())
+ .append(", entries=" + mSelection)
+ .append("}, provisional{size=" + mProvisionalSelection.size())
+ .append(", entries=" + mProvisionalSelection)
+ .append("}}");
return buffer.toString();
}
@@ -237,10 +236,10 @@
@Override
public boolean equals(Object other) {
return (this == other)
- || (other instanceof Selection && isEqualTo((Selection) other));
+ || (other instanceof Selection && isEqualTo((Selection<?>) other));
}
- private boolean isEqualTo(Selection other) {
+ private boolean isEqualTo(Selection<?> other) {
return mSelection.equals(other.mSelection)
&& mProvisionalSelection.equals(other.mProvisionalSelection);
}
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
index 68f9d02..3ca42df 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
@@ -110,7 +110,7 @@
* may use an observer to control the enabled status of menu items,
* or to initiate {@link android.view.ActionMode}.
*/
- public abstract void addObserver(SelectionObserver observer);
+ public abstract void addObserver(@NonNull SelectionObserver<K> observer);
/** @return true if has a selection */
public abstract boolean hasSelection();
@@ -703,7 +703,7 @@
// GestureRouter is responsible for routing GestureDetector events
// to tool-type specific handlers.
- GestureRouter<MotionInputHandler> gestureRouter = new GestureRouter<>();
+ GestureRouter<MotionInputHandler<K>> gestureRouter = new GestureRouter<>();
GestureDetector gestureDetector = new GestureDetector(mContext, gestureRouter);
// GestureSelectionHelper provides logic that interprets a combination
@@ -758,7 +758,7 @@
// Provides high level glue for binding touch events
// and gestures to selection framework.
- TouchInputHandler<K> touchHandler = new TouchInputHandler<K>(
+ TouchInputHandler<K> touchHandler = new TouchInputHandler<>(
tracker,
mKeyProvider,
mDetailsLookup,
@@ -800,7 +800,7 @@
gestureRouter.register(toolType, mouseHandler);
}
- @Nullable BandSelectionHelper bandHelper = null;
+ @Nullable BandSelectionHelper<K> bandHelper = null;
// Band selection not supported in single select mode, or when key access
// is limited to anything less than the entire corpus.
diff --git a/recyclerview/recyclerview/api/api_lint.ignore b/recyclerview/recyclerview/api/api_lint.ignore
index 4579639..b7865b2 100644
--- a/recyclerview/recyclerview/api/api_lint.ignore
+++ b/recyclerview/recyclerview/api/api_lint.ignore
@@ -759,3 +759,7 @@
Missing nullability on parameter `source` in method `LayoutParams`
MissingNullability: androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams#LayoutParams(androidx.recyclerview.widget.RecyclerView.LayoutParams) parameter #0:
Missing nullability on parameter `source` in method `LayoutParams`
+
+
+SetterReturnsThis: androidx.recyclerview.widget.AsyncDifferConfig.Builder:
+ Methods must return the builder object (return type Builder instead of androidx.recyclerview.widget.AsyncDifferConfig.Builder<T>): method androidx.recyclerview.widget.AsyncDifferConfig.Builder.setBackgroundThreadExecutor(java.util.concurrent.Executor)
diff --git a/room/runtime/api/api_lint.ignore b/room/runtime/api/api_lint.ignore
index cfb48c3..e00e9db 100644
--- a/room/runtime/api/api_lint.ignore
+++ b/room/runtime/api/api_lint.ignore
@@ -17,3 +17,7 @@
RegistrationName: androidx.room.RoomDatabase.Builder#addCallback(androidx.room.RoomDatabase.Callback):
Callback methods should be named register/unregister; was addCallback
+
+
+SetterReturnsThis: androidx.room.RoomDatabase.Builder:
+ Methods must return the builder object (return type Builder instead of androidx.room.RoomDatabase.Builder<T>): method androidx.room.RoomDatabase.Builder.setJournalMode(androidx.room.RoomDatabase.JournalMode)
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
index 4e46aa7..8d2c2e2 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
@@ -90,7 +90,7 @@
// TODO: Glue selection to ActionMode, since that'll be a common practice.
mSelectionTracker.addObserver(
- new SelectionObserver<Long>() {
+ new SelectionObserver<Uri>() {
@Override
public void onSelectionChanged() {
Log.i(TAG, "Selection changed to: " + mSelectionTracker.getSelection());
diff --git a/testutils/testutils-ktx/src/main/java/androidx/testutils/TestDispatcher.kt b/testutils/testutils-ktx/src/main/java/androidx/testutils/TestDispatcher.kt
index 127eaef..be8b54f 100644
--- a/testutils/testutils-ktx/src/main/java/androidx/testutils/TestDispatcher.kt
+++ b/testutils/testutils-ktx/src/main/java/androidx/testutils/TestDispatcher.kt
@@ -21,7 +21,7 @@
import kotlin.coroutines.CoroutineContext
/**
- * [CoroutineDispatcher] which keeps track of all its queues jobs.
+ * [CoroutineDispatcher] which keeps track of all its queued jobs.
*/
class TestDispatcher : CoroutineDispatcher() {
val queue = ConcurrentLinkedQueue<Runnable>()
@@ -31,8 +31,9 @@
}
fun executeAll() {
- while (queue.peek() != null) {
- queue.poll()!!.run()
- }
+ do {
+ val runnable = queue.poll()
+ runnable?.run()
+ } while (runnable != null)
}
}
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt
new file mode 100644
index 0000000..347f2b2
--- /dev/null
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.benchmark.test
+
+import android.app.Activity
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.benchmark.measureDrawPerf
+import androidx.ui.benchmark.measureFirstCompose
+import androidx.ui.benchmark.measureFirstDraw
+import androidx.ui.benchmark.measureFirstLayout
+import androidx.ui.benchmark.measureFirstMeasure
+import androidx.ui.benchmark.measureLayoutPerf
+import androidx.ui.benchmark.toggleStateMeasureDraw
+import androidx.ui.benchmark.toggleStateMeasureLayout
+import androidx.ui.benchmark.toggleStateMeasureMeasure
+import androidx.ui.benchmark.toggleStateMeasureRecompose
+import androidx.ui.test.cases.SimpleRadioButton1TestCase
+import androidx.ui.test.cases.SimpleRadioButton2TestCase
+import androidx.ui.test.cases.SimpleRadioButton3TestCase
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@LargeTest
+@RunWith(JUnit4::class)
+class SimpleRadioButtonBenchmark {
+ @get:Rule
+ val benchmarkRule = BenchmarkRule()
+
+ @get:Rule
+ val activityRule = ActivityTestRule(Activity::class.java)
+
+ private val activity: Activity get() = activityRule.activity
+
+ @Test
+ fun radio_button_1_first_compose() {
+ benchmarkRule.measureFirstCompose(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_first_measure() {
+ benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_first_layout() {
+ benchmarkRule.measureFirstLayout(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_first_draw() {
+ benchmarkRule.measureFirstDraw(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_update_recompose() {
+ benchmarkRule.toggleStateMeasureRecompose(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_update_measure() {
+ benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_update_layout() {
+ benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_update_draw() {
+ benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_layout() {
+ benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_1_draw() {
+ benchmarkRule.measureDrawPerf(activity, SimpleRadioButton1TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_first_compose() {
+ benchmarkRule.measureFirstCompose(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_first_measure() {
+ benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_first_layout() {
+ benchmarkRule.measureFirstLayout(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_first_draw() {
+ benchmarkRule.measureFirstDraw(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_update_recompose() {
+ benchmarkRule.toggleStateMeasureRecompose(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_update_measure() {
+ benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_update_layout() {
+ benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_update_draw() {
+ benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_layout() {
+ benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_2_draw() {
+ benchmarkRule.measureDrawPerf(activity, SimpleRadioButton2TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_3_first_compose() {
+ benchmarkRule.measureFirstCompose(activity, SimpleRadioButton3TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_3_first_measure() {
+ benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton3TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_3_first_layout() {
+ benchmarkRule.measureFirstLayout(activity, SimpleRadioButton3TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_3_first_draw() {
+ benchmarkRule.measureFirstDraw(activity, SimpleRadioButton3TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_3_update_measure() {
+ benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton3TestCase(activity),
+ toggleCausesRecompose = false)
+ }
+
+ @Test
+ fun radio_button_3_update_layout() {
+ benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton3TestCase(activity),
+ toggleCausesRecompose = false)
+ }
+
+ @Test
+ fun radio_button_3_update_draw() {
+ benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton3TestCase(activity),
+ toggleCausesRecompose = false)
+ }
+
+ @Test
+ fun radio_button_3_layout() {
+ benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton3TestCase(activity))
+ }
+
+ @Test
+ fun radio_button_3_draw() {
+ benchmarkRule.measureDrawPerf(activity, SimpleRadioButton3TestCase(activity))
+ }
+}
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
index a8cf1be..c788ed7 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
@@ -19,7 +19,6 @@
import android.app.Activity
import androidx.benchmark.junit4.BenchmarkRule
import androidx.test.filters.LargeTest
-import androidx.test.filters.Suppress
import androidx.test.rule.ActivityTestRule
import androidx.ui.benchmark.measureDrawPerf
import androidx.ui.benchmark.measureFirstCompose
@@ -28,13 +27,12 @@
import androidx.ui.benchmark.measureFirstMeasure
import androidx.ui.benchmark.measureLayoutPerf
import androidx.ui.test.DisableTransitions
-import androidx.ui.test.RandomTextGeneratorTestRule
+import androidx.ui.test.TextBenchmarkTestRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-@Suppress
@LargeTest
@RunWith(Parameterized::class)
class TextBasicBenchmark(
@@ -50,19 +48,19 @@
val disableAnimationRule = DisableTransitions()
@get:Rule
- val textGeneratorRule = RandomTextGeneratorTestRule()
+ val textBenchmarkRule = TextBenchmarkTestRule()
private val activity: Activity get() = activityRule.activity
companion object {
@JvmStatic
@Parameterized.Parameters(name = "length={0}")
- fun initParameters(): Array<Any> = arrayOf(8, 16, 32, 64, 128, 256, 512, 1024)
+ fun initParameters(): Array<Any> = arrayOf(32, 512)
}
@Test
fun first_compose() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstCompose(
activity,
TextBasicTestCase(activity, textLength, textGenerator)
@@ -72,7 +70,7 @@
@Test
fun first_measure() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstMeasure(
activity,
TextBasicTestCase(activity, textLength, textGenerator)
@@ -82,7 +80,7 @@
@Test
fun first_layout() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstLayout(
activity,
TextBasicTestCase(activity, textLength, textGenerator)
@@ -92,7 +90,7 @@
@Test
fun first_draw() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstDraw(
activity,
TextBasicTestCase(activity, textLength, textGenerator)
@@ -102,7 +100,7 @@
@Test
fun layout() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureLayoutPerf(
activity,
TextBasicTestCase(activity, textLength, textGenerator)
@@ -112,7 +110,7 @@
@Test
fun draw() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureDrawPerf(
activity,
TextBasicTestCase(activity, textLength, textGenerator)
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
index fd08fe9..19e5813 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
@@ -28,7 +28,7 @@
import androidx.ui.benchmark.measureFirstMeasure
import androidx.ui.benchmark.measureLayoutPerf
import androidx.ui.test.DisableTransitions
-import androidx.ui.test.RandomTextGeneratorTestRule
+import androidx.ui.test.TextBenchmarkTestRule
import androidx.ui.test.cartesian
import org.junit.Rule
import org.junit.Test
@@ -40,8 +40,7 @@
@RunWith(Parameterized::class)
class TextMultiStyleBenchmark(
private val textLength: Int,
- private val styleCount: Int,
- private val hasMetricAffectingStyle: Boolean
+ private val styleCount: Int
) {
@get:Rule
val benchmarkRule = BenchmarkRule()
@@ -53,30 +52,28 @@
val disableAnimationRule = DisableTransitions()
@get:Rule
- val textGeneratorRule = RandomTextGeneratorTestRule()
+ val textBenchmarkRule = TextBenchmarkTestRule()
private val activity: Activity get() = activityRule.activity
companion object {
@JvmStatic
- @Parameterized.Parameters(name = "length={0} styleCount={1} hasMetricAffectingStyle={2}")
+ @Parameterized.Parameters(name = "length={0} styleCount={1}")
fun initParameters() = cartesian(
- arrayOf(8, 64, 512),
- arrayOf(8, 64, 512),
- arrayOf(true, false)
+ arrayOf(32, 512),
+ arrayOf(32, 512)
)
}
@Test
fun first_compose() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstCompose(
activity,
TextMultiStyleTestCase(
activity,
textLength,
styleCount,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -85,14 +82,13 @@
@Test
fun first_measure() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstMeasure(
activity,
TextMultiStyleTestCase(
activity,
textLength,
styleCount,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -101,14 +97,13 @@
@Test
fun first_layout() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstLayout(
activity,
TextMultiStyleTestCase(
activity,
textLength,
styleCount,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -117,14 +112,13 @@
@Test
fun first_draw() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstDraw(
activity,
TextMultiStyleTestCase(
activity,
textLength,
styleCount,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -133,14 +127,13 @@
@Test
fun layout() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureLayoutPerf(
activity,
TextMultiStyleTestCase(
activity,
textLength,
styleCount,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -149,14 +142,13 @@
@Test
fun draw() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureDrawPerf(
activity,
TextMultiStyleTestCase(
activity,
textLength,
styleCount,
- hasMetricAffectingStyle,
textGenerator
)
)
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
index 48d1152..2fd7bf0 100644
--- 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
@@ -28,7 +28,7 @@
import androidx.ui.benchmark.measureFirstMeasure
import androidx.ui.benchmark.measureLayoutPerf
import androidx.ui.test.DisableTransitions
-import androidx.ui.test.RandomTextGeneratorTestRule
+import androidx.ui.test.TextBenchmarkTestRule
import androidx.ui.test.cartesian
import org.junit.Rule
import org.junit.Test
@@ -39,8 +39,7 @@
@LargeTest
@RunWith(Parameterized::class)
class TextWithSpanBenchmark(
- private val textLength: Int,
- private val hasMetricAffectingStyle: Boolean
+ private val textLength: Int
) {
@get:Rule
val benchmarkRule = BenchmarkRule()
@@ -52,28 +51,26 @@
val disableAnimationRule = DisableTransitions()
@get:Rule
- val textGeneratorRule = RandomTextGeneratorTestRule()
+ val textBenchmarkRule = TextBenchmarkTestRule()
private val activity: Activity get() = activityRule.activity
companion object {
@JvmStatic
- @Parameterized.Parameters(name = "length={0} hasMetricAffectingStyle={1}")
+ @Parameterized.Parameters(name = "length={0}")
fun initParameters() = cartesian(
- arrayOf(8, 64, 512),
- arrayOf(true, false)
+ arrayOf(32, 512)
)
}
@Test
fun first_compose() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstCompose(
activity,
TextWithSpanTestCase(
activity,
textLength,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -82,13 +79,12 @@
@Test
fun first_measure() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstMeasure(
activity,
TextWithSpanTestCase(
activity,
textLength,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -97,13 +93,12 @@
@Test
fun first_layout() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstLayout(
activity,
TextWithSpanTestCase(
activity,
textLength,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -112,13 +107,12 @@
@Test
fun first_draw() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureFirstDraw(
activity,
TextWithSpanTestCase(
activity,
textLength,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -127,13 +121,12 @@
@Test
fun layout() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureLayoutPerf(
activity,
TextWithSpanTestCase(
activity,
textLength,
- hasMetricAffectingStyle,
textGenerator
)
)
@@ -142,13 +135,12 @@
@Test
fun draw() {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
benchmarkRule.measureDrawPerf(
activity,
TextWithSpanTestCase(
activity,
textLength,
- hasMetricAffectingStyle,
textGenerator
)
)
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/text/ParagraphBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/text/ParagraphBenchmark.kt
index a471008..d60e197 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/text/ParagraphBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/text/ParagraphBenchmark.kt
@@ -24,7 +24,7 @@
import androidx.ui.core.sp
import androidx.ui.test.Alphabet
import androidx.ui.test.RandomTextGenerator
-import androidx.ui.test.RandomTextGeneratorTestRule
+import androidx.ui.test.TextBenchmarkTestRule
import androidx.ui.test.TextType
import androidx.ui.test.cartesian
import androidx.ui.text.font.Font
@@ -45,7 +45,7 @@
@JvmStatic
@Parameterized.Parameters(name = "length={0} type={1} alphabet={2}")
fun initParameters(): List<Array<Any>> = cartesian(
- arrayOf(32, 128, 512),
+ arrayOf(32, 512),
arrayOf(TextType.PlainText, TextType.StyledText),
arrayOf(Alphabet.Latin, Alphabet.Cjk)
)
@@ -55,7 +55,7 @@
val benchmarkRule = BenchmarkRule()
@get:Rule
- val textGeneratorRule = RandomTextGeneratorTestRule(alphabet)
+ val textBenchmarkRule = TextBenchmarkTestRule(alphabet)
// dummy object required to construct Paragraph
private val resourceLoader = object : Font.ResourceLoader {
@@ -87,7 +87,7 @@
fun minIntrinsicWidth() {
benchmarkRule.measureRepeated {
val paragraph = runWithTimingDisabled {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
paragraph(textGenerator)
}
}
@@ -100,7 +100,7 @@
fun layout() {
benchmarkRule.measureRepeated {
val pair = runWithTimingDisabled {
- textGeneratorRule.generator { textGenerator ->
+ textBenchmarkRule.generator { textGenerator ->
val paragraph = paragraph(textGenerator)
paragraph.layout(ParagraphConstraints(Float.MAX_VALUE))
// create a new paragraph and use a smaller width to get
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
index dfb4d0e..d000723 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
@@ -20,7 +20,9 @@
import android.view.ViewGroup
import androidx.compose.composer
import androidx.ui.graphics.Color
-import androidx.ui.material.MaterialTheme
+import androidx.ui.layout.ConstrainedBox
+import androidx.ui.layout.DpConstraints
+import androidx.ui.layout.Wrap
import androidx.ui.test.ComposeTestCase
import androidx.ui.test.RandomTextGenerator
import androidx.ui.text.TextStyle
@@ -39,8 +41,10 @@
}
override fun setComposeContent(activity: Activity) = activity.setContent {
- MaterialTheme {
- Text(text = text, style = TextStyle(color = Color.Black, fontSize = 14.sp))
+ Wrap {
+ ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
+ Text(text = text, style = TextStyle(color = Color.Black, fontSize = 8.sp))
+ }
}
}!!
}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
index 6490de2..06abd8a 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
@@ -20,7 +20,9 @@
import android.view.ViewGroup
import androidx.compose.composer
import androidx.ui.graphics.Color
-import androidx.ui.material.MaterialTheme
+import androidx.ui.layout.ConstrainedBox
+import androidx.ui.layout.DpConstraints
+import androidx.ui.layout.Wrap
import androidx.ui.test.ComposeTestCase
import androidx.ui.test.RandomTextGenerator
import androidx.ui.text.AnnotatedString
@@ -30,7 +32,6 @@
activity: Activity,
private val textLength: Int,
private val styleCount: Int,
- private val hasMetricAffectingStyle: Boolean,
private val randomTextGenerator: RandomTextGenerator
) : ComposeTestCase(activity) {
@@ -40,14 +41,16 @@
text = randomTextGenerator.nextAnnotatedString(
length = textLength,
styleCount = styleCount,
- hasMetricAffectingStyle = hasMetricAffectingStyle
+ hasMetricAffectingStyle = true
)
return super.setupContentInternal(activity)
}
override fun setComposeContent(activity: Activity) = activity.setContent {
- MaterialTheme {
- Text(text = text, style = TextStyle(color = Color.Black, fontSize = 14.sp))
+ Wrap {
+ ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
+ Text(text = text, style = TextStyle(color = Color.Black, fontSize = 8.sp))
+ }
}
}!!
}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
index 80f93c3..f2d5275 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
@@ -20,7 +20,9 @@
import android.view.ViewGroup
import androidx.compose.composer
import androidx.ui.graphics.Color
-import androidx.ui.material.MaterialTheme
+import androidx.ui.layout.ConstrainedBox
+import androidx.ui.layout.DpConstraints
+import androidx.ui.layout.Wrap
import androidx.ui.test.ComposeTestCase
import androidx.ui.test.RandomTextGenerator
import androidx.ui.text.TextStyle
@@ -28,7 +30,6 @@
class TextWithSpanTestCase(
activity: Activity,
private val textLength: Int,
- private val hasMetricAffectingStyle: Boolean,
private val randomTextGenerator: RandomTextGenerator
) : ComposeTestCase(activity) {
@@ -37,16 +38,18 @@
override fun setupContentInternal(activity: Activity): ViewGroup {
textPieces = randomTextGenerator.nextStyledWordList(
length = textLength,
- hasMetricAffectingStyle = hasMetricAffectingStyle
+ hasMetricAffectingStyle = true
)
return super.setupContentInternal(activity)
}
override fun setComposeContent(activity: Activity) = activity.setContent {
- MaterialTheme {
- Text(style = TextStyle(color = Color.Black, fontSize = 14.sp)) {
- textPieces.forEach { (text, style) ->
- Span(text = text, style = style)
+ Wrap {
+ ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
+ Text(style = TextStyle(color = Color.Black, fontSize = 8.sp)) {
+ textPieces.forEach { (text, style) ->
+ Span(text = text, style = style)
+ }
}
}
}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/RandomTextGeneratorTestRule.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/RandomTextGeneratorTestRule.kt
deleted file mode 100644
index 8265c50..0000000
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/RandomTextGeneratorTestRule.kt
+++ /dev/null
@@ -1,50 +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.test
-
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import kotlin.random.Random
-
-/**
- * Test rule that initiates a [RandomTextGenerator] using a different seed based on the class and
- * function name. This way each function will have a different text generated, but at each run
- * the same function will get the same text.
- *
- * This will ensure that the execution order of a test class or functions in a test class does
- * not affect others because of the native text layout cache.
- */
-class RandomTextGeneratorTestRule(private val alphabet: Alphabet = Alphabet.Latin) : TestRule {
- private lateinit var textGenerator: RandomTextGenerator
-
- override fun apply(base: Statement, description: Description): Statement =
- object : Statement() {
- override fun evaluate() {
- // gives the full class and function name including the parameters
- val fullName = "${description.className}#${description.methodName}"
-
- textGenerator = RandomTextGenerator(alphabet, Random(fullName.hashCode()))
-
- base.evaluate()
- }
- }
-
- fun <T> generator(block: (generator: RandomTextGenerator) -> T): T {
- return block(textGenerator)
- }
-}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkTestRule.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkTestRule.kt
new file mode 100644
index 0000000..23e4653
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkTestRule.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.test
+
+import android.graphics.Canvas
+import android.util.Log
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import kotlin.random.Random
+
+/**
+ * Collection of text benchmark utilities. It tries to
+ * - trigger garbage collection
+ * - free text layout caches
+ * before each test run.
+ *
+ * It also provides random text generation capabilities.
+ *
+ */
+class TextBenchmarkTestRule(alphabet: Alphabet = Alphabet.Latin) : TestRule {
+ private val textGeneratorTestRule = RandomTextGeneratorTestRule(alphabet)
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return RuleChain
+ .outerRule(GarbageCollectTestRule())
+ .around(TextLayoutCacheTestRule())
+ .around(textGeneratorTestRule)
+ .apply(base, description)
+ }
+
+ fun <T> generator(block: (generator: RandomTextGenerator) -> T): T {
+ return textGeneratorTestRule.generator { textGenerator ->
+ block(textGenerator)
+ }
+ }
+}
+
+/**
+ * At the beginning of each test calls Canvas.freeTextLayoutCaches in order to clear the native
+ * text layout cache.
+ */
+private class TextLayoutCacheTestRule : TestRule {
+ private val TAG = "TextLayoutCacheTestRule"
+
+ override fun apply(base: Statement, description: Description): Statement =
+ object : Statement() {
+ override fun evaluate() {
+ tryFreeTextLayoutCache()
+ base.evaluate()
+ }
+ }
+
+ fun tryFreeTextLayoutCache() {
+ try {
+ val freeCaches = Canvas::class.java.getDeclaredMethod("freeTextLayoutCaches")
+ freeCaches.isAccessible = true
+ freeCaches.invoke(null)
+ } catch (e: Exception) {
+ Log.w(TAG, "Cannot fre text layout cache", e)
+ // ignore
+ }
+ }
+}
+
+/**
+ * At the beginning of each test calls Runtime.getRuntime().gc() in order to free memory and
+ * possibly prevent GC during measurement.
+ */
+private class GarbageCollectTestRule : TestRule {
+ override fun apply(base: Statement, description: Description): Statement =
+ object : Statement() {
+ override fun evaluate() {
+ Runtime.getRuntime().gc()
+ base.evaluate()
+ }
+ }
+}
+
+/**
+ * Test rule that initiates a [RandomTextGenerator] using a different seed based on the class and
+ * function name. This way each function will have a different text generated, but at each run
+ * the same function will get the same text.
+ *
+ * This will ensure that the execution order of a test class or functions in a test class does
+ * not affect others because of the native text layout cache.
+ */
+private class RandomTextGeneratorTestRule(
+ private val alphabet: Alphabet = Alphabet.Latin
+) : TestRule {
+ private lateinit var textGenerator: RandomTextGenerator
+
+ override fun apply(base: Statement, description: Description): Statement =
+ object : Statement() {
+ override fun evaluate() {
+ // gives the full class and function name including the parameters
+ val fullName = "${description.className}#${description.methodName}"
+
+ textGenerator = RandomTextGenerator(alphabet, Random(fullName.hashCode()))
+
+ base.evaluate()
+ }
+ }
+
+ fun <T> generator(block: (generator: RandomTextGenerator) -> T): T {
+ return block(textGenerator)
+ }
+}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt
new file mode 100644
index 0000000..e854f7d1
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.test.cases
+
+import android.app.Activity
+import androidx.compose.FrameManager
+import androidx.compose.State
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.Dp
+import androidx.ui.core.dp
+import androidx.ui.test.ComposeTestCase
+import androidx.ui.test.ToggleableTestCase
+
+abstract class BaseSimpleRadioButtonTestCase(
+ activity: Activity
+) : ComposeTestCase(activity), ToggleableTestCase {
+
+ private var state: State<Dp>? = null
+
+ fun getInnerSize(): State<Dp> {
+ val innerSize = +state { 10.dp }
+ state = innerSize
+ return innerSize
+ }
+
+ override fun toggleState() {
+ with(state!!) {
+ value = if (value == 10.dp) {
+ 20.dp
+ } else {
+ 10.dp
+ }
+ }
+ FrameManager.nextFrame()
+ }
+}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
index a975728..b03ca4f 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
@@ -31,6 +31,7 @@
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.LayoutSize
import androidx.ui.foundation.HorizontalScroller
import androidx.ui.layout.Row
import androidx.ui.foundation.ScrollerPosition
@@ -54,7 +55,7 @@
MaterialTheme {
Surface {
VerticalScroller {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
repeat(5) { index ->
SquareRow(index == 0)
}
@@ -73,7 +74,7 @@
fun SquareRow(useScrollerPosition: Boolean) {
val playStoreColor = Color(red = 0x00, green = 0x00, blue = 0x80)
val content = @Composable {
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
repeat(6) {
WithDensity {
FlexColumn(crossAxisAlignment = CrossAxisAlignment.Start) {
@@ -92,7 +93,10 @@
text = "Some title",
style = TextStyle(Color.Black, 60.px.toSp())
)
- Row(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
Text("3.5 ★", TextStyle(fontSize = 40.px.toSp()))
ColoredRect(
width = 40.px.toDp(),
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
index 48fdd1d..be4b757 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
@@ -32,6 +32,7 @@
import androidx.ui.layout.Column
import androidx.ui.layout.Container
import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
import androidx.ui.foundation.ScrollerPosition
import androidx.ui.foundation.VerticalScroller
import androidx.ui.graphics.Paint
@@ -51,7 +52,10 @@
VerticalScroller(
scrollerPosition = scrollerPosition
) {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
for (green in 0..0xFF) {
ColorStripe(0xFF, green, 0)
}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt
new file mode 100644
index 0000000..48ba67e
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.test.cases
+
+import android.app.Activity
+import androidx.compose.composer
+import androidx.ui.core.dp
+import androidx.ui.core.setContent
+import androidx.ui.foundation.shape.DrawShape
+import androidx.ui.foundation.shape.border.Border
+import androidx.ui.foundation.shape.border.DrawBorder
+import androidx.ui.foundation.shape.corner.CircleShape
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Container
+
+class SimpleRadioButton1TestCase(
+ activity: Activity
+) : BaseSimpleRadioButtonTestCase(activity) {
+
+ override fun setComposeContent(activity: Activity) = activity.setContent {
+ Container(width = 48.dp, height = 48.dp) {
+ DrawBorder(CircleShape, Border(Color.Cyan, 1.dp))
+ val innerSize = getInnerSize().value
+ Container(width = innerSize, height = innerSize) {
+ DrawShape(CircleShape, Color.Cyan)
+ }
+ }
+ }!!
+}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt
new file mode 100644
index 0000000..7623891
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.test.cases
+
+import android.app.Activity
+import androidx.compose.composer
+import androidx.ui.core.Density
+import androidx.ui.core.Dp
+import androidx.ui.core.Px
+import androidx.ui.core.PxSize
+import androidx.ui.core.dp
+import androidx.ui.core.setContent
+import androidx.ui.core.withDensity
+import androidx.ui.engine.geometry.Offset
+import androidx.ui.engine.geometry.Outline
+import androidx.ui.engine.geometry.Shape
+import androidx.ui.engine.geometry.shift
+import androidx.ui.foundation.shape.DrawShape
+import androidx.ui.foundation.shape.border.Border
+import androidx.ui.foundation.shape.border.DrawBorder
+import androidx.ui.foundation.shape.corner.CircleShape
+import androidx.ui.graphics.Color
+import androidx.ui.graphics.Path
+import androidx.ui.layout.Container
+
+class SimpleRadioButton2TestCase(
+ activity: Activity
+) : BaseSimpleRadioButtonTestCase(activity) {
+
+ override fun setComposeContent(activity: Activity) = activity.setContent {
+ Container(width = 48.dp, height = 48.dp) {
+ DrawBorder(CircleShape, Border(Color.Cyan, 1.dp))
+ val padding = (48.dp - getInnerSize().value) / 2
+ DrawShape(PaddingShape(padding, CircleShape), Color.Cyan)
+ }
+ }!!
+}
+
+private data class PaddingShape(val padding: Dp, val shape: Shape) : Shape {
+ override fun createOutline(size: PxSize, density: Density): Outline {
+ val twoPaddings = withDensity(density) { (padding * 2).toPx() }
+ val sizeMinusPaddings = PxSize(size.width - twoPaddings, size.height - twoPaddings)
+ val rawResult = shape.createOutline(sizeMinusPaddings, density)
+ return rawResult.offset(twoPaddings / 2)
+ }
+}
+
+private fun Outline.offset(size: Px): Outline {
+ val offset = Offset(size.value, size.value)
+ return when (this) {
+ is Outline.Rectangle -> Outline.Rectangle(rect.shift(offset))
+ is Outline.Rounded -> Outline.Rounded(rrect.shift(offset))
+ is Outline.Generic -> Outline.Generic(Path().apply {
+ addPath(path)
+ shift(offset)
+ })
+ }
+}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt
new file mode 100644
index 0000000..9c505c6
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.test.cases
+
+import android.app.Activity
+import androidx.compose.composer
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.Draw
+import androidx.ui.core.PxSize
+import androidx.ui.core.dp
+import androidx.ui.core.minDimension
+import androidx.ui.core.setContent
+import androidx.ui.engine.geometry.Offset
+import androidx.ui.graphics.Canvas
+import androidx.ui.graphics.Paint
+import androidx.ui.graphics.PaintingStyle
+import androidx.ui.layout.Container
+
+class SimpleRadioButton3TestCase(
+ activity: Activity
+) : BaseSimpleRadioButtonTestCase(activity) {
+
+ override fun setComposeContent(activity: Activity) = activity.setContent {
+ Container(width = 48.dp, height = 48.dp) {
+ val innerSize = getInnerSize()
+ val borderPaint = +memo { Paint().apply { style = PaintingStyle.stroke } }
+ val fillPaint = +memo { Paint() }
+ Draw { canvas: Canvas, parentSize: PxSize ->
+ val center = Offset(parentSize.width.value / 2f, parentSize.height.value / 2f)
+ canvas.drawCircle(center, parentSize.minDimension.value, borderPaint)
+ val innerRadius = innerSize.value.value / 2f
+ canvas.drawCircle(center, innerRadius, fillPaint)
+ }
+ }
+ }!!
+}
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/AnimatableSeekBar.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/AnimatableSeekBar.kt
index b4dfff8..3bbc4cb 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/AnimatableSeekBar.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/AnimatableSeekBar.kt
@@ -38,6 +38,7 @@
import androidx.ui.graphics.Color
import androidx.ui.layout.Column
import androidx.ui.layout.Container
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Padding
import androidx.ui.graphics.Paint
import androidx.ui.text.TextStyle
@@ -47,7 +48,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Padding(40.dp) {
Text("Drag or tap on the seek bar", style = TextStyle(fontSize = 8.sp))
}
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/FancyScrolling.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/FancyScrolling.kt
index e3f5539..ef30435 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/FancyScrolling.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/FancyScrolling.kt
@@ -42,6 +42,7 @@
import androidx.ui.engine.geometry.Rect
import androidx.ui.graphics.Color
import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Padding
import androidx.ui.graphics.Canvas
import androidx.ui.graphics.Paint
@@ -59,7 +60,7 @@
@Composable
fun FancyScrollingExample() {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Padding(40.dp) {
Text("<== Scroll horizontally ==>", style = TextStyle(fontSize = 20.sp))
}
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/RepeatedRotationActivity.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/RepeatedRotationActivity.kt
index e29d21d..1223527 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/RepeatedRotationActivity.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/RepeatedRotationActivity.kt
@@ -30,6 +30,7 @@
import androidx.ui.layout.Center
import androidx.ui.layout.Column
import androidx.ui.layout.Container
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.graphics.Color
import androidx.ui.graphics.Paint
@@ -53,7 +54,10 @@
fun RepeatedRotation() {
Center {
val state = +state { RotationStates.Original }
- Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
val textStyle = TextStyle(fontSize = 18.sp)
PressReleasedGestureDetector(>
state.value = RotationStates.Rotated
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/SpringBackScrolling.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/SpringBackScrolling.kt
index 8b668d8..36b937b 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/SpringBackScrolling.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/SpringBackScrolling.kt
@@ -42,6 +42,7 @@
import androidx.ui.engine.geometry.Rect
import androidx.ui.graphics.Color
import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Padding
import androidx.ui.graphics.Canvas
import androidx.ui.graphics.Paint
@@ -59,7 +60,7 @@
@Composable
fun SpringBackExample() {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Padding(40.dp) {
Text("<== Scroll horizontally ==>", style = TextStyle(fontSize = 20.sp))
}
diff --git a/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/PopupActivity.kt b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/PopupActivity.kt
index 9d59c2d..3128c74f 100644
--- a/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/PopupActivity.kt
+++ b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/PopupActivity.kt
@@ -68,7 +68,7 @@
val exampleIndex = +state { 0 }
val totalExamples = 9
- Column(mainAxisSize = LayoutSize.Wrap, crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(crossAxisAlignment = CrossAxisAlignment.Center) {
FlexRow(
mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.SpaceBetween
@@ -192,7 +192,10 @@
@Composable
fun PopupWithChangingContent() {
Container {
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
val heightSize = 120.dp
val widthSize = 160.dp
val popupContentState = +state { 0 }
@@ -244,7 +247,10 @@
val parentHeight = +state { 60.dp }
val parentSizeChanged = +state { false }
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
Container(
height = containerHeight,
width = containerWidth,
@@ -295,7 +301,10 @@
@Composable
fun PopupDropdownAlignment() {
Container {
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
val heightSize = 120.dp
val widthSize = 160.dp
val dropDownAlignment = +state { DropDownAlignment.Left }
@@ -341,7 +350,10 @@
val counter = +state { 0 }
val popupAlignment = +state { Alignment.TopLeft }
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
ColoredContainer(
height = heightSize,
width = widthSize,
@@ -381,7 +393,10 @@
@Composable
fun PopupWithEditText() {
Container {
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
val widthSize = 190.dp
val heightSize = 120.dp
val editLineSize = 150.dp
@@ -428,7 +443,10 @@
@Composable
fun PopupWithChangingSize() {
Container {
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
val showPopup = +state { true }
val heightSize = 120.dp
val widthSize = 160.dp
@@ -485,7 +503,10 @@
val widthSize = 200.dp
Container(width = widthSize, height = heightSize) {
VerticalScroller {
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
ColoredContainer(
width = 80.dp,
height = 160.dp,
@@ -507,7 +528,10 @@
@Composable
fun PopupOnKeyboardUp() {
Container {
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
val widthSize = 190.dp
val heightSize = 120.dp
diff --git a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt
index 8866884..3a146a2 100644
--- a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt
+++ b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt
@@ -48,6 +48,7 @@
import androidx.ui.layout.EdgeInsets
import androidx.ui.layout.FlexColumn
import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Stack
import androidx.ui.layout.Table
import androidx.ui.layout.Wrap
@@ -104,7 +105,7 @@
// Scroller will be clipped to this padding
Padding(padding = 10.dp) {
VerticalScroller {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
phrases.forEach { phrase ->
Text(text = phrase, style = style)
}
@@ -117,7 +118,7 @@
@Composable
fun SimpleHorizontalScrollerSample() {
HorizontalScroller {
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
repeat(100) { index ->
Square(index)
}
@@ -131,9 +132,9 @@
// Create and own ScrollerPosition to call `smoothScrollTo` later
val position = +memo { ScrollerPosition() }
val scrollable = +state { true }
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
HorizontalScroller(scrollerPosition = position, isScrollable = scrollable.value) {
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
repeat(1000) { index ->
Square(index)
}
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
index a24fa7b..9ca9993 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
@@ -25,6 +25,7 @@
import androidx.ui.layout.Column
import androidx.ui.layout.Container
import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.material.Button
import androidx.ui.material.MaterialTheme
@@ -167,7 +168,7 @@
actions: Set<SemanticAction<out Any?>> = setOf(),
children: @Composable() () -> Unit
) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
MaterialTheme {
Collapsable {
InvokeActionsByType(actions)
@@ -177,6 +178,7 @@
}
}
Row(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.Center,
crossAxisAlignment = CrossAxisAlignment.Center
) {
@@ -196,7 +198,10 @@
val secondary =
actions.firstOrNull { it.types.contains(AccessibilityAction.Secondary) }
Text(text = "Accessibility Actions By Type", style = +themeTextStyle { h6.copy() })
- Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
Button(
text = "Primary",
primary?.invoke(ActionCaller.Accessibility) })
@@ -214,7 +219,10 @@
Text(
text = "Accessibility Actions By Phrase",
style = +themeTextStyle { h6.copy() })
- Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
actions.forEach {
Button(
text = it.phrase,
@@ -231,7 +239,10 @@
val positive = actions.firstOrNull { it.types.contains(PolarityAction.Positive) }
val negative = actions.firstOrNull { it.types.contains(PolarityAction.Negative) }
Text(text = "Assistant Actions", style = +themeTextStyle { h6.copy() })
- Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
Button(
text = "Negative",
negative?.invoke(ActionCaller.Assistant) })
@@ -255,7 +266,10 @@
val unitAction =
actions.firstOrNull { it.defaultParam is Unit } as SemanticAction<Unit>?
Text(text = "Actions using Parameters", style = +themeTextStyle { h6.copy() })
- Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
Button(
text = "IntAction",
pxPositionAction?.invoke(param = PxPosition(1.px, 1.px)) })
@@ -279,7 +293,10 @@
val collapsedState = +state { CollapseMode.Collapsed }
- Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
Button(text = "Show/Hide Actions", >
collapsedState.value = when (collapsedState.value) {
CollapseMode.Collapsed -> CollapseMode.Visible
diff --git a/ui/ui-layout/api/0.1.0-dev01.txt b/ui/ui-layout/api/0.1.0-dev01.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/0.1.0-dev01.txt
+++ b/ui/ui-layout/api/0.1.0-dev01.txt
@@ -108,10 +108,10 @@
public final class FlexKt {
ctor public FlexKt();
- method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
- method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
}
public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
public final class FlowKt {
ctor public FlowKt();
- method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/current.txt b/ui/ui-layout/api/current.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/current.txt
+++ b/ui/ui-layout/api/current.txt
@@ -108,10 +108,10 @@
public final class FlexKt {
ctor public FlexKt();
- method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
- method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
}
public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
public final class FlowKt {
ctor public FlowKt();
- method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/public_plus_experimental_0.1.0-dev01.txt b/ui/ui-layout/api/public_plus_experimental_0.1.0-dev01.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/public_plus_experimental_0.1.0-dev01.txt
+++ b/ui/ui-layout/api/public_plus_experimental_0.1.0-dev01.txt
@@ -108,10 +108,10 @@
public final class FlexKt {
ctor public FlexKt();
- method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
- method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
}
public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
public final class FlowKt {
ctor public FlowKt();
- method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/public_plus_experimental_current.txt b/ui/ui-layout/api/public_plus_experimental_current.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/public_plus_experimental_current.txt
+++ b/ui/ui-layout/api/public_plus_experimental_current.txt
@@ -108,10 +108,10 @@
public final class FlexKt {
ctor public FlexKt();
- method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
- method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
}
public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
public final class FlowKt {
ctor public FlowKt();
- method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/restricted_0.1.0-dev01.txt b/ui/ui-layout/api/restricted_0.1.0-dev01.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/restricted_0.1.0-dev01.txt
+++ b/ui/ui-layout/api/restricted_0.1.0-dev01.txt
@@ -108,10 +108,10 @@
public final class FlexKt {
ctor public FlexKt();
- method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
- method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
}
public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
public final class FlowKt {
ctor public FlowKt();
- method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/restricted_current.txt b/ui/ui-layout/api/restricted_current.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/restricted_current.txt
+++ b/ui/ui-layout/api/restricted_current.txt
@@ -108,10 +108,10 @@
public final class FlexKt {
ctor public FlexKt();
- method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
- method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
}
public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
public final class FlowKt {
ctor public FlowKt();
- method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+ method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
}
public final class IntrinsicKt {
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
index 7ce5495..05e04c4 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
@@ -397,7 +397,7 @@
fun RowBaselineAlignment() {
Row(crossAxisAlignment = CrossAxisAlignment.AlignmentLine(FirstBaseline)) {
Text("First text")
- Column(mainAxisSize = LayoutSize.Wrap) {
+ Column {
SizedRectangle(Color.Blue, width = 10.dp, height = 50.dp)
Padding(30.dp) {
Text("Second text", style = TextStyle(fontSize = 45.sp))
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
index 97c8d8e..d841d57 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
@@ -25,6 +25,7 @@
import androidx.ui.layout.Container
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.layout.Row
import androidx.ui.layout.WidthSpacer
@@ -66,77 +67,102 @@
fun LayoutDemo() {
val lightGrey = Color(0xFFCFD8DC)
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.Start,
crossAxisAlignment = CrossAxisAlignment.Start
) {
Text(text = "Row", style = TextStyle(fontSize = 48.sp))
ContainerWithBackground(width = ExampleSize, color = lightGrey) {
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
PurpleSquare()
CyanSquare()
}
}
HeightSpacer(height = 24.dp)
ContainerWithBackground(width = ExampleSize, color = lightGrey) {
- Row(mainAxisAlignment = MainAxisAlignment.Center) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.Center
+ ) {
PurpleSquare()
CyanSquare()
}
}
HeightSpacer(height = 24.dp)
ContainerWithBackground(width = ExampleSize, color = lightGrey) {
- Row(mainAxisAlignment = MainAxisAlignment.End) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.End
+ ) {
PurpleSquare()
CyanSquare()
}
}
HeightSpacer(height = 24.dp)
ContainerWithBackground(width = ExampleSize, color = lightGrey) {
- Row(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
PurpleSquare()
CyanSquare()
}
}
HeightSpacer(height = 24.dp)
ContainerWithBackground(width = ExampleSize, color = lightGrey) {
- Row(crossAxisAlignment = CrossAxisAlignment.End) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.End
+ ) {
PurpleSquare()
CyanSquare()
}
}
HeightSpacer(height = 24.dp)
Text(text = "Column", style = TextStyle(fontSize = 48.sp))
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
ContainerWithBackground(height = ExampleSize, color = lightGrey) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
PurpleSquare()
CyanSquare()
}
}
WidthSpacer(width = 24.dp)
ContainerWithBackground(height = ExampleSize, color = lightGrey) {
- Column(mainAxisAlignment = MainAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.Center
+ ) {
PurpleSquare()
CyanSquare()
}
}
WidthSpacer(width = 24.dp)
ContainerWithBackground(height = ExampleSize, color = lightGrey) {
- Column(mainAxisAlignment = MainAxisAlignment.End) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.End
+ ) {
PurpleSquare()
CyanSquare()
}
}
WidthSpacer(width = 24.dp)
ContainerWithBackground(height = ExampleSize, color = lightGrey) {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
PurpleSquare()
CyanSquare()
}
}
WidthSpacer(width = 24.dp)
ContainerWithBackground(height = ExampleSize, color = lightGrey) {
- Column(crossAxisAlignment = CrossAxisAlignment.End) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.End
+ ) {
PurpleSquare()
CyanSquare()
}
diff --git a/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/IntrinsicSamples.kt b/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/IntrinsicSamples.kt
index 8059330..e5dfa8e 100644
--- a/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/IntrinsicSamples.kt
+++ b/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/IntrinsicSamples.kt
@@ -31,6 +31,7 @@
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.DpConstraints
import androidx.ui.layout.FlexRow
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MaxIntrinsicHeight
import androidx.ui.layout.MaxIntrinsicWidth
import androidx.ui.layout.MinIntrinsicHeight
@@ -51,7 +52,10 @@
fun SameWidthBoxes() {
Wrap {
MinIntrinsicWidth {
- Column(crossAxisAlignment = CrossAxisAlignment.Stretch) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Stretch
+ ) {
ConstrainedBox(DpConstraints.tightConstraints(width = 20.dp, height = 10.dp)) {
DrawShape(RectangleShape, Color.Gray)
}
@@ -111,7 +115,10 @@
fun SameWidthTextBoxes() {
Wrap {
MaxIntrinsicWidth {
- Column(crossAxisAlignment = CrossAxisAlignment.Stretch) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Stretch
+ ) {
Wrap {
DrawShape(RectangleShape, Color.Gray)
Text("Short text")
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 ea2d8c5..5e99116 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
@@ -42,6 +42,7 @@
import androidx.ui.core.OnPositioned
import androidx.ui.core.VerticalAlignmentLine
import androidx.ui.core.min
+import androidx.ui.layout.Align
import androidx.ui.layout.Center
import androidx.ui.layout.Column
import androidx.ui.layout.Container
@@ -356,7 +357,7 @@
val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
- Center {
+ Align(Alignment.CenterLeft) {
Row(crossAxisAlignment = CrossAxisAlignment.Start) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -405,7 +406,7 @@
val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
- Center {
+ Align(Alignment.CenterLeft) {
Row(crossAxisAlignment = CrossAxisAlignment.End) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -457,7 +458,7 @@
val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
- Center {
+ Align(Alignment.CenterLeft) {
Row(crossAxisAlignment = CrossAxisAlignment.Stretch) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -500,7 +501,7 @@
val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
- Center {
+ Align(Alignment.TopCenter) {
Column(crossAxisAlignment = CrossAxisAlignment.Start) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -549,7 +550,7 @@
val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
- Center {
+ Align(Alignment.TopCenter) {
Column(crossAxisAlignment = CrossAxisAlignment.End) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -602,7 +603,7 @@
val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
- Center {
+ Align(Alignment.TopCenter) {
Column(crossAxisAlignment = CrossAxisAlignment.Stretch) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1246,7 +1247,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Row(mainAxisAlignment = MainAxisAlignment.Start) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.Start
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1287,7 +1291,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Row(mainAxisAlignment = MainAxisAlignment.End) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.End
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1328,7 +1335,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Row(mainAxisAlignment = MainAxisAlignment.Center) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.Center
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1370,7 +1380,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1412,7 +1425,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Row(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceBetween
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1454,7 +1470,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Row(mainAxisAlignment = MainAxisAlignment.SpaceAround) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceAround
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1496,7 +1515,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Column(mainAxisAlignment = MainAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.Start
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1537,7 +1559,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Column(mainAxisAlignment = MainAxisAlignment.End) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.End
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1578,7 +1603,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Column(mainAxisAlignment = MainAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.Center
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1620,7 +1648,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1662,7 +1693,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Column(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceBetween
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1704,7 +1738,10 @@
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
- Column(mainAxisAlignment = MainAxisAlignment.SpaceAround) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceAround
+ ) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned( coordinates ->
@@ -1806,12 +1843,13 @@
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
}, @Composable {
- Row(mainAxisSize = LayoutSize.Wrap) {
+ Row(mainAxisSize = LayoutSize.Expand) {
AspectRatio(2f) { }
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
}, @Composable {
Row(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.Start,
crossAxisAlignment = CrossAxisAlignment.Start
) {
@@ -1820,6 +1858,7 @@
}
}, @Composable {
Row(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.Center,
crossAxisAlignment = CrossAxisAlignment.Center
) {
@@ -1828,6 +1867,7 @@
}
}, @Composable {
Row(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.End,
crossAxisAlignment = CrossAxisAlignment.End
) {
@@ -1836,6 +1876,7 @@
}
}, @Composable {
Row(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.SpaceAround,
crossAxisAlignment = CrossAxisAlignment.Stretch
) {
@@ -1843,12 +1884,18 @@
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
}, @Composable {
- Row(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceBetween
+ ) {
AspectRatio(2f) { }
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
}, @Composable {
- Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
AspectRatio(2f) { }
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
@@ -1880,12 +1927,13 @@
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
}, @Composable {
- Column(mainAxisSize = LayoutSize.Wrap) {
+ Column(mainAxisSize = LayoutSize.Expand) {
AspectRatio(2f) { }
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
}, @Composable {
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.Start,
crossAxisAlignment = CrossAxisAlignment.Start
) {
@@ -1894,6 +1942,7 @@
}
}, @Composable {
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.Center,
crossAxisAlignment = CrossAxisAlignment.Center
) {
@@ -1902,6 +1951,7 @@
}
}, @Composable {
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.End,
crossAxisAlignment = CrossAxisAlignment.End
) {
@@ -1910,6 +1960,7 @@
}
}, @Composable {
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.SpaceAround,
crossAxisAlignment = CrossAxisAlignment.Stretch
) {
@@ -1917,12 +1968,18 @@
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
}, @Composable {
- Column(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceBetween
+ ) {
AspectRatio(2f) { }
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
}, @Composable {
- Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+ ) {
AspectRatio(2f) { }
ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
}
@@ -2318,10 +2375,7 @@
val layoutLatch = CountDownLatch(4)
show {
Wrap {
- Row(
- mainAxisSize = LayoutSize.Wrap,
- crossAxisAlignment = CrossAxisAlignment.AlignmentLine(TestAlignmentLine)
- ) {
+ Row(crossAxisAlignment = CrossAxisAlignment.AlignmentLine(TestAlignmentLine)) {
SaveLayoutInfo(rowSize, Ref(), layoutLatch)
OnChildPositioned({ coordinates ->
childPosition[0].value = coordinates.localToGlobal(PxPosition(0.px, 0.px))
@@ -2360,10 +2414,7 @@
val layoutLatch = CountDownLatch(4)
show {
Wrap {
- Column(
- mainAxisSize = LayoutSize.Wrap,
- crossAxisAlignment = CrossAxisAlignment.AlignmentLine(TestAlignmentLine)
- ) {
+ Column(crossAxisAlignment = CrossAxisAlignment.AlignmentLine(TestAlignmentLine)) {
SaveLayoutInfo(columnSize, Ref(), layoutLatch)
OnChildPositioned({ coordinates ->
childPosition[0].value = coordinates.localToGlobal(PxPosition(0.px, 0.px))
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlowTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlowTest.kt
index 5368563..9ca2b06 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlowTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlowTest.kt
@@ -84,7 +84,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3),
+ PxSize(width = size * 5, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -138,7 +138,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 5, size * 3),
+ PxSize(width = size * 5, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -192,7 +192,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3),
+ PxSize(width = flowWidth, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -227,7 +227,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
- FlowRow(mainAxisAlignment = FlowMainAxisAlignment.Center) {
+ FlowRow(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.Center
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -246,7 +249,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3),
+ PxSize(width = flowWidth, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -281,7 +284,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
- FlowRow(mainAxisAlignment = FlowMainAxisAlignment.Start) {
+ FlowRow(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.Start
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -300,7 +306,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3),
+ PxSize(width = flowWidth, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -335,7 +341,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
- FlowRow(mainAxisAlignment = FlowMainAxisAlignment.End) {
+ FlowRow(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.End
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -354,7 +363,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3),
+ PxSize(width = flowWidth, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -389,7 +398,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
- FlowRow(mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly) {
+ FlowRow(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -408,7 +420,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3),
+ PxSize(width = flowWidth, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -446,7 +458,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
- FlowRow(mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween) {
+ FlowRow(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -465,7 +480,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3),
+ PxSize(width = flowWidth, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -503,7 +518,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
- FlowRow(mainAxisAlignment = FlowMainAxisAlignment.SpaceAround) {
+ FlowRow(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.SpaceAround
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -522,7 +540,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3),
+ PxSize(width = flowWidth, height = size * 3),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -581,7 +599,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 5),
+ PxSize(width = size * 3 + spacing * 2, height = size * 5),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -638,7 +656,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 6),
+ PxSize(width = size * 5, height = size * 6),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -701,7 +719,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 6),
+ PxSize(width = size * 5, height = size * 6),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -761,7 +779,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 6),
+ PxSize(width = size * 5, height = size * 6),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -823,7 +841,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(flowWidth, size * 3 + spacing * 2),
+ PxSize(width = size * 5, height = size * 3 + spacing * 2),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -877,7 +895,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, flowHeight),
+ PxSize(width = size * 3, height = size * 5),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -931,7 +949,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, size * 5),
+ PxSize(width = size * 3, height = size * 5),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -985,7 +1003,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, flowHeight),
+ PxSize(width = size * 3, height = flowHeight),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1020,7 +1038,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
- FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.Center) {
+ FlowColumn(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.Center
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -1039,7 +1060,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, flowHeight),
+ PxSize(width = size * 3, height = flowHeight),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1074,7 +1095,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
- FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.Start) {
+ FlowColumn(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.Start
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -1093,7 +1117,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, flowHeight),
+ PxSize(width = size * 3, height = flowHeight),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1128,7 +1152,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
- FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.End) {
+ FlowColumn(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.End
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -1147,7 +1174,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, flowHeight),
+ PxSize(width = size * 3, height = flowHeight),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1182,7 +1209,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
- FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly) {
+ FlowColumn(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -1201,7 +1231,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, flowHeight),
+ PxSize(width = size * 3, height = flowHeight),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1239,7 +1269,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
- FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween) {
+ FlowColumn(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -1258,7 +1291,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, flowHeight),
+ PxSize(width = size * 3, height = flowHeight),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1296,7 +1329,10 @@
positionedLatch.countDown()
}) {
ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
- FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.SpaceAround) {
+ FlowColumn(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = FlowMainAxisAlignment.SpaceAround
+ ) {
for (i in 0 until numberOfSquares) {
Container(width = sizeDp, height = sizeDp) {
SaveLayoutInfo(
@@ -1315,7 +1351,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3, flowHeight),
+ PxSize(width = size * 3, height = flowHeight),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1374,7 +1410,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 5, flowHeight),
+ PxSize(width = size * 5, height = size * 3 + spacing * 2),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1431,7 +1467,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 6, flowHeight),
+ PxSize(width = size * 6, height = size * 5),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1494,7 +1530,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 6, flowHeight),
+ PxSize(width = size * 6, height = size * 5),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1551,7 +1587,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 6, flowHeight),
+ PxSize(width = size * 6, height = size * 5),
flowSize.value
)
for (i in 0 until numberOfSquares) {
@@ -1613,7 +1649,7 @@
assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
assertEquals(
- PxSize(size * 3 + spacing * 2, flowHeight),
+ PxSize(width = size * 3 + spacing * 2, height = size * 5),
flowSize.value
)
for (i in 0 until numberOfSquares) {
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 b3ece57..6c03fba 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
@@ -92,7 +92,7 @@
* @param mainAxisSize The size of the layout in the main axis dimension.
* Default is [LayoutSize.Expand].
* @param crossAxisAlignment The alignment of the layout's children in cross axis direction.
- * Default is [CrossAxisAlignment.Center].
+ * Default is [CrossAxisAlignment.Start].
* @param crossAxisSize The size of the layout in the cross axis dimension.
* Default is [LayoutSize.Wrap].
*/
@@ -135,7 +135,7 @@
* @param mainAxisSize The size of the layout in the main axis dimension.
* Default is [LayoutSize.Expand].
* @param crossAxisAlignment The alignment of the layout's children in cross axis direction.
- * Default is [CrossAxisAlignment.Center].
+ * Default is [CrossAxisAlignment.Start].
* @param crossAxisSize The size of the layout in the cross axis dimension.
* Default is [LayoutSize.Wrap].
*/
@@ -167,16 +167,16 @@
* @param mainAxisAlignment The alignment of the layout's children in main axis direction.
* Default is [MainAxisAlignment.Start].
* @param mainAxisSize The size of the layout in the main axis dimension.
- * Default is [LayoutSize.Expand].
+ * Default is [LayoutSize.Wrap].
* @param crossAxisAlignment The alignment of the layout's children in cross axis direction.
- * Default is [CrossAxisAlignment.Center].
+ * Default is [CrossAxisAlignment.Start].
* @param crossAxisSize The size of the layout in the cross axis dimension.
* Default is [LayoutSize.Wrap].
*/
@Composable
fun Row(
mainAxisAlignment: MainAxisAlignment = MainAxisAlignment.Start,
- mainAxisSize: LayoutSize = LayoutSize.Expand,
+ mainAxisSize: LayoutSize = LayoutSize.Wrap,
crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Start,
crossAxisSize: LayoutSize = LayoutSize.Wrap,
block: @Composable() () -> Unit
@@ -203,16 +203,16 @@
* @param mainAxisAlignment The alignment of the layout's children in main axis direction.
* Default is [MainAxisAlignment.Start].
* @param mainAxisSize The size of the layout in the main axis dimension.
- * Default is [LayoutSize.Expand].
+ * Default is [LayoutSize.Wrap].
* @param crossAxisAlignment The alignment of the layout's children in cross axis direction.
- * Default is [CrossAxisAlignment.Center].
+ * Default is [CrossAxisAlignment.Start].
* @param crossAxisSize The size of the layout in the cross axis dimension.
* Default is [LayoutSize.Wrap].
*/
@Composable
fun Column(
mainAxisAlignment: MainAxisAlignment = MainAxisAlignment.Start,
- mainAxisSize: LayoutSize = LayoutSize.Expand,
+ mainAxisSize: LayoutSize = LayoutSize.Wrap,
crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Start,
crossAxisSize: LayoutSize = LayoutSize.Wrap,
block: @Composable() () -> Unit
@@ -480,10 +480,10 @@
@Composable
private fun Flex(
orientation: LayoutOrientation,
- mainAxisSize: LayoutSize = LayoutSize.Expand,
- mainAxisAlignment: MainAxisAlignment = MainAxisAlignment.Start,
- crossAxisSize: LayoutSize = LayoutSize.Wrap,
- crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Start,
+ mainAxisSize: LayoutSize,
+ mainAxisAlignment: MainAxisAlignment,
+ crossAxisSize: LayoutSize,
+ crossAxisAlignment: CrossAxisAlignment,
block: FlexChildren.() -> Unit
) {
fun Placeable.mainAxisSize() =
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Flow.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Flow.kt
index e290629..0dded70 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Flow.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Flow.kt
@@ -47,7 +47,7 @@
*/
@Composable
fun FlowRow(
- mainAxisSize: LayoutSize = LayoutSize.Expand,
+ mainAxisSize: LayoutSize = LayoutSize.Wrap,
mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
mainAxisSpacing: Dp = 0.dp,
crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
@@ -83,7 +83,7 @@
*/
@Composable
fun FlowColumn(
- mainAxisSize: LayoutSize = LayoutSize.Expand,
+ mainAxisSize: LayoutSize = LayoutSize.Wrap,
mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
mainAxisSpacing: Dp = 0.dp,
crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
@@ -127,11 +127,11 @@
@Composable
private fun Flow(
orientation: LayoutOrientation,
- mainAxisSize: LayoutSize = LayoutSize.Expand,
- mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
- mainAxisSpacing: Dp = 0.dp,
- crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
- crossAxisSpacing: Dp = 0.dp,
+ mainAxisSize: LayoutSize,
+ mainAxisAlignment: FlowMainAxisAlignment,
+ mainAxisSpacing: Dp,
+ crossAxisAlignment: FlowCrossAxisAlignment,
+ crossAxisSpacing: Dp,
children: @Composable() () -> Unit
) {
fun Placeable.mainAxisSize() =
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
index a94a1cf..950b91b 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
@@ -27,6 +27,7 @@
import androidx.ui.layout.Container
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.material.RadioGroup
import androidx.ui.material.demos.AppBarActivity.BottomAppBarOption.CenterFab
@@ -90,6 +91,7 @@
}
flexible(1f) {
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.SpaceBetween,
crossAxisAlignment = CrossAxisAlignment.Center
) {
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
index ea37d05..3831189 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
@@ -39,6 +39,7 @@
import androidx.ui.layout.Center
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.material.Button
import androidx.ui.material.ContainedButtonStyle
@@ -56,6 +57,7 @@
val onClick: () -> Unit = { Log.e("ButtonDemo", "onClick") }
Center {
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.SpaceEvenly,
crossAxisAlignment = CrossAxisAlignment.Center
) {
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
index b7e20c9..4655eb6 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
@@ -23,6 +23,7 @@
import androidx.ui.layout.Center
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.material.FloatingActionButton
@@ -34,6 +35,7 @@
Center {
val onClick: () -> Unit = { Log.e("FABDemo", "onClick") }
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.SpaceEvenly,
crossAxisAlignment = CrossAxisAlignment.Center
) {
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
index 2b6aded..30fc9c4 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
@@ -18,6 +18,7 @@
import android.os.Handler
import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment.SpaceEvenly
import androidx.ui.layout.Row
import androidx.ui.material.CircularProgressIndicator
@@ -86,12 +87,18 @@
FlexColumn {
expanded(flex = 1f) {
- Row(mainAxisAlignment = SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = SpaceEvenly
+ ) {
// Determinate indicators
LinearProgressIndicator(progress = state.progress)
CircularProgressIndicator(progress = state.progress)
}
- Row(mainAxisAlignment = SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = SpaceEvenly
+ ) {
// Fancy colours!
LinearProgressIndicator(progress = (state.progress), color = state.generateColor())
CircularProgressIndicator(
@@ -99,7 +106,10 @@
color = state.generateColor()
)
}
- Row(mainAxisAlignment = SpaceEvenly) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = SpaceEvenly
+ ) {
// Indeterminate indicators
LinearProgressIndicator()
CircularProgressIndicator()
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
index 9db2df8..cf1e081 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
@@ -28,7 +28,6 @@
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.EdgeInsets
-import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.layout.Padding
import androidx.ui.layout.Row
@@ -97,7 +96,7 @@
val textStyle = +themeTextStyle { subtitle1 }
RadioGroup {
- Row(mainAxisSize = LayoutSize.Wrap) {
+ Row {
radioOptions.forEach { text ->
val selected = text == selectedOption
RadioGroupItem(
@@ -150,10 +149,7 @@
@Composable
fun SwitchDemo() {
- Row(
- mainAxisAlignment = MainAxisAlignment.SpaceAround,
- mainAxisSize = LayoutSize.Wrap
- ) {
+ Row(mainAxisAlignment = MainAxisAlignment.SpaceAround) {
val (checked, onChecked) = +state { false }
val (checked2, onChecked2) = +state { false }
val (checked3, onChecked3) = +state { true }
@@ -167,10 +163,7 @@
@Composable
fun RadioButtonDemo() {
- Row(
- mainAxisAlignment = MainAxisAlignment.SpaceAround,
- mainAxisSize = LayoutSize.Wrap
- ) {
+ Row(mainAxisAlignment = MainAxisAlignment.SpaceAround) {
RadioButton(selected = true, >
RadioButton(selected = false, >
RadioButton(selected = true, color = customColor, >
diff --git a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyActivity.kt b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyActivity.kt
index 9d7ac85..5d4d808 100644
--- a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyActivity.kt
+++ b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyActivity.kt
@@ -26,6 +26,7 @@
import androidx.ui.core.setContent
import androidx.ui.layout.Column
import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Padding
import androidx.ui.layout.Row
import androidx.ui.material.studies.Scaffold
@@ -55,7 +56,7 @@
@Composable
fun RallyAppBar() {
// TODO: Transform to tabs
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
// Icon()
Text(text = "Overview", style = +themeTextStyle{ h4 })
// TODO: Other items
@@ -66,7 +67,7 @@
@Composable
fun RallyBody() {
Padding(padding = 16.dp) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
// TODO: scrolling container
RallyAlertCard()
HeightSpacer(height = 10.dp)
diff --git a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyCards.kt b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyCards.kt
index 9ba52df..ec5dc49 100644
--- a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyCards.kt
+++ b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyCards.kt
@@ -29,6 +29,7 @@
import androidx.ui.layout.EdgeInsets
import androidx.ui.layout.FixedSpacer
import androidx.ui.layout.FlexRow
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.layout.Padding
import androidx.ui.layout.Row
@@ -50,8 +51,11 @@
fun RallyAlertCard() {
Card(color = cardInternalColor) {
Padding(padding = 12.dp) {
- Column {
- Row(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+ Column(mainAxisSize = LayoutSize.Expand) {
+ Row(
+ mainAxisSize = LayoutSize.Expand,
+ mainAxisAlignment = MainAxisAlignment.SpaceBetween
+ ) {
Text(text = "Alerts", style = +themeTextStyle { subtitle2 })
Button(text = "See All", }, style = TextButtonStyle())
}
@@ -86,16 +90,16 @@
@Composable
fun RallyAccountsCard() {
Card(color = cardInternalColor) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Padding(padding = 12.dp) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Text(text = "Accounts", style = +themeTextStyle { body1 })
Text(text = "$12,132.49", style = +themeTextStyle { h3 })
}
}
Divider(color = rallyGreen, height = 1.dp)
Padding(padding = 12.dp) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
val colors = +ambient(Colors)
RallyAccountRow(
name = "Checking",
@@ -135,7 +139,10 @@
inflexible {
AccountIndicator(color = color)
WidthSpacer(width = 8.dp)
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
Text(text = name, style = +themeTextStyle { body1 })
Text(text = "•••••$number", style = +themeTextStyle { subtitle1 })
}
@@ -164,9 +171,9 @@
@Composable
fun RallyBillsCard() {
Card(color = cardInternalColor) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Padding(padding = 12.dp) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Text(text = "Bills", style = +themeTextStyle { subtitle2 })
Text(text = "$1,810.00", style = +themeTextStyle { h1 })
}
diff --git a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
index 9572048..b099612 100644
--- a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
+++ b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
@@ -29,6 +29,7 @@
import androidx.ui.layout.Column
import androidx.ui.layout.Container
import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Row
import androidx.ui.material.BottomDrawerLayout
import androidx.ui.material.Button
@@ -39,7 +40,7 @@
@Sampled
@Composable
fun StaticDrawerSample() {
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
StaticDrawer {
Center {
Text("Drawer Content")
@@ -81,7 +82,7 @@
@Composable
private fun YourDrawerContent(onStateChange: (DrawerState) -> Unit) {
Container(expanded = true) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Text(text = "Drawer Content")
HeightSpacer(20.dp)
Button(
@@ -94,7 +95,7 @@
@Composable
private fun YourAppContent(text: String, onDrawerStateChange: (DrawerState) -> Unit) {
Center {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
Text(text = text)
HeightSpacer(20.dp)
Button(
diff --git a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
index 3968506..ccc8c6a 100644
--- a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
+++ b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
@@ -41,6 +41,7 @@
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.EdgeInsets
import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Padding
import androidx.ui.material.Tab
import androidx.ui.material.TabRow
@@ -212,7 +213,10 @@
fun FancyTab(title: String, onClick: () -> Unit, selected: Boolean) {
MutuallyExclusiveSetItem(selected = selected, onClick() }) {
Container(height = 50.dp, padding = EdgeInsets(10.dp)) {
- Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Center
+ ) {
val color = if (selected) Color.Red else Color.Gray
ColoredRect(height = 10.dp, width = 10.dp, color = color)
Padding(5.dp) {
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
index 3bba78c..957cb5c 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
@@ -42,6 +42,7 @@
import androidx.ui.test.findAll
import androidx.ui.test.isInMutuallyExclusiveGroup
import com.google.common.truth.Truth
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -103,6 +104,7 @@
.assertHeightEqualsTo(ExpectedLargeTabHeight)
}
+ @Ignore("b/140292836")
@Test
fun tabRow_indicatorPosition() {
val indicatorHeight = 1.dp
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/AlertDialog.kt b/ui/ui-material/src/main/java/androidx/ui/material/AlertDialog.kt
index fa338c69..0e8f3a3 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/AlertDialog.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/AlertDialog.kt
@@ -81,8 +81,7 @@
MaterialTheme(colors = currentColors, typography = currentTypography) {
Surface(shape = AlertDialogShape) {
Container(width = AlertDialogWidth) {
- Column(mainAxisSize = LayoutSize.Wrap,
- crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(crossAxisAlignment = CrossAxisAlignment.Start) {
if (title != null) {
Container(
alignment = Alignment.CenterLeft,
@@ -138,10 +137,7 @@
) {
Container(padding = ButtonsPadding, alignment = Alignment.CenterRight, expanded = true) {
if (buttonLayout == AlertDialogButtonLayout.SideBySide) {
- Row(
- mainAxisAlignment = MainAxisAlignment.End,
- mainAxisSize = LayoutSize.Wrap
- ) {
+ Row(mainAxisAlignment = MainAxisAlignment.End) {
if (dismissButton != null) {
dismissButton()
WidthSpacer(ButtonsWidthSpace)
@@ -150,7 +146,7 @@
confirmButton()
}
} else {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
confirmButton()
if (dismissButton != null) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt b/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
index de75291..7b9e7f2 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
@@ -50,6 +50,7 @@
import androidx.ui.material.surface.Surface
import androidx.ui.graphics.Color
import androidx.ui.layout.EdgeInsets
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Wrap
import androidx.ui.material.BottomAppBar.FabConfiguration
import androidx.ui.material.BottomAppBar.FabPosition
@@ -672,7 +673,7 @@
it.index < actionsToDisplay
}
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
shownActions.forEach { (index, shownAction) ->
action(shownAction)
if (index != shownActions.lastIndex) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt b/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
index 9467fbd..972e635 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
@@ -144,7 +144,7 @@
}
} else {
Padding(left = ExtendedFabIconPadding, right = ExtendedFabTextPadding) {
- Row(mainAxisSize = LayoutSize.Wrap) {
+ Row {
SimpleImage(image = icon)
WidthSpacer(width = ExtendedFabIconPadding)
Text(text = text, style = textStyle)
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt b/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
index 0c6fd3c..85a74d3 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
@@ -109,7 +109,7 @@
textStyle: TextStyle? = null
) {
RadioGroup {
- Column(mainAxisSize = LayoutSize.Wrap) {
+ Column {
options.forEach { text ->
RadioGroupTextItem(
selected = (text == selectedOption),
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 518db90..bc1dac4 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
@@ -41,6 +41,7 @@
import androidx.ui.layout.Container
import androidx.ui.layout.CrossAxisAlignment
import androidx.ui.layout.FlexRow
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.MainAxisAlignment
import androidx.ui.layout.Padding
import androidx.ui.layout.Stack
@@ -363,6 +364,7 @@
Padding(top = SingleRowTextImagePadding, bottom = SingleRowTextBaselinePadding) {
TabTransition(color = tint, selected = selected) { tabTintColor ->
Column(
+ mainAxisSize = LayoutSize.Expand,
mainAxisAlignment = MainAxisAlignment.SpaceBetween,
crossAxisAlignment = CrossAxisAlignment.Center
) {
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputField.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputField.kt
index f590b27..c20c77b 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputField.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputField.kt
@@ -27,6 +27,7 @@
import androidx.ui.input.KeyboardType
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
import androidx.ui.foundation.VerticalScroller
import androidx.ui.text.TextStyle
@@ -54,7 +55,10 @@
@Composable
fun InputFieldDemo() {
VerticalScroller {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
TagLine(tag = "simple editing")
EditLine()
TagLine(tag = "simple editing2")
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldFocusTransition.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldFocusTransition.kt
index f76b700..bcbc6cb 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldFocusTransition.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldFocusTransition.kt
@@ -31,12 +31,16 @@
import androidx.ui.input.EditorStyle
import androidx.ui.input.ImeAction
import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
import androidx.ui.text.TextStyle
@Composable
fun TextFieldFocusTransition() {
VerticalScroller {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
TextFiledWithFocusId("Focus 1", "Focus 2")
TextFiledWithFocusId("Focus 2", "Focus 3")
TextFiledWithFocusId("Focus 3", "Focus 4")
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldTrickyUseCase.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldTrickyUseCase.kt
index 435813b..eb1e6ca 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldTrickyUseCase.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldTrickyUseCase.kt
@@ -24,6 +24,7 @@
import androidx.ui.core.sp
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
import androidx.ui.foundation.VerticalScroller
import androidx.ui.input.EditorModel
import androidx.ui.input.EditorStyle
@@ -33,7 +34,10 @@
@Composable
fun InputFieldTrickyUseCase() {
VerticalScroller {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
TagLine(tag = "don't set if non number is added")
RejectNonDigits()
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 1703ca8..4211daf 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
@@ -36,6 +36,7 @@
import androidx.ui.graphics.lerp
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
import androidx.ui.layout.Row
import androidx.ui.foundation.VerticalScroller
import androidx.ui.text.ParagraphStyle
@@ -61,7 +62,10 @@
@Composable
fun TextDemo() {
VerticalScroller {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
TagLine(tag = "color, fontSize, fontWeight and fontStyle")
TextDemoBasic()
TagLine(tag = "Chinese, Arabic, and Hindi")
@@ -315,7 +319,7 @@
@Composable
fun TextDemoHeight() {
// This group of text widgets show different height.
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
Text {
Span(
text = "$displayText\n$displayText ",
@@ -406,7 +410,10 @@
for (i in 1..10) {
text = "$text$displayText "
}
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
SecondTagLine(tag = "textAlign = TextAlign.Left")
Text(paragraphStyle = ParagraphStyle(textAlign = TextAlign.Left)) {
Span(text = displayText, style = TextStyle(fontSize = fontSize8))
@@ -475,7 +482,10 @@
val textStyle =
TextStyle(fontSize = fontSize8, color = Color(0xFFFF0000))
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
Text {
Span(text = text, style = textStyle)
}
@@ -610,9 +620,9 @@
SelectionContainer(
selection = selection.value,
selection.value = it }) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
for (i in 0..2) {
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
for (j in 0..2) {
Text {
Span(
@@ -655,9 +665,9 @@
selection.value = it },
mode = SelectionMode.Horizontal
) {
- Column {
+ Column(mainAxisSize = LayoutSize.Expand) {
for (i in 0..2) {
- Row {
+ Row(mainAxisSize = LayoutSize.Expand) {
for (j in 0..2) {
Text {
Span(
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeVariousInputField.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeVariousInputField.kt
index dab3dae..8ccd251 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeVariousInputField.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeVariousInputField.kt
@@ -35,6 +35,7 @@
import androidx.ui.input.KeyboardType
import androidx.ui.layout.Column
import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
import androidx.ui.foundation.VerticalScroller
import androidx.ui.text.AnnotatedString
import androidx.ui.text.Locale
@@ -176,7 +177,10 @@
@Composable
fun VariousInputFieldDemo() {
VerticalScroller {
- Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+ Column(
+ mainAxisSize = LayoutSize.Expand,
+ crossAxisAlignment = CrossAxisAlignment.Start
+ ) {
TagLine(tag = "Capitalization")
VariousEditLine(
keyboardType = KeyboardType.Ascii,
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
index ca5f7fa..1910394 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
@@ -35,6 +35,7 @@
import androidx.test.filters.LargeTest
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
import org.hamcrest.CoreMatchers.allOf
+import org.junit.Assert.assertFalse
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.TimeUnit.SECONDS
@@ -81,25 +82,24 @@
test_notCurrentPage_requestFocus(true)
}
- /** Verifies we navigate to another page if focus is requested on its element. */
+ /** Verifies we don't navigate to another page if focus is requested on its element. */
private fun test_notCurrentPage_requestFocus(wrapEditTextInViewGroup: Boolean) {
val pageCount = 3
setUpTest(pageCount, wrapEditTextInViewGroup).apply {
- repeat(pageCount) {
+ (0 until pageCount).forEach { targetPage ->
// given
- val currentItem = viewPager.currentItem
- assertBasicState(currentItem, null)
- val targetPage = (currentItem + 1) % pageCount
- val editText = editTextForPage(
- viewPager.linearLayoutManager.findViewByPosition
- (targetPage)!!
- )
+ viewPager.setCurrentItemSync(targetPage, false, 2, SECONDS)
+ assertBasicState(targetPage, null)
+
+ val otherPage = (targetPage + 1) % pageCount
+ val editText =
+ editTextForPage(viewPager.linearLayoutManager.findViewByPosition(otherPage)!!)
// when
closeSoftKeyboard() // setCurrentItem ignored otherwise; TODO: check if on purpose
- val latch = viewPager.addWaitForScrolledLatch(targetPage)
+ val latch = viewPager.addWaitForFirstScrollEventLatch()
runOnUiThreadSync { editText.requestFocus() }
- latch.await(2, SECONDS)
+ assertFalse(latch.await(1, SECONDS)) // TODO: replace with Robolectric
// then
assertBasicState(targetPage, null)
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
index 772699c..ed39c81 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
@@ -1013,14 +1013,7 @@
public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
@NonNull View child, @NonNull Rect rect, boolean immediate,
boolean focusedChildVisible) {
- int position = getPosition(child);
- if (getCurrentItem() == position) {
- return false;
- }
-
- boolean smoothScroll = !immediate;
- setCurrentItem(position, smoothScroll);
- return smoothScroll;
+ return false; // users should use setCurrentItem instead
}
}
diff --git a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
index c827c61..abb80ac 100644
--- a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -20,6 +20,7 @@
tools:ignore="GoogleAppIndexingWarning">
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="false"
@@ -92,5 +93,8 @@
<activity
android:name=".MultiProcessEnabledActivity"
android:exported="true" />
+ <activity
+ android:name=".TracingControllerActivity"
+ android:exported="true" />
</application>
</manifest>
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
index e0dd98f..ed37250 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
@@ -57,6 +57,9 @@
new MenuListView.MenuItem(
getResources().getString(R.string.multi_process_enabled_activity_title),
new Intent(activityContext, MultiProcessEnabledActivity.class)),
+ new MenuListView.MenuItem(
+ getResources().getString(R.string.tracing_controller_activity_title),
+ new Intent(activityContext, TracingControllerActivity.class)),
};
listView.setItems(menuItems);
}
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/TracingControllerActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/TracingControllerActivity.java
new file mode 100644
index 0000000..209b269
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/TracingControllerActivity.java
@@ -0,0 +1,146 @@
+/*
+ * 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 android.app.Activity;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.webkit.TracingConfig;
+import androidx.webkit.TracingController;
+import androidx.webkit.WebViewFeature;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * An {@link Activity} to exercise Tracing Controller functionality.
+ */
+public class TracingControllerActivity extends AppCompatActivity {
+ TracingController mTracingController;
+ private WebView mWebView;
+ private TextView mInfo;
+ private EditText mNavigationBar;
+ private Button mButton;
+ private String mLogPath;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_tracing_controller);
+ setTitle(R.string.tracing_controller_activity_title);
+
+ mNavigationBar = findViewById(R.id.tracing_controller_edittext);
+ mNavigationBar.setOnEditorActionListener((TextView v, int actionId, KeyEvent event) -> {
+ if (actionId == EditorInfo.IME_ACTION_NEXT) {
+ String url = mNavigationBar.getText().toString();
+ if (!url.isEmpty()) {
+ if (!url.startsWith("http")) url = "http://" + url;
+ mWebView.loadUrl(url);
+ mNavigationBar.setText("");
+ }
+ return true;
+ }
+ return false;
+ });
+
+ mInfo = findViewById(R.id.tracing_controller_textview);
+ mInfo.setVisibility(View.GONE);
+
+ mButton = findViewById(R.id.tracing_controller_button);
+ mButton.setOnClickListener(v -> {
+ if (mTracingController.isTracing()) {
+ try {
+ mButton.setEnabled(false);
+ mLogPath = getExternalFilesDir(null) + File.separator + "tc.json";
+ FileOutputStream os = new FileOutputStream(new File(mLogPath)) {
+ @Override
+ public void close() throws IOException {
+ super.close();
+ runOnUiThread(() -> {
+ mInfo.setVisibility(View.VISIBLE);
+ mButton.setVisibility(View.GONE);
+ mInfo.setText(
+ getString(R.string.tracing_controller_log_path, mLogPath));
+ try {
+ verifyJSON();
+ } catch (IOException | JSONException e) {
+ mInfo.setText(R.string.tracing_controller_invalid_log);
+ }
+ });
+ }
+ };
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ mTracingController.stop(os, executor);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ } else {
+ TracingConfig config = new TracingConfig.Builder()
+ .addCategories(TracingConfig.CATEGORIES_ANDROID_WEBVIEW)
+ .build();
+ mTracingController.start(config);
+ mButton.setText(R.string.tracing_controller_stop_tracing);
+ }
+ });
+
+ mWebView = findViewById(R.id.tracing_controller_webview);
+ mWebView.setWebViewClient(new WebViewClient());
+
+ WebkitHelpers.appendWebViewVersionToTitle(this);
+ if (!WebViewFeature.isFeatureSupported(WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE)) {
+ mNavigationBar.setVisibility(View.GONE);
+ mWebView.setVisibility(View.GONE);
+ mButton.setVisibility(View.GONE);
+ mInfo.setVisibility(View.GONE);
+ WebkitHelpers.showMessageInActivity(this, R.string.webkit_api_not_available);
+ return;
+ }
+
+ mTracingController = TracingController.getInstance();
+ }
+
+ private void verifyJSON() throws IOException, JSONException {
+ StringBuilder builder = new StringBuilder();
+ BufferedReader br = new BufferedReader(new FileReader(mLogPath));
+ String sCurrentLine;
+ while ((sCurrentLine = br.readLine()) != null) {
+ builder.append(sCurrentLine).append("\n");
+ }
+
+ // Throw exception if JSON is incorrect
+ new JSONObject(builder.toString());
+ }
+}
diff --git a/webkit/integration-tests/testapp/src/main/res/layout/activity_tracing_controller.xml b/webkit/integration-tests/testapp/src/main/res/layout/activity_tracing_controller.xml
new file mode 100644
index 0000000..fcffdd6
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/res/layout/activity_tracing_controller.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<RelativeLayout
+ 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"
+ tools:context=".TracingControllerActivity">
+
+ <TextView
+ android:id="@+id/tracing_controller_textview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="22sp"
+ android:layout_marginTop="12dp"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"/>
+
+ <EditText
+ android:id="@+id/tracing_controller_edittext"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dp"
+ android:layout_marginRight="6dp"
+ android:maxLines="1"
+ android:inputType="textUri"
+ android:hint="Type an URL here"
+ android:layout_below="@id/tracing_controller_textview"/>
+
+ <Button
+ android:id="@+id/tracing_controller_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/tracing_controller_start_tracing"
+ android:layout_below="@id/tracing_controller_edittext"/>
+
+ <WebView
+ android:id="@+id/tracing_controller_webview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/tracing_controller_button" />
+
+</RelativeLayout>
diff --git a/webkit/integration-tests/testapp/src/main/res/values/strings.xml b/webkit/integration-tests/testapp/src/main/res/values/strings.xml
index 13c5706..8d04851 100644
--- a/webkit/integration-tests/testapp/src/main/res/values/strings.xml
+++ b/webkit/integration-tests/testapp/src/main/res/values/strings.xml
@@ -56,4 +56,10 @@
<string name="multi_process_enabled">Multi Process is enabled</string>
<string name="multi_process_disabled">Multi Process is disabled</string>
<string name="multi_process_query_unavailable">Multi Process query not available</string>
+ <string name="tracing_controller_activity_title">Tracing Controller Demo</string>
+ <string name="tracing_controller_start_tracing">Start tracing</string>
+ <string name="tracing_controller_stop_tracing">Stop tracing</string>
+ <string name="tracing_controller_invalid_log">Log is invalid</string>
+ <string name="tracing_controller_log_path">Wrote log to %1$s. This file can be loaded in
+ chrome://tracing for further analysis.</string>>
</resources>
diff --git a/work/workmanager/api/api_lint.ignore b/work/workmanager/api/api_lint.ignore
index 8bca693..1381c66 100644
--- a/work/workmanager/api/api_lint.ignore
+++ b/work/workmanager/api/api_lint.ignore
@@ -23,3 +23,7 @@
Missing nullability on parameter `o` in method `equals`
MissingNullability: androidx.work.WorkInfo#toString():
Missing nullability on method `toString` return
+
+
+SetterReturnsThis: androidx.work.WorkRequest.Builder:
+ Methods must return the builder object (return type Builder instead of B): method androidx.work.WorkRequest.Builder.setBackoffCriteria(androidx.work.BackoffPolicy,long,java.util.concurrent.TimeUnit)