Merge "Fix crash in cubic easing when 1ulp away from 1.0f" into androidx-main
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityResultTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityResultTest.kt
index 7678999..322f5ec 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityResultTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityResultTest.kt
@@ -77,8 +77,8 @@
scenario.withActivity { }
scenario.withActivity {
- assertThat(firstLaunchCount).isEqualTo(0)
- assertThat(secondLaunchCount).isEqualTo(1)
+ assertThat(launchCountDownLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+ assertThat(launchedList).containsExactly("second")
}
}
}
@@ -179,19 +179,21 @@
class RegisterBeforeOnCreateActivity : ComponentActivity() {
lateinit var launcher: ActivityResultLauncher<Intent>
- var firstLaunchCount = 0
- var secondLaunchCount = 0
+ var launchCountDownLatch = CountDownLatch(1)
+ val launchedList = mutableListOf<String>()
var recreated = false
init {
addOnContextAvailableListener {
launcher = if (!recreated) {
registerForActivityResult(StartActivityForResult()) {
- firstLaunchCount++
+ launchedList.add("first")
+ launchCountDownLatch.countDown()
}
} else {
registerForActivityResult(StartActivityForResult()) {
- secondLaunchCount++
+ launchedList.add("second")
+ launchCountDownLatch.countDown()
}
}
}
diff --git a/activity/activity/src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt b/activity/activity/src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt
index bcfcf26..a391c75 100644
--- a/activity/activity/src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt
+++ b/activity/activity/src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt
@@ -33,6 +33,7 @@
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts.GetMultipleContents.Companion.getClipDataUris
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.ACTION_SYSTEM_FALLBACK_PICK_IMAGES
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.GMS_ACTION_PICK_IMAGES
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.GMS_EXTRA_PICK_IMAGES_MAX
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.getGmsPicker
@@ -899,7 +900,7 @@
Intent(ACTION_SYSTEM_FALLBACK_PICK_IMAGES).apply {
setClassName(fallbackPicker.applicationInfo.packageName, fallbackPicker.name)
type = PickVisualMedia.getVisualMimeType(input.mediaType)
- putExtra(GMS_EXTRA_PICK_IMAGES_MAX, maxItems)
+ putExtra(EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX, maxItems)
}
} else if (PickVisualMedia.isGmsPickerAvailable(context)) {
val gmsPicker = checkNotNull(getGmsPicker(context)).activityInfo
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index d20b0f7..3833958 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -114,7 +114,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -501,7 +501,7 @@
mAppSearchImpl.close();
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()),
+ new LocalStorageIcingOptionsConfig()),
initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
// Check recovery state
@@ -733,7 +733,7 @@
tempFolder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -909,7 +909,7 @@
tempFolder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -2867,7 +2867,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -2939,7 +2939,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -3018,7 +3018,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -3145,7 +3145,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3224,7 +3224,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3281,7 +3281,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3318,7 +3318,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3431,7 +3431,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3527,7 +3527,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3584,7 +3584,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3737,7 +3737,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3820,7 +3820,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3877,7 +3877,7 @@
public int getMaxSuggestionCount() {
return Integer.MAX_VALUE;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3923,7 +3923,7 @@
public int getMaxSuggestionCount() {
return 2;
}
- }, new DefaultIcingOptionsConfig()),
+ }, new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -4026,7 +4026,7 @@
tempFolder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -4079,7 +4079,7 @@
tempFolder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -4130,7 +4130,7 @@
tempFolder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -4183,7 +4183,7 @@
tempFolder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -4522,7 +4522,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -4563,7 +4563,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -4596,7 +4596,7 @@
tempFolder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -4694,7 +4694,7 @@
tempFolder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -4783,7 +4783,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
@@ -4887,7 +4887,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
@@ -4949,7 +4949,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
@@ -5277,7 +5277,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
@@ -5435,7 +5435,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
@@ -5524,7 +5524,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
@@ -5617,7 +5617,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
index 22afe67..e9b8e47 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
@@ -77,7 +77,7 @@
mTemporaryFolder.newFolder(),
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -370,7 +370,7 @@
mTemporaryFolder.newFolder(),
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
initStatsBuilder,
ALWAYS_OPTIMIZE,
@@ -403,7 +403,7 @@
folder,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -442,7 +442,7 @@
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
appSearchImpl = AppSearchImpl.create(
folder, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()),
+ new LocalStorageIcingOptionsConfig()),
initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
InitializeStats iStats = initStatsBuilder.build();
@@ -470,7 +470,7 @@
AppSearchImpl appSearchImpl = AppSearchImpl.create(
folder, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()),
+ new LocalStorageIcingOptionsConfig()),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
List<AppSearchSchema> schemas = ImmutableList.of(
@@ -510,7 +510,7 @@
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
appSearchImpl = AppSearchImpl.create(
folder, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()),
+ new LocalStorageIcingOptionsConfig()),
initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
InitializeStats iStats = initStatsBuilder.build();
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
index dfbc317..0690cd5 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
@@ -53,7 +53,7 @@
mTemporaryFolder.newFolder(),
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
index 59b2e66..a8bb683 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -20,7 +20,7 @@
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.localstorage.AppSearchConfigImpl;
-import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
+import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import com.google.android.icing.proto.DocumentProto;
@@ -119,7 +119,7 @@
GenericDocument convertedGenericDocument =
GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX,
SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()));
+ new LocalStorageIcingOptionsConfig()));
DocumentProto convertedDocumentProto =
GenericDocumentToProtoConverter.toDocumentProto(document);
@@ -217,7 +217,7 @@
GenericDocument convertedGenericDocument =
GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX,
schemaMap, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()));
+ new LocalStorageIcingOptionsConfig()));
DocumentProto convertedDocumentProto =
GenericDocumentToProtoConverter.toDocumentProto(document);
assertThat(convertedDocumentProto).isEqualTo(documentProto);
@@ -346,7 +346,7 @@
GenericDocument convertedGenericDocument =
GenericDocumentToProtoConverter.toGenericDocument(outerDocumentProto, PREFIX,
schemaMap, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()));
+ new LocalStorageIcingOptionsConfig()));
DocumentProto convertedDocumentProto =
GenericDocumentToProtoConverter.toDocumentProto(outerDocument);
assertThat(convertedDocumentProto).isEqualTo(outerDocumentProto);
@@ -409,13 +409,13 @@
GenericDocument actualDocWithParentAsMetaField =
GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX,
schemaMap, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig(),
+ new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ false,
/* shouldRetrieveParentInfo= */ true));
GenericDocument actualDocWithParentAsSyntheticProperty =
GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX,
schemaMap, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig(),
+ new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ true,
/* shouldRetrieveParentInfo= */ true));
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
index 11e0e8e..ccf3b51 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
@@ -26,7 +26,7 @@
import androidx.appsearch.app.SearchResultPage;
import androidx.appsearch.exceptions.AppSearchException;
import androidx.appsearch.localstorage.AppSearchConfigImpl;
-import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
+import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -49,7 +49,7 @@
final String namespace = prefix + "namespace";
final String schemaType = prefix + "schema";
final AppSearchConfigImpl config = new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig());
+ new LocalStorageIcingOptionsConfig());
// Building the SearchResult received from query.
DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder()
@@ -150,7 +150,7 @@
Exception e = assertThrows(AppSearchException.class,
() -> SearchResultToProtoConverter.toSearchResultPage(searchResultProto, schemaMap,
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig())));
+ new LocalStorageIcingOptionsConfig())));
assertThat(e.getMessage())
.isEqualTo("Nesting joined results within joined results not allowed.");
}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
index 2f3b067..92a86eb 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
@@ -27,8 +27,8 @@
import androidx.appsearch.app.SearchSpec;
import androidx.appsearch.localstorage.AppSearchConfigImpl;
import androidx.appsearch.localstorage.AppSearchImpl;
-import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
import androidx.appsearch.localstorage.IcingOptionsConfig;
+import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
import androidx.appsearch.localstorage.OptimizeStrategy;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -64,7 +64,8 @@
@Rule
public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- private final IcingOptionsConfig mDefaultIcingOptionsConfig = new DefaultIcingOptionsConfig();
+ private final IcingOptionsConfig mLocalStorageIcingOptionsConfig =
+ new LocalStorageIcingOptionsConfig();
private AppSearchImpl mAppSearchImpl;
@@ -74,7 +75,7 @@
mTemporaryFolder.newFolder(),
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- mDefaultIcingOptionsConfig
+ mLocalStorageIcingOptionsConfig
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
@@ -111,7 +112,7 @@
prefix2, ImmutableMap.of(
prefix2 + "typeA", configProto,
prefix2 + "typeB", configProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
// Convert SearchSpec to proto.
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -161,7 +162,7 @@
prefix2, ImmutableMap.of(
prefix2 + "typeA", configProto,
prefix2 + "typeB", configProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
// Convert SearchSpec to proto.
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -235,7 +236,7 @@
ImmutableMap.of(
prefix2 + "typeA", configProto,
prefix2 + "typeB", configProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
VisibilityStore visibilityStore = new VisibilityStore(mAppSearchImpl);
converter.removeInaccessibleSchemaFilter(
@@ -288,7 +289,7 @@
/*namespaceMap=*/ImmutableMap.of(prefix, ImmutableSet.of(prefix + namespace)),
/*schemaMap=*/ImmutableMap.of(prefix, ImmutableMap.of(prefix + schemaType,
SchemaTypeConfigProto.getDefaultInstance())),
- mDefaultIcingOptionsConfig).toScoringSpecProto();
+ mLocalStorageIcingOptionsConfig).toScoringSpecProto();
TypePropertyWeights typePropertyWeights = TypePropertyWeights.newBuilder()
.setSchemaType(prefix + schemaType)
.addPropertyWeights(PropertyWeight.newBuilder()
@@ -317,7 +318,7 @@
/*prefixes=*/ImmutableSet.of(),
/*namespaceMap=*/ImmutableMap.of(),
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig).toScoringSpecProto();
+ mLocalStorageIcingOptionsConfig).toScoringSpecProto();
assertThat(scoringSpecProto.getOrderBy().getNumber())
.isEqualTo(ScoringSpecProto.Order.Code.ASC_VALUE);
@@ -342,7 +343,7 @@
/*prefixes=*/ImmutableSet.of(),
/*namespaceMap=*/ImmutableMap.of(),
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = convert.toResultSpecProto(
/*namespaceMap=*/ImmutableMap.of(),
/*schemaMap=*/ImmutableMap.of());
@@ -380,7 +381,7 @@
/*prefixes=*/ImmutableSet.of(),
/*namespaceMap=*/ImmutableMap.of(),
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
/*namespaceMap=*/ImmutableMap.of(),
@@ -428,7 +429,7 @@
/*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
namespaceMap,
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
namespaceMap,
@@ -479,7 +480,7 @@
/*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
namespaceMap,
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
namespaceMap,
@@ -535,7 +536,7 @@
/*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
namespaceMap,
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -589,7 +590,7 @@
/*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
namespaceMap,
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ScoringSpecProto scoringSpecProto = converter.toScoringSpecProto();
@@ -627,7 +628,7 @@
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
/*namespaceMap=*/ImmutableMap.of(),
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
/*namespaceMap=*/ImmutableMap.of(
prefix1, ImmutableSet.of(
@@ -680,7 +681,7 @@
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
namespaceMap,
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
namespaceMap,
/*schemaMap=*/ImmutableMap.of());
@@ -729,7 +730,7 @@
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
/*namespaceMap=*/ImmutableMap.of(),
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
/*namespaceMap=*/ImmutableMap.of(),
schemaMap);
@@ -775,7 +776,7 @@
searchSpec,
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
namespaceMap, /*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
namespaceMap,
/*schemaMap=*/ImmutableMap.of());
@@ -814,7 +815,7 @@
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
/*namespaceMap=*/ImmutableMap.of(),
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
/*namespaceMap=*/ImmutableMap.of(),
schemaMap);
@@ -858,7 +859,7 @@
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
namespaceMap,
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap, schemaMap);
assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(4);
@@ -950,7 +951,7 @@
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
namespaceMap,
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap, schemaMap);
assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(8);
@@ -1045,7 +1046,7 @@
searchSpec,
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
namespaceMap, /*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -1071,7 +1072,7 @@
prefix2, ImmutableSet.of("package$database2/namespace3",
"package$database2/namespace4")),
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// Only search prefix1 will return namespace 1 and 2.
@@ -1094,7 +1095,7 @@
prefix1, ImmutableSet.of("package$database1/namespace1",
"package$database1/namespace2")),
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// If the searching namespace filter is not empty, the target namespace filter will be the
// intersection of the searching namespace filters that users want to search over and
@@ -1118,7 +1119,7 @@
prefix1, ImmutableSet.of("package$database1/namespace1",
"package$database1/namespace2")),
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// If the searching namespace filter is not empty, the target namespace filter will be the
// intersection of the searching namespace filters that users want to search over and
@@ -1146,7 +1147,7 @@
prefix2, ImmutableMap.of(
"package$database2/typeC", schemaTypeConfigProto,
"package$database2/typeD", schemaTypeConfigProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// Empty searching filter will get all types for target filter
assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
@@ -1175,7 +1176,7 @@
prefix2, ImmutableMap.of(
"package$database2/typeC", schemaTypeConfigProto,
"package$database2/typeD", schemaTypeConfigProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// Only search prefix1 will return typeA and B.
assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
@@ -1200,7 +1201,7 @@
prefix1, ImmutableMap.of(
"package$database1/typeA", schemaTypeConfigProto,
"package$database1/typeB", schemaTypeConfigProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// If the searching schema filter is not empty, the target schema filter will be the
// intersection of the schema filters that users want to search over and those candidates
@@ -1227,7 +1228,7 @@
prefix1, ImmutableMap.of(
"package$database1/typeA", schemaTypeConfigProto,
"package$database1/typeB", schemaTypeConfigProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// If there is no intersection of the schema filters that user want to search over and
// those filters which are stored in AppSearch, return empty.
@@ -1257,7 +1258,7 @@
"package$database/schema1", schemaTypeConfigProto,
"package$database/schema2", schemaTypeConfigProto,
"package$database/schema3", schemaTypeConfigProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
converter.removeInaccessibleSchemaFilter(
new CallerAccess(/*callingPackageName=*/"otherPackageName"),
@@ -1304,7 +1305,7 @@
searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
/*namespaceMap=*/namespaceMap,
/*schemaMap=*/ImmutableMap.of(),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
assertThat(emptySchemaConverter.hasNothingToSearch()).isTrue();
SearchSpecToProtoConverter emptyNamespaceConverter =
@@ -1313,7 +1314,7 @@
searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
/*namespaceMap=*/ImmutableMap.of(),
schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
assertThat(emptyNamespaceConverter.hasNothingToSearch()).isTrue();
SearchSpecToProtoConverter nonEmptyConverter =
@@ -1321,7 +1322,7 @@
/*queryExpression=*/"",
searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
namespaceMap, schemaMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
assertThat(nonEmptyConverter.hasNothingToSearch()).isFalse();
// remove all target schema filter, and the query becomes nothing to search.
@@ -1359,7 +1360,7 @@
"package$database/schema1", schemaTypeConfigProto,
"package$database/schema2", schemaTypeConfigProto,
"package$database/schema3", schemaTypeConfigProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
converter.removeInaccessibleSchemaFilter(
new CallerAccess(/*callingPackageName=*/"otherPackageName"),
@@ -1407,7 +1408,7 @@
searchSpec, /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
namespaceMap,
schemaTypeMap,
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
TypePropertyWeights expectedTypePropertyWeight1 =
TypePropertyWeights.newBuilder().setSchemaType(prefix1 + schemaTypeA)
@@ -1458,7 +1459,7 @@
/*schemaMap=*/ImmutableMap.of(
prefix1,
ImmutableMap.of(prefix1 + "typeA", schemaTypeConfigProto)),
- mDefaultIcingOptionsConfig);
+ mLocalStorageIcingOptionsConfig);
ScoringSpecProto convertedScoringSpecProto = converter.toScoringSpecProto();
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SnippetTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SnippetTest.java
index 026424f..fbc9a93 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SnippetTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SnippetTest.java
@@ -22,7 +22,7 @@
import androidx.appsearch.app.SearchResult;
import androidx.appsearch.app.SearchResultPage;
import androidx.appsearch.localstorage.AppSearchConfigImpl;
-import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
+import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -96,7 +96,7 @@
SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
searchResultProto,
SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()));
+ new LocalStorageIcingOptionsConfig()));
assertThat(searchResultPage.getResults()).hasSize(1);
SearchResult.MatchInfo match = searchResultPage.getResults().get(0).getMatchInfos().get(0);
assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
@@ -137,7 +137,7 @@
SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
searchResultProto,
SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()));
+ new LocalStorageIcingOptionsConfig()));
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getMatchInfos()).isEmpty();
}
@@ -194,7 +194,7 @@
SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
searchResultProto,
SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()));
+ new LocalStorageIcingOptionsConfig()));
assertThat(searchResultPage.getResults()).hasSize(1);
SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatchInfos().get(0);
assertThat(match1.getPropertyPath()).isEqualTo("senderName");
@@ -281,7 +281,7 @@
SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
searchResultProto,
SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()));
+ new LocalStorageIcingOptionsConfig()));
assertThat(searchResultPage.getResults()).hasSize(1);
SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatchInfos().get(0);
assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
index 77772c4..7b03a10 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
@@ -33,7 +33,7 @@
import androidx.appsearch.app.VisibilityDocument;
import androidx.appsearch.localstorage.AppSearchConfigImpl;
import androidx.appsearch.localstorage.AppSearchImpl;
-import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
+import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
import androidx.appsearch.localstorage.OptimizeStrategy;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -130,7 +130,7 @@
appSearchImplInV0.close();
AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile,
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()), /*initStatsBuilder=*/ null,
+ new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -196,7 +196,7 @@
// Set deprecated visibility schema version 0 into AppSearchImpl.
AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile,
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()), /*initStatsBuilder=*/ null,
+ new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
InternalSetSchemaResponse internalSetSchemaResponse = appSearchImpl.setSchema(
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
index c5bd8ac..a70da64 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
@@ -28,7 +28,7 @@
import androidx.appsearch.app.VisibilityDocument;
import androidx.appsearch.localstorage.AppSearchConfigImpl;
import androidx.appsearch.localstorage.AppSearchImpl;
-import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
+import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
import androidx.appsearch.localstorage.OptimizeStrategy;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -74,7 +74,7 @@
// Create AppSearchImpl with visibility document version 1;
AppSearchImpl appSearchImplInV1 = AppSearchImpl.create(mFile,
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()), /*initStatsBuilder=*/ null,
+ new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
InternalSetSchemaResponse internalSetSchemaResponse = appSearchImplInV1.setSchema(
@@ -124,7 +124,7 @@
appSearchImplInV1.close();
AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile,
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()), /*initStatsBuilder=*/ null,
+ new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
index 17e8a15..056acea 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
@@ -27,7 +27,7 @@
import androidx.appsearch.exceptions.AppSearchException;
import androidx.appsearch.localstorage.AppSearchConfigImpl;
import androidx.appsearch.localstorage.AppSearchImpl;
-import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
+import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
import androidx.appsearch.localstorage.OptimizeStrategy;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -63,7 +63,7 @@
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig()
+ new LocalStorageIcingOptionsConfig()
),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
index 628af6a..db55481 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
@@ -341,7 +341,7 @@
icingDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
- new DefaultIcingOptionsConfig(),
+ new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ false,
/* shouldRetrieveParentInfo= */ true
),
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/DefaultIcingOptionsConfig.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorageIcingOptionsConfig.java
similarity index 94%
rename from appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/DefaultIcingOptionsConfig.java
rename to appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorageIcingOptionsConfig.java
index 11a0c04..a328058 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/DefaultIcingOptionsConfig.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorageIcingOptionsConfig.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-// @exportToFramework:copyToPath(testing/testutils/src/android/app/appsearch/testutil/external/DefaultIcingOptionsConfig.java)
+// @exportToFramework:copyToPath(testing/testutils/src/android/app/appsearch/testutil/external/LocalStorageIcingOptionsConfig.java)
package androidx.appsearch.localstorage;
import androidx.annotation.RestrictTo;
@@ -23,7 +23,7 @@
* set in {@link com.google.android.icing.proto.IcingSearchEngineOptions} proto.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class DefaultIcingOptionsConfig implements IcingOptionsConfig {
+public class LocalStorageIcingOptionsConfig implements IcingOptionsConfig {
@Override
public int getMaxTokenLength() {
return DEFAULT_MAX_TOKEN_LENGTH;
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/SliceTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/SliceTest.kt
new file mode 100644
index 0000000..f0f754b
--- /dev/null
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/SliceTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 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.benchmark.perfetto
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import kotlin.test.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SliceTest {
+ @Test
+ fun frameId() {
+ assertEquals(
+ 1234,
+ Slice("Choreographer#doFrame 1234", 1, 2).frameId
+ )
+ }
+
+ @Test
+ fun frameId_extended() {
+ // some OEMs have added additional metadata to standard tracepoints
+ // we'll fix these best effort as they are reported
+ assertEquals(
+ 123,
+ Slice("Choreographer#doFrame 123 234 extra=91929", 1, 2).frameId
+ )
+ }
+}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
index 5064d8f..7cec6f9 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
@@ -159,6 +159,7 @@
val groupedData = slices
.filter { it.dur > 0 } // drop non-terminated slices
+ .filter { !it.name.contains("resynced") } // drop "#doFrame - resynced to" slices
.groupBy {
when {
// note: we use "startsWith" as starting in S, all of these will end
@@ -180,6 +181,11 @@
return emptyList()
}
+ check(rtSlices.isNotEmpty()) {
+ "Observed no renderthread slices in trace - verify that your benchmark is redrawing" +
+ " and is hardware accelerated (which is the default)."
+ }
+
return if (captureApiLevel >= 31) {
check(actualSlices.isNotEmpty() && expectedSlices.isNotEmpty()) {
"Observed no expect/actual slices in trace," +
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
index c5bc405..63afa7d 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
@@ -18,6 +18,9 @@
import androidx.annotation.RestrictTo
import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+import androidx.benchmark.InstrumentationResults
+import androidx.benchmark.Outputs
+import androidx.benchmark.Profiler
import androidx.benchmark.inMemoryTrace
import androidx.benchmark.macro.perfetto.server.PerfettoHttpServer
import java.io.File
@@ -129,7 +132,29 @@
): T {
loadTraceImpl(trace.path)
// TODO: unload trace after block
- return block.invoke(Session(this))
+ try {
+ return block.invoke(Session(this))
+ } catch (t: Throwable) {
+ // TODO: move this behavior to an extension function in benchmark when
+ // this class moves out of benchmark group
+ // TODO: consider a label argument to control logging like this in the success case as
+ // well, which lets us get rid of FileLinkingRule (which doesn't work well anyway)
+ if (trace.path.startsWith(Outputs.outputDirectory.absolutePath)) {
+ // only link trace with failure to Studio if it's an output file
+ InstrumentationResults.instrumentationReport {
+ val label = "Trace with processing error: ${t.message?.take(50)?.trim()}..."
+ reportSummaryToIde(
+ profilerResults = listOf(
+ Profiler.ResultFile(
+ label = label,
+ absolutePath = trace.path
+ )
+ )
+ )
+ }
+ }
+ throw t
+ }
}
/**
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/Slice.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/Slice.kt
index fd823e9..7614683 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/Slice.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/Slice.kt
@@ -25,7 +25,20 @@
val dur: Long
) {
val endTs: Long = ts + dur
- val frameId = name.substringAfterLast(" ").toIntOrNull()
+
+ val frameId: Int?
+
+ init {
+ val firstSpaceIndex = name.indexOf(" ")
+ frameId = if (firstSpaceIndex >= 0) {
+ // if see a space, check for id from end of first space to next space (or end of String)
+ val secondSpaceIndex = name.indexOf(" ", firstSpaceIndex + 1)
+ val endFrameIdIndex = if (secondSpaceIndex < 0) name.length else secondSpaceIndex
+ name.substring(firstSpaceIndex + 1, endFrameIdIndex).toIntOrNull()
+ } else {
+ null
+ }
+ }
fun contains(targetTs: Long): Boolean {
return targetTs >= ts && targetTs <= (ts + dur)
diff --git a/buildSrc/jetpad-integration/build.gradle b/buildSrc/jetpad-integration/build.gradle
index 6941452..e1c0559 100644
--- a/buildSrc/jetpad-integration/build.gradle
+++ b/buildSrc/jetpad-integration/build.gradle
@@ -4,6 +4,10 @@
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
+project.tasks.withType(Jar).configureEach { task ->
+ task.reproducibleFileOrder = true
+ task.preserveFileTimestamps = false
+}
apply from: "../out.gradle"
init.chooseBuildSrcBuildDir()
diff --git a/buildSrc/shared.gradle b/buildSrc/shared.gradle
index 0c85b7a..b135cf6 100644
--- a/buildSrc/shared.gradle
+++ b/buildSrc/shared.gradle
@@ -26,7 +26,7 @@
targetCompatibility = JavaVersion.VERSION_17
}
-project.tasks.withType(Jar) { task ->
+project.tasks.withType(Jar).configureEach { task ->
task.reproducibleFileOrder = true
task.preserveFileTimestamps = false
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
index 05fed81..7b21869 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
@@ -40,6 +40,7 @@
import androidx.camera.camera2.pipe.integration.impl.CameraProperties
import androidx.camera.camera2.pipe.integration.impl.DeviceInfoLogger
import androidx.camera.camera2.pipe.integration.impl.FocusMeteringControl
+import androidx.camera.camera2.pipe.integration.internal.CameraFovInfo
import androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
import androidx.camera.core.CameraInfo
@@ -83,6 +84,7 @@
private val cameraQuirks: CameraQuirks,
private val encoderProfilesProviderAdapter: EncoderProfilesProviderAdapter,
private val streamConfigurationMapCompat: StreamConfigurationMapCompat,
+ private val cameraFovInfo: CameraFovInfo,
) : CameraInfoInternal {
init { DeviceInfoLogger.logDeviceInfo(cameraProperties) }
@@ -228,6 +230,19 @@
CONTROL_VIDEO_STABILIZATION_MODE_ON)
}
+ override fun getIntrinsicZoomRatio(): Float {
+ var intrinsicZoomRatio = CameraInfo.INTRINSIC_ZOOM_RATIO_UNKNOWN
+ try {
+ intrinsicZoomRatio =
+ cameraFovInfo.getDefaultCameraDefaultViewAngleDegrees().toFloat() /
+ cameraFovInfo.getDefaultViewAngleDegrees().toFloat()
+ } catch (e: Exception) {
+ Log.error(e) { "Failed to get the intrinsic zoom ratio" }
+ }
+
+ return intrinsicZoomRatio
+ }
+
private fun profileSetToDynamicRangeSet(profileSet: Set<Long>): Set<DynamicRange> {
return profileSet.map { profileToDynamicRange(it) }.toSet()
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
index 5020ead..351a853 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
@@ -53,7 +53,7 @@
) : CameraInternal {
private val cameraId = config.cameraId
private var coreCameraConfig: androidx.camera.core.impl.CameraConfig =
- CameraConfigs.emptyConfig()
+ CameraConfigs.defaultConfig()
private val debugId = cameraAdapterIds.incrementAndGet()
init {
@@ -126,7 +126,7 @@
}
override fun setExtendedConfig(cameraConfig: androidx.camera.core.impl.CameraConfig?) {
- coreCameraConfig = cameraConfig ?: CameraConfigs.emptyConfig()
+ coreCameraConfig = cameraConfig ?: CameraConfigs.defaultConfig()
}
override fun toString(): String = "CameraInternalAdapter<$cameraId>"
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraFovInfo.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraFovInfo.kt
new file mode 100644
index 0000000..1f60e1e
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraFovInfo.kt
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2023 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.camera2.pipe.integration.internal
+
+import android.graphics.Rect
+import android.hardware.camera2.CameraCharacteristics
+import android.util.Size
+import android.util.SizeF
+import androidx.annotation.IntRange
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraDevices
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.integration.config.CameraScope
+import androidx.camera.camera2.pipe.integration.impl.CameraProperties
+import androidx.camera.core.impl.utils.TransformUtils
+import androidx.core.util.Preconditions
+import javax.inject.Inject
+import kotlin.math.atan
+
+@RequiresApi(21)
+@CameraScope
+class CameraFovInfo @Inject constructor(
+ private val cameraDevices: CameraDevices,
+ private val cameraProperties: CameraProperties,
+) {
+ /**
+ * Gets the default focal length from a [CameraMetadata].
+ *
+ * If the camera is a logical camera that consists of multiple physical cameras, the
+ * default focal length is the focal length of the physical camera that produces image at
+ * zoom ratio `1.0`.
+ *
+ * @throws NullPointerException If any of the required [CameraCharacteristics] is not available.
+ *
+ * @throws IllegalStateException If [CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS] is
+ * empty.
+ */
+ private fun getDefaultFocalLength(
+ cameraMetadata: CameraMetadata = cameraProperties.metadata
+ ): Float {
+ val focalLengths: FloatArray = Preconditions.checkNotNull(
+ cameraMetadata[CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS],
+ "The focal lengths can not be empty."
+ )
+
+ Preconditions.checkState(
+ focalLengths.isNotEmpty(),
+ "The focal lengths can not be empty."
+ )
+
+ // Assume the first focal length is the default focal length. This will not be true if the
+ // camera is a logical camera consist of multiple physical cameras and reports multiple
+ // focal lengths. However for this kind of cameras, it's suggested to use zoom ratio to
+ // do optical zoom.
+ return focalLengths[0]
+ }
+
+ /**
+ * Gets the length of the horizontal side of the sensor.
+ *
+ * The horizontal side is the width of the sensor size after rotated by the sensor
+ * orientation.
+ *
+ * @throws NullPointerException If any of the required [CameraCharacteristics] is not available.
+ */
+ private fun getSensorHorizontalLength(
+ cameraMetadata: CameraMetadata = cameraProperties.metadata
+ ): Float {
+ var sensorSize: SizeF = Preconditions.checkNotNull(
+ cameraMetadata[CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE],
+ "The sensor size can't be null."
+ )
+
+ val activeArrayRect: Rect = Preconditions.checkNotNull(
+ cameraMetadata[CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE],
+ "The sensor orientation can't be null."
+ )
+
+ var pixelArraySize: Size = Preconditions.checkNotNull(
+ cameraMetadata[CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE],
+ "The active array size can't be null."
+ )
+
+ val sensorOrientation: Int = Preconditions.checkNotNull(
+ cameraMetadata[CameraCharacteristics.SENSOR_ORIENTATION],
+ "The pixel array size can't be null."
+ )
+
+ var activeArraySize = TransformUtils.rectToSize(activeArrayRect)
+ if (TransformUtils.is90or270(sensorOrientation)) {
+ sensorSize = TransformUtils.reverseSizeF(sensorSize)
+ activeArraySize = TransformUtils.reverseSize(activeArraySize)
+ pixelArraySize = TransformUtils.reverseSize(pixelArraySize)
+ }
+ return sensorSize.width * activeArraySize.width / pixelArraySize.width
+ }
+
+ /**
+ * Calculates view angle by focal length and sensor length.
+ *
+ * The returned view angle is inexact and might not be hundred percent accurate comparing
+ * to the output image.
+ *
+ * The returned view angle should between 0 and 360.
+ *
+ * @throws IllegalArgumentException If the provided focal length or sensor length is not
+ * positive, or results in an invalid view angle.
+ */
+ @IntRange(from = 0, to = 360)
+ private fun focalLengthToViewAngleDegrees(focalLength: Float, sensorLength: Float): Int {
+ Preconditions.checkArgument(focalLength > 0, "Focal length should be positive.")
+ Preconditions.checkArgument(sensorLength > 0, "Sensor length should be positive.")
+ val viewAngleDegrees = Math.toDegrees(
+ 2 * atan((sensorLength / (2 * focalLength)).toDouble())
+ ).toInt()
+ Preconditions.checkArgumentInRange(
+ viewAngleDegrees,
+ 0,
+ 360,
+ "The provided focal length and sensor length result in an invalid view" +
+ " angle degrees."
+ )
+ return viewAngleDegrees
+ }
+
+ /**
+ * Gets the angle of view of the current camera on the device.
+ *
+ * @throws IllegalStateException If a valid view angle could not be found.
+ */
+ @Throws(IllegalStateException::class)
+ fun getDefaultViewAngleDegrees(): Int {
+ try {
+ return focalLengthToViewAngleDegrees(
+ getDefaultFocalLength(),
+ getSensorHorizontalLength()
+ )
+ } catch (e: Exception) {
+ throw IllegalStateException("Failed to get a valid view angle", e)
+ }
+ }
+
+ /**
+ * Gets the angle of view of the default camera on the device.
+ *
+ *
+ * The default cameras is the camera selected by
+ * [androidx.camera.core.CameraSelector.DEFAULT_FRONT_CAMERA] or
+ * [androidx.camera.core.CameraSelector.DEFAULT_BACK_CAMERA]
+ * depending on the specified lens facing.
+ *
+ * @throws IllegalStateException If a valid view angle could not be found.
+ */
+ @Throws(IllegalStateException::class)
+ fun getDefaultCameraDefaultViewAngleDegrees(): Int {
+ try {
+ val cameraIds = Preconditions.checkNotNull(
+ cameraDevices.awaitCameraIds(),
+ "Failed to get available camera IDs",
+ )
+
+ cameraIds.forEach { cameraId ->
+ val cameraMetadata = Preconditions.checkNotNull(
+ cameraDevices.awaitCameraMetadata(cameraId),
+ "Failed to get CameraMetadata for $cameraId",
+ )
+ val cameraLensFacing = Preconditions.checkNotNull(
+ cameraMetadata[CameraCharacteristics.LENS_FACING],
+ "Failed to get CameraCharacteristics.LENS_FACING for $cameraId"
+ )
+ val currentLensFacing = Preconditions.checkNotNull(
+ cameraProperties.metadata[CameraCharacteristics.LENS_FACING],
+ "Failed to get the required LENS_FACING" +
+ " for ${cameraProperties.cameraId}"
+ )
+ if (cameraLensFacing == currentLensFacing) {
+ return focalLengthToViewAngleDegrees(
+ getDefaultFocalLength(cameraMetadata),
+ getSensorHorizontalLength(cameraMetadata)
+ )
+ }
+ }
+
+ throw IllegalStateException(
+ "Could not find the default camera for ${cameraProperties.cameraId}"
+ )
+ } catch (e: Exception) {
+ throw IllegalStateException("Failed to get a valid view angle", e)
+ }
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
index aafb109..cf910d3 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
@@ -24,6 +24,7 @@
import androidx.camera.camera2.pipe.integration.impl.CameraProperties
import androidx.camera.core.CameraInfo
import androidx.camera.core.impl.CameraInfoInternal
+import androidx.core.util.Preconditions
/**
* An interface for retrieving Camera2-related camera information.
@@ -31,7 +32,7 @@
@ExperimentalCamera2Interop
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
class Camera2CameraInfo private constructor(
- internal val cameraProperties: CameraProperties,
+ private val cameraProperties: CameraProperties,
) {
/**
@@ -72,31 +73,6 @@
fun getCameraId(): String = cameraProperties.cameraId.value
- /**
- * Returns a map consisting of the camera ids and the
- * [android.hardware.camera2.CameraCharacteristics]s.
- *
- * For every camera, the map contains at least the CameraCharacteristics for the camera id.
- * If the camera is logical camera, it will also contain associated physical camera ids and
- * their CameraCharacteristics.
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- fun getCameraCharacteristicsMap(): Map<String, CameraCharacteristics> {
- return buildMap {
- put(
- cameraProperties.cameraId.value,
- cameraProperties.metadata.unwrapAs(CameraCharacteristics::class)!!
- )
- for (cameraId in cameraProperties.metadata.physicalCameraIds) {
- put(
- cameraId.value,
- cameraProperties.metadata.awaitPhysicalMetadata(cameraId)
- .unwrapAs(CameraCharacteristics::class)!!
- )
- }
- }
- }
-
companion object {
/**
@@ -111,10 +87,11 @@
@JvmStatic
fun from(@Suppress("UNUSED_PARAMETER") cameraInfo: CameraInfo): Camera2CameraInfo {
var cameraInfoImpl = (cameraInfo as CameraInfoInternal).implementation
- require(cameraInfoImpl is CameraInfoAdapter) {
+ Preconditions.checkArgument(
+ cameraInfoImpl is CameraInfoAdapter,
"CameraInfo doesn't contain Camera2 implementation."
- }
- return cameraInfoImpl.camera2CameraInfo
+ )
+ return (cameraInfoImpl as CameraInfoAdapter).camera2CameraInfo
}
/**
@@ -123,29 +100,5 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
@JvmStatic
fun create(cameraProperties: CameraProperties) = Camera2CameraInfo(cameraProperties)
-
- /**
- * Returns the [android.hardware.camera2.CameraCharacteristics] for this camera.
- *
- * The CameraCharacteristics will be the ones that would be obtained by
- * [android.hardware.camera2.CameraManager.getCameraCharacteristics]. The
- * CameraCharacteristics that are retrieved are not static and can change depending on the
- * current internal configuration of the camera.
- *
- * @param cameraInfo The [CameraInfo] to extract the CameraCharacteristics from.
- * @throws IllegalStateException if the camera info does not contain the camera 2
- * characteristics(e.g., if CameraX was not initialized with a
- * [androidx.camera.camera2.Camera2Config]).
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- @JvmStatic
- fun extractCameraCharacteristics(cameraInfo: CameraInfo): CameraCharacteristics {
- var cameraInfoImpl = (cameraInfo as CameraInfoInternal).implementation
- require(cameraInfoImpl is CameraInfoAdapter) {
- "CameraInfo doesn't contain Camera2 implementation."
- }
- return cameraInfoImpl.camera2CameraInfo
- .cameraProperties.metadata.unwrapAs(CameraCharacteristics::class)!!
- }
}
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
index d352ea7..716e8e8 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
@@ -16,13 +16,18 @@
package androidx.camera.camera2.pipe.integration.adapter
+import android.graphics.Rect
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_OFF
import android.hardware.camera2.CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_ON
import android.hardware.camera2.CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
+import android.hardware.camera2.CameraMetadata
import android.os.Build
import android.util.Range
import android.util.Size
+import android.util.SizeF
+import androidx.camera.camera2.pipe.CameraBackendId
+import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.integration.impl.ZoomControl
import androidx.camera.camera2.pipe.integration.internal.DOLBY_VISION_10B_UNCONSTRAINED
import androidx.camera.camera2.pipe.integration.internal.HLG10_UNCONSTRAINED
@@ -31,6 +36,7 @@
import androidx.camera.camera2.pipe.integration.testing.FakeCameraProperties
import androidx.camera.camera2.pipe.integration.testing.FakeUseCaseCamera
import androidx.camera.camera2.pipe.integration.testing.FakeZoomCompat
+import androidx.camera.camera2.pipe.testing.FakeCameraDevices
import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
import androidx.camera.core.CameraInfo
import androidx.camera.core.DynamicRange
@@ -67,6 +73,41 @@
@get:Rule
val dispatcherRule = MainDispatcherRule(MoreExecutors.directExecutor().asCoroutineDispatcher())
+ private val defaultCameraId = "0"
+ private val defaultCameraCharacteristics = mapOf(
+ CameraCharacteristics.LENS_FACING to CameraMetadata.LENS_FACING_BACK,
+ CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS to floatArrayOf(1.0f),
+ CameraCharacteristics.SENSOR_ORIENTATION to 0,
+ CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE to Size(10, 10),
+ CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE to Rect(0, 0, 10, 10),
+ CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE to SizeF(10f, 10f),
+ )
+ private val defaultCameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(characteristics = defaultCameraCharacteristics)
+ )
+
+ private val telephotoCameraId = "2"
+
+ // Only SENSOR_INFO_PHYSICAL_SIZE has been made less wider and everything else kept the same
+ private val telephotoCameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ characteristics = defaultCameraCharacteristics.toMutableMap().apply {
+ put(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE, SizeF(1f, 10f))
+ }
+ )
+ )
+
+ private val ultraWideCameraId = "2"
+
+ // Only SENSOR_INFO_PHYSICAL_SIZE has been made wider and everything else kept the same
+ private val ultraWideCameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ characteristics = defaultCameraCharacteristics.toMutableMap().apply {
+ put(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE, SizeF(100f, 10f))
+ }
+ )
+ )
+
@Test
fun getSupportedResolutions() {
// Act.
@@ -391,4 +432,186 @@
assertThat(cameraInfo.querySupportedDynamicRanges(emptySet())).isEmpty()
}
+
+ @Test
+ fun intrinsicZoomRatioIsLessThan1_whenSensorHorizontalLengthWiderThanDefault() {
+ val cameraInfo: CameraInfoInternal = createCameraInfoAdapter(
+ cameraId = CameraId(ultraWideCameraId),
+ cameraProperties = ultraWideCameraProperties,
+ cameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(defaultCameraId),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(defaultCameraId) to listOf(defaultCameraProperties.metadata),
+ CameraBackendId(ultraWideCameraId) to
+ listOf(ultraWideCameraProperties.metadata),
+ )
+ )
+ )
+
+ assertThat(cameraInfo.intrinsicZoomRatio).isLessThan(1)
+ }
+
+ @Test
+ fun intrinsicZoomRatioIsGreaterThan1_whenSensorHorizontalLengthSmallerThanDefault() {
+ val cameraInfo: CameraInfoInternal = createCameraInfoAdapter(
+ cameraId = CameraId(telephotoCameraId),
+ cameraProperties = telephotoCameraProperties,
+ cameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(defaultCameraId),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(defaultCameraId) to listOf(defaultCameraProperties.metadata),
+ CameraBackendId(telephotoCameraId) to
+ listOf(telephotoCameraProperties.metadata),
+ )
+ )
+ )
+
+ assertThat(cameraInfo.intrinsicZoomRatio).isGreaterThan(1)
+ }
+
+ @Test
+ fun intrinsicZoomRatioIsUnknown_whenNoLensFacingInfo() {
+ val cameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ characteristics = defaultCameraCharacteristics.toMutableMap().apply {
+ remove(CameraCharacteristics.LENS_FACING)
+ }
+ )
+ )
+ val cameraInfo: CameraInfoInternal = createCameraInfoAdapter(
+ cameraId = CameraId(defaultCameraId),
+ cameraProperties = cameraProperties,
+ cameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(defaultCameraId),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(defaultCameraId) to listOf(cameraProperties.metadata),
+ )
+ )
+ )
+
+ assertThat(cameraInfo.intrinsicZoomRatio).isEqualTo(CameraInfo.INTRINSIC_ZOOM_RATIO_UNKNOWN)
+ }
+
+ @Test
+ fun intrinsicZoomRatioIsUnknown_whenNoLensFocalLengthInfo() {
+ val cameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ characteristics = defaultCameraCharacteristics.toMutableMap().apply {
+ remove(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
+ }
+ )
+ )
+ val cameraInfo: CameraInfoInternal = createCameraInfoAdapter(
+ cameraId = CameraId(defaultCameraId),
+ cameraProperties = cameraProperties,
+ cameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(defaultCameraId),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(defaultCameraId) to listOf(cameraProperties.metadata),
+ )
+ )
+ )
+
+ assertThat(cameraInfo.intrinsicZoomRatio).isEqualTo(CameraInfo.INTRINSIC_ZOOM_RATIO_UNKNOWN)
+ }
+
+ @Test
+ fun intrinsicZoomRatioIsUnknown_whenNoSensorOrientationInfo() {
+ val cameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ characteristics = defaultCameraCharacteristics.toMutableMap().apply {
+ remove(CameraCharacteristics.SENSOR_ORIENTATION)
+ }
+ )
+ )
+ val cameraInfo: CameraInfoInternal = createCameraInfoAdapter(
+ cameraId = CameraId(defaultCameraId),
+ cameraProperties = cameraProperties,
+ cameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(defaultCameraId),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(defaultCameraId) to listOf(cameraProperties.metadata),
+ )
+ )
+ )
+
+ assertThat(cameraInfo.intrinsicZoomRatio).isEqualTo(CameraInfo.INTRINSIC_ZOOM_RATIO_UNKNOWN)
+ }
+
+ @Test
+ fun intrinsicZoomRatioIsUnknown_whenNoSensorPixelArraySizeInfo() {
+ val cameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ characteristics = defaultCameraCharacteristics.toMutableMap().apply {
+ remove(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)
+ }
+ )
+ )
+ val cameraInfo: CameraInfoInternal = createCameraInfoAdapter(
+ cameraId = CameraId(defaultCameraId),
+ cameraProperties = cameraProperties,
+ cameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(defaultCameraId),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(defaultCameraId) to listOf(cameraProperties.metadata),
+ )
+ )
+ )
+
+ assertThat(cameraInfo.intrinsicZoomRatio).isEqualTo(CameraInfo.INTRINSIC_ZOOM_RATIO_UNKNOWN)
+ }
+
+ @Test
+ fun intrinsicZoomRatioIsUnknown_whenNoSensorActiveArraySizeInfo() {
+ val cameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ characteristics = defaultCameraCharacteristics.toMutableMap().apply {
+ remove(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
+ }
+ )
+ )
+ val cameraInfo: CameraInfoInternal = createCameraInfoAdapter(
+ cameraId = CameraId(defaultCameraId),
+ cameraProperties = cameraProperties,
+ cameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(defaultCameraId),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(defaultCameraId) to listOf(cameraProperties.metadata),
+ )
+ )
+ )
+
+ assertThat(cameraInfo.intrinsicZoomRatio).isEqualTo(CameraInfo.INTRINSIC_ZOOM_RATIO_UNKNOWN)
+ }
+
+ @Test
+ fun intrinsicZoomRatioIsUnknown_whenNoSensorPhysicalSizeInfo() {
+ val cameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ characteristics = defaultCameraCharacteristics.toMutableMap().apply {
+ remove(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)
+ }
+ )
+ )
+ val cameraInfo: CameraInfoInternal = createCameraInfoAdapter(
+ cameraId = CameraId(defaultCameraId),
+ cameraProperties = cameraProperties,
+ cameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(defaultCameraId),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(defaultCameraId) to listOf(cameraProperties.metadata),
+ )
+ )
+ )
+
+ assertThat(cameraInfo.intrinsicZoomRatio).isEqualTo(CameraInfo.INTRINSIC_ZOOM_RATIO_UNKNOWN)
+ }
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
index 21c789a..0edc033 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
@@ -22,6 +22,8 @@
import android.util.Range
import android.util.Size
import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraBackendId
+import androidx.camera.camera2.pipe.CameraDevices
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.integration.adapter.CameraControlStateAdapter
import androidx.camera.camera2.pipe.integration.adapter.CameraInfoAdapter
@@ -42,6 +44,8 @@
import androidx.camera.camera2.pipe.integration.impl.TorchControl
import androidx.camera.camera2.pipe.integration.impl.UseCaseThreads
import androidx.camera.camera2.pipe.integration.impl.ZoomControl
+import androidx.camera.camera2.pipe.integration.internal.CameraFovInfo
+import androidx.camera.camera2.pipe.testing.FakeCameraDevices
import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
import androidx.camera.core.impl.ImageFormatConstants
import com.google.common.util.concurrent.MoreExecutors
@@ -96,6 +100,14 @@
cameraId
),
zoomControl: ZoomControl = this.zoomControl,
+ cameraDevices: CameraDevices = FakeCameraDevices(
+ defaultCameraBackendId = CameraBackendId(cameraId.value),
+ concurrentCameraBackendIds = emptySet(),
+ cameraMetadataMap = mapOf(
+ CameraBackendId(cameraId.value) to listOf(cameraProperties.metadata)
+ )
+ )
+
): CameraInfoAdapter {
val fakeUseCaseCamera = FakeUseCaseCamera()
val fakeStreamConfigurationMap = StreamConfigurationMapCompat(
@@ -137,6 +149,7 @@
fakeCameraQuirks,
EncoderProfilesProviderAdapter(cameraId.value),
fakeStreamConfigurationMap,
+ CameraFovInfo(cameraDevices, cameraProperties),
)
}
}
diff --git a/camera/camera-camera2-pipe/build.gradle b/camera/camera-camera2-pipe/build.gradle
index d9a7bf3..80fad89 100644
--- a/camera/camera-camera2-pipe/build.gradle
+++ b/camera/camera-camera2-pipe/build.gradle
@@ -39,6 +39,7 @@
testImplementation(libs.junit)
testImplementation(libs.truth)
testImplementation(libs.robolectric)
+ testImplementation(libs.kotlinTest)
testImplementation(libs.kotlinCoroutinesTest)
testImplementation(libs.mockitoCore4)
testImplementation(libs.mockitoKotlin4)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
index 1aad2bf..278646b 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
@@ -26,6 +26,7 @@
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
import androidx.camera.camera2.pipe.core.Debug
+import androidx.camera.camera2.pipe.core.Log
/**
* A [RequestNumber] is an artificial identifier that is created for each request that is submitted
@@ -361,7 +362,17 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun CaptureRequest.Builder.writeParameter(key: Any?, value: Any?) {
if (key != null && key is CaptureRequest.Key<*>) {
- @Suppress("UNCHECKED_CAST") this.set(key as CaptureRequest.Key<Any>, value)
+ try {
+ @Suppress("UNCHECKED_CAST") this.set(key as CaptureRequest.Key<Any>, value)
+ } catch (e: IllegalArgumentException) {
+ // Setting keys on CaptureRequest.Builder can fail if the key is defined on some
+ // OS versions, but not on others. Log and ignore these kinds of failures.
+ //
+ // See b/309518353 for an example failure.
+ Log.warn(e) {
+ "Failed to set [${key.name}: $value] on CaptureRequest.Builder"
+ }
+ }
}
}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/CoroutineMutex.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/CoroutineMutex.kt
new file mode 100644
index 0000000..1ed414fe
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/CoroutineMutex.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2023 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.camera2.pipe.core
+
+import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
+import kotlin.coroutines.intrinsics.intercepted
+import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn
+import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
+import kotlin.coroutines.resume
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.ensureActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
+
+/**
+ * [CoroutineMutex] is a shared [Mutex] instance with extension functions to allow callers to lock
+ * the mutex from a non-coroutine function, while ensuring that the locked execution block is
+ * always suspended (Which avoids synchronously running code that should be executed on a different
+ * thread). This guaranteed suspension, plus the additional extension functions to make it easier
+ * to correctly lock and run suspending code from a non-suspending function, is the primary
+ * difference from using a [Mutex] object directly.
+ *
+ * This should not be used to run long running operations, since the lock will be held for the
+ * entire duration. In addition, kotlin [Mutex] objects are non-reentrant, unlike standard java
+ * `synchronized` locks.
+ */
+class CoroutineMutex {
+ internal val mutex = Mutex()
+}
+
+/**
+ * Execute the provided [block] within the current [CoroutineMutex] while ensuring that only one
+ * operation is executed at a time.
+ */
+fun <T> CoroutineMutex.withLockAsync(
+ scope: CoroutineScope,
+ block: suspend CoroutineScope.() -> T
+): Deferred<T> {
+ // Using CoroutineStart.UNDISPATCHED ensures that the mutex.lock call is invoked synchronously
+ // on the current thread. This prevents two operations from being mis-ordered since the lock
+ // is acquired before the block is scheduled for execution.
+ return scope.async(start = CoroutineStart.UNDISPATCHED) {
+ ensureActive()
+
+ // The block is called within a new CoroutineScope, while holding the lock. This ensures
+ // that any child coroutines started via `block` are completed before `lock` gets released
+ // and the next block starts, which includes other async/launch calls invoked on the
+ // scope.
+ mutex.withLockAndSuspend { coroutineScope(block) }
+ }
+}
+
+/**
+ * Execute the provided [block] after acquiring a lock to the [CoroutineMutex] in the provided
+ * [CoroutineScope].
+ */
+fun CoroutineMutex.withLockLaunch(
+ scope: CoroutineScope,
+ block: suspend CoroutineScope.() -> Unit
+): Job {
+ // Using CoroutineStart.UNDISPATCHED ensures that the mutex.lock call is invoked synchronously
+ // on the current thread. This prevents two operations from being mis-ordered since the lock
+ // is acquired before the block is scheduled for execution.
+ return scope.launch(start = CoroutineStart.UNDISPATCHED) {
+ ensureActive()
+
+ // The block is called within a new CoroutineScope, while holding the lock. This ensures
+ // that any child coroutines started via `block` are completed before `lock` gets released
+ // and the next block starts.
+ mutex.withLockAndSuspend { coroutineScope(block) }
+ }
+}
+
+/**
+ * This function allows the implementation of [Mutex.lockAndSuspend] to call the overload of
+ * [startCoroutineUninterceptedOrReturn] that takes a receiver, which saves an allocation and allows
+ * the compiler to skip the coroutine state machine logic in [Mutex.lockAndSuspend].
+ */
+private suspend fun Mutex.lockWithoutOwner() = lock(owner = null)
+
+/**
+ * Same as [kotlinx.coroutines.sync.withLock], but guarantees that the coroutine is suspended before
+ * the lock is acquired whether or not the lock is locked at the time this function is called.
+ */
+private suspend inline fun <T> Mutex.withLockAndSuspend(action: () -> T): T {
+ lockAndSuspend()
+ try {
+ return action()
+ } finally {
+ unlock()
+ }
+}
+
+/**
+ * Same as [Mutex.lock], but guarantees that the coroutine is *always* suspended before the lock is
+ * acquired, regardless of if the lock is locked at the time this function is called. This ensures
+ * consistent behavior when calling lock from within a [CoroutineStart.UNDISPATCHED] coroutine.
+ */
+private suspend fun Mutex.lockAndSuspend() {
+ val lockFn = Mutex::lockWithoutOwner
+ return suspendCoroutineUninterceptedOrReturn { continuation ->
+ if (lockFn.startCoroutineUninterceptedOrReturn(
+ this,
+ continuation
+ ) !== COROUTINE_SUSPENDED
+ ) {
+ // If the mutex.lock call did *not* suspend (likely because the lock was acquired
+ // immediately), intercept the continuation block, which will schedule it for execution.
+ continuation.intercepted().resume(Unit)
+ }
+
+ // The result is always that the continuation is always suspending.
+ COROUTINE_SUSPENDED
+ }
+}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
index 01fc505..fd603e5 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
@@ -35,10 +35,12 @@
import androidx.camera.camera2.pipe.compat.Camera2Quirks
import androidx.camera.camera2.pipe.config.CameraGraphScope
import androidx.camera.camera2.pipe.config.ForCameraGraph
+import androidx.camera.camera2.pipe.core.CoroutineMutex
import androidx.camera.camera2.pipe.core.Debug
import androidx.camera.camera2.pipe.core.Log.debug
import androidx.camera.camera2.pipe.core.Log.warn
import androidx.camera.camera2.pipe.core.Threads
+import androidx.camera.camera2.pipe.core.withLockLaunch
import androidx.camera.camera2.pipe.formatForLogs
import androidx.camera.camera2.pipe.putAllMetadata
import java.util.concurrent.CountDownLatch
@@ -133,6 +135,7 @@
) : GraphProcessor, GraphListener {
private val lock = Any()
private val tryStartRepeatingExecutionLock = Any()
+ private val coroutineMutex = CoroutineMutex()
@GuardedBy("lock")
private val submitQueue: MutableList<List<Request>> = ArrayList()
@@ -275,9 +278,11 @@
if (closed) return
repeatingQueue.add(request)
debug { "startRepeating with ${request.formatForLogs()}" }
- }
- graphScope.launch(threads.lightweightDispatcher) { tryStartRepeating() }
+ coroutineMutex.withLockLaunch(graphScope) {
+ tryStartRepeating()
+ }
+ }
}
override fun stopRepeating() {
@@ -287,15 +292,15 @@
processor = _requestProcessor
repeatingQueue.clear()
currentRepeatingRequest = null
- }
- graphScope.launch(threads.lightweightDispatcher) {
- Debug.traceStart { "$this#stopRepeating" }
- // Start with requests that have already been submitted
- if (processor != null) {
- synchronized(processor) { processor.stopRepeating() }
+ coroutineMutex.withLockLaunch(graphScope) {
+ Debug.traceStart { "$this#stopRepeating" }
+ // Start with requests that have already been submitted
+ if (processor != null) {
+ synchronized(processor) { processor.stopRepeating() }
+ }
+ Debug.traceStop()
}
- Debug.traceStop()
}
}
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/core/CoroutineMutexTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/core/CoroutineMutexTest.kt
new file mode 100644
index 0000000..cd4fe23
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/core/CoroutineMutexTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2023 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.camera2.pipe.core
+
+import android.os.Build
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CancellationException
+import kotlin.test.assertFailsWith
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.robolectric.annotation.Config
+
+@RunWith(JUnit4::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class CoroutineMutexTest {
+ private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
+
+ @Test
+ fun testMultipleSequencesAreIndependent() = runBlocking {
+ val sequence1 = CoroutineMutex()
+ val sequence2 = CoroutineMutex()
+
+ val internalResult =
+ sequence1.withLockAsync(scope) {
+ sequence2.withLockAsync(scope) { 42 }.await()
+ }
+ .await()
+
+ assertThat(internalResult).isEqualTo(42)
+ }
+
+ @Test
+ fun testSequenceOrder() = runBlocking {
+ var counter = 0
+ val sequence = CoroutineMutex()
+
+ val first = sequence.withLockAsync(scope) { ++counter }
+ val second = sequence.withLockAsync(scope) { ++counter }
+ val third = sequence.withLockAsync(scope) { ++counter }
+
+ assertThat(listOf(second, first, third).awaitAll()).containsExactly(2, 1, 3).inOrder()
+ }
+
+ @Test
+ fun testLaunchOnOuterScopeWithinExecuteAsyncDoesNotDeadlock() = runBlocking {
+ var output: Int = -1
+ val sequence = CoroutineMutex()
+
+ coroutineScope {
+ val sharedMutex = Mutex(locked = true)
+
+ sequence.withLockAsync(this) {
+ // Note: The receiver is `this@coroutineScope`. The `sequenceAsync {}` block will return
+ // racing the launched block.
+ this@coroutineScope.launch {
+ sharedMutex.lock()
+ // Doesn't throw as no block reentered `sequenceAsync {}`.
+ sequence.withLockAsync(this) { output = 42 }.await()
+ }
+ }
+ .await()
+
+ sharedMutex.unlock()
+ }
+ assertThat(output).isEqualTo(42)
+ }
+
+ @Test
+ fun testLaunchOrderIndependentOfScope() {
+ var counter = 0
+ val sequence = CoroutineMutex()
+ val scope1 = CoroutineScope(Job() + Dispatchers.Default)
+ val scope2 = CoroutineScope(Job() + Dispatchers.Default)
+
+ val output = mutableListOf<Deferred<Int>>()
+
+ for (i in 0..5000) {
+ if (i % 2 > 0) {
+ output.add(sequence.withLockAsync(scope1) { counter++ })
+ } else {
+ output.add(sequence.withLockAsync(scope2) { counter++ })
+ }
+ }
+
+ runBlocking {
+ assertThat(output.awaitAll()).containsExactlyElementsIn(0.rangeTo(5000)).inOrder()
+ }
+ }
+
+ @Test
+ fun testCancelledCoroutineDropsFromSequence() {
+ var counter = 0
+ val sequence = CoroutineMutex()
+ val mutex = Mutex(locked = true)
+
+ val first = sequence.withLockAsync(scope) { mutex.withLock { ++counter } }
+ val second = sequence.withLockAsync(scope) { mutex.withLock { ++counter } }
+ val third = sequence.withLockAsync(scope) { mutex.withLock { ++counter } }
+
+ second.cancel()
+
+ mutex.unlock()
+ runBlocking {
+ assertFailsWith(CancellationException::class) { second.await() }
+ assertThat(first.await()).isEqualTo(1)
+ assertThat(third.await()).isEqualTo(2)
+ }
+ }
+
+ @Test
+ fun testFailedCoroutineDropsFromSequenceWithoutAffectingLaterBlocks() {
+ class ExpectedException : Exception()
+
+ var counter = 0
+ val sequence = CoroutineMutex()
+
+ val first = sequence.withLockAsync(scope) { ++counter }
+ val second = sequence.withLockAsync(scope) { throw ExpectedException() }
+ val third = sequence.withLockAsync(scope) { ++counter }
+
+ runBlocking {
+ assertThat(first.await()).isEqualTo(1)
+ assertFailsWith(ExpectedException::class) { second.await() }
+ assertThat(third.await()).isEqualTo(2)
+ }
+ }
+}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
index 1cb826b..a4656f8 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
@@ -51,6 +51,7 @@
import androidx.camera.core.internal.CameraUseCaseAdapter;
import androidx.camera.testing.impl.CameraUtil;
import androidx.camera.testing.impl.CameraXUtil;
+import androidx.camera.testing.impl.WakelockEmptyActivityRule;
import androidx.camera.testing.impl.fakes.FakeLifecycleOwner;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -95,6 +96,9 @@
new CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
);
+ @Rule
+ public TestRule wakelockEmptyActivityRule = new WakelockEmptyActivityRule();
+
private CameraDevice.StateCallback mDeviceStateCallback;
private FakeLifecycleOwner mLifecycle;
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/FovDeviceTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/FovDeviceTest.kt
deleted file mode 100644
index 8df88c2..0000000
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/FovDeviceTest.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2022 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.camera2.internal
-
-import android.content.Context
-import androidx.camera.camera2.Camera2Config
-import androidx.camera.core.CameraInfo
-import androidx.camera.core.CameraSelector
-import androidx.camera.core.impl.CameraInfoInternal
-import androidx.camera.core.internal.CameraUseCaseAdapter
-import androidx.camera.testing.impl.CameraUtil
-import androidx.camera.testing.impl.CameraXUtil
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.filters.SdkSuppress
-import com.google.common.truth.Truth.assertThat
-import java.util.Collections
-import java.util.concurrent.TimeUnit
-import org.junit.After
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-private val DEFAULT_CAMERA_ID_GROUP = Collections.unmodifiableSet(setOf("0", "1"))
-
-@RunWith(Parameterized::class)
-@SdkSuppress(minSdkVersion = 21)
-class FovDeviceTest(private val cameraId: String) {
- private val cameraConfig = Camera2Config.defaultConfig()
-
- @get:Rule
- val cameraRule = CameraUtil.grantCameraPermissionAndPreTest(
- CameraUtil.PreTestCameraIdList(cameraConfig)
- )
-
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "{0}")
- fun data(): ArrayList<String> {
- return ArrayList(CameraUtil.getBackwardCompatibleCameraIdListOrThrow())
- }
- }
-
- private lateinit var cameraUseCaseAdapter: CameraUseCaseAdapter
- private val context: Context = ApplicationProvider.getApplicationContext()
-
- @Before
- fun setUp() {
- CameraXUtil.initialize(
- context,
- cameraConfig
- ).get()
-
- val cameraSelector = CameraSelector.Builder().addCameraFilter { cameraInfoList ->
- val filteredList = ArrayList<CameraInfo>()
- cameraInfoList.forEach { cameraInfo ->
- if ((cameraInfo as CameraInfoInternal).cameraId == cameraId) {
- filteredList.add(cameraInfo)
- }
- }
- filteredList
- }.build()
- cameraUseCaseAdapter =
- CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
- }
-
- @After
- fun tearDown() {
- CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
- }
-
- @Test
- fun intrinsicZoomRatio_greaterThanZero() {
- assertThat(cameraUseCaseAdapter.cameraInfo.intrinsicZoomRatio).isGreaterThan(0)
- }
-
- @Test
- fun intrinsicZoomRatio_defaultToOne() {
- assumeTrue(DEFAULT_CAMERA_ID_GROUP.contains(cameraId))
- assertThat(cameraUseCaseAdapter.cameraInfo.intrinsicZoomRatio).isEqualTo(1.0F)
- }
-}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
index 4dd1132..02378b2 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
@@ -33,13 +33,8 @@
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
-import androidx.camera.core.impl.CameraConfig
import androidx.camera.core.impl.CameraInfoInternal
import androidx.camera.core.impl.CameraThreadConfig
-import androidx.camera.core.impl.Config
-import androidx.camera.core.impl.Identifier
-import androidx.camera.core.impl.MutableOptionsBundle
-import androidx.camera.core.impl.SessionProcessor
import androidx.camera.core.impl.SurfaceCombination
import androidx.camera.core.impl.SurfaceConfig
import androidx.camera.core.impl.utils.executor.CameraXExecutors
@@ -48,6 +43,7 @@
import androidx.camera.testing.impl.CameraUtil.PreTestCameraIdList
import androidx.camera.testing.impl.CameraXUtil
import androidx.camera.testing.impl.SurfaceTextureProvider
+import androidx.camera.testing.impl.fakes.FakeCameraConfig
import androidx.camera.testing.impl.fakes.FakeSessionProcessor
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
@@ -126,7 +122,14 @@
@Test
fun successCaptureImage_whenExtraYuvPrivYuvConfigurationSupported() = runBlocking {
var cameraSelector = createCameraSelectorById(cameraId)
- cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
+ val fakeCameraConfig = FakeCameraConfig(
+ sessionProcessor = FakeSessionProcessor(
+ inputFormatPreview = null,
+ inputFormatCapture = ImageFormat.YUV_420_888
+ )
+ )
+ cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
+ context, cameraSelector, fakeCameraConfig)
var camera2CameraInfo = Camera2CameraInfo.from(cameraUseCaseAdapter.cameraInfo)
var hardwareLevel: Int? = camera2CameraInfo.getCameraCharacteristic(
@@ -159,11 +162,6 @@
var preview = Preview.Builder().build()
var imageCapture = ImageCapture.Builder().build()
// This will force ImageCapture to use YUV_420_888 to configure capture session.
- val fakeSessionProcessor = FakeSessionProcessor(
- inputFormatPreview = null,
- inputFormatCapture = ImageFormat.YUV_420_888
- )
- enableSessionProcessor(cameraUseCaseAdapter, fakeSessionProcessor)
withContext(Dispatchers.Main) {
preview.setSurfaceProvider(getSurfaceProvider())
cameraUseCaseAdapter.addUseCases(Arrays.asList(imageAnalysis, preview, imageCapture))
@@ -185,7 +183,15 @@
@Test
fun successCaptureImage_whenExtraYuvYuvYuvConfigurationSupported() = runBlocking {
var cameraSelector = createCameraSelectorById(cameraId)
- cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
+ // This will force ImageCapture / Preview to use YUV_420_888 to configure capture session.
+ val fakeCameraConfig = FakeCameraConfig(
+ sessionProcessor = FakeSessionProcessor(
+ inputFormatPreview = ImageFormat.YUV_420_888,
+ inputFormatCapture = ImageFormat.YUV_420_888
+ )
+ )
+ cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
+ context, cameraSelector, fakeCameraConfig)
var camera2CameraInfo = Camera2CameraInfo.from(cameraUseCaseAdapter.cameraInfo)
var hardwareLevel: Int? = camera2CameraInfo.getCameraCharacteristic(
@@ -216,13 +222,6 @@
var imageAnalysis = ImageAnalysis.Builder().build()
var preview = Preview.Builder().build()
var imageCapture = ImageCapture.Builder().build()
-
- // This will force ImageCapture / Preview to use YUV_420_888 to configure capture session.
- val fakeSessionProcessor = FakeSessionProcessor(
- inputFormatPreview = ImageFormat.YUV_420_888,
- inputFormatCapture = ImageFormat.YUV_420_888
- )
- enableSessionProcessor(cameraUseCaseAdapter, fakeSessionProcessor)
withContext(Dispatchers.Main) {
preview.setSurfaceProvider(getSurfaceProvider())
cameraUseCaseAdapter.addUseCases(Arrays.asList(imageAnalysis, preview, imageCapture))
@@ -240,31 +239,6 @@
}
}
- private fun enableSessionProcessor(
- cameraUseCaseAdapter: CameraUseCaseAdapter,
- sessionProcessor: SessionProcessor
- ) {
- cameraUseCaseAdapter.setExtendedConfig(object : CameraConfig {
- override fun getConfig(): Config {
- return MutableOptionsBundle.create()
- }
-
- override fun getCompatibilityId(): Identifier {
- return Identifier.create(0)
- }
-
- override fun getSessionProcessor(
- valueIfMissing: SessionProcessor?
- ): SessionProcessor? {
- return sessionProcessor
- }
-
- override fun getSessionProcessor(): SessionProcessor {
- return sessionProcessor
- }
- })
- }
-
private fun createCameraSelectorById(id: String): CameraSelector {
var builder = CameraSelector.Builder()
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 2a9afab..1a7363d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -196,7 +196,7 @@
private final Set<String> mNotifyStateAttachedSet = new HashSet<>();
@NonNull
- private CameraConfig mCameraConfig = CameraConfigs.emptyConfig();
+ private CameraConfig mCameraConfig = CameraConfigs.defaultConfig();
final Object mLock = new Object();
// mSessionProcessor will be used to transform capture session if non-null.
@GuardedBy("mLock")
@@ -889,10 +889,7 @@
@Override
public void setExtendedConfig(@Nullable CameraConfig cameraConfig) {
- if (cameraConfig == null) {
- cameraConfig = CameraConfigs.emptyConfig();
- }
-
+ cameraConfig = cameraConfig != null ? cameraConfig : CameraConfigs.defaultConfig();
SessionProcessor sessionProcessor = cameraConfig.getSessionProcessor(null);
mCameraConfig = cameraConfig;
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Camera.java b/camera/camera-core/src/main/java/androidx/camera/core/Camera.java
index 5616fc8..719b4ff 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Camera.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Camera.java
@@ -17,7 +17,6 @@
package androidx.camera.core;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.camera.core.impl.CameraConfig;
@@ -76,11 +75,6 @@
* However, it will be transparent to the {@link CameraControl} and {@link CameraInfo}
* retrieved from {@link #getCameraControl()} and {@link #getCameraInfo()}.
*
- * <p> The CameraInternal are returned in the order of preference. The
- * {@link CameraConfig} that is set via {@link #setExtendedConfig(CameraConfig)} can filter
- * out specific instances of the CameraInternal. The remaining CameraInternal that comes
- * first in this ordering will be used.
- *
* <p> The set of CameraInternal should be static for the lifetime of the Camera.
*
*/
@@ -97,19 +91,6 @@
CameraConfig getExtendedConfig();
/**
- * Set the extended config of the Camera.
- *
- * <p>This is used to apply additional configs that modifying the behavior of the camera and
- * any attached {@link UseCase}. For example, it may configure the {@link ImageCapture} to use a
- * {@link androidx.camera.core.impl.CaptureProcessor} in order to implement effects such as
- * HDR or bokeh.
- *
- * @param cameraConfig if null then it will reset the camera to an empty config.
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- void setExtendedConfig(@Nullable CameraConfig cameraConfig);
-
- /**
* Checks whether the use cases combination is supported.
*
* @param useCases to be checked whether can be supported.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/SurfaceProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/SurfaceProcessor.java
index a622897..f4be960 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/SurfaceProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/SurfaceProcessor.java
@@ -77,10 +77,15 @@
/**
* Invoked when CameraX requires an input {@link Surface} for reading original frames.
*
+ * <p>CameraX requests {@link Surface}s when the upstream pipeline is reconfigured. For
+ * example, when {@link UseCase}s are bound to lifecycle.
+ *
* <p>With OpenGL, the implementation should create a {@link Surface} backed by
* {@link SurfaceTexture} with the size of {@link SurfaceRequest#getResolution()}, then
* listen for the {@link SurfaceTexture#setOnFrameAvailableListener} to get the incoming
- * frames.
+ * frames. The {@link Surface} should not be released until the callback provided in
+ * {@link SurfaceRequest#provideSurface} is invoked. CameraX may request new input
+ * {@link Surface} before releasing the existing one.
*
* <p>If the implementation encounters errors in creating the input {@link Surface}, it
* should throw an {@link ProcessingException} to notify CameraX.
@@ -124,11 +129,16 @@
/**
* Invoked when CameraX provides output Surface(s) for drawing processed frames.
*
+ * <p>CameraX provides the output {@link Surface}s when the downstream pipeline is
+ * reconfigured, for example, when {@link UseCase}s are bound or the preview viewfinder is
+ * reset.
+ *
* <p>The provided {@link Surface}s are for drawing processed frames. The implementation must
* get the {@link Surface} via {@link SurfaceOutput#getSurface} and provide a
* {@link Consumer<SurfaceOutput.Event>} listening to the end-of-life event of the
* {@link Surface}. Then, the implementation should call {@link SurfaceOutput#close()} after it
- * stops drawing to the {@link Surface}.
+ * stops drawing to the {@link Surface}. CameraX may provide new output {@link Surface}
+ * before requesting to close the existing one.
*
* <p>If the implementation encounters an error and cannot consume the {@link Surface},
* it should throw an {@link ProcessingException} to notify CameraX.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraConfigs.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraConfigs.java
index 1eb8774e..e50a548 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraConfigs.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraConfigs.java
@@ -24,17 +24,17 @@
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class CameraConfigs {
- private static final CameraConfig EMPTY_CONFIG = new EmptyCameraConfig();
+ private static final CameraConfig DEFAULT_CAMERA_CONFIG = new DefaultCameraConfig();
/**
* Gets the empty config instance.
*/
@NonNull
- public static CameraConfig emptyConfig() {
- return EMPTY_CONFIG;
+ public static CameraConfig defaultConfig() {
+ return DEFAULT_CAMERA_CONFIG;
}
- static final class EmptyCameraConfig implements CameraConfig {
+ static final class DefaultCameraConfig implements CameraConfig {
private final Identifier mIdentifier = Identifier.create(new Object());
@NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
index a80242d..4a1e3ce 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
@@ -224,13 +224,18 @@
return new LinkedHashSet<>(Collections.singleton(this));
}
+ /**
+ * Returns the current {@link CameraConfig}.
+ */
@NonNull
@Override
default CameraConfig getExtendedConfig() {
- return CameraConfigs.emptyConfig();
+ return CameraConfigs.defaultConfig();
}
- @Override
+ /**
+ * Sets the {@link CameraConfig} to configure the camera.
+ */
default void setExtendedConfig(@Nullable CameraConfig cameraConfig) {
// Ignore the config since CameraInternal won't use the config
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 877252a..0ed5456 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -138,7 +138,7 @@
// Additional configs to apply onto the UseCases when added to this Camera
@GuardedBy("mLock")
@NonNull
- private CameraConfig mCameraConfig = CameraConfigs.emptyConfig();
+ private final CameraConfig mCameraConfig;
private final Object mLock = new Object();
@@ -166,16 +166,11 @@
private final RestrictedCameraControl mAdapterCameraControl;
@NonNull
private final RestrictedCameraInfo mAdapterCameraInfo;
-
-
/**
* Create a new {@link CameraUseCaseAdapter} instance.
*
* @param cameras The set of cameras that are wrapped, with them in order
- * of preference. The actual camera used will be dependent
- * on configs set by
- * {@link #setExtendedConfig(CameraConfig)} which can
- * filter out specific camera instances
+ * of preference.
* @param cameraCoordinator Camera coordinator that exposes concurrent camera mode.
* @param cameraDeviceSurfaceManager A class that checks for whether a specific camera
* can support the set of Surface with set resolutions.
@@ -186,6 +181,29 @@
@NonNull CameraCoordinator cameraCoordinator,
@NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager,
@NonNull UseCaseConfigFactory useCaseConfigFactory) {
+ this(cameras, cameraCoordinator, cameraDeviceSurfaceManager, useCaseConfigFactory,
+ CameraConfigs.defaultConfig());
+ }
+
+ /**
+ * Create a new {@link CameraUseCaseAdapter} instance.
+ *
+ * @param cameras The set of cameras that are wrapped, with them in order
+ * of preference.
+ * @param cameraCoordinator Camera coordinator that exposes concurrent camera mode.
+ * @param cameraDeviceSurfaceManager A class that checks for whether a specific camera
+ * can support the set of Surface with set resolutions.
+ * @param useCaseConfigFactory UseCase config factory that exposes configuration for
+ * each UseCase.
+ * @param cameraConfig the CameraConfig to configure the {@link CameraInternal}
+ * when attaching the uses cases of this adapter to the
+ * camera.
+ */
+ public CameraUseCaseAdapter(@NonNull LinkedHashSet<CameraInternal> cameras,
+ @NonNull CameraCoordinator cameraCoordinator,
+ @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager,
+ @NonNull UseCaseConfigFactory useCaseConfigFactory,
+ @NonNull CameraConfig cameraConfig) {
mCameraInternal = cameras.iterator().next();
mCameraInternals = new LinkedHashSet<>(cameras);
mId = new CameraId(mCameraInternals);
@@ -198,6 +216,20 @@
mAdapterCameraInfo =
new RestrictedCameraInfo(mCameraInternal.getCameraInfoInternal(),
mAdapterCameraControl);
+
+ mCameraConfig = cameraConfig;
+ SessionProcessor sessionProcessor = mCameraConfig.getSessionProcessor(null);
+ if (sessionProcessor != null) {
+ @CameraOperation Set<Integer> supportedOps =
+ sessionProcessor.getSupportedCameraOperations();
+ mAdapterCameraControl.enableRestrictedOperations(true, supportedOps);
+ } else {
+ mAdapterCameraControl.enableRestrictedOperations(false, null);
+ }
+ mAdapterCameraInfo.setPostviewSupported(
+ mCameraConfig.isPostviewSupported());
+ mAdapterCameraInfo.setCaptureProcessProgressSupported(
+ mCameraConfig.isCaptureProcessProgressSupported());
}
/**
@@ -249,6 +281,8 @@
*/
public void addUseCases(@NonNull Collection<UseCase> appUseCasesToAdd) throws CameraException {
synchronized (mLock) {
+ // Configure the CameraConfig when binding
+ mCameraInternal.setExtendedConfig(mCameraConfig);
Set<UseCase> appUseCases = new LinkedHashSet<>(mAppUseCases);
//TODO(b/266641900): must be LinkedHashSet otherwise ExistingActivityLifecycleTest
// fails due to a camera-pipe integration bug.
@@ -590,6 +624,10 @@
public void attachUseCases() {
synchronized (mLock) {
if (!mAttached) {
+ // Ensure the current opening camera has the right camera config.
+ if (!mCameraUseCases.isEmpty()) {
+ mCameraInternal.setExtendedConfig(mCameraConfig);
+ }
mCameraInternal.attachUseCases(mCameraUseCases);
restoreInteropConfig();
@@ -988,8 +1026,8 @@
return mCameraInternals;
}
- @NonNull
@Override
+ @NonNull
public CameraConfig getExtendedConfig() {
synchronized (mLock) {
return mCameraConfig;
@@ -997,37 +1035,6 @@
}
@Override
- public void setExtendedConfig(@Nullable CameraConfig cameraConfig) {
- synchronized (mLock) {
- if (cameraConfig == null) {
- cameraConfig = CameraConfigs.emptyConfig();
- }
-
- if (!mAppUseCases.isEmpty() && !mCameraConfig.getCompatibilityId().equals(
- cameraConfig.getCompatibilityId())) {
- throw new IllegalStateException(
- "Need to unbind all use cases before binding with extension enabled");
- }
-
- mCameraConfig = cameraConfig;
- SessionProcessor sessionProcessor = mCameraConfig.getSessionProcessor(null);
- if (sessionProcessor != null) {
- @CameraOperation Set<Integer> supportedOps =
- sessionProcessor.getSupportedCameraOperations();
- mAdapterCameraControl.enableRestrictedOperations(true, supportedOps);
- } else {
- mAdapterCameraControl.enableRestrictedOperations(false, null);
- }
- mAdapterCameraInfo.setPostviewSupported(
- mCameraConfig.isPostviewSupported());
- mAdapterCameraInfo.setCaptureProcessProgressSupported(
- mCameraConfig.isCaptureProcessProgressSupported());
- //Configure the CameraInternal as well so that it can get SessionProcessor.
- mCameraInternal.setExtendedConfig(mCameraConfig);
- }
- }
-
- @Override
public boolean isUseCasesCombinationSupported(boolean withStreamSharing,
@NonNull UseCase... useCases) {
Collection<UseCase> useCasesToVerify = Arrays.asList(useCases);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
index 45efcbd..9498690 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
@@ -74,7 +74,7 @@
private final Rational mFullFovRatio;
private final SupportedOutputSizesSorterLegacy mSupportedOutputSizesSorterLegacy;
- SupportedOutputSizesSorter(@NonNull CameraInfoInternal cameraInfoInternal,
+ public SupportedOutputSizesSorter(@NonNull CameraInfoInternal cameraInfoInternal,
@Nullable Size activeArraySize) {
mCameraInfoInternal = cameraInfoInternal;
mSensorOrientation = mCameraInfoInternal.getSensorRotationDegrees();
@@ -120,7 +120,7 @@
* will be sorted according to the legacy resolution API settings and logic.
*/
@NonNull
- List<Size> getSortedSupportedOutputSizes(@NonNull UseCaseConfig<?> useCaseConfig) {
+ public List<Size> getSortedSupportedOutputSizes(@NonNull UseCaseConfig<?> useCaseConfig) {
ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
List<Size> customOrderedResolutions = imageOutputConfig.getCustomOrderedResolutions(null);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/ResolutionsMerger.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/ResolutionsMerger.java
new file mode 100644
index 0000000..f97ebed
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/ResolutionsMerger.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright 2023 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.streamsharing;
+
+
+import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_SUPPORTED_RESOLUTIONS;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3;
+import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
+import static androidx.camera.core.impl.utils.TransformUtils.rectToSize;
+
+import static java.lang.Math.sqrt;
+
+import android.util.Pair;
+import android.util.Rational;
+import android.util.Size;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.VisibleForTesting;
+import androidx.camera.core.Logger;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.CameraInternal;
+import androidx.camera.core.impl.MutableConfig;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
+import androidx.camera.core.internal.SupportedOutputSizesSorter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A class for calculating parent resolutions based on the children's configs.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class ResolutionsMerger {
+
+ private static final String TAG = "ResolutionsMerger";
+ // The width to height ratio that has same area when cropping into 4:3 and 16:9.
+ private static final double SAME_AREA_WIDTH_HEIGHT_RATIO = sqrt(4.0 / 3.0 * 16.0 / 9.0);
+
+ @NonNull
+ private final Rational mSensorAspectRatio;
+ @NonNull
+ private final Rational mFallbackAspectRatio;
+ @NonNull
+ private final Set<UseCaseConfig<?>> mChildrenConfigs;
+ @NonNull
+ private final SupportedOutputSizesSorter mSizeSorter;
+ @NonNull
+ private final List<Size> mCameraSupportedSizes;
+ @NonNull
+ private final Map<UseCaseConfig<?>, List<Size>> mChildSizesCache = new HashMap<>();
+
+ ResolutionsMerger(@NonNull CameraInternal cameraInternal,
+ @NonNull Set<UseCaseConfig<?>> childrenConfigs) {
+ this(rectToSize(cameraInternal.getCameraControlInternal().getSensorRect()),
+ cameraInternal.getCameraInfoInternal(), childrenConfigs);
+ }
+
+ private ResolutionsMerger(@NonNull Size sensorSize, @NonNull CameraInfoInternal cameraInfo,
+ @NonNull Set<UseCaseConfig<?>> childrenConfigs) {
+ this(sensorSize, childrenConfigs, new SupportedOutputSizesSorter(cameraInfo, sensorSize),
+ cameraInfo.getSupportedResolutions(INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE));
+ }
+
+ @VisibleForTesting
+ ResolutionsMerger(@NonNull Size sensorSize, @NonNull Set<UseCaseConfig<?>> childrenConfigs,
+ @NonNull SupportedOutputSizesSorter supportedOutputSizesSorter,
+ @NonNull List<Size> cameraSupportedResolutions) {
+ mSensorAspectRatio = getSensorAspectRatio(sensorSize);
+ mFallbackAspectRatio = getFallbackAspectRatio(mSensorAspectRatio);
+ mChildrenConfigs = childrenConfigs;
+ mSizeSorter = supportedOutputSizesSorter;
+ mCameraSupportedSizes = cameraSupportedResolutions;
+ }
+
+ /**
+ * Returns a list of {@link Surface} resolution sorted by priority.
+ *
+ * <p> This method calculates the resolution for the parent {@link StreamSharing} based on 1)
+ * the supported PRIV resolutions, 2) the sensor size and 3) the children's configs.
+ */
+ @NonNull
+ List<Size> getMergedResolutions(@NonNull MutableConfig parentConfig) {
+ List<Size> candidateSizes = mCameraSupportedSizes;
+
+ // Use parent config's supported resolutions when it is set (e.g. Extensions may have
+ // its limitations on resolutions).
+ List<Pair<Integer, Size[]>> parentSupportedSizesMap =
+ parentConfig.retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, null);
+ if (parentSupportedSizesMap != null) {
+ candidateSizes = getSupportedPrivResolutions(parentSupportedSizesMap);
+ }
+
+ return mergeChildrenResolutions(candidateSizes);
+ }
+
+ /**
+ * Returns the preferred child size with considering parent size and child's configuration.
+ *
+ * <p>Returns the first size in the child's ordered size list that can be cropped from {@code
+ * parentSize} without upscaling it and causing double-cropping, or {@code parentSize} if no
+ * matching is found.
+ *
+ * <p>Notes that the input {@code childConfig} is expected to be one of the values that use to
+ * construct the {@link ResolutionsMerger}, if not an IllegalArgumentException will be thrown.
+ */
+ @NonNull
+ Size getPreferredChildSize(@NonNull Size parentSize, @NonNull UseCaseConfig<?> childConfig) {
+ boolean isParentCropped = !isSensorAspectRatio(parentSize);
+
+ List<Size> candidateChildSizes = getSortedChildSizes(childConfig);
+ for (Size childSize : candidateChildSizes) {
+ // Skip child sizes that need another cropping when parent is already cropped.
+ if (isParentCropped) {
+ boolean needAnotherCropping = !(isFallbackAspectRatio(parentSize)
+ && isFallbackAspectRatio(childSize));
+ if (needAnotherCropping) {
+ continue;
+ }
+ }
+
+ if (!hasUpscaling(childSize, parentSize)) {
+ return childSize;
+ }
+ }
+
+ return parentSize;
+ }
+
+ @NonNull
+ private List<Size> mergeChildrenResolutions(@NonNull List<Size> candidateParentResolutions) {
+ // The following sequence of parent resolution selection is used to prevent double-cropping
+ // from happening:
+ // 1. Add sensor aspect-ratio resolutions, which will not cause double-cropping when the
+ // child resolution is in any aspect-ratio. This is to provide parent resolution that can
+ // be accepted by children in general cases.
+ // 2. Add fallback aspect-ratio resolutions, which will not cause double-cropping only when
+ // the child resolution is in fallback aspect-ratio.
+ List<Size> result = new ArrayList<>();
+
+ // Add resolutions for sensor aspect-ratio.
+ if (needToAddSensorResolutions()) {
+ result.addAll(mergeChildrenResolutionsByAspectRatio(mSensorAspectRatio,
+ candidateParentResolutions));
+ }
+
+ // Add resolutions for fallback aspect-ratio if needed.
+ if (needToAddFallbackResolutions()) {
+ result.addAll(mergeChildrenResolutionsByAspectRatio(mFallbackAspectRatio,
+ candidateParentResolutions));
+ }
+
+ // TODO(b/315098647): When the resulting parent resolution list is empty, consider adding
+ // resolutions that are neither 4:3 nor 16:9, but have a high overlap area (e.g. 80%)
+ // compared to the sensor size, which do not cause severe reduction of FOV, to prevent
+ // binding failures in some edge cases.
+
+ Logger.d(TAG, "Parent resolutions: " + result);
+
+ return result;
+ }
+
+ private List<Size> mergeChildrenResolutionsByAspectRatio(@NonNull Rational aspectRatio,
+ @NonNull List<Size> candidateParentResolutions) {
+ List<Size> candidates = filterResolutionsByAspectRatio(aspectRatio,
+ candidateParentResolutions);
+ sortInDescendingOrder(candidates);
+
+ // Filter resolutions that are too small and track resolutions that might be too large.
+ Set<Size> sizesTooLarge = new HashSet<>(candidates);
+ for (UseCaseConfig<?> childConfig : mChildrenConfigs) {
+ List<Size> childSizes = getSortedChildSizes(childConfig);
+ candidates = filterOutParentSizeThatIsTooSmall(childSizes, candidates);
+ sizesTooLarge.retainAll(getParentSizesThatAreTooLarge(childSizes, candidates));
+ }
+
+ // Filter out sizes that are too large.
+ List<Size> result = new ArrayList<>();
+ for (Size candidate : candidates) {
+ if (!sizesTooLarge.contains(candidate)) {
+ result.add(candidate);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets child sizes sorted by {@link SupportedOutputSizesSorter}.
+ *
+ * <p>Notes that the input {@code childConfig} is expected to be one of the values that use to
+ * construct the {@link ResolutionsMerger}, if not an IllegalArgumentException will be thrown.
+ *
+ * <p>When {@link SupportedOutputSizesSorter#getSortedSupportedOutputSizes(UseCaseConfig)}
+ * returns an empty list, which means no child required resolution is supported, an
+ * IllegalArgumentException will also be thrown.
+ */
+ @NonNull
+ private List<Size> getSortedChildSizes(@NonNull UseCaseConfig<?> childConfig) {
+ if (!mChildrenConfigs.contains(childConfig)) {
+ throw new IllegalArgumentException("Invalid child config: " + childConfig);
+ }
+
+ // Since getSortedSupportedOutputSizes() might be time consuming, use caching to improve
+ // the performance.
+ if (mChildSizesCache.containsKey(childConfig)) {
+ return Objects.requireNonNull(mChildSizesCache.get(childConfig));
+ }
+
+ List<Size> childSizes = mSizeSorter.getSortedSupportedOutputSizes(childConfig);
+ if (childSizes.isEmpty()) {
+ throw new IllegalArgumentException(
+ "No supported resolution for child config: " + childConfig);
+ }
+ mChildSizesCache.put(childConfig, childSizes);
+
+ return childSizes;
+ }
+
+ private boolean needToAddSensorResolutions() {
+ // Need to add sensor resolutions if any required resolution is not fallback aspect-ratio.
+ for (Size size : getChildrenRequiredResolutions()) {
+ if (!hasMatchingAspectRatio(size, mFallbackAspectRatio)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean needToAddFallbackResolutions() {
+ // Need to add fallback resolutions if any required resolution is fallback aspect-ratio.
+ for (Size size : getChildrenRequiredResolutions()) {
+ if (hasMatchingAspectRatio(size, mFallbackAspectRatio)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @NonNull
+ private Set<Size> getChildrenRequiredResolutions() {
+ Set<Size> result = new HashSet<>();
+ for (UseCaseConfig<?> childConfig : mChildrenConfigs) {
+ List<Size> childSizes = getSortedChildSizes(childConfig);
+ result.addAll(childSizes);
+ }
+
+ return result;
+ }
+
+ private boolean isSensorAspectRatio(@NonNull Size size) {
+ return hasMatchingAspectRatio(size, mSensorAspectRatio);
+ }
+
+ private boolean isFallbackAspectRatio(@NonNull Size size) {
+ return hasMatchingAspectRatio(size, mFallbackAspectRatio);
+ }
+
+ @NonNull
+ private static List<Size> getSupportedPrivResolutions(
+ @NonNull List<Pair<Integer, Size[]>> supportedResolutionsMap) {
+ for (Pair<Integer, Size[]> pair : supportedResolutionsMap) {
+ if (pair.first.equals(INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE)) {
+ return Arrays.asList(pair.second);
+ }
+ }
+
+ return new ArrayList<>();
+ }
+
+ /**
+ * Returns the aspect-ratio of 4:3 or 16:9 that is closer to the sensor size.
+ *
+ * <p>Parent resolutions with sensor aspect-ratio are considered to be non-cropped, so child
+ * resolution can have a different aspect-ratio than the parents without causing
+ * double-cropping.
+ */
+ @NonNull
+ private static Rational getSensorAspectRatio(@NonNull Size sensorSize) {
+ Rational result = findCloserAspectRatio(sensorSize);
+ Logger.d(TAG, "The closer aspect ratio to the sensor size (" + sensorSize + ") is "
+ + result + ".");
+
+ return result;
+ }
+
+ @NonNull
+ private static Rational findCloserAspectRatio(@NonNull Size size) {
+ double widthHeightRatio = size.getWidth() / (double) size.getHeight();
+
+ if (widthHeightRatio > SAME_AREA_WIDTH_HEIGHT_RATIO) {
+ return ASPECT_RATIO_16_9;
+ } else {
+ return ASPECT_RATIO_4_3;
+ }
+ }
+
+ /**
+ * Returns the aspect-ratio of 4:3 or 16:9 that is not the sensor aspect-ratio.
+ *
+ * <p>Parent resolutions with fallback aspect-ratio are considered to be cropped, so child
+ * resolution should not different to the parent or double-cropping will happen.
+ */
+ @NonNull
+ private static Rational getFallbackAspectRatio(@NonNull Rational sensorAspectRatio) {
+ if (sensorAspectRatio.equals(ASPECT_RATIO_4_3)) {
+ return ASPECT_RATIO_16_9;
+ } else if (sensorAspectRatio.equals(ASPECT_RATIO_16_9)) {
+ return ASPECT_RATIO_4_3;
+ } else {
+ throw new IllegalArgumentException("Invalid sensor aspect-ratio: " + sensorAspectRatio);
+ }
+ }
+
+ /**
+ * Sorts the input resolutions in descending order.
+ */
+ @VisibleForTesting
+ static void sortInDescendingOrder(@NonNull List<Size> resolutions) {
+ Collections.sort(resolutions, new CompareSizesByArea(true));
+ }
+
+ /**
+ * Returns a list of resolution that all resolutions are with the input aspect-ratio.
+ *
+ * <p>The order of the {@code resolutionsToFilter} will be preserved in the resulting list.
+ */
+ @VisibleForTesting
+ @NonNull
+ static List<Size> filterResolutionsByAspectRatio(@NonNull Rational aspectRatio,
+ @NonNull List<Size> resolutionsToFilter) {
+ List<Size> result = new ArrayList<>();
+ for (Size resolution : resolutionsToFilter) {
+ if (hasMatchingAspectRatio(resolution, aspectRatio)) {
+ result.add(resolution);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Filters out the parent size that is too small with consider target children sizes.
+ *
+ * <p>A size is too small if it cannot find any child size that can be cropped out without
+ * upscaling.
+ *
+ * <p>The order of the {@code sortedParentSizes} will be preserved in the resulting list.
+ *
+ * <p>Assuming {@code sortedParentSizes} is sorted in descending order and all sizes have same
+ * aspect-ratio.
+ */
+ @VisibleForTesting
+ @NonNull
+ static List<Size> filterOutParentSizeThatIsTooSmall(
+ @NonNull Collection<Size> childSizes, @NonNull List<Size> sortedParentSizes) {
+ int n = sortedParentSizes.size();
+
+ // Find the smallest parent size that can be cropped to at least one child size without
+ // upscaling by using binary search.
+ int lo = 0;
+ int hi = n - 1;
+ while (lo < hi) {
+ int mid = lo + (hi - lo + 1) / 2;
+ Size parentSize = sortedParentSizes.get(mid);
+ if (isAnyChildSizeCanBeCroppedOutWithoutUpscalingParent(childSizes, parentSize)) {
+ lo = mid;
+ } else {
+ hi = mid - 1;
+ }
+ }
+
+ // Add all parent sizes that can be cropped to at least one child size.
+ List<Size> result = new ArrayList<>();
+ for (int i = 0; i <= lo; i++) {
+ result.add(sortedParentSizes.get(i));
+ }
+
+ return result;
+ }
+
+ private static boolean isAnyChildSizeCanBeCroppedOutWithoutUpscalingParent(
+ @NonNull Collection<Size> childSizes, @NonNull Size parentSize) {
+ for (Size childSize : childSizes) {
+ if (!hasUpscaling(childSize, parentSize)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns resolutions that are too large with consider target children sizes.
+ *
+ * <p>A size is too large if there is another size smaller than that size and all children
+ * sizes can be cropped from that other size without upscaling.
+ *
+ * <p>The order of the {@code sortedParentSizes} will be preserved in the resulting list.
+ *
+ * <p>Assuming {@code sortedParentSizes} is sorted in descending order and all sizes have same
+ * aspect-ratio.
+ */
+ @VisibleForTesting
+ @NonNull
+ static List<Size> getParentSizesThatAreTooLarge(@NonNull Collection<Size> childSizes,
+ @NonNull List<Size> sortedParentSizes) {
+ int n = sortedParentSizes.size();
+
+ // Find the smallest parent size that can be cropped to all child sizes without upscaling
+ // by using binary search.
+ int lo = 0;
+ int hi = n - 1;
+ while (lo < hi) {
+ int mid = lo + (hi - lo + 1) / 2;
+ Size parentSize = sortedParentSizes.get(mid);
+ if (isAllChildSizesCanBeCroppedOutWithoutUpscalingParent(childSizes, parentSize)) {
+ lo = mid;
+ } else {
+ hi = mid - 1;
+ }
+ }
+
+ // Add all parent sizes that can be cropped to all child sizes, except the smallest one.
+ List<Size> result = new ArrayList<>();
+ for (int i = 0; i < lo; i++) {
+ result.add(sortedParentSizes.get(i));
+ }
+
+ return result;
+ }
+
+ private static boolean isAllChildSizesCanBeCroppedOutWithoutUpscalingParent(
+ @NonNull Collection<Size> childSizes, @NonNull Size parentSize) {
+ for (Size childSize : childSizes) {
+ if (hasUpscaling(childSize, parentSize)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Whether the parent size needs upscaling to fill the child size.
+ */
+ @VisibleForTesting
+ static boolean hasUpscaling(@NonNull Size childSize, @NonNull Size parentSize) {
+ // Upscaling is needed if child size is larger than the parent.
+ return childSize.getHeight() > parentSize.getHeight()
+ || childSize.getWidth() > parentSize.getWidth();
+ }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
index 265a359..f04f6e7 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
@@ -33,13 +33,10 @@
import androidx.camera.core.CameraEffect.VIDEO_CAPTURE
import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
-import androidx.camera.core.impl.CameraConfig
import androidx.camera.core.impl.CameraFactory
import androidx.camera.core.impl.CaptureConfig
-import androidx.camera.core.impl.Identifier
import androidx.camera.core.impl.ImageCaptureConfig
import androidx.camera.core.impl.MutableOptionsBundle
-import androidx.camera.core.impl.OptionsBundle
import androidx.camera.core.impl.SessionConfig
import androidx.camera.core.impl.SessionProcessor
import androidx.camera.core.impl.StreamSpec
@@ -554,18 +551,16 @@
cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
ApplicationProvider.getApplicationContext(),
- CameraSelector.DEFAULT_BACK_CAMERA
- )
-
- val cameraConfig = FakeCameraConfig(
- sessionProcessor = FakeSessionProcessor(
- postviewSupportedSizes = mapOf(
- ImageFormat.JPEG to
- listOf(Size(1920, 1080), Size(640, 480)))
+ CameraSelector.DEFAULT_BACK_CAMERA,
+ FakeCameraConfig(
+ sessionProcessor = FakeSessionProcessor(
+ postviewSupportedSizes = mapOf(
+ ImageFormat.JPEG to
+ listOf(Size(1920, 1080), Size(640, 480))
+ )
+ )
)
)
-
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
cameraUseCaseAdapter.addUseCases(listOf(imageCapture))
assertThat(imageCapture.imagePipeline!!.postviewSize).isEqualTo(Size(1920, 1080))
}
@@ -584,18 +579,16 @@
cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
ApplicationProvider.getApplicationContext(),
- CameraSelector.DEFAULT_BACK_CAMERA
- )
-
- val cameraConfig = FakeCameraConfig(
- sessionProcessor = FakeSessionProcessor(
- postviewSupportedSizes = mapOf(
- ImageFormat.JPEG to
- listOf(Size(4000, 3000), Size(1920, 1080)))
+ CameraSelector.DEFAULT_BACK_CAMERA,
+ FakeCameraConfig(
+ sessionProcessor = FakeSessionProcessor(
+ postviewSupportedSizes = mapOf(
+ ImageFormat.JPEG to
+ listOf(Size(4000, 3000), Size(1920, 1080)))
+ )
)
)
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
cameraUseCaseAdapter.addUseCases(listOf(imageCapture))
assertThat(imageCapture.imagePipeline!!.postviewSize).isEqualTo(Size(1920, 1080))
}
@@ -612,17 +605,15 @@
cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
ApplicationProvider.getApplicationContext(),
- CameraSelector.DEFAULT_BACK_CAMERA
- )
-
- val cameraConfig = FakeCameraConfig(
- sessionProcessor = FakeSessionProcessor(
- postviewSupportedSizes = mapOf(
- ImageFormat.JPEG to listOf(Size(1920, 1080)))
+ CameraSelector.DEFAULT_BACK_CAMERA,
+ FakeCameraConfig(
+ sessionProcessor = FakeSessionProcessor(
+ postviewSupportedSizes = mapOf(
+ ImageFormat.JPEG to listOf(Size(1920, 1080)))
+ )
)
)
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
// the CameraException will be converted to IllegalArgumentException in camera-lifecycle.
assertThrows(CameraUseCaseAdapter.CameraException::class.java) {
cameraUseCaseAdapter.addUseCases(listOf(imageCapture))
@@ -645,34 +636,14 @@
imageReaderProxyProvider,
)
+ val cameraConfig = FakeCameraConfig(sessionProcessor = sessionProcessor)
cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
ApplicationProvider.getApplicationContext(),
- cameraSelector
+ cameraSelector,
+ cameraConfig
)
cameraUseCaseAdapter.setViewPort(viewPort)
- if (sessionProcessor != null) {
- cameraUseCaseAdapter.setExtendedConfig(object : CameraConfig {
- override fun getConfig(): androidx.camera.core.impl.Config {
- return OptionsBundle.emptyBundle()
- }
-
- override fun getSessionProcessor(
- valueIfMissing: SessionProcessor?
- ): SessionProcessor {
- return sessionProcessor
- }
-
- override fun getSessionProcessor(): SessionProcessor {
- return sessionProcessor
- }
-
- override fun getCompatibilityId(): Identifier {
- return Identifier.create(Any())
- }
- })
- }
-
cameraUseCaseAdapter.addUseCases(Collections.singleton<UseCase>(imageCapture))
return imageCapture
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStoreTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStoreTest.kt
index 27291ae..07418da 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStoreTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStoreTest.kt
@@ -33,7 +33,7 @@
public fun canRetrieveStoredCameraConfigProvider() {
val id = Object()
val cameraConfigProvider =
- CameraConfigProvider { _, _ -> CameraConfigs.emptyConfig() }
+ CameraConfigProvider { _, _ -> CameraConfigs.defaultConfig() }
ExtendedCameraConfigProviderStore.addConfig(id, cameraConfigProvider)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
index 7b3c239..acda8c7 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.kt
@@ -60,6 +60,7 @@
import androidx.camera.testing.fakes.FakeCamera
import androidx.camera.testing.fakes.FakeCameraControl
import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.impl.fakes.FakeCameraConfig
import androidx.camera.testing.impl.fakes.FakeCameraCoordinator
import androidx.camera.testing.impl.fakes.FakeCameraDeviceSurfaceManager
import androidx.camera.testing.impl.fakes.FakeSessionProcessor
@@ -201,6 +202,68 @@
assertThat(fakeCamera.attachedUseCases).isEmpty()
}
+ @Test
+ fun addUseCases_cameraConfigIsConfigured() {
+ // Arrange: Prepare two sets of CameraConfig and CameraUseCaseAdapter.
+ val cameraConfig = FakeCameraConfig()
+ val adapter = CameraUseCaseAdapter(
+ fakeCameraSet,
+ cameraCoordinator,
+ fakeCameraDeviceSurfaceManager,
+ useCaseConfigFactory,
+ cameraConfig
+ )
+ val cameraInternal = fakeCameraSet.iterator().next();
+
+ // Act: Add use cases.
+ adapter.addUseCases(setOf(preview, video, image))
+
+ // Assert: CameraConfig is configured to the underlying CameraInternal.
+ assertThat(cameraInternal.extendedConfig).isSameInstanceAs(cameraConfig)
+ }
+
+ @Test
+ fun attachUseCases_cameraConfigIsConfigured() {
+ // Arrange: Prepare two sets of CameraConfig and CameraUseCaseAdapter.
+ val cameraConfig1 = FakeCameraConfig()
+ val adapter1 = CameraUseCaseAdapter(
+ fakeCameraSet,
+ cameraCoordinator,
+ fakeCameraDeviceSurfaceManager,
+ useCaseConfigFactory,
+ cameraConfig1
+ )
+ val cameraConfig2 = FakeCameraConfig()
+ val adapter2 = CameraUseCaseAdapter(
+ fakeCameraSet,
+ cameraCoordinator,
+ fakeCameraDeviceSurfaceManager,
+ useCaseConfigFactory,
+ cameraConfig2,
+ )
+ val cameraInternal = fakeCameraSet.iterator().next();
+ val preview2 = Preview.Builder().build()
+ val video2 = createFakeVideoCaptureUseCase()
+ val image2 = ImageCapture.Builder().build()
+
+ // Act: bind adapter1 and attach to camera, bind adapter 2 and attach to camera, and then
+ // switch back to adapter1 to attach to camera (without unbinding).
+ adapter1.detachUseCases()
+ adapter1.addUseCases(setOf(preview, video, image))
+ adapter1.attachUseCases()
+
+ adapter1.detachUseCases()
+ adapter2.detachUseCases()
+ adapter2.addUseCases(setOf(preview2, video2, image2))
+ adapter2.attachUseCases()
+
+ adapter2.detachUseCases()
+ adapter1.attachUseCases()
+
+ // Assert: CameraConfig1 is configured because adapter1 is active (attached to camera)
+ assertThat(cameraInternal.extendedConfig).isSameInstanceAs(cameraConfig1)
+ }
+
@RequiresApi(33) // 10-bit HDR only supported on API 33+
@Test
fun canUseHdrWithoutExtensions() {
@@ -218,7 +281,14 @@
fun useHDRWithExtensions_throwsException() {
// Arrange: enable extensions.
val extensionsConfig = createCoexistingRequiredRuleCameraConfig(FakeSessionProcessor())
- adapter.setExtendedConfig(extensionsConfig)
+
+ val adapter = CameraUseCaseAdapter(
+ fakeCameraSet,
+ cameraCoordinator,
+ fakeCameraDeviceSurfaceManager,
+ useCaseConfigFactory,
+ extensionsConfig,
+ )
// Act: add UseCase that uses HDR.
val hdrUseCase = FakeUseCaseConfig.Builder().setDynamicRange(HDR_UNSPECIFIED_10_BIT).build()
adapter.addUseCases(setOf(hdrUseCase))
@@ -372,7 +442,13 @@
fun extensionEnabledAndVideoCaptureExisted_streamSharingOn() {
// Arrange: enable extensions.
val extensionsConfig = createCoexistingRequiredRuleCameraConfig(FakeSessionProcessor())
- adapter.setExtendedConfig(extensionsConfig)
+ val adapter = CameraUseCaseAdapter(
+ fakeCameraSet,
+ cameraCoordinator,
+ fakeCameraDeviceSurfaceManager,
+ useCaseConfigFactory,
+ extensionsConfig,
+ )
// Act: add UseCases that require StreamSharing.
adapter.addUseCases(setOf(preview, video, image))
// Assert: StreamSharing exists and bound.
@@ -389,7 +465,13 @@
fun extensionEnabledAndOnlyVideoCaptureAttached_streamSharingOn() {
// Arrange: enable extensions.
val extensionsConfig = createCoexistingRequiredRuleCameraConfig(FakeSessionProcessor())
- adapter.setExtendedConfig(extensionsConfig)
+ val adapter = CameraUseCaseAdapter(
+ fakeCameraSet,
+ cameraCoordinator,
+ fakeCameraDeviceSurfaceManager,
+ useCaseConfigFactory,
+ extensionsConfig,
+ )
// Act: add UseCases that require StreamSharing.
adapter.addUseCases(setOf(video))
// Assert: StreamSharing exists and bound.
@@ -704,80 +786,14 @@
}
@Test
- fun canSetExtendedCameraConfig_whenNoUseCase() {
- val cameraUseCaseAdapter = CameraUseCaseAdapter(
- fakeCameraSet,
- cameraCoordinator,
- fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
- )
- cameraUseCaseAdapter.setExtendedConfig(FakeCameraConfig())
- }
-
- @Test(expected = IllegalStateException::class)
- fun canNotSetExtendedCameraConfig_whenUseCaseHasExisted() {
- val cameraUseCaseAdapter = CameraUseCaseAdapter(
- fakeCameraSet,
- cameraCoordinator,
- fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
- )
-
- // Adds use case first
- cameraUseCaseAdapter.addUseCases(listOf(FakeUseCase()))
-
- // Sets extended config after a use case is added
- cameraUseCaseAdapter.setExtendedConfig(FakeCameraConfig())
- }
-
- @Test
- fun canSetSameExtendedCameraConfig_whenUseCaseHasExisted() {
- val cameraUseCaseAdapter = CameraUseCaseAdapter(
- fakeCameraSet,
- cameraCoordinator,
- fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
- )
- val cameraConfig: CameraConfig = FakeCameraConfig()
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
- cameraUseCaseAdapter.addUseCases(listOf(FakeUseCase()))
-
- // Sets extended config with the same camera config
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
- }
-
- @Test
- fun canSwitchExtendedCameraConfig_afterUnbindUseCases() {
- val cameraUseCaseAdapter = CameraUseCaseAdapter(
- fakeCameraSet,
- cameraCoordinator,
- fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
- )
- val cameraConfig1: CameraConfig = FakeCameraConfig()
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig1)
-
- // Binds use case
- val fakeUseCase = FakeUseCase()
- cameraUseCaseAdapter.addUseCases(listOf(fakeUseCase))
-
- // Unbinds use case
- cameraUseCaseAdapter.removeUseCases(listOf(fakeUseCase))
-
- // Sets extended config with different camera config
- val cameraConfig2: CameraConfig = FakeCameraConfig()
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig2)
- }
-
- @Test
fun noExtraUseCase_whenBindEmptyUseCaseList() {
val cameraUseCaseAdapter = CameraUseCaseAdapter(
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ createCoexistingRequiredRuleCameraConfig()
)
- cameraUseCaseAdapter.setExtendedConfig(createCoexistingRequiredRuleCameraConfig())
cameraUseCaseAdapter.addUseCases(emptyList())
val useCases = cameraUseCaseAdapter.useCases
assertThat(useCases.size).isEqualTo(0)
@@ -789,9 +805,9 @@
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ createCoexistingRequiredRuleCameraConfig()
)
- cameraUseCaseAdapter.setExtendedConfig(createCoexistingRequiredRuleCameraConfig())
val preview = Preview.Builder().build()
// Adds a Preview only
@@ -807,16 +823,16 @@
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ createCoexistingRequiredRuleCameraConfig()
)
- cameraUseCaseAdapter.setExtendedConfig(createCoexistingRequiredRuleCameraConfig())
val preview = Preview.Builder().build()
// Adds a Preview only
cameraUseCaseAdapter.addUseCases(listOf(preview))
// Checks whether an extra ImageCapture is added.
- assertThat(containsImageCapture(cameraUseCaseAdapter.cameraUseCases))
+ assertThat(containsImageCapture(cameraUseCaseAdapter.cameraUseCases)).isTrue()
val imageCapture = ImageCapture.Builder().build()
// Adds an ImageCapture
@@ -832,9 +848,9 @@
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ createCoexistingRequiredRuleCameraConfig()
)
- cameraUseCaseAdapter.setExtendedConfig(createCoexistingRequiredRuleCameraConfig())
val useCases = mutableListOf<UseCase>()
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder().build()
@@ -860,9 +876,9 @@
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ createCoexistingRequiredRuleCameraConfig()
)
- cameraUseCaseAdapter.setExtendedConfig(createCoexistingRequiredRuleCameraConfig())
val imageCapture = ImageCapture.Builder().build()
// Adds an ImageCapture only
@@ -878,16 +894,16 @@
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ createCoexistingRequiredRuleCameraConfig()
)
- cameraUseCaseAdapter.setExtendedConfig(createCoexistingRequiredRuleCameraConfig())
val imageCapture = ImageCapture.Builder().build()
// Adds a ImageCapture only
cameraUseCaseAdapter.addUseCases(listOf(imageCapture))
// Checks whether an extra Preview is added.
- assertThat(containsPreview(cameraUseCaseAdapter.cameraUseCases))
+ assertThat(containsPreview(cameraUseCaseAdapter.cameraUseCases)).isTrue()
val preview = Preview.Builder().build()
// Adds an Preview
@@ -902,9 +918,9 @@
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ createCoexistingRequiredRuleCameraConfig()
)
- cameraUseCaseAdapter.setExtendedConfig(createCoexistingRequiredRuleCameraConfig())
val useCases = mutableListOf<UseCase>()
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder().build()
@@ -930,9 +946,9 @@
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ createCoexistingRequiredRuleCameraConfig()
)
- cameraUseCaseAdapter.setExtendedConfig(createCoexistingRequiredRuleCameraConfig())
val useCases = mutableListOf<UseCase>()
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder().build()
@@ -1050,18 +1066,19 @@
private fun createAdapterWithSupportedCameraOperations(
@RestrictedCameraControl.CameraOperation supportedOps: Set<Int>
): CameraUseCaseAdapter {
- val cameraUseCaseAdapter = CameraUseCaseAdapter(
- fakeCameraSet,
- cameraCoordinator,
- fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
- )
-
val fakeSessionProcessor = FakeSessionProcessor()
// no camera operations are supported.
fakeSessionProcessor.restrictedCameraOperations = supportedOps
val cameraConfig: CameraConfig = FakeCameraConfig(fakeSessionProcessor)
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
+
+ val cameraUseCaseAdapter = CameraUseCaseAdapter(
+ fakeCameraSet,
+ cameraCoordinator,
+ fakeCameraDeviceSurfaceManager,
+ useCaseConfigFactory,
+ cameraConfig
+ )
+
return cameraUseCaseAdapter
}
@@ -1374,39 +1391,33 @@
@Test
fun cameraInfo_postviewSupported(): Unit = runBlocking {
// 1. Arrange
+ val cameraConfig: CameraConfig = FakeCameraConfig(postviewSupported = true)
val cameraUseCaseAdapter = CameraUseCaseAdapter(
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ cameraConfig
)
val cameraInfoInternal = cameraUseCaseAdapter.cameraInfo as CameraInfoInternal
- assertThat(cameraInfoInternal.isPostviewSupported).isFalse()
- // 2. Act
- val cameraConfig: CameraConfig = FakeCameraConfig(postviewSupported = true)
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
-
- // 3. Assert
+ // 2. Act && Assert
assertThat(cameraInfoInternal.isPostviewSupported).isTrue()
}
@Test
fun cameraInfo_captureProcessProgressSupported(): Unit = runBlocking {
// 1. Arrange
+ val cameraConfig: CameraConfig = FakeCameraConfig(captureProcessProgressSupported = true)
val cameraUseCaseAdapter = CameraUseCaseAdapter(
fakeCameraSet,
cameraCoordinator,
fakeCameraDeviceSurfaceManager,
- useCaseConfigFactory
+ useCaseConfigFactory,
+ cameraConfig
)
val cameraInfoInternal = cameraUseCaseAdapter.cameraInfo as CameraInfoInternal
- assertThat(cameraInfoInternal.isCaptureProcessProgressSupported).isFalse()
- // 2. Act
- val cameraConfig: CameraConfig = FakeCameraConfig(captureProcessProgressSupported = true)
- cameraUseCaseAdapter.setExtendedConfig(cameraConfig)
-
- // 3. Assert
+ // 2. Act && Assert
assertThat(cameraInfoInternal.isCaptureProcessProgressSupported).isTrue()
}
@@ -1468,42 +1479,4 @@
}
return false
}
-
- private class FakeCameraConfig(
- val sessionProcessor: FakeSessionProcessor? = null,
- val postviewSupported: Boolean = false,
- val captureProcessProgressSupported: Boolean = false
- ) : CameraConfig {
- private val mUseCaseConfigFactory =
- UseCaseConfigFactory { _, _ -> null }
- private val mIdentifier = Identifier.create(Any())
-
- override fun getUseCaseConfigFactory(): UseCaseConfigFactory {
- return mUseCaseConfigFactory
- }
-
- override fun isPostviewSupported(): Boolean {
- return postviewSupported
- }
-
- override fun isCaptureProcessProgressSupported(): Boolean {
- return captureProcessProgressSupported
- }
-
- override fun getCompatibilityId(): Identifier {
- return mIdentifier
- }
-
- override fun getConfig(): Config {
- return OptionsBundle.emptyBundle()
- }
-
- override fun getSessionProcessor(valueIfMissing: SessionProcessor?): SessionProcessor? {
- return sessionProcessor ?: valueIfMissing
- }
-
- override fun getSessionProcessor(): SessionProcessor {
- return sessionProcessor!!
- }
- }
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/ResolutionsMergerTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/ResolutionsMergerTest.kt
new file mode 100644
index 0000000..6caf41a
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/ResolutionsMergerTest.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2023 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.streamsharing
+
+import android.os.Build
+import android.util.Size
+import androidx.camera.core.impl.MutableOptionsBundle
+import androidx.camera.core.impl.UseCaseConfig
+import androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9
+import androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3
+import androidx.camera.core.internal.SupportedOutputSizesSorter
+import androidx.camera.core.streamsharing.ResolutionsMerger.filterOutParentSizeThatIsTooSmall
+import androidx.camera.core.streamsharing.ResolutionsMerger.filterResolutionsByAspectRatio
+import androidx.camera.core.streamsharing.ResolutionsMerger.getParentSizesThatAreTooLarge
+import androidx.camera.core.streamsharing.ResolutionsMerger.hasUpscaling
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.impl.fakes.FakeUseCaseConfig
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+/**
+ * Unit tests for [ResolutionsMerger].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class ResolutionsMergerTest {
+
+ @Test(expected = IllegalArgumentException::class)
+ fun getMergedResolutions_whenNoSupportedChildSize_throwsException() {
+ // Arrange.
+ val sensorSize = SIZE_3264_2448 // 4:3
+ val config1 = createUseCaseConfig()
+ val config2 = createUseCaseConfig()
+ val childConfigs = setOf(config1, config2)
+ val candidateChildSizes1 = listOf(SIZE_1920_1080, SIZE_1280_720) // 16:9
+ val candidateChildSizes2 = emptyList<Size>() // no supported size
+ val sorter = FakeSupportedOutputSizesSorter(
+ mapOf(
+ config1 to candidateChildSizes1,
+ config2 to candidateChildSizes2
+ )
+ )
+ val merger = ResolutionsMerger(sensorSize, childConfigs, sorter, CAMERA_SUPPORTED_SIZES)
+
+ // Act & Assert.
+ val parentConfig = MutableOptionsBundle.create()
+ merger.getMergedResolutions(parentConfig)
+ }
+
+ @Test
+ fun getMergedResolutions_whenChildRequiresSensorAndNonSensorAspectRatio_canReturnCorrectly() {
+ // Arrange.
+ val sensorSize = SIZE_3264_2448 // 4:3
+ val config1 = createUseCaseConfig()
+ val config2 = createUseCaseConfig()
+ val childConfigs = setOf(config1, config2)
+ val candidateChildSizes1 = listOf(SIZE_1920_1080, SIZE_1280_720) // 16:9
+ val candidateChildSizes2 = listOf(SIZE_1280_960, SIZE_960_720, SIZE_640_480) // 4:3
+ val sorter = FakeSupportedOutputSizesSorter(
+ mapOf(
+ config1 to candidateChildSizes1,
+ config2 to candidateChildSizes2
+ )
+ )
+ val merger = ResolutionsMerger(sensorSize, childConfigs, sorter, CAMERA_SUPPORTED_SIZES)
+
+ // Act & Assert, should returns a list that concatenates 4:3 resolutions before 16:9
+ // resolutions and removes resolutions that are too large (no need for multiple resolutions
+ // that can be cropped to all child sizes) and too small (causing upscaling).
+ val parentConfig = MutableOptionsBundle.create()
+ assertThat(merger.getMergedResolutions(parentConfig)).containsExactly(
+ SIZE_1920_1440, SIZE_1280_960, SIZE_1920_1080, SIZE_1280_720
+ ).inOrder()
+ }
+
+ @Test
+ fun getMergedResolutions_whenChildRequiresOnlySensorAspectRatio_canReturnCorrectly() {
+ // Arrange.
+ val sensorSize = SIZE_3264_2448 // 4:3
+ val config1 = createUseCaseConfig()
+ val config2 = createUseCaseConfig()
+ val childConfigs = setOf(config1, config2)
+ val candidateChildSizes1 = listOf(SIZE_2560_1920, SIZE_1920_1440) // 4:3
+ val candidateChildSizes2 = listOf(SIZE_1280_960, SIZE_960_720) // 4:3
+ val sorter = FakeSupportedOutputSizesSorter(
+ mapOf(
+ config1 to candidateChildSizes1,
+ config2 to candidateChildSizes2
+ )
+ )
+ val merger = ResolutionsMerger(sensorSize, childConfigs, sorter, CAMERA_SUPPORTED_SIZES)
+
+ // Act & Assert, should returns a list of 4:3 resolutions and removes resolutions that are
+ // too large and too small.
+ val parentConfig = MutableOptionsBundle.create()
+ assertThat(merger.getMergedResolutions(parentConfig)).containsExactly(
+ SIZE_2560_1920, SIZE_1920_1440
+ ).inOrder()
+ }
+
+ @Test
+ fun getMergedResolutions_whenChildRequiresOnlyNonSensorAspectRatio_canReturnCorrectly() {
+ // Arrange.
+ val sensorSize = SIZE_3264_2448 // 4:3
+ val config1 = createUseCaseConfig()
+ val config2 = createUseCaseConfig()
+ val childConfigs = setOf(config1, config2)
+ val candidateChildSizes1 = listOf(SIZE_2560_1440, SIZE_1280_720) // 16:9
+ val candidateChildSizes2 = listOf(SIZE_1920_1080, SIZE_960_540) // 16:9
+ val sorter = FakeSupportedOutputSizesSorter(
+ mapOf(
+ config1 to candidateChildSizes1,
+ config2 to candidateChildSizes2
+ )
+ )
+ val merger = ResolutionsMerger(sensorSize, childConfigs, sorter, CAMERA_SUPPORTED_SIZES)
+
+ // Act & Assert, should returns a list of 16:9 resolutions and removes resolutions that are
+ // too large and too small.
+ val parentConfig = MutableOptionsBundle.create()
+ assertThat(merger.getMergedResolutions(parentConfig)).containsExactly(
+ SIZE_2560_1440, SIZE_1920_1080, SIZE_1280_720
+ ).inOrder()
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun getPreferredChildSize_withConfigNotPassedToConstructor_throwsException() {
+ // Arrange.
+ val config = createUseCaseConfig()
+ val sorter = FakeSupportedOutputSizesSorter(mapOf(config to SIZES_16_9))
+ val merger = ResolutionsMerger(SENSOR_SIZE, setOf(config), sorter, CAMERA_SUPPORTED_SIZES)
+
+ // Act.
+ val useCaseConfigNotPassedToConstructor = createUseCaseConfig()
+ merger.getPreferredChildSize(SIZE_1920_1440, useCaseConfigNotPassedToConstructor)
+ }
+
+ @Test
+ fun getPreferredChildSize_whenParentSizeIsSensorAspectRatio_canReturnCorrectly() {
+ // Arrange.
+ val config = createUseCaseConfig()
+ val candidateChildSizes = listOf(SIZE_2560_1440, SIZE_1920_1080, SIZE_960_540) // 16:9
+ val sorter = FakeSupportedOutputSizesSorter(mapOf(config to candidateChildSizes))
+ val merger = ResolutionsMerger(SENSOR_SIZE, setOf(config), sorter, CAMERA_SUPPORTED_SIZES)
+
+ // Act & Assert, should returns the first child size that do not need upscale.
+ assertThat(merger.getPreferredChildSize(SIZE_1920_1440, config)).isEqualTo(SIZE_1920_1080)
+ assertThat(merger.getPreferredChildSize(SIZE_1280_960, config)).isEqualTo(SIZE_960_540)
+
+ // Act & Assert, should returns parent size when no matching.
+ assertThat(merger.getPreferredChildSize(SIZE_640_480, config)).isEqualTo(SIZE_640_480)
+ }
+
+ @Test
+ fun getPreferredChildSize_whenParentSizeIsNotSensorAspectRatio_canReturnCorrectly() {
+ // Arrange.
+ val config = createUseCaseConfig()
+ val candidateChildSizes = listOf(
+ // 4:3
+ SIZE_2560_1920,
+ SIZE_1920_1440,
+ SIZE_1280_960,
+ SIZE_960_720,
+ // 16:9
+ SIZE_1920_1080,
+ SIZE_960_540
+ )
+ val sorter = FakeSupportedOutputSizesSorter(mapOf(config to candidateChildSizes))
+ val merger = ResolutionsMerger(SENSOR_SIZE, setOf(config), sorter, CAMERA_SUPPORTED_SIZES)
+
+ // Act & Assert, should returns the first child size that do not need upscale and cause
+ // double-cropping.
+ assertThat(merger.getPreferredChildSize(SIZE_2560_1440, config)).isEqualTo(SIZE_1920_1080)
+ assertThat(merger.getPreferredChildSize(SIZE_1280_720, config)).isEqualTo(SIZE_960_540)
+
+ // Act & Assert, should returns parent size when no matching.
+ assertThat(merger.getPreferredChildSize(SIZE_192_108, config)).isEqualTo(SIZE_192_108)
+ }
+
+ @Test
+ fun filterResolutionsByAspectRatio_canFilter_4_3() {
+ val sizes = SIZES_4_3 + SIZES_16_9 + SIZES_OTHER_ASPECT_RATIO
+ assertThat(filterResolutionsByAspectRatio(ASPECT_RATIO_4_3, sizes)).containsExactly(
+ *SIZES_4_3.toTypedArray()
+ ).inOrder()
+ }
+
+ @Test
+ fun filterResolutionsByAspectRatio_canFilter_16_9() {
+ val sizes = SIZES_4_3 + SIZES_16_9 + SIZES_OTHER_ASPECT_RATIO
+ assertThat(filterResolutionsByAspectRatio(ASPECT_RATIO_16_9, sizes)).containsExactly(
+ *SIZES_16_9.toTypedArray()
+ ).inOrder()
+ }
+
+ @Test
+ fun filterOutParentSizeThatIsTooSmall_canFilterOutSmallSizes() {
+ val parentSizes = listOf(
+ SIZE_3264_2448,
+ SIZE_2560_1920,
+ SIZE_1920_1440,
+ SIZE_1280_960,
+ SIZE_960_720,
+ SIZE_640_480,
+ SIZE_320_240
+ )
+ val childSizes = setOf(SIZE_1920_1080, SIZE_1280_720, SIZE_960_540)
+ assertThat(filterOutParentSizeThatIsTooSmall(childSizes, parentSizes)).containsExactly(
+ SIZE_3264_2448,
+ SIZE_2560_1920,
+ SIZE_1920_1440,
+ SIZE_1280_960,
+ SIZE_960_720,
+ ).inOrder()
+ }
+
+ @Test
+ fun getParentSizesThatAreTooLarge_canReturnLargeSizes() {
+ val parentSizes = listOf(
+ SIZE_3264_2448,
+ SIZE_2560_1920,
+ SIZE_1920_1440,
+ SIZE_1280_960,
+ SIZE_960_720,
+ SIZE_640_480,
+ SIZE_320_240
+ )
+ val childSizes = setOf(SIZE_1920_1080, SIZE_1280_720, SIZE_960_540)
+ assertThat(getParentSizesThatAreTooLarge(childSizes, parentSizes)).containsExactly(
+ SIZE_3264_2448,
+ SIZE_2560_1920
+ ).inOrder()
+ }
+
+ @Test
+ fun hasUpscaling_return_false_whenTwoSizesAreEqualed() {
+ assertThat(hasUpscaling(SIZE_1280_960, SIZE_1280_960)).isFalse()
+ assertThat(hasUpscaling(SIZE_1920_1080, SIZE_1920_1080)).isFalse()
+ }
+
+ @Test
+ fun hasUpscaling_return_false_whenChildSizeIsSmaller() {
+ assertThat(hasUpscaling(SIZE_1280_960, SIZE_1920_1440)).isFalse()
+ assertThat(hasUpscaling(SIZE_1280_720, SIZE_1920_1080)).isFalse()
+ }
+
+ @Test
+ fun hasUpscaling_return_true_whenChildSizeIsLarger() {
+ assertThat(hasUpscaling(SIZE_1920_1440, SIZE_1280_960)).isTrue()
+ assertThat(hasUpscaling(SIZE_1920_1080, SIZE_1280_720)).isTrue()
+ }
+
+ @Test
+ fun hasUpscaling_return_true_whenChildSizeIsLargerOnWidth() {
+ assertThat(hasUpscaling(SIZE_1440_720, SIZE_1280_960)).isTrue()
+ assertThat(hasUpscaling(SIZE_1440_720, SIZE_1280_720)).isTrue()
+ }
+
+ @Test
+ fun hasUpscaling_return_true_whenChildSizeIsLargerOnHeight() {
+ assertThat(hasUpscaling(SIZE_800_800, SIZE_1280_720)).isTrue()
+ assertThat(hasUpscaling(SIZE_720_720, SIZE_960_540)).isTrue()
+ }
+
+ private fun createUseCaseConfig(): UseCaseConfig<*> {
+ return FakeUseCaseConfig.Builder().useCaseConfig
+ }
+
+ /**
+ * A fake implementation of [SupportedOutputSizesSorter] for testing.
+ */
+ private class FakeSupportedOutputSizesSorter(
+ private val supportedOutputSizes: Map<UseCaseConfig<*>, List<Size>>
+ ) : SupportedOutputSizesSorter(FakeCameraInfoInternal(), null) {
+ override fun getSortedSupportedOutputSizes(useCaseConfig: UseCaseConfig<*>): List<Size> {
+ return supportedOutputSizes[useCaseConfig]!!
+ }
+ }
+
+ companion object {
+ // 4:3 resolutions.
+ private val SIZE_3264_2448 = Size(3264, 2448)
+ private val SIZE_2560_1920 = Size(2560, 1920)
+ private val SIZE_1920_1440 = Size(1920, 1440)
+ private val SIZE_1280_960 = Size(1280, 960)
+ private val SIZE_960_720 = Size(960, 720)
+ private val SIZE_640_480 = Size(640, 480)
+ private val SIZE_320_240 = Size(320, 240)
+ private val SIZES_4_3 = listOf(
+ SIZE_3264_2448,
+ SIZE_2560_1920,
+ SIZE_1920_1440,
+ SIZE_1280_960,
+ SIZE_960_720,
+ SIZE_640_480,
+ SIZE_320_240
+ )
+ // 16:9 resolutions.
+ private val SIZE_3840_2160 = Size(3840, 2160)
+ private val SIZE_2560_1440 = Size(2560, 1440)
+ private val SIZE_1920_1080 = Size(1920, 1080)
+ private val SIZE_1280_720 = Size(1280, 720)
+ private val SIZE_960_540 = Size(960, 540)
+ private val SIZE_192_108 = Size(192, 108)
+ private val SIZES_16_9 = listOf(
+ SIZE_3840_2160,
+ SIZE_2560_1440,
+ SIZE_1920_1080,
+ SIZE_1280_720,
+ SIZE_960_540,
+ SIZE_192_108
+ )
+ // Other aspect-ratio resolutions.
+ private val SIZE_1440_720 = Size(1440, 720)
+ private val SIZE_800_800 = Size(800, 800)
+ private val SIZE_720_720 = Size(720, 720)
+ private val SIZE_500_400 = Size(500, 400)
+ private val SIZE_176_144 = Size(176, 144)
+ private val SIZES_OTHER_ASPECT_RATIO = listOf(
+ SIZE_1440_720,
+ SIZE_800_800,
+ SIZE_720_720,
+ SIZE_500_400,
+ SIZE_176_144
+ )
+ private val CAMERA_SUPPORTED_SIZES = SIZES_4_3 + SIZES_16_9 + SIZES_OTHER_ASPECT_RATIO
+ private val SENSOR_SIZE = SIZE_3264_2448 // 4:3
+ }
+}
diff --git a/camera/camera-effects/src/main/java/androidx/camera/effects/opengl/GlContext.java b/camera/camera-effects/src/main/java/androidx/camera/effects/opengl/GlContext.java
index 56919d0..504e18a 100644
--- a/camera/camera-effects/src/main/java/androidx/camera/effects/opengl/GlContext.java
+++ b/camera/camera-effects/src/main/java/androidx/camera/effects/opengl/GlContext.java
@@ -273,7 +273,8 @@
if (!EGL14.eglMakeCurrent(mEglDisplay, eglSurface.getEglSurface(),
eglSurface.getEglSurface(),
mEglContext)) {
- throw new IllegalStateException("eglMakeCurrent failed");
+ throw new IllegalStateException(
+ "eglMakeCurrent failed. GL Error: " + EGL14.eglGetError());
}
mCurrentSurface = eglSurface;
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 3ae5439..fbe84e3 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -30,7 +30,6 @@
implementation("androidx.core:core:1.0.0")
implementation("androidx.concurrent:concurrent-futures:1.0.0")
implementation(libs.autoValueAnnotations)
- implementation(project(':camera:camera-camera2-pipe-integration'))
annotationProcessor(libs.autoValue)
compileOnly(project(":camera:camera-extensions-stub"))
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
index 96616ad..0818c348 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
@@ -39,6 +39,7 @@
import androidx.camera.testing.impl.ExifUtil
import androidx.camera.testing.impl.SurfaceTextureProvider
import androidx.camera.testing.impl.SurfaceTextureProvider.SurfaceTextureCallback
+import androidx.camera.testing.impl.WakelockEmptyActivityRule
import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
@@ -81,6 +82,9 @@
val temporaryFolder =
TemporaryFolder(ApplicationProvider.getApplicationContext<Context>().cacheDir)
+ @get:Rule
+ val wakelockEmptyActivityRule = WakelockEmptyActivityRule()
+
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var cameraProvider: ProcessCameraProvider
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java
index a859edb..0da3b2b 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java
@@ -19,12 +19,16 @@
import android.hardware.camera2.CameraCharacteristics;
import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
+import androidx.camera.camera2.interop.Camera2CameraInfo;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraFilter;
import androidx.camera.core.CameraInfo;
+import androidx.camera.core.impl.CameraInfoInternal;
import androidx.camera.core.impl.Identifier;
-import androidx.camera.extensions.internal.Camera2CameraInfoWrapper;
import androidx.camera.extensions.internal.VendorExtender;
+import androidx.core.util.Preconditions;
import java.util.ArrayList;
import java.util.List;
@@ -50,15 +54,18 @@
return mId;
}
+ @OptIn(markerClass = ExperimentalCamera2Interop.class)
@NonNull
@Override
public List<CameraInfo> filter(@NonNull List<CameraInfo> cameraInfos) {
List<CameraInfo> result = new ArrayList<>();
for (CameraInfo cameraInfo : cameraInfos) {
- String cameraId = Camera2CameraInfoWrapper.from(cameraInfo).getCameraId();
+ Preconditions.checkArgument(cameraInfo instanceof CameraInfoInternal,
+ "The camera info doesn't contain internal implementation.");
+ String cameraId = Camera2CameraInfo.from(cameraInfo).getCameraId();
Map<String, CameraCharacteristics> cameraCharacteristicsMap =
- Camera2CameraInfoWrapper.from(cameraInfo).getCameraCharacteristicsMap();
+ Camera2CameraInfo.from(cameraInfo).getCameraCharacteristicsMap();
if (mVendorExtender
.isExtensionAvailable(cameraId, cameraCharacteristicsMap)) {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
index 11975ad..c70750a 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java
@@ -26,8 +26,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
+import androidx.camera.camera2.interop.Camera2CameraInfo;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.Logger;
import androidx.camera.core.impl.SessionProcessor;
@@ -94,12 +97,13 @@
mMode = ExtensionMode.NONE;
}
+ @OptIn(markerClass = ExperimentalCamera2Interop.class)
@Override
public void init(@NonNull CameraInfo cameraInfo) {
- mCameraId = Camera2CameraInfoWrapper.from(cameraInfo).getCameraId();
+ mCameraId = Camera2CameraInfo.from(cameraInfo).getCameraId();
Map<String, CameraCharacteristics> cameraCharacteristicsMap =
- Camera2CameraInfoWrapper.from(cameraInfo).getCameraCharacteristicsMap();
+ Camera2CameraInfo.from(cameraInfo).getCameraCharacteristicsMap();
mAdvancedExtenderImpl.init(mCameraId, cameraCharacteristicsMap);
}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
index e190a59..b170459 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
@@ -29,8 +29,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
+import androidx.camera.camera2.interop.Camera2CameraInfo;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.Logger;
import androidx.camera.core.impl.ImageFormatConstants;
@@ -91,7 +94,6 @@
CaptureRequest.FLASH_MODE,
CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION
));
-
static {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
sBaseSupportedKeys.add(CaptureRequest.CONTROL_ZOOM_RATIO);
@@ -156,6 +158,7 @@
&& mImageCaptureExtenderImpl.isExtensionAvailable(cameraId, cameraCharacteristics);
}
+ @OptIn(markerClass = ExperimentalCamera2Interop.class)
@Override
public void init(@NonNull CameraInfo cameraInfo) {
mCameraInfo = cameraInfo;
@@ -164,9 +167,9 @@
return;
}
- mCameraId = Camera2CameraInfoWrapper.from(cameraInfo).getCameraId();
+ mCameraId = Camera2CameraInfo.from(cameraInfo).getCameraId();
mCameraCharacteristics =
- Camera2CameraInfoWrapper.extractCameraCharacteristics(cameraInfo);
+ Camera2CameraInfo.extractCameraCharacteristics(cameraInfo);
mPreviewExtenderImpl.init(mCameraId, mCameraCharacteristics);
mImageCaptureExtenderImpl.init(mCameraId, mCameraCharacteristics);
@@ -189,8 +192,9 @@
return null;
}
+ @OptIn(markerClass = ExperimentalCamera2Interop.class)
private Size[] getOutputSizes(int imageFormat) {
- StreamConfigurationMap map = Camera2CameraInfoWrapper.from(mCameraInfo)
+ StreamConfigurationMap map = Camera2CameraInfo.from(mCameraInfo)
.getCameraCharacteristic(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
return map.getOutputSizes(imageFormat);
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/Camera2CameraInfoWrapper.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/Camera2CameraInfoWrapper.java
deleted file mode 100644
index 48d3935..0000000
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/Camera2CameraInfoWrapper.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2023 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.extensions.internal;
-
-import android.hardware.camera2.CameraCharacteristics;
-import android.os.Build;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.OptIn;
-import androidx.annotation.RequiresApi;
-import androidx.camera.camera2.internal.Camera2CameraInfoImpl;
-import androidx.camera.camera2.pipe.integration.adapter.CameraInfoAdapter;
-import androidx.camera.core.CameraInfo;
-import androidx.camera.core.impl.CameraInfoInternal;
-
-import java.util.Map;
-
-
-@OptIn(markerClass = {androidx.camera.camera2.interop.ExperimentalCamera2Interop.class,
- androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop.class})
-@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
-public final class Camera2CameraInfoWrapper {
- private androidx.camera.camera2.interop.Camera2CameraInfo mCamera2CameraInfo;
- private androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo
- mCameraPipeCameraInfo;
-
- Camera2CameraInfoWrapper(@NonNull Camera2CameraInfoImpl camera2CameraInfo) {
- mCamera2CameraInfo =
- androidx.camera.camera2.interop.Camera2CameraInfo.from(camera2CameraInfo);
- }
-
- Camera2CameraInfoWrapper(@NonNull CameraInfoAdapter cameraInfoAdapter) {
- mCameraPipeCameraInfo =
- androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo.from(
- cameraInfoAdapter);
- }
-
- @NonNull
- public static Camera2CameraInfoWrapper from(@NonNull CameraInfo cameraInfo) {
- CameraInfoInternal cameraInfoInternal =
- ((CameraInfoInternal) cameraInfo).getImplementation();
- if (cameraInfoInternal instanceof Camera2CameraInfoImpl) {
- return new Camera2CameraInfoWrapper((Camera2CameraInfoImpl) cameraInfoInternal);
- } else if (cameraInfoInternal instanceof CameraInfoAdapter) {
- return new Camera2CameraInfoWrapper((CameraInfoAdapter) cameraInfoInternal);
- } else {
- throw new IllegalArgumentException("Not a Camera2 implementation!");
- }
- }
-
- @NonNull
- public String getCameraId() {
- if (mCamera2CameraInfo != null) {
- return mCamera2CameraInfo.getCameraId();
- } else {
- return mCameraPipeCameraInfo.getCameraId();
- }
- }
-
- @NonNull
- public <T> T getCameraCharacteristic(@NonNull CameraCharacteristics.Key<T> key) {
- if (mCamera2CameraInfo != null) {
- return mCamera2CameraInfo.getCameraCharacteristic(key);
- } else {
- return mCameraPipeCameraInfo.getCameraCharacteristic(key);
- }
- }
-
- @NonNull
- public static CameraCharacteristics extractCameraCharacteristics(
- @NonNull CameraInfo cameraInfo) {
- CameraInfoInternal cameraInfoInternal =
- ((CameraInfoInternal) cameraInfo).getImplementation();
- if (cameraInfoInternal instanceof Camera2CameraInfoImpl) {
- return androidx.camera.camera2.interop.Camera2CameraInfo.extractCameraCharacteristics(
- cameraInfo);
- } else if (cameraInfoInternal instanceof CameraInfoAdapter) {
- return androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo
- .extractCameraCharacteristics(cameraInfo);
- } else {
- throw new IllegalArgumentException("Not a Camera2 implementation!");
- }
- }
-
- @NonNull
- public Map<String, CameraCharacteristics> getCameraCharacteristicsMap() {
- if (mCamera2CameraInfo != null) {
- return mCamera2CameraInfo.getCameraCharacteristicsMap();
- } else {
- return mCameraPipeCameraInfo.getCameraCharacteristicsMap();
- }
- }
-}
diff --git a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraRepositoryTest.java b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraRepositoryTest.java
index 63ae7a7..56ab0c1 100644
--- a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraRepositoryTest.java
+++ b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraRepositoryTest.java
@@ -23,9 +23,12 @@
import static java.util.Collections.emptyList;
import androidx.camera.core.concurrent.CameraCoordinator;
+import androidx.camera.core.impl.CameraConfig;
+import androidx.camera.core.impl.CameraConfigs;
import androidx.camera.core.impl.CameraInternal;
import androidx.camera.core.internal.CameraUseCaseAdapter;
import androidx.camera.testing.fakes.FakeCamera;
+import androidx.camera.testing.impl.fakes.FakeCameraConfig;
import androidx.camera.testing.impl.fakes.FakeCameraCoordinator;
import androidx.camera.testing.impl.fakes.FakeCameraDeviceSurfaceManager;
import androidx.camera.testing.impl.fakes.FakeLifecycleOwner;
@@ -106,6 +109,19 @@
}
@Test
+ public void differentLifecycleCamerasAreCreated_forDifferentCameraConfig() {
+ LifecycleCamera firstLifecycleCamera = mRepository.createLifecycleCamera(
+ mLifecycle, mCameraUseCaseAdapter);
+
+ // Creates LifecycleCamera with different camera set
+ LifecycleCamera secondLifecycleCamera =
+ mRepository.createLifecycleCamera(mLifecycle,
+ createCameraUseCaseAdapterWithNewCameraConfig());
+
+ assertThat(firstLifecycleCamera).isNotEqualTo(secondLifecycleCamera);
+ }
+
+ @Test
public void lifecycleCameraIsNotActive_createWithNoUseCasesAfterLifecycleStarted() {
mLifecycle.start();
LifecycleCamera lifecycleCamera = mRepository.createLifecycleCamera(mLifecycle,
@@ -431,17 +447,43 @@
LifecycleCamera lifecycleCamera = mRepository.createLifecycleCamera(
mLifecycle, mCameraUseCaseAdapter);
CameraUseCaseAdapter.CameraId cameraId = CameraUseCaseAdapter.generateCameraId(mCameraSet);
- LifecycleCamera retrieved = mRepository.getLifecycleCamera(mLifecycle, cameraId);
+ LifecycleCamera retrieved = mRepository.getLifecycleCamera(mLifecycle, cameraId,
+ mCameraUseCaseAdapter.getExtendedConfig());
assertThat(lifecycleCamera).isSameInstanceAs(retrieved);
}
@Test
+ public void getLifecycleCameraWithDifferentCameraConfig_returnDifferentInstance() {
+ LifecycleCamera lifecycleCamera1 = mRepository.createLifecycleCamera(mLifecycle,
+ mCameraUseCaseAdapter);
+
+ CameraUseCaseAdapter newCameraUseCaseAdapter =
+ createCameraUseCaseAdapterWithNewCameraConfig();
+ LifecycleCamera lifecycleCamera2 = mRepository.createLifecycleCamera(mLifecycle,
+ newCameraUseCaseAdapter);
+
+ LifecycleCamera retrieved1 = mRepository.getLifecycleCamera(mLifecycle,
+ mCameraUseCaseAdapter.getCameraId(),
+ mCameraUseCaseAdapter.getExtendedConfig());
+
+ LifecycleCamera retrieved2 = mRepository.getLifecycleCamera(mLifecycle,
+ newCameraUseCaseAdapter.getCameraId(),
+ newCameraUseCaseAdapter.getExtendedConfig());
+
+ assertThat(lifecycleCamera1).isSameInstanceAs(retrieved1);
+ assertThat(lifecycleCamera2).isSameInstanceAs(retrieved2);
+ assertThat(retrieved1).isNotSameInstanceAs(retrieved2);
+ }
+
+ @Test
public void keys() {
LifecycleCameraRepository.Key key0 = LifecycleCameraRepository.Key.create(mLifecycle,
- mCameraUseCaseAdapter.getCameraId());
+ mCameraUseCaseAdapter.getCameraId(),
+ CameraConfigs.defaultConfig().getCompatibilityId());
LifecycleCameraRepository.Key key1 = LifecycleCameraRepository.Key.create(mLifecycle,
- CameraUseCaseAdapter.generateCameraId(mCameraSet));
+ CameraUseCaseAdapter.generateCameraId(mCameraSet),
+ CameraConfigs.defaultConfig().getCompatibilityId());
Map<LifecycleCameraRepository.Key, LifecycleOwner> map = new HashMap<>();
map.put(key0, mLifecycle);
@@ -586,4 +628,13 @@
new FakeCameraDeviceSurfaceManager(),
new FakeUseCaseConfigFactory());
}
+
+ private CameraUseCaseAdapter createCameraUseCaseAdapterWithNewCameraConfig() {
+ CameraConfig cameraConfig = new FakeCameraConfig();
+ return new CameraUseCaseAdapter(mCameraSet,
+ mCameraCoordinator,
+ new FakeCameraDeviceSurfaceManager(),
+ new FakeUseCaseConfigFactory(),
+ cameraConfig);
+ }
}
diff --git a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
index 0939e5f..ec280ea2 100644
--- a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
+++ b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
@@ -23,6 +23,8 @@
import android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT
import androidx.annotation.OptIn
import androidx.annotation.RequiresApi
+import androidx.camera.core.CameraFilter
+import androidx.camera.core.CameraInfo
import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraSelector.LENS_FACING_BACK
import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
@@ -31,7 +33,13 @@
import androidx.camera.core.Preview
import androidx.camera.core.UseCaseGroup
import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
+import androidx.camera.core.impl.CameraConfig
import androidx.camera.core.impl.CameraFactory
+import androidx.camera.core.impl.Config
+import androidx.camera.core.impl.ExtendedCameraConfigProviderStore
+import androidx.camera.core.impl.Identifier
+import androidx.camera.core.impl.MutableOptionsBundle
+import androidx.camera.core.impl.SessionProcessor
import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
import androidx.camera.testing.fakes.FakeAppConfig
import androidx.camera.testing.fakes.FakeCamera
@@ -40,6 +48,7 @@
import androidx.camera.testing.impl.fakes.FakeCameraDeviceSurfaceManager
import androidx.camera.testing.impl.fakes.FakeCameraFactory
import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
+import androidx.camera.testing.impl.fakes.FakeSessionProcessor
import androidx.camera.testing.impl.fakes.FakeSurfaceEffect
import androidx.camera.testing.impl.fakes.FakeSurfaceProcessor
import androidx.camera.testing.impl.fakes.FakeUseCaseConfigFactory
@@ -846,6 +855,81 @@
}
}
+ @Test
+ @RequiresApi(23)
+ fun bindWithExtensions_doesNotImpactPreviousCamera(): Unit = runBlocking(Dispatchers.Main) {
+ // 1. Arrange.
+ val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+ val cameraSelectorWithExtensions = getCameraSelectorWithLimitedCapabilities(
+ cameraSelector,
+ emptySet() // All capabilities are not supported.
+ )
+ provider = ProcessCameraProvider.getInstance(context).await()
+ val useCase = Preview.Builder().build()
+
+ // 2. Act: bind with and then without Extensions.
+ // bind with regular cameraSelector to get the regular camera (with empty use cases)
+ val camera = provider.bindToLifecycle(lifecycleOwner0, cameraSelector)
+ // bind with extensions cameraSelector to get the restricted version of camera.
+ val cameraWithExtensions = provider.bindToLifecycle(lifecycleOwner0,
+ cameraSelectorWithExtensions, useCase)
+
+ // 3. Assert: ensure we can different instances of Camera and one does not affect the other.
+ assertThat(camera).isNotSameInstanceAs(cameraWithExtensions)
+
+ // only the Extensions CameraControl does not support the zoom.
+ camera.cameraControl.setZoomRatio(1.0f).await()
+ assertThrows<IllegalStateException> {
+ cameraWithExtensions.cameraControl.setZoomRatio(1.0f).await()
+ }
+
+ // only the Extensions CameraInfo does not support the zoom.
+ assertThat(camera.cameraInfo.zoomState.value!!.maxZoomRatio).isGreaterThan(1.0f)
+ assertThat(cameraWithExtensions.cameraInfo.zoomState.value!!.maxZoomRatio).isEqualTo(1.0f)
+ }
+
+ @RequiresApi(23)
+ private fun getCameraSelectorWithLimitedCapabilities(
+ cameraSelector: CameraSelector,
+ supportedCapabilities: Set<Int>
+ ): CameraSelector {
+ val identifier = Identifier.create("idStr")
+ val sessionProcessor = FakeSessionProcessor()
+ sessionProcessor.restrictedCameraOperations = supportedCapabilities
+ ExtendedCameraConfigProviderStore.addConfig(identifier) { _, _ ->
+ object : CameraConfig {
+ override fun getConfig(): Config {
+ return MutableOptionsBundle.create()
+ }
+
+ override fun getCompatibilityId(): Identifier {
+ return identifier
+ }
+
+ override fun getSessionProcessor(
+ valueIfMissing: SessionProcessor?
+ ) = sessionProcessor
+
+ override fun getSessionProcessor() = sessionProcessor
+ }
+ }
+
+ val builder = CameraSelector.Builder.fromSelector(cameraSelector)
+ builder.addCameraFilter(object : CameraFilter {
+ override fun filter(cameraInfos: MutableList<CameraInfo>): MutableList<CameraInfo> {
+ val newCameraInfos = mutableListOf<CameraInfo>()
+ newCameraInfos.addAll(cameraInfos)
+ return newCameraInfos
+ }
+
+ override fun getIdentifier(): Identifier {
+ return identifier
+ }
+ })
+
+ return builder.build()
+ }
+
private fun createConcurrentCameraAppConfig(): CameraXConfig {
val combination0 = mapOf(
"0" to CameraSelector.Builder().requireLensFacing(LENS_FACING_BACK).build(),
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
index 056e784..8df240b 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
@@ -20,7 +20,6 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraControl;
@@ -276,11 +275,6 @@
}
@Override
- public void setExtendedConfig(@Nullable CameraConfig cameraConfig) {
- mCameraUseCaseAdapter.setExtendedConfig(cameraConfig);
- }
-
- @Override
public boolean isUseCasesCombinationSupported(boolean withStreamSharing,
@NonNull UseCase... useCases) {
return mCameraUseCaseAdapter.isUseCasesCombinationSupported(withStreamSharing, useCases);
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
index 3b8fae9..3023fbc 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
@@ -24,7 +24,9 @@
import androidx.camera.core.UseCase;
import androidx.camera.core.ViewPort;
import androidx.camera.core.concurrent.CameraCoordinator;
+import androidx.camera.core.impl.CameraConfig;
import androidx.camera.core.impl.CameraInternal;
+import androidx.camera.core.impl.Identifier;
import androidx.camera.core.internal.CameraUseCaseAdapter;
import androidx.core.util.Preconditions;
import androidx.lifecycle.Lifecycle;
@@ -103,7 +105,10 @@
@NonNull CameraUseCaseAdapter cameraUseCaseAdaptor) {
LifecycleCamera lifecycleCamera;
synchronized (mLock) {
- Key key = Key.create(lifecycleOwner, cameraUseCaseAdaptor.getCameraId());
+ Key key = Key.create(lifecycleOwner,
+ cameraUseCaseAdaptor.getCameraId(),
+ cameraUseCaseAdaptor.getExtendedConfig().getCompatibilityId()
+ );
Preconditions.checkArgument(mCameraMap.get(key) == null, "LifecycleCamera already "
+ "exists for the given LifecycleOwner and set of cameras");
@@ -132,9 +137,11 @@
*/
@Nullable
LifecycleCamera getLifecycleCamera(LifecycleOwner lifecycleOwner,
- CameraUseCaseAdapter.CameraId cameraId) {
+ @NonNull CameraUseCaseAdapter.CameraId cameraId,
+ @NonNull CameraConfig cameraConfig) {
synchronized (mLock) {
- return mCameraMap.get(Key.create(lifecycleOwner, cameraId));
+ return mCameraMap.get(Key.create(lifecycleOwner, cameraId,
+ cameraConfig.getCompatibilityId()));
}
}
@@ -174,7 +181,10 @@
synchronized (mLock) {
LifecycleOwner lifecycleOwner = lifecycleCamera.getLifecycleOwner();
Key key = Key.create(lifecycleOwner,
- lifecycleCamera.getCameraUseCaseAdapter().getCameraId());
+ lifecycleCamera.getCameraUseCaseAdapter().getCameraId(),
+ lifecycleCamera.getCameraUseCaseAdapter()
+ .getExtendedConfig().getCompatibilityId());
+
LifecycleCameraRepositoryObserver observer =
getLifecycleCameraRepositoryObserver(lifecycleOwner);
Set<Key> lifecycleCameraKeySet;
@@ -501,8 +511,10 @@
@AutoValue
abstract static class Key {
static Key create(@NonNull LifecycleOwner lifecycleOwner,
- @NonNull CameraUseCaseAdapter.CameraId cameraId) {
- return new AutoValue_LifecycleCameraRepository_Key(lifecycleOwner, cameraId);
+ @NonNull CameraUseCaseAdapter.CameraId cameraId,
+ @NonNull Identifier cameraConfigId) {
+ return new AutoValue_LifecycleCameraRepository_Key(
+ lifecycleOwner, cameraId, cameraConfigId);
}
@NonNull
@@ -510,6 +522,9 @@
@NonNull
public abstract CameraUseCaseAdapter.CameraId getCameraId();
+
+ @NonNull
+ public abstract Identifier getCameraConfigId();
}
private static class LifecycleCameraRepositoryObserver implements LifecycleObserver {
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
index bc5ca14..9508ef1 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
@@ -56,6 +56,7 @@
import androidx.camera.core.ViewPort;
import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode;
import androidx.camera.core.impl.CameraConfig;
+import androidx.camera.core.impl.CameraConfigs;
import androidx.camera.core.impl.CameraInternal;
import androidx.camera.core.impl.ExtendedCameraConfigProviderStore;
import androidx.camera.core.impl.utils.ContextUtil;
@@ -572,11 +573,16 @@
throw new IllegalArgumentException("Provided camera selector unable to resolve a "
+ "camera for the given use case");
}
+
+ CameraConfig cameraConfig = getCameraConfig(cameraSelector,
+ cameraInternals.iterator().next().getCameraInfo());
+
CameraUseCaseAdapter.CameraId cameraId =
CameraUseCaseAdapter.generateCameraId(cameraInternals);
LifecycleCamera lifecycleCameraToBind =
- mLifecycleCameraRepository.getLifecycleCamera(lifecycleOwner, cameraId);
+ mLifecycleCameraRepository.getLifecycleCamera(
+ lifecycleOwner, cameraId, cameraConfig);
Collection<LifecycleCamera> lifecycleCameras =
mLifecycleCameraRepository.getLifecycleCameras();
@@ -600,34 +606,10 @@
new CameraUseCaseAdapter(cameraInternals,
mCameraX.getCameraFactory().getCameraCoordinator(),
mCameraX.getCameraDeviceSurfaceManager(),
- mCameraX.getDefaultConfigFactory()));
+ mCameraX.getDefaultConfigFactory(),
+ cameraConfig));
}
- CameraConfig cameraConfig = null;
-
- // Retrieves extended camera configs from ExtendedCameraConfigProviderStore
- for (CameraFilter cameraFilter : cameraSelector.getCameraFilterSet()) {
- if (cameraFilter.getIdentifier() != CameraFilter.DEFAULT_ID) {
- CameraConfig extendedCameraConfig =
- ExtendedCameraConfigProviderStore.getConfigProvider(
- cameraFilter.getIdentifier()).getConfig(
- lifecycleCameraToBind.getCameraInfo(), mContext);
- if (extendedCameraConfig == null) { // ignore IDs unrelated to camera configs.
- continue;
- }
-
- // Only allows one camera config now.
- if (cameraConfig != null) {
- throw new IllegalArgumentException(
- "Cannot apply multiple extended camera configs at the same time.");
- }
- cameraConfig = extendedCameraConfig;
- }
- }
-
- // Applies extended camera configs to the camera
- lifecycleCameraToBind.setExtendedConfig(cameraConfig);
-
if (useCases.length == 0) {
return lifecycleCameraToBind;
}
@@ -642,6 +624,35 @@
return lifecycleCameraToBind;
}
+ @NonNull
+ private CameraConfig getCameraConfig(@NonNull CameraSelector cameraSelector,
+ @NonNull CameraInfo cameraInfo) {
+ CameraConfig cameraConfig = null;
+ for (CameraFilter cameraFilter : cameraSelector.getCameraFilterSet()) {
+ if (cameraFilter.getIdentifier() != CameraFilter.DEFAULT_ID) {
+ CameraConfig extendedCameraConfig =
+ ExtendedCameraConfigProviderStore
+ .getConfigProvider(cameraFilter.getIdentifier())
+ .getConfig(cameraInfo, mContext);
+ if (extendedCameraConfig == null) { // ignore IDs unrelated to camera configs.
+ continue;
+ }
+
+ // Only allows one camera config now.
+ if (cameraConfig != null) {
+ throw new IllegalArgumentException(
+ "Cannot apply multiple extended camera configs at the same time.");
+ }
+ cameraConfig = extendedCameraConfig;
+ }
+ }
+
+ if (cameraConfig == null) {
+ cameraConfig = CameraConfigs.defaultConfig();
+ }
+ return cameraConfig;
+ }
+
/**
* Returns true if the {@link UseCase} is bound to a lifecycle. Otherwise returns false.
*
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
index 7a89cc5..dcee293 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
@@ -76,7 +76,7 @@
private List<DeferrableSurface> mConfiguredDeferrableSurfaces = Collections.emptyList();
- private CameraConfig mCameraConfig = CameraConfigs.emptyConfig();
+ private CameraConfig mCameraConfig = CameraConfigs.defaultConfig();
public FakeCamera() {
this(DEFAULT_CAMERA_ID, /*cameraControl=*/null,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraUtil.java
index a372b22..ec89c70 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraUtil.java
@@ -56,6 +56,8 @@
import androidx.camera.core.Logger;
import androidx.camera.core.UseCase;
import androidx.camera.core.concurrent.CameraCoordinator;
+import androidx.camera.core.impl.CameraConfig;
+import androidx.camera.core.impl.CameraConfigs;
import androidx.camera.core.impl.CameraInternal;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.camera.core.internal.CameraUseCaseAdapter;
@@ -620,7 +622,8 @@
public static CameraUseCaseAdapter createCameraUseCaseAdapter(
@NonNull Context context,
@NonNull CameraCoordinator cameraCoordinator,
- @NonNull CameraSelector cameraSelector) {
+ @NonNull CameraSelector cameraSelector,
+ @NonNull CameraConfig cameraConfig) {
try {
CameraX cameraX = CameraXUtil.getOrCreateInstance(context, null).get(5000,
TimeUnit.MILLISECONDS);
@@ -629,7 +632,8 @@
return new CameraUseCaseAdapter(cameras,
cameraCoordinator,
cameraX.getCameraDeviceSurfaceManager(),
- cameraX.getDefaultConfigFactory());
+ cameraX.getDefaultConfigFactory(),
+ cameraConfig);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
throw new RuntimeException("Unable to retrieve CameraX instance");
}
@@ -655,7 +659,22 @@
public static CameraUseCaseAdapter createCameraUseCaseAdapter(
@NonNull Context context,
@NonNull CameraSelector cameraSelector) {
- return createCameraUseCaseAdapter(context, new FakeCameraCoordinator(), cameraSelector);
+ return createCameraUseCaseAdapter(context, new FakeCameraCoordinator(),
+ cameraSelector, CameraConfigs.defaultConfig());
+ }
+
+ /**
+ * Creates the CameraUseCaseAdapter that would be created with the given CameraSelector and
+ * CameraConfig
+ */
+ @VisibleForTesting
+ @NonNull
+ public static CameraUseCaseAdapter createCameraUseCaseAdapter(
+ @NonNull Context context,
+ @NonNull CameraSelector cameraSelector,
+ @NonNull CameraConfig cameraConfig) {
+ return createCameraUseCaseAdapter(context, new FakeCameraCoordinator(),
+ cameraSelector, cameraConfig);
}
/**
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CoreAppTestUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CoreAppTestUtil.java
index 206ff7d..4a33a63 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CoreAppTestUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CoreAppTestUtil.java
@@ -49,6 +49,8 @@
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public final class CoreAppTestUtil {
+ private static final String TAG = "CoreAppTestUtil";
+
/** ADB shell input key code for dismissing keyguard for device with API level <= 22. */
private static final int DISMISS_LOCK_SCREEN_CODE = 82;
/** ADB shell command for dismissing keyguard for device with API level >= 23. */
@@ -153,8 +155,11 @@
}
device.pressHome();
- device.waitForIdle(MAX_TIMEOUT_MS);
-
+ try {
+ device.waitForIdle(MAX_TIMEOUT_MS);
+ } catch (IllegalStateException e) {
+ Logger.d(TAG, "Fail to waitForIdle", e);
+ }
// Close system dialogs first to avoid interrupt.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
instrumentation.getTargetContext().sendBroadcast(
@@ -205,7 +210,7 @@
instrumentation.waitForIdleSync();
if (activityRef == null) {
- Logger.d("CoreAppTestUtil", String.format("Activity %s, failed to launch",
+ Logger.d(TAG, String.format("Activity %s, failed to launch",
startIntent.getComponent()) + ", ignore the foreground checking");
return;
}
@@ -216,7 +221,7 @@
Espresso.onIdle();
return;
} catch (Exception e) {
- Logger.d("CoreAppTestUtil", "Fail to get foreground", e);
+ Logger.d(TAG, "Fail to get foreground", e);
} finally {
if (activityRef != null) {
IdlingRegistry.getInstance().unregister(activityRef.getViewReadyIdlingResource());
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
index db5fffe..7acb575 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
@@ -26,6 +26,7 @@
import androidx.camera.core.Logger
import androidx.camera.testing.impl.Api27Impl.setShowWhenLocked
import androidx.camera.testing.impl.Api27Impl.setTurnScreenOn
+import androidx.camera.testing.impl.CoreAppTestUtil.clearDeviceUI
import androidx.camera.testing.impl.activity.EmptyActivity
import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
@@ -42,6 +43,7 @@
object : Statement() {
override fun evaluate() {
val instrumentation = InstrumentationRegistry.getInstrumentation()
+ clearDeviceUI(instrumentation)
var activityRef: EmptyActivity? = null
try {
activityRef = CoreAppTestUtil.launchActivity(
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index 8b56757..16792c2 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -44,6 +44,7 @@
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
+import androidx.camera.core.UseCase
import androidx.camera.core.impl.utils.AspectRatioUtil
import androidx.camera.core.impl.utils.TransformUtils.is90or270
import androidx.camera.core.impl.utils.TransformUtils.rectToSize
@@ -271,7 +272,7 @@
// Verify.
val metadataRotation2 = cameraInfo.getSensorRotationDegrees(targetRotation2).let {
- if (videoCapture.node != null) {
+ if (isSurfaceProcessingEnabled(videoCapture)) {
// If effect is enabled, the rotation should eliminate the video content rotation.
it - videoContentRotation
} else it
@@ -408,6 +409,11 @@
)
}
+ // TODO(b/264936115): In stream sharing (VirtualCameraAdapter), children's ViewPortCropRect
+ // is ignored and override to the parent size, the cropRect is also rotated. Skip the test
+ // for now.
+ assumeTrue(!isStreamSharingEnabled(videoCapture))
+
val file = File.createTempFile("video_", ".tmp").apply { deleteOnExit() }
latchForVideoSaved = CountDownLatch(1)
@@ -1200,7 +1206,7 @@
cameraInfo: CameraInfo
): ExpectedRotation {
val rotationNeeded = cameraInfo.getSensorRotationDegrees(videoCapture.targetRotation)
- return if (videoCapture.node != null) {
+ return if (isSurfaceProcessingEnabled(videoCapture)) {
ExpectedRotation(rotationNeeded, 0)
} else {
ExpectedRotation(0, rotationNeeded)
@@ -1323,6 +1329,11 @@
assumeExtraCroppingQuirk(implName)
}
+ private fun isStreamSharingEnabled(useCase: UseCase) = !useCase.camera!!.hasTransform
+
+ private fun isSurfaceProcessingEnabled(videoCapture: VideoCapture<*>) =
+ videoCapture.node != null || isStreamSharingEnabled(videoCapture)
+
private class ImageSavedCallback :
ImageCapture.OnImageSavedCallback {
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java b/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
index 9d2eb01..3628e69 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/RecorderVideoCapabilities.java
@@ -49,6 +49,7 @@
import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
import androidx.camera.video.internal.encoder.VideoEncoderConfig;
import androidx.camera.video.internal.encoder.VideoEncoderInfo;
+import androidx.camera.video.internal.workaround.QualityAddedEncoderProfilesProvider;
import androidx.camera.video.internal.workaround.QualityResolutionModifiedEncoderProfilesProvider;
import androidx.camera.video.internal.workaround.QualityValidatedEncoderProfilesProvider;
@@ -105,6 +106,11 @@
"Not a supported video capabilities source: " + videoCapabilitiesSource);
EncoderProfilesProvider encoderProfilesProvider = cameraInfo.getEncoderProfilesProvider();
+ Quirks deviceQuirks = DeviceQuirks.getAll();
+ // Add extra supported quality.
+ encoderProfilesProvider = new QualityAddedEncoderProfilesProvider(encoderProfilesProvider,
+ deviceQuirks, cameraInfo, videoEncoderInfoFinder);
+
if (videoCapabilitiesSource == VIDEO_CAPABILITIES_SOURCE_CODEC_CAPABILITIES) {
encoderProfilesProvider = new QualityExploredEncoderProfilesProvider(
encoderProfilesProvider,
@@ -115,7 +121,6 @@
}
// Modify qualities' matching resolution to the value supported by camera.
- Quirks deviceQuirks = DeviceQuirks.getAll();
encoderProfilesProvider = new QualityResolutionModifiedEncoderProfilesProvider(
encoderProfilesProvider, deviceQuirks);
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/QualityExploredEncoderProfilesProvider.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/QualityExploredEncoderProfilesProvider.java
index 84faba9..f2ac9c5 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/QualityExploredEncoderProfilesProvider.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/QualityExploredEncoderProfilesProvider.java
@@ -19,6 +19,7 @@
import static androidx.camera.core.internal.utils.SizeUtil.findNearestHigherFor;
import static androidx.camera.video.internal.config.VideoConfigUtil.toVideoEncoderConfig;
import static androidx.camera.video.internal.utils.DynamicRangeUtil.isHdrSettingsMatched;
+import static androidx.camera.video.internal.utils.EncoderProfilesUtil.deriveVideoProfile;
import static androidx.core.util.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
@@ -37,7 +38,6 @@
import androidx.camera.core.impl.utils.CompareSizesByArea;
import androidx.camera.video.CapabilitiesByQuality;
import androidx.camera.video.Quality;
-import androidx.camera.video.internal.config.VideoConfigUtil;
import androidx.camera.video.internal.encoder.VideoEncoderConfig;
import androidx.camera.video.internal.encoder.VideoEncoderInfo;
@@ -170,7 +170,8 @@
encoderProfiles);
// Generate VideoProfile from base VideoProfile and new size.
generatedVideoProfiles.add(
- generateVideoProfile(baseVideoProfile, size, encoderInfo));
+ deriveVideoProfile(baseVideoProfile, size,
+ encoderInfo.getSupportedBitrateRange()));
}
if (!generatedVideoProfiles.isEmpty()) {
// Use the nearest higher EncoderProfiles as base EncoderProfiles.
@@ -210,31 +211,6 @@
return capabilities;
}
- @NonNull
- private static VideoProfileProxy generateVideoProfile(@NonNull VideoProfileProxy baseProfile,
- @NonNull Size size, @NonNull VideoEncoderInfo encoderInfo) {
- // "Guess" bit rate. Scale the bitrate based on size difference.
- int derivedBitrate = VideoConfigUtil.scaleAndClampBitrate(
- baseProfile.getBitrate(),
- baseProfile.getBitDepth(), baseProfile.getBitDepth(),
- baseProfile.getFrameRate(), baseProfile.getFrameRate(),
- size.getWidth(), baseProfile.getWidth(),
- size.getHeight(), baseProfile.getHeight(),
- encoderInfo.getSupportedBitrateRange());
- return VideoProfileProxy.create(
- baseProfile.getCodec(),
- baseProfile.getMediaType(),
- derivedBitrate,
- baseProfile.getFrameRate(),
- size.getWidth(),
- size.getHeight(),
- baseProfile.getProfile(),
- baseProfile.getBitDepth(),
- baseProfile.getChromaSubsampling(),
- baseProfile.getHdrFormat()
- );
- }
-
@Nullable
private static EncoderProfilesProxy mergeEncoderProfiles(
@Nullable EncoderProfilesProxy baseProfiles,
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
index 0234352..910c92b 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java
@@ -101,6 +101,9 @@
if (StopCodecAfterSurfaceRemovalCrashMediaServerQuirk.load()) {
quirks.add(new StopCodecAfterSurfaceRemovalCrashMediaServerQuirk());
}
+ if (ExtraSupportedQualityQuirk.load()) {
+ quirks.add(new ExtraSupportedQualityQuirk());
+ }
return quirks;
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/ExtraSupportedQualityQuirk.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/ExtraSupportedQualityQuirk.java
new file mode 100644
index 0000000..08fa121
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/ExtraSupportedQualityQuirk.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2022 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.video.internal.compat.quirk;
+
+import static android.media.CamcorderProfile.QUALITY_480P;
+import static android.media.CamcorderProfile.QUALITY_HIGH;
+
+import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_480P;
+import static androidx.camera.core.internal.utils.SizeUtil.getArea;
+import static androidx.camera.video.internal.config.VideoConfigUtil.toVideoEncoderConfig;
+import static androidx.camera.video.internal.utils.EncoderProfilesUtil.deriveVideoProfile;
+import static androidx.camera.video.internal.utils.EncoderProfilesUtil.getFirstVideoProfile;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.singletonList;
+
+import android.os.Build;
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.arch.core.util.Function;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.EncoderProfilesProvider;
+import androidx.camera.core.impl.EncoderProfilesProxy;
+import androidx.camera.core.impl.Quirk;
+import androidx.camera.video.VideoSpec;
+import androidx.camera.video.internal.encoder.VideoEncoderConfig;
+import androidx.camera.video.internal.encoder.VideoEncoderInfo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>QuirkSummary
+ * Bug Id: b/311311853
+ * Description: MotoC doesn't have any supported Quality for front camera. The reason is
+ * that the highest supported CamcorderProfile is QUALITY_CIF(352x288).
+ * By experimental result, QUALITY_480P(720x480) can be used to record video so
+ * we can add at least one supported quality.
+ * In addition, MotoC only has two camera id, "0" for rear and "1" for front, so
+ * it is feasible to simply check camera id "1" to create EncoderProfilesProxy.
+ * Device(s): moto c
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class ExtraSupportedQualityQuirk implements Quirk {
+ private static final String MOTO_C_FRONT_CAM_ID = "1";
+
+ static boolean load() {
+ return isMotoC();
+ }
+
+ private static boolean isMotoC() {
+ return "motorola".equalsIgnoreCase(Build.BRAND) && "moto c".equalsIgnoreCase(Build.MODEL);
+ }
+
+ /** Gets the EncoderProfilesProxy for the extra supported quality. */
+ @Nullable
+ public Map<Integer, EncoderProfilesProxy> getExtraEncoderProfiles(
+ @NonNull CameraInfoInternal cameraInfo,
+ @NonNull EncoderProfilesProvider encoderProfilesProvider,
+ @NonNull Function<VideoEncoderConfig, VideoEncoderInfo> videoEncoderInfoFinder) {
+ if (isMotoC()) {
+ return getExtraEncoderProfilesForMotoC(cameraInfo, encoderProfilesProvider,
+ videoEncoderInfoFinder);
+ }
+ return emptyMap();
+ }
+
+ // Create 480P EncoderProfiles for front Camera if not exist. Derive profile from QUALITY_HIGH
+ // which should be QUALITY_CIF. Even if QUALITY_HIGH is not QUALITY_CIF due to ROM
+ // update, the code should still work.
+ @Nullable
+ private Map<Integer, EncoderProfilesProxy> getExtraEncoderProfilesForMotoC(
+ @NonNull CameraInfoInternal cameraInfo,
+ @NonNull EncoderProfilesProvider encoderProfilesProvider,
+ @NonNull Function<VideoEncoderConfig, VideoEncoderInfo> videoEncoderInfoFinder) {
+ if (!MOTO_C_FRONT_CAM_ID.equals(cameraInfo.getCameraId())
+ || encoderProfilesProvider.hasProfile(QUALITY_480P)) {
+ return null;
+ }
+
+ // Derive from QUALITY_HIGH
+ EncoderProfilesProxy profilesHigh = encoderProfilesProvider.getAll(QUALITY_HIGH);
+ EncoderProfilesProxy.VideoProfileProxy videoProfileHigh =
+ getFirstVideoProfile(profilesHigh);
+ if (videoProfileHigh == null) {
+ return null;
+ }
+ Range<Integer> supportedBitrateRange =
+ getSupportedBitrateRange(videoProfileHigh, videoEncoderInfoFinder);
+ EncoderProfilesProxy.VideoProfileProxy derivedVideoProfile =
+ deriveVideoProfile(videoProfileHigh, RESOLUTION_480P, supportedBitrateRange);
+ EncoderProfilesProxy profiles480p =
+ EncoderProfilesProxy.ImmutableEncoderProfilesProxy.create(
+ profilesHigh.getDefaultDurationSeconds(),
+ profilesHigh.getRecommendedFileFormat(),
+ profilesHigh.getAudioProfiles(),
+ singletonList(derivedVideoProfile));
+
+ // Return mapping
+ Map<Integer, EncoderProfilesProxy> extraEncoderProfilesMap = new HashMap<>();
+ extraEncoderProfilesMap.put(QUALITY_480P, profiles480p);
+ // Update QUALITY_HIGH if necessary.
+ Size sizeHigh = new Size(videoProfileHigh.getWidth(), videoProfileHigh.getHeight());
+ if (getArea(RESOLUTION_480P) > getArea(sizeHigh)) {
+ extraEncoderProfilesMap.put(QUALITY_HIGH, profiles480p);
+ }
+ return extraEncoderProfilesMap;
+ }
+
+ @NonNull
+ private static Range<Integer> getSupportedBitrateRange(
+ @NonNull EncoderProfilesProxy.VideoProfileProxy videoProfile,
+ @NonNull Function<VideoEncoderConfig, VideoEncoderInfo> videoEncoderInfoFinder) {
+ VideoEncoderInfo encoderInfo =
+ videoEncoderInfoFinder.apply(toVideoEncoderConfig(videoProfile));
+ return encoderInfo != null ? encoderInfo.getSupportedBitrateRange()
+ : VideoSpec.BITRATE_RANGE_AUTO;
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/EncoderProfilesUtil.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/EncoderProfilesUtil.java
new file mode 100644
index 0000000..0f68caf
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/EncoderProfilesUtil.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023 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.video.internal.utils;
+
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.EncoderProfilesProxy;
+import androidx.camera.video.VideoSpec;
+import androidx.camera.video.internal.config.VideoConfigUtil;
+
+/** Utility class for encoder profiles related operations. */
+@RequiresApi(21)
+public class EncoderProfilesUtil {
+
+ private EncoderProfilesUtil() {
+ }
+
+ /**
+ * Derives a VideoProfile from a base VideoProfile and a new resolution.
+ *
+ * <p>Most fields are directly copied from the base VideoProfile except the bitrate, which will
+ * be scaled and clamped according to the new resolution and the given bitrate range.
+ *
+ * @param baseVideoProfile the VideoProfile to derive.
+ * @param newResolution the new resolution.
+ * @param bitrateRangeToClamp the bitrate range to clamp. This is usually the supported
+ * bitrate range of the target codec. Set
+ * {@link VideoSpec#BITRATE_RANGE_AUTO} as no clamp required.
+ * @return a derived VideoProfile.
+ */
+ @NonNull
+ public static EncoderProfilesProxy.VideoProfileProxy deriveVideoProfile(
+ @NonNull EncoderProfilesProxy.VideoProfileProxy baseVideoProfile,
+ @NonNull Size newResolution,
+ @NonNull Range<Integer> bitrateRangeToClamp) {
+
+ // "Guess" bit rate.
+ int derivedBitrate = VideoConfigUtil.scaleAndClampBitrate(
+ baseVideoProfile.getBitrate(),
+ baseVideoProfile.getBitDepth(), baseVideoProfile.getBitDepth(),
+ baseVideoProfile.getFrameRate(), baseVideoProfile.getFrameRate(),
+ newResolution.getWidth(), baseVideoProfile.getWidth(),
+ newResolution.getHeight(), baseVideoProfile.getHeight(),
+ bitrateRangeToClamp);
+
+ return EncoderProfilesProxy.VideoProfileProxy.create(
+ baseVideoProfile.getCodec(),
+ baseVideoProfile.getMediaType(),
+ derivedBitrate,
+ baseVideoProfile.getFrameRate(),
+ newResolution.getWidth(),
+ newResolution.getHeight(),
+ baseVideoProfile.getProfile(),
+ baseVideoProfile.getBitDepth(),
+ baseVideoProfile.getChromaSubsampling(),
+ baseVideoProfile.getHdrFormat()
+ );
+ }
+
+ /**
+ * Gets the first VideoProfile from the given EncoderProfileProxy. Returns null if
+ * encoderProfiles is null or there is no VideoProfile.
+ */
+ @Nullable
+ public static EncoderProfilesProxy.VideoProfileProxy getFirstVideoProfile(
+ @Nullable EncoderProfilesProxy encoderProfiles) {
+ if (encoderProfiles != null && !encoderProfiles.getVideoProfiles().isEmpty()) {
+ return encoderProfiles.getVideoProfiles().get(0);
+ }
+ return null;
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/QualityAddedEncoderProfilesProvider.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/QualityAddedEncoderProfilesProvider.java
new file mode 100644
index 0000000..7471dad
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/workaround/QualityAddedEncoderProfilesProvider.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 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.video.internal.workaround;
+
+import static androidx.core.util.Preconditions.checkState;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.arch.core.util.Function;
+import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.EncoderProfilesProvider;
+import androidx.camera.core.impl.EncoderProfilesProxy;
+import androidx.camera.core.impl.Quirks;
+import androidx.camera.video.internal.compat.quirk.ExtraSupportedQualityQuirk;
+import androidx.camera.video.internal.encoder.VideoEncoderConfig;
+import androidx.camera.video.internal.encoder.VideoEncoderInfo;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An implementation that adds extra supported qualities.
+ *
+ * @see ExtraSupportedQualityQuirk
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class QualityAddedEncoderProfilesProvider implements EncoderProfilesProvider {
+
+ private final EncoderProfilesProvider mProvider;
+ @Nullable
+ private Map<Integer, EncoderProfilesProxy> mExtraQualityToEncoderProfiles;
+
+ public QualityAddedEncoderProfilesProvider(
+ @NonNull EncoderProfilesProvider provider,
+ @NonNull Quirks quirks,
+ @NonNull CameraInfoInternal cameraInfo,
+ @NonNull Function<VideoEncoderConfig, VideoEncoderInfo> videoEncoderInfoFinder) {
+ mProvider = provider;
+
+ List<ExtraSupportedQualityQuirk> extraQuirks = quirks.getAll(
+ ExtraSupportedQualityQuirk.class);
+ if (!extraQuirks.isEmpty()) {
+ checkState(extraQuirks.size() == 1);
+ Map<Integer, EncoderProfilesProxy> extraEncoderProfiles = extraQuirks.get(0)
+ .getExtraEncoderProfiles(cameraInfo, mProvider, videoEncoderInfoFinder);
+ if (extraEncoderProfiles != null) {
+ mExtraQualityToEncoderProfiles = new HashMap<>(extraEncoderProfiles);
+ }
+ }
+ }
+
+ @Override
+ public boolean hasProfile(int quality) {
+ return getProfilesInternal(quality) != null;
+ }
+
+ @Nullable
+ @Override
+ public EncoderProfilesProxy getAll(int quality) {
+ return getProfilesInternal(quality);
+ }
+
+ @Nullable
+ private EncoderProfilesProxy getProfilesInternal(int quality) {
+ if (mExtraQualityToEncoderProfiles != null
+ && mExtraQualityToEncoderProfiles.containsKey(quality)) {
+ return mExtraQualityToEncoderProfiles.get(quality);
+ }
+ return mProvider.getAll(quality);
+ }
+}
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/internal/compat/quirk/ExtraSupportedQualityQuirkTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/internal/compat/quirk/ExtraSupportedQualityQuirkTest.kt
new file mode 100644
index 0000000..a671e68
--- /dev/null
+++ b/camera/camera-video/src/test/java/androidx/camera/video/internal/compat/quirk/ExtraSupportedQualityQuirkTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 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.video.internal.compat.quirk
+
+import android.media.CamcorderProfile.QUALITY_480P
+import android.media.CamcorderProfile.QUALITY_CIF
+import android.media.CamcorderProfile.QUALITY_HIGH
+import android.media.CamcorderProfile.QUALITY_LOW
+import android.media.CamcorderProfile.QUALITY_QCIF
+import android.os.Build
+import android.util.Size
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.impl.EncoderProfilesProxy
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.impl.EncoderProfilesUtil.DEFAULT_DURATION
+import androidx.camera.testing.impl.EncoderProfilesUtil.DEFAULT_OUTPUT_FORMAT
+import androidx.camera.testing.impl.EncoderProfilesUtil.RESOLUTION_480P
+import androidx.camera.testing.impl.EncoderProfilesUtil.RESOLUTION_CIF
+import androidx.camera.testing.impl.EncoderProfilesUtil.RESOLUTION_QCIF
+import androidx.camera.testing.impl.EncoderProfilesUtil.createFakeAudioProfileProxy
+import androidx.camera.testing.impl.EncoderProfilesUtil.createFakeVideoProfileProxy
+import androidx.camera.testing.impl.fakes.FakeEncoderProfilesProvider
+import androidx.camera.testing.impl.fakes.FakeVideoEncoderInfo
+import androidx.camera.video.internal.utils.EncoderProfilesUtil.getFirstVideoProfile
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.ShadowBuild
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class ExtraSupportedQualityQuirkTest {
+ companion object {
+ private const val MOTO_C_BRAND = "motorola"
+ private const val MOTO_C_MODEL = "moto c"
+ }
+
+ @Test
+ fun motoC_frontCamera_canGetExtraSupportedQuality() {
+ // Arrange: Simulate the condition of MotoC. See b/311311853.
+ ShadowBuild.setBrand(MOTO_C_BRAND)
+ ShadowBuild.setModel(MOTO_C_MODEL)
+ val cameraInfo = FakeCameraInfoInternal("1", CameraSelector.LENS_FACING_FRONT)
+ val profilesCif = EncoderProfilesProxy.ImmutableEncoderProfilesProxy.create(
+ DEFAULT_DURATION,
+ DEFAULT_OUTPUT_FORMAT,
+ listOf(createFakeAudioProfileProxy()),
+ listOf(createFakeVideoProfileProxy(
+ RESOLUTION_CIF.width,
+ RESOLUTION_CIF.height,
+ ))
+ )
+ val profilesQcif = EncoderProfilesProxy.ImmutableEncoderProfilesProxy.create(
+ DEFAULT_DURATION,
+ DEFAULT_OUTPUT_FORMAT,
+ listOf(createFakeAudioProfileProxy()),
+ listOf(createFakeVideoProfileProxy(
+ RESOLUTION_QCIF.width,
+ RESOLUTION_QCIF.height,
+ ))
+ )
+ val encoderProfileProvider = FakeEncoderProfilesProvider.Builder()
+ .add(QUALITY_HIGH, profilesCif)
+ .add(QUALITY_CIF, profilesCif)
+ .add(QUALITY_QCIF, profilesQcif)
+ .add(QUALITY_LOW, profilesQcif)
+ .build()
+
+ // Act.
+ val qualityEncoderProfilesMap = ExtraSupportedQualityQuirk()
+ .getExtraEncoderProfiles(cameraInfo, encoderProfileProvider) {
+ FakeVideoEncoderInfo()
+ }
+
+ // Assert: check QUALITY_480P
+ assertThat(qualityEncoderProfilesMap).isNotNull()
+ assertThat(qualityEncoderProfilesMap).containsKey(QUALITY_480P)
+ val profiles480p = qualityEncoderProfilesMap!![QUALITY_480P]
+ val videoProfile480p = getFirstVideoProfile(profiles480p)!!
+ assertThat(videoProfile480p.getResolution()).isEqualTo(RESOLUTION_480P)
+ // Assert: QUALITY_HIGH is the same as QUALITY_480P
+ assertThat(qualityEncoderProfilesMap).containsKey(QUALITY_HIGH)
+ assertThat(qualityEncoderProfilesMap[QUALITY_HIGH]).isEqualTo(profiles480p)
+ }
+
+ private fun EncoderProfilesProxy.VideoProfileProxy.getResolution() = Size(width, height)
+}
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/internal/workaround/QualityAddedEncoderProfilesProviderTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/internal/workaround/QualityAddedEncoderProfilesProviderTest.kt
new file mode 100644
index 0000000..f7dd799
--- /dev/null
+++ b/camera/camera-video/src/test/java/androidx/camera/video/internal/workaround/QualityAddedEncoderProfilesProviderTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 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.video.internal.workaround
+
+import android.media.CamcorderProfile.QUALITY_480P
+import android.os.Build
+import androidx.arch.core.util.Function
+import androidx.camera.core.impl.CameraInfoInternal
+import androidx.camera.core.impl.EncoderProfilesProvider
+import androidx.camera.core.impl.EncoderProfilesProxy
+import androidx.camera.core.impl.EncoderProfilesProxy.ImmutableEncoderProfilesProxy
+import androidx.camera.core.impl.Quirks
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.camera.testing.impl.EncoderProfilesUtil.DEFAULT_DURATION
+import androidx.camera.testing.impl.EncoderProfilesUtil.DEFAULT_OUTPUT_FORMAT
+import androidx.camera.testing.impl.EncoderProfilesUtil.RESOLUTION_480P
+import androidx.camera.testing.impl.EncoderProfilesUtil.createFakeAudioProfileProxy
+import androidx.camera.testing.impl.EncoderProfilesUtil.createFakeVideoProfileProxy
+import androidx.camera.testing.impl.fakes.FakeEncoderProfilesProvider
+import androidx.camera.testing.impl.fakes.FakeVideoEncoderInfo
+import androidx.camera.video.internal.compat.quirk.ExtraSupportedQualityQuirk
+import androidx.camera.video.internal.encoder.VideoEncoderConfig
+import androidx.camera.video.internal.encoder.VideoEncoderInfo
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class QualityAddedEncoderProfilesProviderTest {
+
+ @Test
+ fun canSupportExtraQuality() {
+ // Arrange.
+ val baseProvider = FakeEncoderProfilesProvider.Builder().build()
+ val encoderProfiles = ImmutableEncoderProfilesProxy.create(
+ DEFAULT_DURATION,
+ DEFAULT_OUTPUT_FORMAT,
+ listOf(createFakeAudioProfileProxy()),
+ listOf(createFakeVideoProfileProxy(RESOLUTION_480P.width, RESOLUTION_480P.height)),
+ )
+ val quirks = Quirks(listOf(FakeQuirk(mapOf(QUALITY_480P to encoderProfiles))))
+ val cameraInfo = FakeCameraInfoInternal()
+ val encoderInfo = FakeVideoEncoderInfo()
+
+ // Act.
+ val provider = QualityAddedEncoderProfilesProvider(baseProvider, quirks, cameraInfo) {
+ encoderInfo
+ }
+
+ // Assert.
+ assertThat(provider.getAll(QUALITY_480P)).isNotNull()
+ }
+
+ private class FakeQuirk(private val qualityToEncoderProfiles: Map<Int, EncoderProfilesProxy>) :
+ ExtraSupportedQualityQuirk() {
+
+ override fun getExtraEncoderProfiles(
+ cameraInfo: CameraInfoInternal,
+ encoderProfilesProvider: EncoderProfilesProvider,
+ videoEncoderInfoFinder: Function<VideoEncoderConfig, VideoEncoderInfo>
+ ) = qualityToEncoderProfiles
+ }
+}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
index 72d6808..518c7ae 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
@@ -836,7 +836,7 @@
*
* <p>The saved image is cropped to match the aspect ratio of the {@link PreviewView}. To
* take a picture with the maximum available resolution, make sure that the
- * {@link PreviewView}'s aspect ratio is 4:3.
+ * {@link PreviewView}'s aspect ratio matches the max JPEG resolution supported by the camera.
*
* @param outputFileOptions Options to store the newly captured image.
* @param executor The executor in which the callback methods will be run.
diff --git a/camera/camera-viewfinder-core/src/main/java/androidx/viewfinder/core/ZoomGestureDetector.java b/camera/camera-viewfinder-core/src/main/java/androidx/viewfinder/core/ZoomGestureDetector.java
deleted file mode 100644
index 1888109..0000000
--- a/camera/camera-viewfinder-core/src/main/java/androidx/viewfinder/core/ZoomGestureDetector.java
+++ /dev/null
@@ -1,591 +0,0 @@
-/*
- * Copyright 2023 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.viewfinder.core;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.os.Build;
-import android.os.Handler;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-
-/**
- * Detects scaling transformation gestures that interprets zooming events using the supplied
- * {@link MotionEvent}s.
- *
- * <p>The {@link OnZoomGestureListener} callback will notify users when a particular
- * gesture event has occurred.
- *
- * <p>This class should only be used with {@link MotionEvent}s reported via touch.
- *
- * <p>To use this class:
- * <ul>
- * <li>Create an instance of the {@code ZoomGestureDetector} for your
- * {@link View}
- * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
- * {@link #onTouchEvent(MotionEvent)}. The methods defined in your
- * callback will be executed when the events occur.
- * </ul>
- */
-// TODO(b/314701735): update the documentation with examples using camera classes.
-// TODO(b/314701401): convert to kotlin implementation.
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-public class ZoomGestureDetector {
- private static final String TAG = "ZoomGestureDetector";
- // The default minimum span that the detector interprets a zooming event with. It's set to 0
- // to give the most responsiveness.
- // TODO(b/314702145): define a different span if appropriate.
- private static final int DEFAULT_MIN_SPAN = 0;
-
- /**
- * The listener for receiving notifications when gestures occur.
- * If you want to listen for all the different gestures then implement
- * this interface.
- *
- * <p>An application will receive events in the following order:
- * <ul>
- * <li>One {@link OnZoomGestureListener#onZoomBegin(ZoomGestureDetector)}
- * <li>Zero or more {@link OnZoomGestureListener#onZoom(ZoomGestureDetector)}
- * <li>One {@link OnZoomGestureListener#onZoomEnd(ZoomGestureDetector)}
- * </ul>
- */
- public interface OnZoomGestureListener {
- /**
- * Responds to zooming events for a gesture in progress.
- * Reported by pointer motion.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return Whether or not the detector should consider this event
- * as handled. If an event was not handled, the detector
- * will continue to accumulate movement until an event is
- * handled. This can be useful if an application, for example,
- * only wants to update scaling factors if the change is
- * greater than 0.01.
- */
- default boolean onZoom(@NonNull ZoomGestureDetector detector) {
- return false;
- }
-
- /**
- * Responds to the beginning of a zooming gesture. Reported by
- * new pointers going down.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return Whether or not the detector should continue recognizing
- * this gesture. For example, if a gesture is beginning
- * with a focal point outside of a region where it makes
- * sense, onZoomBegin() may return false to ignore the
- * rest of the gesture.
- */
- default boolean onZoomBegin(@NonNull ZoomGestureDetector detector) {
- return true;
- }
-
- /**
- * Responds to the end of a zoom gesture. Reported by existing
- * pointers going up.
- *
- * <p>Once a zoom has ended, {@link ZoomGestureDetector#getFocusX()}
- * and {@link ZoomGestureDetector#getFocusY()} will return focal point
- * of the pointers remaining on the screen.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- */
- default void onZoomEnd(@NonNull ZoomGestureDetector detector) {
- // Intentionally empty
- }
- }
-
- private final Context mContext;
- private final OnZoomGestureListener mListener;
-
- private float mFocusX;
- private float mFocusY;
-
- private boolean mQuickZoomEnabled;
- private boolean mStylusZoomEnabled;
-
- private float mCurrSpan;
- private float mPrevSpan;
- private float mInitialSpan;
- private float mCurrSpanX;
- private float mCurrSpanY;
- private float mPrevSpanX;
- private float mPrevSpanY;
- private long mCurrTime;
- private long mPrevTime;
- private boolean mInProgress;
- private int mSpanSlop;
-
- private int mMinSpan;
-
- private final Handler mHandler;
-
- private float mAnchoredZoomStartX;
- private float mAnchoredZoomStartY;
- private int mAnchoredZoomMode = ANCHORED_ZOOM_MODE_NONE;
-
- private static final float SCALE_FACTOR = .5f;
- private static final int ANCHORED_ZOOM_MODE_NONE = 0;
- private static final int ANCHORED_ZOOM_MODE_DOUBLE_TAP = 1;
- private static final int ANCHORED_ZOOM_MODE_STYLUS = 2;
- private GestureDetector mGestureDetector;
-
- private boolean mEventBeforeOrAboveStartingGestureEvent;
-
- /**
- * Creates a ZoomGestureDetector with the supplied listener.
- * You may only use this constructor from a {@link android.os.Looper Looper} thread.
- *
- * @param context the application's context
- * @param listener the listener invoked for all the callbacks, this must
- * not be null.
- *
- * @throws NullPointerException if {@code listener} is null.
- */
- public ZoomGestureDetector(@NonNull Context context,
- @NonNull OnZoomGestureListener listener) {
- this(context, null, listener);
- }
-
- /**
- * Creates a ZoomGestureDetector with the supplied listener.
- * @see android.os.Handler#Handler()
- *
- * @param context the application's context
- * @param listener the listener invoked for all the callbacks, this must
- * not be null.
- * @param handler the handler to use for running deferred listener events.
- *
- * @throws NullPointerException if {@code listener} is null.
- */
- public ZoomGestureDetector(@NonNull Context context, @Nullable Handler handler,
- @NonNull OnZoomGestureListener listener) {
- this(context, ViewConfiguration.get(context).getScaledTouchSlop() * 2,
- DEFAULT_MIN_SPAN, handler, listener);
- }
-
- /**
- * Creates a ZoomGestureDetector with span slop and min span.
- *
- * @param context the application's context.
- * @param spanSlop the threshold for interpreting a touch movement as zooming.
- * @param minSpan the minimum threshold of zooming span. The span could be
- * overridden by other usages to specify a different zooming span, for instance,
- * if you need pinch gestures to continue closer together than the default.
- * @param listener the listener invoked for all the callbacks, this must not be null.
- * @param handler the handler to use for running deferred listener events.
- *
- * @throws NullPointerException if {@code listener} is null.
- */
- @SuppressLint("ExecutorRegistration")
- public ZoomGestureDetector(@NonNull Context context, int spanSlop,
- int minSpan, @Nullable Handler handler,
- @NonNull OnZoomGestureListener listener) {
- mContext = context;
- mListener = listener;
- mSpanSlop = spanSlop;
- mMinSpan = minSpan;
- mHandler = handler;
- // Quick zoom is enabled by default after JB_MR2
- final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
- setQuickZoomEnabled(true);
- }
- // Stylus zoom is enabled by default after LOLLIPOP_MR1
- if (targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
- setStylusZoomEnabled(true);
- }
- }
-
- /**
- * Accepts MotionEvents and dispatches events to a {@link OnZoomGestureListener}
- * when appropriate.
- *
- * <p>Applications should pass a complete and consistent event stream to this method.
- * A complete and consistent event stream involves all MotionEvents from the initial
- * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.</p>
- *
- * @param event The event to process
- * @return true if the event was processed and the detector wants to receive the
- * rest of the MotionEvents in this event stream.
- */
- public boolean onTouchEvent(@NonNull MotionEvent event) {
- mCurrTime = event.getEventTime();
-
- final int action = event.getActionMasked();
-
- // Forward the event to check for double tap gesture
- if (mQuickZoomEnabled) {
- mGestureDetector.onTouchEvent(event);
- }
-
- final int count = event.getPointerCount();
- final boolean isStylusButtonDown =
- (event.getButtonState() & MotionEvent.BUTTON_STYLUS_PRIMARY) != 0;
-
- final boolean anchoredZoomCancelled =
- mAnchoredZoomMode == ANCHORED_ZOOM_MODE_STYLUS && !isStylusButtonDown;
- final boolean streamComplete = action == MotionEvent.ACTION_UP
- || action == MotionEvent.ACTION_CANCEL
- || anchoredZoomCancelled;
-
- if (action == MotionEvent.ACTION_DOWN || streamComplete) {
- // Reset any scale in progress with the listener.
- // If it's an ACTION_DOWN we're beginning a new event stream.
- // This means the app probably didn't give us all the events. Shame on it.
- if (mInProgress) {
- mListener.onZoomEnd(this);
- mInProgress = false;
- mInitialSpan = 0;
- mAnchoredZoomMode = ANCHORED_ZOOM_MODE_NONE;
- } else if (inAnchoredZoomMode() && streamComplete) {
- mInProgress = false;
- mInitialSpan = 0;
- mAnchoredZoomMode = ANCHORED_ZOOM_MODE_NONE;
- }
-
- if (streamComplete) {
- return true;
- }
- }
-
- if (!mInProgress && mStylusZoomEnabled && !inAnchoredZoomMode()
- && !streamComplete && isStylusButtonDown) {
- // Start of a button zoom gesture
- mAnchoredZoomStartX = event.getX();
- mAnchoredZoomStartY = event.getY();
- mAnchoredZoomMode = ANCHORED_ZOOM_MODE_STYLUS;
- mInitialSpan = 0;
- }
-
- final boolean configChanged = action == MotionEvent.ACTION_DOWN
- || action == MotionEvent.ACTION_POINTER_UP
- || action == MotionEvent.ACTION_POINTER_DOWN
- || anchoredZoomCancelled;
-
- final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
- final int skipIndex = pointerUp ? event.getActionIndex() : -1;
-
- // Determine focal point
- float sumX = 0, sumY = 0;
- final int div = pointerUp ? count - 1 : count;
- final float focusX;
- final float focusY;
- if (inAnchoredZoomMode()) {
- // In anchored scale mode, the focal pt is always where the double tap
- // or button down gesture started
- focusX = mAnchoredZoomStartX;
- focusY = mAnchoredZoomStartY;
- if (event.getY() < focusY) {
- mEventBeforeOrAboveStartingGestureEvent = true;
- } else {
- mEventBeforeOrAboveStartingGestureEvent = false;
- }
- } else {
- for (int i = 0; i < count; i++) {
- if (skipIndex == i) continue;
- sumX += event.getX(i);
- sumY += event.getY(i);
- }
-
- focusX = sumX / div;
- focusY = sumY / div;
- }
-
- // Determine average deviation from focal point
- float devSumX = 0, devSumY = 0;
- for (int i = 0; i < count; i++) {
- if (skipIndex == i) continue;
-
- // Convert the resulting diameter into a radius.
- devSumX += Math.abs(event.getX(i) - focusX);
- devSumY += Math.abs(event.getY(i) - focusY);
- }
- final float devX = devSumX / div;
- final float devY = devSumY / div;
-
- // Span is the average distance between touch points through the focal point;
- // i.e. the diameter of the circle with a radius of the average deviation from
- // the focal point.
- final float spanX = devX * 2;
- final float spanY = devY * 2;
- final float span;
- if (inAnchoredZoomMode()) {
- span = spanY;
- } else {
- span = (float) Math.hypot(spanX, spanY);
- }
-
- // Dispatch begin/end events as needed.
- // If the configuration changes, notify the app to reset its current state by beginning
- // a fresh zoom event stream.
- final boolean wasInProgress = mInProgress;
- mFocusX = focusX;
- mFocusY = focusY;
- if (!inAnchoredZoomMode() && mInProgress && (span < mMinSpan || configChanged)) {
- mListener.onZoomEnd(this);
- mInProgress = false;
- mInitialSpan = span;
- }
- if (configChanged) {
- mPrevSpanX = mCurrSpanX = spanX;
- mPrevSpanY = mCurrSpanY = spanY;
- mInitialSpan = mPrevSpan = mCurrSpan = span;
- }
-
- final int minSpan = inAnchoredZoomMode() ? mSpanSlop : mMinSpan;
- if (!mInProgress && span >= minSpan
- && (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
- mPrevSpanX = mCurrSpanX = spanX;
- mPrevSpanY = mCurrSpanY = spanY;
- mPrevSpan = mCurrSpan = span;
- mPrevTime = mCurrTime;
- mInProgress = mListener.onZoomBegin(this);
- }
-
- // Handle motion; focal point and span/scale factor are changing.
- if (action == MotionEvent.ACTION_MOVE) {
- mCurrSpanX = spanX;
- mCurrSpanY = spanY;
- mCurrSpan = span;
-
- boolean updatePrev = true;
-
- if (mInProgress) {
- updatePrev = mListener.onZoom(this);
- }
-
- if (updatePrev) {
- mPrevSpanX = mCurrSpanX;
- mPrevSpanY = mCurrSpanY;
- mPrevSpan = mCurrSpan;
- mPrevTime = mCurrTime;
- }
- }
-
- return true;
- }
-
- private boolean inAnchoredZoomMode() {
- return mAnchoredZoomMode != ANCHORED_ZOOM_MODE_NONE;
- }
-
- /**
- * Set whether the associated {@link OnZoomGestureListener} should receive onZoom callbacks
- * when the user performs a doubleTap followed by a swipe.
- *
- * <p>If not set, this is enabled by default.
- *
- * @param enabled {@code true} to enable quick zooming, {@code false} to disable.
- */
- public void setQuickZoomEnabled(boolean enabled) {
- mQuickZoomEnabled = enabled;
- if (mQuickZoomEnabled && mGestureDetector == null) {
- GestureDetector.SimpleOnGestureListener gestureListener =
- new GestureDetector.SimpleOnGestureListener() {
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- // Double tap: start watching for a swipe
- mAnchoredZoomStartX = e.getX();
- mAnchoredZoomStartY = e.getY();
- mAnchoredZoomMode = ANCHORED_ZOOM_MODE_DOUBLE_TAP;
- return true;
- }
- };
- mGestureDetector = new GestureDetector(mContext, gestureListener, mHandler);
- }
- }
-
- /**
- * Return whether the quick zoom gesture, in which the user performs a double tap followed by a
- * swipe, should perform zooming.
- *
- * @see #setQuickZoomEnabled(boolean)
- */
- public boolean isQuickZoomEnabled() {
- return mQuickZoomEnabled;
- }
-
- /**
- * Sets whether the associates {@link OnZoomGestureListener} should receive
- * onZoom callbacks when the user uses a stylus and presses the button.
- *
- * <p>If not set, this is enabled by default.
- *
- * @param enabled {@code true} to enable stylus zooming, {@code false} to disable.
- */
- public void setStylusZoomEnabled(boolean enabled) {
- mStylusZoomEnabled = enabled;
- }
-
- /**
- * Return whether the stylus zoom gesture, in which the user uses a stylus and presses the
- * button, should perform zooming. {@see #setStylusScaleEnabled(boolean)}
- */
- public boolean isStylusZoomEnabled() {
- return mStylusZoomEnabled;
- }
-
- /**
- * Returns {@code true} if a zoom gesture is in progress.
- */
- public boolean isInProgress() {
- return mInProgress;
- }
-
- /**
- * Get the X coordinate of the current gesture's focal point.
- * If a gesture is in progress, the focal point is between
- * each of the pointers forming the gesture.
- *
- * <p>If {@link #isInProgress()} would return false, the result of this
- * function is undefined.
- *
- * @return X coordinate of the focal point in pixels.
- */
- public float getFocusX() {
- return mFocusX;
- }
-
- /**
- * Get the Y coordinate of the current gesture's focal point.
- * If a gesture is in progress, the focal point is between
- * each of the pointers forming the gesture.
- *
- * <p>If {@link #isInProgress()} would return false, the result of this
- * function is undefined.
- *
- * @return Y coordinate of the focal point in pixels.
- */
- public float getFocusY() {
- return mFocusY;
- }
-
- /**
- * Return the average distance between each of the pointers forming the
- * gesture in progress through the focal point.
- *
- * @return Distance between pointers in pixels.
- */
- public float getCurrentSpan() {
- return mCurrSpan;
- }
-
- /**
- * Return the average X distance between each of the pointers forming the
- * gesture in progress through the focal point.
- *
- * @return Distance between pointers in pixels.
- */
- public float getCurrentSpanX() {
- return mCurrSpanX;
- }
-
- /**
- * Return the average Y distance between each of the pointers forming the
- * gesture in progress through the focal point.
- *
- * @return Distance between pointers in pixels.
- */
- public float getCurrentSpanY() {
- return mCurrSpanY;
- }
-
- /**
- * Return the previous average distance between each of the pointers forming the
- * gesture in progress through the focal point.
- *
- * @return Previous distance between pointers in pixels.
- */
- public float getPreviousSpan() {
- return mPrevSpan;
- }
-
- /**
- * Return the previous average X distance between each of the pointers forming the
- * gesture in progress through the focal point.
- *
- * @return Previous distance between pointers in pixels.
- */
- public float getPreviousSpanX() {
- return mPrevSpanX;
- }
-
- /**
- * Return the previous average Y distance between each of the pointers forming the
- * gesture in progress through the focal point.
- *
- * @return Previous distance between pointers in pixels.
- */
- public float getPreviousSpanY() {
- return mPrevSpanY;
- }
-
- /**
- * Return the scaling factor from the previous zoom event to the current
- * event. This value is defined as
- * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
- *
- * @return The current scaling factor.
- */
- public float getScaleFactor() {
- if (inAnchoredZoomMode()) {
- // Drag is moving up; the further away from the gesture
- // start, the smaller the span should be, the closer,
- // the larger the span, and therefore the larger the scale
- final boolean scaleUp =
- (mEventBeforeOrAboveStartingGestureEvent
- && (mCurrSpan < mPrevSpan))
- || (!mEventBeforeOrAboveStartingGestureEvent
- && (mCurrSpan > mPrevSpan));
- final float spanDiff = (Math.abs(1 - (mCurrSpan / mPrevSpan)) * SCALE_FACTOR);
- return mPrevSpan <= mSpanSlop ? 1 : scaleUp ? (1 + spanDiff) : (1 - spanDiff);
- }
- return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
- }
-
- /**
- * Return the time difference in milliseconds between the previous
- * accepted zooming event and the current zooming event.
- *
- * @return Time difference since the last zooming event in milliseconds.
- */
- public long getTimeDelta() {
- return mCurrTime - mPrevTime;
- }
-
- /**
- * Return the event time of the current event being processed.
- *
- * @return Current event time in milliseconds.
- */
- public long getEventTime() {
- return mCurrTime;
- }
-}
diff --git a/camera/camera-viewfinder-core/src/main/java/androidx/viewfinder/core/ZoomGestureDetector.kt b/camera/camera-viewfinder-core/src/main/java/androidx/viewfinder/core/ZoomGestureDetector.kt
new file mode 100644
index 0000000..195b4cb
--- /dev/null
+++ b/camera/camera-viewfinder-core/src/main/java/androidx/viewfinder/core/ZoomGestureDetector.kt
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2023 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.viewfinder.core
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewConfiguration
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.viewfinder.core.ZoomGestureDetector.OnZoomGestureListener
+import kotlin.math.abs
+import kotlin.math.hypot
+
+/**
+ * Detects scaling transformation gestures that interprets zooming events using the supplied
+ * [MotionEvent]s.
+ *
+ * The [OnZoomGestureListener] callback will notify users when a particular
+ * gesture event has occurred.
+ *
+ * This class should only be used with [MotionEvent]s reported via touch.
+ *
+ * To use this class:
+ * - Create an instance of the `ZoomGestureDetector` for your [View]
+ * - In the [View.onTouchEvent] method ensure you call [onTouchEvent]. The methods defined in your
+ * callback will be executed when the events occur.
+ */
+// TODO(b/314701735): update the documentation with examples using camera classes.
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class ZoomGestureDetector @SuppressLint("ExecutorRegistration") constructor(
+ private val context: Context,
+ private val spanSlop: Int = ViewConfiguration.get(context).scaledTouchSlop * 2,
+ private val minSpan: Int = DEFAULT_MIN_SPAN,
+ private val listener: OnZoomGestureListener
+) {
+ /**
+ * The listener for receiving notifications when gestures occur.
+ *
+ * An application will receive events in the following order:
+ * - One [ZOOM_GESTURE_BEGIN]
+ * - Zero or more [ZOOM_GESTURE_MOVE]
+ * - One [ZOOM_GESTURE_END]
+ */
+ fun interface OnZoomGestureListener {
+ /**
+ * Responds to the events of a zooming gesture.
+ *
+ * Return `true` to indicate the event is handled by the listener.
+ * - For [ZOOM_GESTURE_MOVE] events, the detector will continue to accumulate movement if
+ * it's not handled. This can be useful if an application, for example, only wants to update
+ * scaling factors if the change is greater than `0.01`.
+ * - For [ZOOM_GESTURE_BEGIN] events, the detector will ignore the rest of the gesture if
+ * it's not handled. For example, if a gesture is beginning with a focal point outside of a
+ * region where it makes sense, [ZOOM_GESTURE_BEGIN] event may return `false` to ignore the
+ * rest of the gesture.
+ * - For [ZOOM_GESTURE_END] events, the return value is ignored and the zoom gesture will
+ * end regardless of what is returned.
+ *
+ * Once receiving [ZOOM_GESTURE_END] event, [focusX] and [focusY] will return focal point of
+ * the pointers remaining on the screen.
+ *
+ * @type The type of the event. Possible values include [ZOOM_GESTURE_MOVE],
+ * [ZOOM_GESTURE_BEGIN] and [ZOOM_GESTURE_END].
+ * @param detector The detector reporting the event - use this to retrieve extended info
+ * about event state.
+ * @return Whether or not the detector should consider this event as handled.
+ */
+ fun onZoom(type: Int, detector: ZoomGestureDetector): Boolean
+ }
+
+ /**
+ * The X coordinate of the current gesture's focal point in pixels. If a gesture is in progress,
+ * the focal point is between each of the pointers forming the gesture.
+ *
+ * If [isInProgress] would return `false`, the result of this function is undefined.
+ */
+ var focusX = 0f
+ private set
+
+ /**
+ * The Y coordinate of the current gesture's focal point in pixels. If a gesture is in progress,
+ * the focal point is between each of the pointers forming the gesture.
+ *
+ * If [isInProgress] would return `false`, the result of this function is undefined.
+ */
+ var focusY = 0f
+ private set
+
+ /**
+ * Whether the quick zoom gesture, in which the user performs a double tap followed by a swipe,
+ * should perform zooming.
+ *
+ * If not set, this is enabled by default.
+ */
+ var isQuickZoomEnabled: Boolean = true
+ set(enabled) {
+ field = enabled
+ if (field && gestureDetector == null) {
+ val gestureListener: GestureDetector.SimpleOnGestureListener =
+ object : GestureDetector.SimpleOnGestureListener() {
+ override fun onDoubleTap(e: MotionEvent): Boolean {
+ // Double tap: start watching for a swipe
+ anchoredZoomStartX = e.x
+ anchoredZoomStartY = e.y
+ anchoredZoomMode = ANCHORED_ZOOM_MODE_DOUBLE_TAP
+ return true
+ }
+ }
+ gestureDetector = GestureDetector(context, gestureListener)
+ }
+ }
+
+ /**
+ * Whether the stylus zoom gesture, in which the user uses a stylus and presses the button,
+ * should perform zooming.
+ *
+ * If not set, this is enabled by default.
+ */
+ var isStylusZoomEnabled = true
+
+ /**
+ * The average distance in pixels between each of the pointers forming the gesture in progress
+ * through the focal point.
+ */
+ var currentSpan = 0f
+ private set
+
+ /**
+ * The previous average distance in pixels between each of the pointers forming the gesture in
+ * progress through the focal point.
+ */
+ var previousSpan = 0f
+ private set
+
+ /**
+ * The average X distance in pixels between each of the pointers forming the gesture in progress
+ * through the focal point.
+ */
+ var currentSpanX = 0f
+ private set
+
+ /**
+ * The average Y distance in pixels between each of the pointers forming the gesture in progress
+ * through the focal point.
+ */
+ var currentSpanY = 0f
+ private set
+
+ /**
+ * The previous average X distance in pixels between each of the pointers forming the gesture in
+ * progress through the focal point.
+ */
+ var previousSpanX = 0f
+ private set
+
+ /**
+ * The previous average Y distance in pixels between each of the pointers forming the gesture in
+ * progress through the focal point.
+ */
+ var previousSpanY = 0f
+ private set
+
+ /**
+ * The event time in milliseconds of the current event being processed.
+ */
+ var eventTime: Long = 0
+ private set
+
+ /**
+ * Whether a zoom gesture is in progress.
+ */
+ var isInProgress = false
+ private set
+
+ private var initialSpan = 0f
+ private var prevTime: Long = 0
+ private var anchoredZoomStartX = 0f
+ private var anchoredZoomStartY = 0f
+ private var anchoredZoomMode = ANCHORED_ZOOM_MODE_NONE
+ private var gestureDetector: GestureDetector? = null
+ private var eventBeforeOrAboveStartingGestureEvent = false
+
+ /**
+ * Accepts [MotionEvent]s and dispatches events to a [OnZoomGestureListener] when appropriate.
+ *
+ * Applications should pass a complete and consistent event stream to this method.
+ *
+ * A complete and consistent event stream involves all [MotionEvent]s from the initial
+ * [MotionEvent.ACTION_DOWN] to the final [MotionEvent.ACTION_UP] or
+ * [MotionEvent.ACTION_CANCEL].
+ *
+ * @param event The event to process.
+ * @return `true` if the event was processed and the detector wants to receive the
+ * rest of the MotionEvents in this event stream.
+ */
+ fun onTouchEvent(event: MotionEvent): Boolean {
+ eventTime = event.eventTime
+
+ val action = event.actionMasked
+
+ // Forward the event to check for double tap gesture
+ if (isQuickZoomEnabled) {
+ gestureDetector!!.onTouchEvent(event)
+ }
+
+ val count = event.pointerCount
+ val isStylusButtonDown = (event.buttonState and MotionEvent.BUTTON_STYLUS_PRIMARY) != 0
+
+ val anchoredZoomCancelled =
+ anchoredZoomMode == ANCHORED_ZOOM_MODE_STYLUS && !isStylusButtonDown
+ val streamComplete = action == MotionEvent.ACTION_UP ||
+ action == MotionEvent.ACTION_CANCEL ||
+ anchoredZoomCancelled
+
+ if (action == MotionEvent.ACTION_DOWN || streamComplete) {
+ // Reset any scale in progress with the listener.
+ // If it's an ACTION_DOWN we're beginning a new event stream.
+ // This means the app probably didn't give us all the events. Shame on it.
+ if (isInProgress) {
+ listener.onZoom(ZOOM_GESTURE_END, this)
+ isInProgress = false
+ initialSpan = 0f
+ anchoredZoomMode = ANCHORED_ZOOM_MODE_NONE
+ } else if (inAnchoredZoomMode() && streamComplete) {
+ isInProgress = false
+ initialSpan = 0f
+ anchoredZoomMode = ANCHORED_ZOOM_MODE_NONE
+ }
+ if (streamComplete) {
+ return true
+ }
+ }
+
+ if (!isInProgress &&
+ isStylusZoomEnabled &&
+ !inAnchoredZoomMode() &&
+ !streamComplete &&
+ isStylusButtonDown) {
+ // Start of a button zoom gesture
+ anchoredZoomStartX = event.x
+ anchoredZoomStartY = event.y
+ anchoredZoomMode = ANCHORED_ZOOM_MODE_STYLUS
+ initialSpan = 0f
+ }
+
+ val configChanged = action == MotionEvent.ACTION_DOWN ||
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN ||
+ anchoredZoomCancelled
+
+ val pointerUp = action == MotionEvent.ACTION_POINTER_UP
+ val skipIndex = if (pointerUp) event.actionIndex else -1
+
+ // Determine focal point
+ var sumX = 0f
+ var sumY = 0f
+ val div = if (pointerUp) count - 1 else count
+ val focusX: Float
+ val focusY: Float
+ if (inAnchoredZoomMode()) {
+ // In anchored scale mode, the focal pt is always where the double tap
+ // or button down gesture started
+ focusX = anchoredZoomStartX
+ focusY = anchoredZoomStartY
+ eventBeforeOrAboveStartingGestureEvent = if (event.y < focusY) {
+ true
+ } else {
+ false
+ }
+ } else {
+ for (i in 0 until count) {
+ if (skipIndex == i) continue
+ sumX += event.getX(i)
+ sumY += event.getY(i)
+ }
+ focusX = sumX / div
+ focusY = sumY / div
+ }
+
+ // Determine average deviation from focal point
+ var devSumX = 0f
+ var devSumY = 0f
+ for (i in 0 until count) {
+ if (skipIndex == i) continue
+
+ // Convert the resulting diameter into a radius.
+ devSumX += abs((event.getX(i) - focusX))
+ devSumY += abs((event.getY(i) - focusY))
+ }
+ val devX = devSumX / div
+ val devY = devSumY / div
+
+ // Span is the average distance between touch points through the focal point;
+ // i.e. the diameter of the circle with a radius of the average deviation from
+ // the focal point.
+ val spanX = devX * 2
+ val spanY = devY * 2
+ val span: Float = if (inAnchoredZoomMode()) {
+ spanY
+ } else {
+ hypot(spanX, spanY)
+ }
+
+ // Dispatch begin/end events as needed.
+ // If the configuration changes, notify the app to reset its current state by beginning
+ // a fresh zoom event stream.
+ val wasInProgress = isInProgress
+ this.focusX = focusX
+ this.focusY = focusY
+ if (!inAnchoredZoomMode() && isInProgress && (span < minSpan || configChanged)) {
+ listener.onZoom(ZOOM_GESTURE_END, this)
+ isInProgress = false
+ initialSpan = span
+ }
+ if (configChanged) {
+ currentSpanX = spanX
+ previousSpanX = currentSpanX
+ currentSpanY = spanY
+ previousSpanY = currentSpanY
+ currentSpan = span
+ previousSpan = currentSpan
+ initialSpan = previousSpan
+ }
+ val minSpan = if (inAnchoredZoomMode()) spanSlop else minSpan
+ if (!isInProgress &&
+ span >= minSpan &&
+ (wasInProgress || abs((span - initialSpan)) > spanSlop)) {
+ currentSpanX = spanX
+ previousSpanX = currentSpanX
+ currentSpanY = spanY
+ previousSpanY = currentSpanY
+ currentSpan = span
+ previousSpan = currentSpan
+ prevTime = eventTime
+ isInProgress = listener.onZoom(ZOOM_GESTURE_BEGIN, this)
+ }
+
+ // Handle motion; focal point and span/scale factor are changing.
+ if (action == MotionEvent.ACTION_MOVE) {
+ currentSpanX = spanX
+ currentSpanY = spanY
+ currentSpan = span
+
+ var updatePrev = true
+
+ if (isInProgress) {
+ updatePrev = listener.onZoom(ZOOM_GESTURE_MOVE, this)
+ }
+
+ if (updatePrev) {
+ previousSpanX = currentSpanX
+ previousSpanY = currentSpanY
+ previousSpan = currentSpan
+ prevTime = eventTime
+ }
+ }
+ return true
+ }
+
+ private fun inAnchoredZoomMode(): Boolean {
+ return anchoredZoomMode != ANCHORED_ZOOM_MODE_NONE
+ }
+
+ val scaleFactor: Float
+ /**
+ * Returns the scaling factor from the previous zoom event to the current event. This value
+ * is defined as ([currentSpan] / [previousSpan]).
+ *
+ * @return The current scaling factor.
+ */
+ get() {
+ if (inAnchoredZoomMode()) {
+ // Drag is moving up; the further away from the gesture start, the smaller the span
+ // should be, the closer, the larger the span, and therefore the larger the scale
+ val scaleUp = eventBeforeOrAboveStartingGestureEvent &&
+ currentSpan < previousSpan ||
+ !eventBeforeOrAboveStartingGestureEvent &&
+ currentSpan > previousSpan
+ val spanDiff = (abs((1 - currentSpan / previousSpan)) * SCALE_FACTOR)
+ return if (previousSpan <= spanSlop) 1.0f
+ else if (scaleUp) 1.0f + spanDiff
+ else 1.0f - spanDiff
+ }
+ return if (previousSpan > 0) currentSpan / previousSpan else 1.0f
+ }
+
+ val timeDelta: Long
+ /**
+ * Returns the time difference in milliseconds between the previous accepted zooming event
+ * and the current zooming event.
+ *
+ * @return Time difference since the last zooming event in milliseconds.
+ */
+ get() = eventTime - prevTime
+
+ companion object {
+ private const val TAG = "ZoomGestureDetector"
+
+ /** The moving events of a gesture in progress. Reported by pointer motion. */
+ const val ZOOM_GESTURE_MOVE = 0
+ /** The beginning of a zoom gesture. Reported by new pointers going down. */
+ const val ZOOM_GESTURE_BEGIN = 1
+ /** The end of a zoom gesture. Reported by existing pointers going up. */
+ const val ZOOM_GESTURE_END = 2
+
+ // The default minimum span that the detector interprets a zooming event with. It's set to 0
+ // to give the most responsiveness.
+ // TODO(b/314702145): define a different span if appropriate.
+ private const val DEFAULT_MIN_SPAN = 0
+ private const val SCALE_FACTOR = .5f
+ private const val ANCHORED_ZOOM_MODE_NONE = 0
+ private const val ANCHORED_ZOOM_MODE_DOUBLE_TAP = 1
+ private const val ANCHORED_ZOOM_MODE_STYLUS = 2
+ }
+}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FovDeviceTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FovDeviceTest.kt
new file mode 100644
index 0000000..3d160b0
--- /dev/null
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FovDeviceTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2023 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.integration.core
+
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
+import androidx.camera.core.CameraInfo
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraXConfig
+import androidx.camera.core.impl.CameraInfoInternal
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.testing.impl.CameraPipeConfigTestRule
+import androidx.camera.testing.impl.CameraUtil
+import androidx.camera.testing.impl.CameraXUtil
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth
+import java.util.Collections
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+private val DEFAULT_CAMERA_ID_GROUP = Collections.unmodifiableSet(setOf("0", "1"))
+
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = 21)
+class FovDeviceTest(
+ private val cameraId: String,
+ private val implName: String,
+ private val cameraXConfig: CameraXConfig
+) {
+ @get:Rule
+ val cameraPipeConfigTestRule = CameraPipeConfigTestRule(
+ active = implName == CameraPipeConfig::class.simpleName,
+ )
+
+ @get:Rule
+ val cameraRule = CameraUtil.grantCameraPermissionAndPreTest(
+ CameraUtil.PreTestCameraIdList(cameraXConfig)
+ )
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "cameraId: {0}, implName: {1}")
+ fun data(): List<Array<Any?>> {
+ val paramList = mutableListOf<Array<Any?>>()
+ CameraUtil.getBackwardCompatibleCameraIdListOrThrow().forEach { cameraId ->
+ paramList.add(
+ arrayOf(
+ cameraId,
+ Camera2Config::class.simpleName,
+ Camera2Config.defaultConfig()
+ )
+ )
+ paramList.add(
+ arrayOf(
+ cameraId,
+ CameraPipeConfig::class.simpleName,
+ CameraPipeConfig.defaultConfig()
+ )
+ )
+ }
+ return paramList
+ }
+ }
+
+ private lateinit var cameraUseCaseAdapter: CameraUseCaseAdapter
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Before
+ fun setUp() {
+ CameraXUtil.initialize(
+ context,
+ cameraXConfig
+ ).get()
+
+ val cameraSelector = CameraSelector.Builder().addCameraFilter { cameraInfoList ->
+ val filteredList = ArrayList<CameraInfo>()
+ cameraInfoList.forEach { cameraInfo ->
+ if ((cameraInfo as CameraInfoInternal).cameraId == cameraId) {
+ filteredList.add(cameraInfo)
+ }
+ }
+ filteredList
+ }.build()
+ cameraUseCaseAdapter =
+ CameraUtil.createCameraUseCaseAdapter(context, cameraSelector)
+ }
+
+ @After
+ fun tearDown() {
+ CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
+ }
+
+ @RequiresApi(Build.VERSION_CODES.R)
+ @Test
+ fun intrinsicZoomRatio_greaterThanZero() {
+ Truth.assertThat(cameraUseCaseAdapter.cameraInfo.intrinsicZoomRatio).isGreaterThan(0)
+ }
+
+ @Test
+ fun intrinsicZoomRatio_defaultToOne() {
+ Assume.assumeTrue(DEFAULT_CAMERA_ID_GROUP.contains(cameraId))
+ Truth.assertThat(cameraUseCaseAdapter.cameraInfo.intrinsicZoomRatio).isEqualTo(1.0F)
+ }
+}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/OpenCloseCaptureSessionStressTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/OpenCloseCaptureSessionStressTest.kt
index cb494fb..39430c27 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/OpenCloseCaptureSessionStressTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/OpenCloseCaptureSessionStressTest.kt
@@ -38,6 +38,7 @@
import androidx.camera.testing.impl.LabTestRule
import androidx.camera.testing.impl.StressTestRule
import androidx.camera.testing.impl.SurfaceTextureProvider
+import androidx.camera.testing.impl.WakelockEmptyActivityRule
import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
import androidx.camera.video.Recorder
import androidx.camera.video.VideoCapture
@@ -84,6 +85,9 @@
@get:Rule
val repeatRule = RepeatRule()
+ @get:Rule
+ val wakelockEmptyActivityRule = WakelockEmptyActivityRule()
+
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var cameraProvider: ProcessCameraProvider
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
index e639565..6a2edcb 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
@@ -47,6 +47,7 @@
import androidx.camera.testing.impl.GLUtil
import androidx.camera.testing.impl.SurfaceTextureProvider
import androidx.camera.testing.impl.SurfaceTextureProvider.SurfaceTextureCallback
+import androidx.camera.testing.impl.WakelockEmptyActivityRule
import androidx.core.util.Consumer
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
@@ -90,6 +91,9 @@
PreTestCameraIdList(cameraConfig)
)
+ @get:Rule
+ val wakelockEmptyActivityRule = WakelockEmptyActivityRule()
+
companion object {
private const val ANY_THREAD_NAME = "any-thread-name"
private val DEFAULT_RESOLUTION: Size by lazy { Size(640, 480) }
diff --git a/camera/integration-tests/extensionstestapp/build.gradle b/camera/integration-tests/extensionstestapp/build.gradle
index b96295f..7e35cf7 100644
--- a/camera/integration-tests/extensionstestapp/build.gradle
+++ b/camera/integration-tests/extensionstestapp/build.gradle
@@ -64,7 +64,6 @@
// Guava
implementation(libs.guavaAndroid)
implementation("androidx.viewpager2:viewpager2:1.0.0")
- implementation(project(':camera:camera-camera2-pipe-integration'))
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
@@ -78,7 +77,6 @@
exclude(group:"androidx.test")
}
androidTestImplementation(project(":internal-testutils-runtime"))
- androidTestImplementation(project(":concurrent:concurrent-futures"))
androidTestCompileOnly(project(":camera:camera-extensions-stub"))
// Testing resource dependency for manifest
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 859569e..2a452c7 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
@@ -73,7 +73,6 @@
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.camera2.interop.Camera2Interop;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
-import androidx.camera.camera2.pipe.integration.CameraPipeConfig;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraControl;
import androidx.camera.core.CameraInfo;
@@ -93,7 +92,6 @@
import androidx.camera.integration.extensions.utils.ExtensionModeUtil;
import androidx.camera.integration.extensions.utils.FpsRecorder;
import androidx.camera.integration.extensions.validation.CameraValidationResultActivity;
-import androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.video.MediaStoreOutputOptions;
import androidx.camera.video.PendingRecording;
@@ -132,8 +130,6 @@
private static final String TAG = "CameraExtensionActivity";
private static final int PERMISSIONS_REQUEST_CODE = 42;
- public static final String INTENT_EXTRA_CAMERA_IMPLEMENTATION = "camera_implementation";
- public static final String CAMERA_PIPE_IMPLEMENTATION_OPTION = "camera_pipe";
private CameraSelector mCurrentCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
@@ -602,10 +598,7 @@
mPreviewView = (PreviewView) viewFinderStub.inflate();
mPreviewView.setImplementationMode(PreviewView.ImplementationMode.COMPATIBLE);
setupPinchToZoomAndTapToFocus(mPreviewView);
- String cameraImplementation =
- getIntent().getStringExtra(INTENT_EXTRA_CAMERA_IMPLEMENTATION);
Futures.addCallback(setupPermissions(), new FutureCallback<Boolean>() {
- @OptIn(markerClass = ExperimentalCameraProviderConfiguration.class)
@Override
public void onSuccess(@Nullable Boolean result) {
mPermissionsGranted = Preconditions.checkNotNull(result);
@@ -618,10 +611,6 @@
return;
}
- if (cameraImplementation != null &&
- cameraImplementation.equals(CAMERA_PIPE_IMPLEMENTATION_OPTION)) {
- ProcessCameraProvider.configureInstance(CameraPipeConfig.defaultConfig());
- }
ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
ProcessCameraProvider.getInstance(CameraExtensionsActivity.this);
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisOrientationConfigChangesTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisOrientationConfigChangesTest.kt
index f1ca9eb..a14034a 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisOrientationConfigChangesTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisOrientationConfigChangesTest.kt
@@ -69,7 +69,10 @@
"Known issue on this device. Please see b/198744779",
listOf(
"redmi note 9s",
- "redmi note 8"
+ "redmi note 8",
+ "m2003j15sc", // Redmi Note 9
+ "m2006c3lg", // Redmi 9A
+ "m2006c3mg" // Redmi 9C
).contains(Build.MODEL.lowercase(Locale.US)) && rotation == Surface.ROTATION_180
)
setUp(lensFacing)
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
index a4f4bf4..32c1c7e 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureOrientationConfigChangesTest.kt
@@ -74,7 +74,10 @@
"Known issue on this device. Please see b/198744779",
listOf(
"redmi note 9s",
- "redmi note 8"
+ "redmi note 8",
+ "m2003j15sc", // Redmi Note 9
+ "m2006c3lg", // Redmi 9A
+ "m2006c3mg" // Redmi 9C
).contains(Build.MODEL.lowercase(Locale.US)) && rotation == Surface.ROTATION_180
)
CoreAppTestUtil.assumeCompatibleDevice()
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/ShowcaseSession.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/ShowcaseSession.java
index 5dd4719..4dc3cc8 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/ShowcaseSession.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/ShowcaseSession.java
@@ -33,9 +33,9 @@
import androidx.car.app.sample.showcase.common.renderer.Renderer;
import androidx.car.app.sample.showcase.common.renderer.SurfaceController;
import androidx.car.app.sample.showcase.common.screens.ResultDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigatingDemoScreen;
import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigationNotificationService;
import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigationNotificationsDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.NavigatingDemoScreen;
import androidx.car.app.sample.showcase.common.screens.userinteractions.RequestPermissionScreen;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/StartScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/StartScreen.java
index 3d7465b..7f9c9f9 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/StartScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/StartScreen.java
@@ -25,6 +25,7 @@
import androidx.car.app.model.ListTemplate;
import androidx.car.app.model.Row;
import androidx.car.app.model.Template;
+import androidx.car.app.sample.showcase.common.screens.MapDemosScreen;
import androidx.car.app.sample.showcase.common.screens.NavigationDemosScreen;
import androidx.car.app.sample.showcase.common.screens.SettingsScreen;
import androidx.car.app.sample.showcase.common.screens.TemplateLayoutsDemoScreen;
@@ -52,6 +53,10 @@
listBuilder.addItem(createRowForScreen(R.string.user_interactions_demo_title,
new UserInteractionsDemoScreen(1, getCarContext())));
+ listBuilder.addItem(createRowForScreen(R.string.map_demos_title,
+ createCarIconForImage(R.drawable.ic_place_white_24dp),
+ new MapDemosScreen(getCarContext())));
+
listBuilder.addItem(createRowForScreen(R.string.nav_demos_title,
createCarIconForImage(R.drawable.ic_map_white_48dp),
NavigationDemosScreen.createScreen(getCarContext())));
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/MapDemosScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/MapDemosScreen.java
new file mode 100644
index 0000000..7a96c58
--- /dev/null
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/MapDemosScreen.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 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.car.app.sample.showcase.common.screens;
+
+import static androidx.car.app.model.Action.BACK;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.ListTemplate;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.sample.showcase.common.R;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.MapWithContentDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.PlaceListNavigationTemplateDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.PlaceListTemplateBrowseDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.RoutePreviewDemoScreen;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** A screen demonstrating different template layouts. */
+public final class MapDemosScreen extends Screen {
+
+ public MapDemosScreen(@NonNull CarContext carContext) {
+ super(carContext);
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate() {
+ List<Row> screenList = new ArrayList<>();
+ screenList.add(buildBrowsableRow(new MapWithContentDemoScreen(getCarContext()),
+ R.string.map_with_content_demo_title));
+ screenList.add(buildRowForTemplate(new PlaceListNavigationTemplateDemoScreen(
+ getCarContext()),
+ R.string.place_list_nav_template_demo_title));
+ screenList.add(buildRowForTemplate(new RoutePreviewDemoScreen(getCarContext()),
+ R.string.route_preview_template_demo_title));
+ screenList.add(buildRowForTemplate(new PlaceListTemplateBrowseDemoScreen(getCarContext()),
+ R.string.place_list_template_demo_title));
+
+ ItemList.Builder listBuilder = new ItemList.Builder();
+
+ for (int i = 0; i < screenList.size(); i++) {
+ listBuilder.addItem(screenList.get(i));
+ }
+
+ return new ListTemplate.Builder()
+ .setSingleList(listBuilder.build())
+ .setTitle(getCarContext().getString(R.string.map_demos_title))
+ .setHeaderAction(BACK)
+ .build();
+ }
+
+ private Row buildRowForTemplate(Screen screen, int title) {
+ return new Row.Builder()
+ .setTitle(getCarContext().getString(title))
+ .setOnClickListener(() -> getScreenManager().push(screen))
+ .build();
+ }
+
+ private Row buildBrowsableRow(Screen screen, int title) {
+ return new Row.Builder()
+ .setTitle(getCarContext().getString(title))
+ .setOnClickListener(() -> getScreenManager().push(screen))
+ .setBrowsable(true)
+ .build();
+ }
+}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/NavigationDemosScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/NavigationDemosScreen.java
index f9de967..032dba6 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/NavigationDemosScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/NavigationDemosScreen.java
@@ -20,21 +20,15 @@
import androidx.car.app.CarContext;
import androidx.car.app.Screen;
import androidx.car.app.ScreenManager;
-import androidx.car.app.model.CarIcon;
import androidx.car.app.model.Row;
import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.MapTemplateWithListDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.MapTemplateWithPaneDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.MapTemplateWithToggleDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.ArrivedDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.JunctionImageDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.LoadingDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigatingDemoScreen;
import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigationMapOnlyScreen;
import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigationNotificationsDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigationTemplateDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.PlaceListNavigationTemplateDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.PlaceListTemplateBrowseDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.RoutePreviewDemoScreen;
import androidx.car.app.sample.showcase.common.screens.paging.PagedListTemplate;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
import java.util.ArrayList;
import java.util.List;
@@ -60,21 +54,26 @@
screenList.add(createRow(
screenManager,
- buildCarIcon(R.drawable.ic_explore_white_24dp),
- mCarContext.getString(R.string.nav_template_demos_title),
- new NavigationTemplateDemoScreen(mCarContext)
+ mCarContext.getString(R.string.loading_demo_title),
+ new LoadingDemoScreen(mCarContext)
));
screenList.add(createRow(
screenManager,
- mCarContext.getString(R.string.place_list_nav_template_demo_title),
- new PlaceListNavigationTemplateDemoScreen(mCarContext)
+ mCarContext.getString(R.string.arrived_demo_title),
+ new ArrivedDemoScreen(mCarContext)
));
screenList.add(createRow(
screenManager,
- mCarContext.getString(R.string.route_preview_template_demo_title),
- new RoutePreviewDemoScreen(mCarContext)
+ mCarContext.getString(R.string.junction_image_demo_title),
+ new JunctionImageDemoScreen(mCarContext)
+ ));
+
+ screenList.add(createRow(
+ screenManager,
+ mCarContext.getString(R.string.navigating_demo_title),
+ new NavigatingDemoScreen(mCarContext)
));
screenList.add(createRow(
@@ -88,33 +87,6 @@
mCarContext.getString(R.string.nav_map_template_demo_title),
new NavigationMapOnlyScreen(mCarContext)
));
-
- screenList.add(createRow(
- screenManager,
- mCarContext.getString(R.string.place_list_template_demo_title),
- new PlaceListTemplateBrowseDemoScreen(mCarContext)
- ));
-
- screenList.add(createRow(
- screenManager,
- mCarContext.getString(R.string.map_template_list_demo_title),
- new MapTemplateWithListDemoScreen(mCarContext)
- ));
-
- screenList.add(createRow(
- screenManager,
- mCarContext.getString(R.string.map_template_pane_demo_title),
- new MapTemplateWithPaneDemoScreen(mCarContext)
- ));
-
- if (mCarContext.getCarAppApiLevel() >= CarAppApiLevels.LEVEL_6) {
- screenList.add(
- createRow(
- screenManager,
- mCarContext.getString(R.string.map_template_toggle_demo_title),
- new MapTemplateWithToggleDemoScreen(mCarContext)));
- }
-
return screenList;
}
@@ -124,13 +96,6 @@
return mCarContext.getString(R.string.nav_demos_title);
}
- private CarIcon buildCarIcon(int imageId) {
- return new CarIcon.Builder(
- IconCompat.createWithResource(
- mCarContext,
- imageId))
- .build();
- }
private Row createRow(ScreenManager screenManager, String title, Screen screen) {
return new Row.Builder()
@@ -138,13 +103,4 @@
.setOnClickListener(() -> screenManager.push(screen))
.build();
}
-
- private Row createRow(ScreenManager screenManager, CarIcon image, String title, Screen screen) {
- return new Row.Builder()
- .setImage(image)
- .setTitle(title)
- .setOnClickListener(() -> screenManager.push(screen))
- .setBrowsable(true)
- .build();
- }
}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/MapWithContentDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/MapWithContentDemoScreen.java
new file mode 100644
index 0000000..49d9658
--- /dev/null
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/MapWithContentDemoScreen.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 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.car.app.sample.showcase.common.screens.mapdemos;
+
+import static androidx.car.app.model.Action.BACK;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.ListTemplate;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.sample.showcase.common.R;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent.MapTemplateWithListDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent.MapTemplateWithPaneDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent.MapTemplateWithToggleDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent.MapWithGridTemplateDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent.MapWithMessageTemplateDemoScreen;
+import androidx.car.app.versioning.CarAppApiLevels;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** A screen demonstrating different template layouts. */
+public final class MapWithContentDemoScreen extends Screen {
+ public MapWithContentDemoScreen(@NonNull CarContext carContext) {
+ super(carContext);
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate() {
+ List<Row> screenList = new ArrayList<>();
+ if (getCarContext().getCarAppApiLevel() >= CarAppApiLevels.LEVEL_7) {
+ screenList.add(buildRowForTemplate(new MapWithMessageTemplateDemoScreen(
+ getCarContext()),
+ R.string.map_with_message_demo_title));
+ screenList.add(buildRowForTemplate(new MapWithGridTemplateDemoScreen(getCarContext()),
+ R.string.map_with_grid_demo_title));
+ }
+
+ screenList.add(buildRowForTemplate(new MapTemplateWithListDemoScreen(getCarContext()),
+ R.string.map_template_list_demo_title));
+ screenList.add(buildRowForTemplate(new MapTemplateWithPaneDemoScreen(getCarContext()),
+ R.string.map_template_pane_demo_title));
+
+ if (getCarContext().getCarAppApiLevel() >= CarAppApiLevels.LEVEL_6) {
+ screenList.add(buildRowForTemplate(new MapTemplateWithToggleDemoScreen(getCarContext()),
+ R.string.map_template_toggle_demo_title));
+ }
+
+ ItemList.Builder listBuilder = new ItemList.Builder();
+
+ for (int i = 0; i < screenList.size(); i++) {
+ listBuilder.addItem(screenList.get(i));
+ }
+
+ return new ListTemplate.Builder()
+ .setSingleList(listBuilder.build())
+ .setTitle(getCarContext().getString(R.string.map_demos_title))
+ .setHeaderAction(BACK)
+ .build();
+ }
+
+ private Row buildRowForTemplate(Screen screen, int title) {
+ return new Row.Builder()
+ .setTitle(getCarContext().getString(title))
+ .setOnClickListener(() -> getScreenManager().push(screen))
+ .build();
+ }
+}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListNavigationTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListNavigationTemplateDemoScreen.java
similarity index 96%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListNavigationTemplateDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListNavigationTemplateDemoScreen.java
index 479d959..b69fcec 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListNavigationTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListNavigationTemplateDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos;
+package androidx.car.app.sample.showcase.common.screens.mapdemos;
import static androidx.car.app.CarToast.LENGTH_SHORT;
@@ -34,7 +34,7 @@
import androidx.car.app.navigation.model.PlaceListNavigationTemplate;
import androidx.car.app.sample.showcase.common.R;
import androidx.car.app.sample.showcase.common.common.SamplePlaces;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.RoutingDemoModels;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.RoutingDemoModels;
import androidx.core.graphics.drawable.IconCompat;
/** Creates a screen using the {@link PlaceListNavigationTemplate} */
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateBrowseDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListTemplateBrowseDemoScreen.java
similarity index 97%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateBrowseDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListTemplateBrowseDemoScreen.java
index 3026fb0..7001be4 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateBrowseDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListTemplateBrowseDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos;
+package androidx.car.app.sample.showcase.common.screens.mapdemos;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListTemplateDemoScreen.java
similarity index 92%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListTemplateDemoScreen.java
index c65d6d3..b7d3b8f 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/PlaceListTemplateDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos;
+package androidx.car.app.sample.showcase.common.screens.mapdemos;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutePreviewDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/RoutePreviewDemoScreen.java
similarity index 97%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutePreviewDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/RoutePreviewDemoScreen.java
index ac3e620..55e317b 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutePreviewDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/RoutePreviewDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos;
+package androidx.car.app.sample.showcase.common.screens.mapdemos;
import static androidx.car.app.CarToast.LENGTH_LONG;
import static androidx.car.app.CarToast.LENGTH_SHORT;
@@ -36,7 +36,7 @@
import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.RoutePreviewNavigationTemplate;
import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.RoutingDemoModels;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.RoutingDemoModels;
import androidx.core.graphics.drawable.IconCompat;
import java.util.concurrent.TimeUnit;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithListDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithListDemoScreen.java
similarity index 97%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithListDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithListDemoScreen.java
index 83a4c93..d8ea173 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithListDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithListDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos;
+package androidx.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent;
import static androidx.car.app.CarToast.LENGTH_LONG;
import static androidx.car.app.CarToast.LENGTH_SHORT;
@@ -37,7 +37,7 @@
import androidx.car.app.navigation.model.MapController;
import androidx.car.app.navigation.model.MapTemplate;
import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.RoutingDemoModels;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.RoutingDemoModels;
import androidx.car.app.versioning.CarAppApiLevels;
import androidx.core.graphics.drawable.IconCompat;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithPaneDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithPaneDemoScreen.java
similarity index 98%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithPaneDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithPaneDemoScreen.java
index da1987c..d28f05b 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithPaneDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithPaneDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos;
+package androidx.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent;
import static androidx.car.app.CarToast.LENGTH_LONG;
import static androidx.car.app.CarToast.LENGTH_SHORT;
@@ -40,7 +40,7 @@
import androidx.car.app.navigation.model.MapController;
import androidx.car.app.navigation.model.MapTemplate;
import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.RoutingDemoModels;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.RoutingDemoModels;
import androidx.car.app.versioning.CarAppApiLevels;
import androidx.core.graphics.drawable.IconCompat;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithToggleDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithToggleDemoScreen.java
similarity index 96%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithToggleDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithToggleDemoScreen.java
index b05c551..6ed0d07 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithToggleDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapTemplateWithToggleDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos;
+package androidx.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapWithGridTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapWithGridTemplateDemoScreen.java
new file mode 100644
index 0000000..0186731
--- /dev/null
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapWithGridTemplateDemoScreen.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 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.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent;
+
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.CarToast;
+import androidx.car.app.Screen;
+import androidx.car.app.annotations.ExperimentalCarApi;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.ActionStrip;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.GridItem;
+import androidx.car.app.model.GridTemplate;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapWithContentTemplate;
+import androidx.car.app.sample.showcase.common.R;
+import androidx.core.graphics.drawable.IconCompat;
+
+/** Simple demo of how to present a map template with a list. */
+public class MapWithGridTemplateDemoScreen extends Screen {
+
+ public MapWithGridTemplateDemoScreen(@NonNull CarContext carContext) {
+ super(carContext);
+ }
+
+ @ExperimentalCarApi
+ @RequiresCarApi(7)
+ @NonNull
+ @Override
+ public Template onGetTemplate() {
+ ItemList.Builder gridItemListBuilder = new ItemList.Builder();
+ for (int i = 0; i <= 7; i++) {
+ gridItemListBuilder.addItem(createGridItem());
+ }
+
+
+ GridTemplate gridTemplate = new GridTemplate.Builder()
+ .setSingleList(gridItemListBuilder.build())
+ .setHeaderAction(Action.BACK)
+ .setTitle("Report?")
+ .build();
+
+
+ ActionStrip actionStrip = new ActionStrip.Builder()
+ .addAction(
+ new Action.Builder()
+ .setOnClickListener(
+ () -> CarToast.makeText(
+ getCarContext(),
+ getCarContext().getString(
+ R.string.bug_reported_toast_msg),
+ CarToast.LENGTH_SHORT)
+ .show())
+ .setIcon(
+ new CarIcon.Builder(
+ IconCompat.createWithResource(
+ getCarContext(),
+ R.drawable.ic_bug_report_24px))
+ .build())
+ .setFlags(Action.FLAG_IS_PERSISTENT)
+ .build())
+ .build();
+
+ MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder()
+ .setContentTemplate(gridTemplate)
+ .setActionStrip(actionStrip);
+
+ return builder.build();
+ }
+
+ private GridItem createGridItem() {
+ return new GridItem.Builder()
+ .setImage(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(),
+ R.drawable.ic_fastfood_white_48dp)).build())
+ .setTitle("Primary")
+ .setText("Secondary")
+ .setOnClickListener(() -> CarToast.makeText(
+ getCarContext(),
+ "Clicked!",
+ CarToast.LENGTH_SHORT)
+ .show())
+ .build();
+ }
+}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapWithMessageTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapWithMessageTemplateDemoScreen.java
new file mode 100644
index 0000000..d71cb7a
--- /dev/null
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/mapdemos/mapwithcontent/MapWithMessageTemplateDemoScreen.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2023 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.car.app.sample.showcase.common.screens.mapdemos.mapwithcontent;
+
+import android.content.res.TypedArray;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.CarToast;
+import androidx.car.app.Screen;
+import androidx.car.app.annotations.ExperimentalCarApi;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.ActionStrip;
+import androidx.car.app.model.CarColor;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.MessageTemplate;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapController;
+import androidx.car.app.navigation.model.MapWithContentTemplate;
+import androidx.car.app.sample.showcase.common.R;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.RoutingDemoModels;
+import androidx.core.graphics.drawable.IconCompat;
+
+/** Simple demo of how to present a map template with a list. */
+public class MapWithMessageTemplateDemoScreen extends Screen {
+ TypedArray mTypedArray =
+ getCarContext().obtainStyledAttributes(R.style.CarAppTheme, R.styleable.ShowcaseTheme);
+ CarColor mIconTintColor =
+ CarColor.createCustom(
+ mTypedArray.getColor(R.styleable.ShowcaseTheme_markerIconTintColor, -1),
+ mTypedArray.getColor(R.styleable.ShowcaseTheme_markerIconTintColorDark, -1));
+ public MapWithMessageTemplateDemoScreen(@NonNull CarContext carContext) {
+ super(carContext);
+ }
+
+ @ExperimentalCarApi
+ @RequiresCarApi(7)
+ @NonNull
+ @Override
+ public Template onGetTemplate() {
+
+ MessageTemplate messageTemplate = new MessageTemplate.Builder("Continue to Google "
+ + "Kirkland Urban WA 98101?")
+ .setHeaderAction(Action.BACK)
+ .setTitle("Drive to Google Kirkland")
+ .setIcon(new CarIcon.Builder(
+ IconCompat.createWithResource(
+ getCarContext(),
+ R.drawable.ic_commute_24px))
+ .setTint(mIconTintColor)
+ .build())
+ .addAction(new Action.Builder()
+ .setOnClickListener(() -> {
+ CarToast.makeText(
+ getCarContext(),
+ "Let's start navigation",
+ CarToast.LENGTH_SHORT
+ ).show();
+ })
+ .setTitle("Navigate").build())
+ .addAction(
+ new Action.Builder()
+ .setOnClickListener(() -> {
+ CarToast.makeText(
+ getCarContext(),
+ "Quitting navigation",
+ CarToast.LENGTH_SHORT
+ ).show();
+ })
+ .setTitle("Cancel").build())
+
+ .build();
+
+
+ MapController mapController = new MapController.Builder()
+ .setMapActionStrip(RoutingDemoModels.getMapActionStrip(getCarContext()))
+ .build();
+ MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder()
+ .setContentTemplate(messageTemplate)
+ .setMapController(mapController)
+ .setActionStrip(
+ new ActionStrip.Builder()
+ .addAction(
+ new Action.Builder()
+ .setTitle(getCarContext().getString(
+ R.string.search_action_title))
+ .setOnClickListener(() -> {
+ })
+ .build())
+ .build());
+
+ return builder.build();
+ }
+}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/ArrivedDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/ArrivedDemoScreen.java
similarity index 96%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/ArrivedDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/ArrivedDemoScreen.java
index 55dd41a..6a5a0bd 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/ArrivedDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/ArrivedDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates;
+package androidx.car.app.sample.showcase.common.screens.navigationdemos;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/JunctionImageDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/JunctionImageDemoScreen.java
similarity index 96%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/JunctionImageDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/JunctionImageDemoScreen.java
index 4d0a7c7..e665a98 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/JunctionImageDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/JunctionImageDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates;
+package androidx.car.app.sample.showcase.common.screens.navigationdemos;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/LoadingDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/LoadingDemoScreen.java
similarity index 94%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/LoadingDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/LoadingDemoScreen.java
index 9cfdf58..85618ef 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/LoadingDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/LoadingDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates;
+package androidx.car.app.sample.showcase.common.screens.navigationdemos;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/NavigatingDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/NavigatingDemoScreen.java
similarity index 96%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/NavigatingDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/NavigatingDemoScreen.java
index eeb08d1..677b4f7 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/NavigatingDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/NavigatingDemoScreen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates;
+package androidx.car.app.sample.showcase.common.screens.navigationdemos;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/NavigationTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/NavigationTemplateDemoScreen.java
deleted file mode 100644
index 26846cb..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/NavigationTemplateDemoScreen.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2021 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.car.app.sample.showcase.common.screens.navigationdemos;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.ArrivedDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.JunctionImageDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.LoadingDemoScreen;
-import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.NavigatingDemoScreen;
-
-/** A screen showing a demos for the navigation template in different states. */
-public final class NavigationTemplateDemoScreen extends Screen {
- public NavigationTemplateDemoScreen(@NonNull CarContext carContext) {
- super(carContext);
- }
-
- @NonNull
- @Override
- public Template onGetTemplate() {
- ItemList.Builder listBuilder = new ItemList.Builder();
-
- listBuilder.addItem(
- new Row.Builder()
- .setTitle(getCarContext().getString(R.string.loading_demo_title))
- .setOnClickListener(
- () ->
- getScreenManager()
- .push(new LoadingDemoScreen(getCarContext())))
- .build());
-
- listBuilder.addItem(
- new Row.Builder()
- .setTitle(getCarContext().getString(R.string.navigating_demo_title))
- .setOnClickListener(
- () ->
- getScreenManager()
- .push(new NavigatingDemoScreen(getCarContext())))
- .build());
-
- listBuilder.addItem(
- new Row.Builder()
- .setTitle(getCarContext().getString(R.string.arrived_demo_title))
- .setOnClickListener(
- () ->
- getScreenManager()
- .push(new ArrivedDemoScreen(getCarContext())))
- .build());
-
- listBuilder.addItem(
- new Row.Builder()
- .setTitle(getCarContext().getString(R.string.junction_image_demo_title))
- .setOnClickListener(
- () ->
- getScreenManager()
- .push(new JunctionImageDemoScreen(getCarContext())))
- .build());
-
- return new ListTemplate.Builder()
- .setSingleList(listBuilder.build())
- .setTitle(getCarContext().getString(R.string.nav_template_demos_title))
- .setHeaderAction(Action.BACK)
- .build();
- }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/RoutingDemoModels.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutingDemoModels.java
similarity index 99%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/RoutingDemoModels.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutingDemoModels.java
index bdc3771..4b3f0bf 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/navigationtemplates/RoutingDemoModels.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutingDemoModels.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates;
+package androidx.car.app.sample.showcase.common.screens.navigationdemos;
import static androidx.car.app.model.Action.FLAG_DEFAULT;
import static androidx.car.app.model.Action.FLAG_PRIMARY;
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
index 146b7c7..7119538 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
@@ -239,7 +239,7 @@
<string name="additional_text" msgid="8410289578276941586">"Consulte nossos Termos de Serviço"</string>
<string name="google_sign_in" msgid="6556259799319701727">"Login do Google"</string>
<string name="use_pin" msgid="7850893299484337431">"Usar PIN"</string>
- <string name="qr_code" msgid="5487041647280777397">"Código QR"</string>
+ <string name="qr_code" msgid="5487041647280777397">"QR code"</string>
<string name="sign_in_template_not_supported_text" msgid="7184733753948837646">"Seu host não oferece suporte ao modelo de login"</string>
<string name="sign_in_template_not_supported_title" msgid="4892883228898541764">"Host incompatível"</string>
<string name="email_hint" msgid="7205549445477319606">"E-mail"</string>
@@ -251,7 +251,7 @@
<string name="password_hint" msgid="2869107073860012864">"senha"</string>
<string name="password_sign_in_instruction_prefix" msgid="9105788349198243508">"Nome de usuário"</string>
<string name="pin_sign_in_instruction" msgid="2288691296234360441">"Digite este PIN no smartphone"</string>
- <string name="qr_code_sign_in_title" msgid="8137070561006464518">"Ler o Código QR para fazer login"</string>
+ <string name="qr_code_sign_in_title" msgid="8137070561006464518">"Ler o QR code para fazer login"</string>
<string name="sign_in_with_google_title" msgid="8043752000786977249">"Fazer login com o Google"</string>
<string name="provider_sign_in_instruction" msgid="7586815688292506743">"Use este botão para concluir o Login do Google"</string>
<string name="sign_in_complete_text" msgid="8423984266325680606">"Você fez login."</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
index 146b7c7..7119538 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
@@ -239,7 +239,7 @@
<string name="additional_text" msgid="8410289578276941586">"Consulte nossos Termos de Serviço"</string>
<string name="google_sign_in" msgid="6556259799319701727">"Login do Google"</string>
<string name="use_pin" msgid="7850893299484337431">"Usar PIN"</string>
- <string name="qr_code" msgid="5487041647280777397">"Código QR"</string>
+ <string name="qr_code" msgid="5487041647280777397">"QR code"</string>
<string name="sign_in_template_not_supported_text" msgid="7184733753948837646">"Seu host não oferece suporte ao modelo de login"</string>
<string name="sign_in_template_not_supported_title" msgid="4892883228898541764">"Host incompatível"</string>
<string name="email_hint" msgid="7205549445477319606">"E-mail"</string>
@@ -251,7 +251,7 @@
<string name="password_hint" msgid="2869107073860012864">"senha"</string>
<string name="password_sign_in_instruction_prefix" msgid="9105788349198243508">"Nome de usuário"</string>
<string name="pin_sign_in_instruction" msgid="2288691296234360441">"Digite este PIN no smartphone"</string>
- <string name="qr_code_sign_in_title" msgid="8137070561006464518">"Ler o Código QR para fazer login"</string>
+ <string name="qr_code_sign_in_title" msgid="8137070561006464518">"Ler o QR code para fazer login"</string>
<string name="sign_in_with_google_title" msgid="8043752000786977249">"Fazer login com o Google"</string>
<string name="provider_sign_in_instruction" msgid="7586815688292506743">"Use este botão para concluir o Login do Google"</string>
<string name="sign_in_complete_text" msgid="8423984266325680606">"Você fez login."</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
index f0623a2..948d8fab6 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
@@ -492,4 +492,8 @@
<string name="route_options_demo_title">Route options</string>
<string name="avoid_highways_row_title">Avoid highways</string>
<string name="avoid_ferries_row_title">Avoid ferry</string>
+ <string name="map_demos_title">Map Demos</string>
+ <string name="map_with_content_demo_title">Map With Content Demos</string>
+ <string name="map_with_message_demo_title">Map With Message Template Demo</string>
+ <string name="map_with_grid_demo_title">Map With Grid Template Demo</string>
</resources>
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
index 716343f..d66d65b5 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
@@ -16,18 +16,41 @@
package androidx.compose.compiler.plugins.kotlin
+import androidx.compose.compiler.plugins.kotlin.facade.SourceFile
+import java.io.File
+import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.ir.util.DumpIrTreeOptions
+import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor
+import org.junit.Assert.assertEquals
import org.junit.Test
+import org.junit.runners.Parameterized
-class StrongSkippingModeTransformTests(useFir: Boolean) :
- FunctionBodySkippingTransformTestsBase(useFir) {
+class StrongSkippingModeTransformTests(
+ useFir: Boolean,
+ private val intrinsicRememberEnabled: Boolean
+) : AbstractIrTransformTest(useFir) {
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "useFir = {0}, intrinsicRemember = {1}")
+ fun data() = arrayOf<Any>(
+ arrayOf(false, false),
+ arrayOf(false, true),
+ arrayOf(true, false),
+ arrayOf(true, true)
+ )
+ }
override fun CompilerConfiguration.updateConfiguration() {
put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, true)
+ put(
+ ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
+ intrinsicRememberEnabled
+ )
}
@Test
- fun testSingleStableParam(): Unit = comparisonPropagation(
+ fun testSingleStableParam(): Unit = verifyMemoization(
"""
class Foo(val value: Int = 0)
@Composable fun A(x: Foo) {}
@@ -41,7 +64,7 @@
)
@Test
- fun testSingleUnstableParam(): Unit = comparisonPropagation(
+ fun testSingleUnstableParam(): Unit = verifyMemoization(
"""
@Composable fun A(x: Foo) {}
class Foo(var value: Int = 0)
@@ -55,7 +78,7 @@
)
@Test
- fun testSingleNullableUnstableParam(): Unit = comparisonPropagation(
+ fun testSingleNullableUnstableParam(): Unit = verifyMemoization(
"""
@Composable fun A(x: Foo?) {}
class Foo(var value: Int = 0)
@@ -69,7 +92,7 @@
)
@Test
- fun testSingleOptionalUnstableParam(): Unit = comparisonPropagation(
+ fun testSingleOptionalUnstableParam(): Unit = verifyMemoization(
"""
@Composable fun A(x: Foo?) {}
class Foo(var value: Int = 0)
@@ -83,7 +106,7 @@
)
@Test
- fun testRuntimeStableParam(): Unit = comparisonPropagation(
+ fun testRuntimeStableParam(): Unit = verifyMemoization(
"""
@Composable fun A(x: Int) {}
""",
@@ -98,7 +121,7 @@
)
@Test
- fun testStableUnstableParams(): Unit = comparisonPropagation(
+ fun testStableUnstableParams(): Unit = verifyMemoization(
"""
@Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 }
class Foo(var value: Int = 0)
@@ -120,25 +143,25 @@
)
@Test
- fun testStaticDefaultParam() = comparisonPropagation(
+ fun testStaticDefaultParam() = verifyMemoization(
"""
@Composable
fun A(i: Int, list: List<Int>? = null, set: Set<Int> = emptySet()) {}
- """.trimIndent(),
+ """,
"""
@Composable
fun Test(i: Int) {
A(i)
}
- """.trimIndent()
+ """
)
@Test
- fun testMemoizingUnstableCapturesInLambda() = comparisonPropagation(
+ fun testMemoizingUnstableCapturesInLambda() = verifyMemoization(
"""
@Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 }
class Foo(var value: Int = 0)
- """.trimIndent(),
+ """,
"""
@Composable
fun Test() {
@@ -149,12 +172,12 @@
)
@Test
- fun testDontMemoizeLambda() = comparisonPropagation(
+ fun testDontMemoizeLambda() = verifyMemoization(
"""
@Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 }
class Foo(var value: Int = 0)
fun Lam(x: ()->Unit) { x() }
- """.trimIndent(),
+ """,
"""
import androidx.compose.runtime.DontMemoize
@@ -168,12 +191,12 @@
)
@Test
- fun testMemoizingUnstableFunctionParameterInLambda() = comparisonPropagation(
+ fun testMemoizingUnstableFunctionParameterInLambda() = verifyMemoization(
"""
@Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 }
class Foo(var value: Int = 0)
class Bar(val value: Int = 0)
- """.trimIndent(),
+ """,
"""
@Composable
fun Test(foo: Foo, bar: Bar) {
@@ -186,12 +209,12 @@
)
@Test
- fun testMemoizingComposableLambda() = comparisonPropagation(
+ fun testMemoizingComposableLambda() = verifyMemoization(
"""
@Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 }
class Foo(var value: Int = 0)
class Bar(val value: Int = 0)
- """.trimIndent(),
+ """,
"""
@Composable
fun Test(foo: Foo, bar: Bar) {
@@ -204,12 +227,12 @@
)
@Test
- fun testMemoizingStableAndUnstableCapturesInLambda() = comparisonPropagation(
+ fun testMemoizingStableAndUnstableCapturesInLambda() = verifyMemoization(
"""
@Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 }
class Foo(var value: Int = 0)
class Bar(val value: Int = 0)
- """.trimIndent(),
+ """,
"""
@Composable
fun Test() {
@@ -224,13 +247,13 @@
)
@Test
- fun testFunctionInterfaceMemorized() = comparisonPropagation(
+ fun testFunctionInterfaceMemorized() = verifyMemoization(
"""
fun interface TestFunInterface {
fun compute(value: Int)
}
fun use(@Suppress("UNUSED_PARAMETER") v: Int) {}
- """.trimIndent(),
+ """,
"""
@Composable fun TestMemoizedFun(compute: TestFunInterface) {}
@Composable fun Test() {
@@ -244,11 +267,11 @@
use(capture)
}
}
- """.trimIndent()
+ """
)
@Test
- fun testVarArgs() = comparisonPropagation(
+ fun testVarArgs() = verifyMemoization(
"",
"""
@Composable fun Varargs(vararg ints: Int) {
@@ -256,11 +279,11 @@
@Composable fun Test() {
Varargs(1, 2, 3)
}
- """.trimIndent()
+ """
)
@Test
- fun testRuntimeStableVarArgs() = comparisonPropagation(
+ fun testRuntimeStableVarArgs() = verifyMemoization(
"""
@Composable fun A(x: Int) {}
""",
@@ -275,7 +298,7 @@
)
@Test
- fun testUnstableReceiverFunctionReferenceMemoized() = comparisonPropagation(
+ fun testUnstableReceiverFunctionReferenceMemoized() = verifyMemoization(
"""
class Unstable(var qux: Int = 0) { fun method(arg1: Int) {} }
val unstable = Unstable()
@@ -289,7 +312,7 @@
)
@Test
- fun testUnstableExtensionReceiverFunctionReferenceMemoized() = comparisonPropagation(
+ fun testUnstableExtensionReceiverFunctionReferenceMemoized() = verifyMemoization(
"""
class Unstable(var foo: Int = 0)
fun Unstable.method(arg1: Int) {}
@@ -302,4 +325,55 @@
}
"""
)
+
+ private fun verifyMemoization(
+ @Language("kotlin")
+ unchecked: String,
+ @Language("kotlin")
+ checked: String,
+ dumpTree: Boolean = false
+ ) {
+ val source = """
+ import androidx.compose.runtime.Composable
+ import androidx.compose.runtime.NonRestartableComposable
+ import androidx.compose.runtime.ReadOnlyComposable
+
+ $checked
+ """
+
+ val extra = """
+ import androidx.compose.runtime.Composable
+
+ $unchecked
+ fun used(x: Any?) {}
+ """
+
+ // verify that generated keys are path independent
+ val module1 = dumpIrWithPath(source, extra, "/home/folder1")
+ val module2 = dumpIrWithPath(source, extra, "/home/folder2")
+
+ assertEquals(module1, module2)
+
+ verifyGoldenComposeIrTransform(
+ source,
+ extra,
+ dumpTree = dumpTree
+ )
+ }
+
+ private fun dumpIrWithPath(
+ source: String,
+ extra: String,
+ path: String,
+ ): String {
+ val sourceFile1 = SourceFile("Test.kt", source, path = path)
+ val extraFile1 = SourceFile("Extra.kt", extra, path = path)
+ return compileToIr(listOf(sourceFile1, extraFile1)).files.joinToString("\n") {
+ buildString {
+ val fileShortName = it.fileEntry.name.takeLastWhile { it != File.separatorChar }
+ appendLine("IrFile: $fileShortName")
+ it.acceptChildren(DumpIrTreeVisitor(this, DumpIrTreeOptions()), "")
+ }
+ }
+ }
}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/facade/KotlinCompilerFacade.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/facade/KotlinCompilerFacade.kt
index 1a5e4b8..8498808 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/facade/KotlinCompilerFacade.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/facade/KotlinCompilerFacade.kt
@@ -48,7 +48,8 @@
class SourceFile(
val name: String,
val source: String,
- private val ignoreParseErrors: Boolean = false
+ private val ignoreParseErrors: Boolean = false,
+ val path: String = ""
) {
fun toKtFile(project: Project): KtFile {
val shortName = name.substring(name.lastIndexOf('/') + 1).let {
@@ -60,7 +61,7 @@
KotlinLanguage.INSTANCE,
StringUtilRt.convertLineSeparators(source)
) {
- override fun getPath(): String = "/$name"
+ override fun getPath(): String = "${this@SourceFile.path}/$name"
}
virtualFile.charset = StandardCharsets.UTF_8
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambda\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambdasInMarkedFunction\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambdasInMarkedFunction\133useFir = false\135.txt"
deleted file mode 100644
index 01d55915..0000000
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambdasInMarkedFunction\133useFir = false\135.txt"
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// Source
-// ------------------------------------------
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.NonRestartableComposable
-import androidx.compose.runtime.ReadOnlyComposable
-
-
-import androidx.compose.runtime.DontMemoize
-
-@Composable
-@DontMemoize
-fun Test() {
- val foo = Foo(0)
- val lambda = { foo }
-}
-
-//
-// Transformed IR
-// ------------------------------------------
-
-@Composable
-@DontMemoize
-fun Test(%composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- sourceInformation(%composer, "C(Test)")
- if (%changed != 0 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %changed, -1, <>)
- }
- val foo = Foo(0)
- val lambda = {
- foo
- }
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- Test(%composer, updateChangedFlags(%changed or 0b0001))
- }
-}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambdasInMarkedFunction\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambdasInMarkedFunction\133useFir = true\135.txt"
deleted file mode 100644
index 01d55915..0000000
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testDontMemoizeLambdasInMarkedFunction\133useFir = true\135.txt"
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// Source
-// ------------------------------------------
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.NonRestartableComposable
-import androidx.compose.runtime.ReadOnlyComposable
-
-
-import androidx.compose.runtime.DontMemoize
-
-@Composable
-@DontMemoize
-fun Test() {
- val foo = Foo(0)
- val lambda = { foo }
-}
-
-//
-// Transformed IR
-// ------------------------------------------
-
-@Composable
-@DontMemoize
-fun Test(%composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- sourceInformation(%composer, "C(Test)")
- if (%changed != 0 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %changed, -1, <>)
- }
- val foo = Foo(0)
- val lambda = {
- foo
- }
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- Test(%composer, updateChangedFlags(%changed or 0b0001))
- }
-}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 86%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = false\135.txt"
index ce69dc9..45de354 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = false\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable fun TestMemoizedFun(compute: TestFunInterface) {}
+
+@Composable fun TestMemoizedFun(compute: TestFunInterface) {}
@Composable fun Test() {
val capture = 0
TestMemoizedFun {
@@ -53,13 +54,13 @@
}, %composer, 0b0110)
TestMemoizedFun(<block>{
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(false) {
+ val tmpCache = %composer.cache(%composer.changed(capture)) {
TestFunInterface { it: Int ->
use(capture)
}
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}, %composer, 0)
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 89%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = true\135.txt"
index ce69dc9..be475f6 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false, intrinsicRemember = true\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable fun TestMemoizedFun(compute: TestFunInterface) {}
+
+@Composable fun TestMemoizedFun(compute: TestFunInterface) {}
@Composable fun Test() {
val capture = 0
TestMemoizedFun {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 86%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = false\135.txt"
index ce69dc9..45de354 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = false\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable fun TestMemoizedFun(compute: TestFunInterface) {}
+
+@Composable fun TestMemoizedFun(compute: TestFunInterface) {}
@Composable fun Test() {
val capture = 0
TestMemoizedFun {
@@ -53,13 +54,13 @@
}, %composer, 0b0110)
TestMemoizedFun(<block>{
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(false) {
+ val tmpCache = %composer.cache(%composer.changed(capture)) {
TestFunInterface { it: Int ->
use(capture)
}
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}, %composer, 0)
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 89%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = true\135.txt"
index ce69dc9..be475f6 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true, intrinsicRemember = true\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable fun TestMemoizedFun(compute: TestFunInterface) {}
+
+@Composable fun TestMemoizedFun(compute: TestFunInterface) {}
@Composable fun Test() {
val capture = 0
TestMemoizedFun {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true\135.txt"
deleted file mode 100644
index ce69dc9..0000000
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true\135.txt"
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Source
-// ------------------------------------------
-
-import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
-
- @Composable fun TestMemoizedFun(compute: TestFunInterface) {}
-@Composable fun Test() {
- val capture = 0
- TestMemoizedFun {
- // no captures
- use(it)
- }
- TestMemoizedFun {
- // stable captures
- use(capture)
- }
-}
-
-//
-// Transformed IR
-// ------------------------------------------
-
-@Composable
-fun TestMemoizedFun(compute: TestFunInterface, %composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- if (%changed and 0b0001 != 0 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %changed, -1, <>)
- }
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- TestMemoizedFun(compute, %composer, updateChangedFlags(%changed or 0b0001))
- }
-}
-@Composable
-fun Test(%composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- if (%changed != 0 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %changed, -1, <>)
- }
- val capture = 0
- TestMemoizedFun(TestFunInterface { it: Int ->
- use(it)
- }, %composer, 0b0110)
- TestMemoizedFun(<block>{
- %composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(false) {
- TestFunInterface { it: Int ->
- use(capture)
- }
- }
- %composer.endReplaceableGroup()
- tmp0_group
- }, %composer, 0)
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- Test(%composer, updateChangedFlags(%changed or 0b0001))
- }
-}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingComposableLambda\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 90%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
index 34d44a4..994ca87 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
@@ -32,14 +32,14 @@
val bar = Bar(1)
val lambda = <block>{
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+ val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
{
foo
bar
}
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 90%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
index 34d44a4..994ca87 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
@@ -32,14 +32,14 @@
val bar = Bar(1)
val lambda = <block>{
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+ val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
{
foo
bar
}
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 91%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
index 1684495..a399ea5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = false\135.txt"
@@ -27,13 +27,13 @@
val foo = Foo(0)
val lambda = <block>{
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(foo)) {
+ val tmpCache = %composer.cache(%composer.changedInstance(foo)) {
{
foo
}
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 91%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
index 1684495..a399ea5 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = false\135.txt"
@@ -27,13 +27,13 @@
val foo = Foo(0)
val lambda = <block>{
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(foo)) {
+ val tmpCache = %composer.cache(%composer.changedInstance(foo)) {
{
foo
}
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 90%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = false\135.txt"
index ad46339..ddced50 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = false\135.txt"
@@ -35,14 +35,14 @@
}
val lambda = <block>{
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %dirty and 0b01110000 == 0b00100000) {
+ val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
{
foo
bar
}
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 90%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = false\135.txt"
index ad46339..ddced50 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = false\135.txt"
@@ -35,14 +35,14 @@
}
val lambda = <block>{
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %dirty and 0b01110000 == 0b00100000) {
+ val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
{
foo
bar
}
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableParam\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testRuntimeStableVarArgs\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleNullableUnstableParam\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleOptionalUnstableParam\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleStableParam\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testSingleUnstableParam\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = false, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = true, intrinsicRemember = false\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStableUnstableParams\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 84%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false, intrinsicRemember = false\135.txt"
index bf4c3ce..6a0231d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false, intrinsicRemember = false\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable
+
+@Composable
fun Test(i: Int) {
A(i)
}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 84%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false, intrinsicRemember = true\135.txt"
index bf4c3ce..6a0231d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false, intrinsicRemember = true\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable
+
+@Composable
fun Test(i: Int) {
A(i)
}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 84%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true, intrinsicRemember = false\135.txt"
index bf4c3ce..6a0231d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true, intrinsicRemember = false\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable
+
+@Composable
fun Test(i: Int) {
A(i)
}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 84%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true, intrinsicRemember = true\135.txt"
index bf4c3ce..6a0231d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true, intrinsicRemember = true\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable
+
+@Composable
fun Test(i: Int) {
A(i)
}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true\135.txt"
deleted file mode 100644
index bf4c3ce..0000000
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testStaticDefaultParam\133useFir = true\135.txt"
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// Source
-// ------------------------------------------
-
-import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
-
- @Composable
-fun Test(i: Int) {
- A(i)
-}
-
-//
-// Transformed IR
-// ------------------------------------------
-
-@Composable
-fun Test(i: Int, %composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- val %dirty = %changed
- if (%changed and 0b0110 == 0) {
- %dirty = %dirty or if (%composer.changed(i)) 0b0100 else 0b0010
- }
- if (%dirty and 0b0011 != 0b0010 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %dirty, -1, <>)
- }
- A(i, null, null, %composer, 0b1110 and %dirty, 0b0110)
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- Test(i, %composer, updateChangedFlags(%changed or 0b0001))
- }
-}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 91%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
index 32f8d7f..046e728 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
@@ -26,11 +26,11 @@
val x = <block>{
val tmp0 = unstable
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
+ val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
tmp0::method
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 91%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
index 32f8d7f..046e728 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
@@ -26,11 +26,11 @@
val x = <block>{
val tmp0 = unstable
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
+ val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
tmp0::method
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 91%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
index 32f8d7f..046e728 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = false\135.txt"
@@ -26,11 +26,11 @@
val x = <block>{
val tmp0 = unstable
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
+ val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
tmp0::method
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 100%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
deleted file mode 100644
index 32f8d7f..0000000
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// Source
-// ------------------------------------------
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.NonRestartableComposable
-import androidx.compose.runtime.ReadOnlyComposable
-
-
-@Composable
-fun Something() {
- val x = unstable::method
-}
-
-//
-// Transformed IR
-// ------------------------------------------
-
-@Composable
-fun Something(%composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- if (%changed != 0 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %changed, -1, <>)
- }
- val x = <block>{
- val tmp0 = unstable
- %composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
- tmp0::method
- }
- %composer.endReplaceableGroup()
- tmp0_group
- }
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- Something(%composer, updateChangedFlags(%changed or 0b0001))
- }
-}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 91%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
index 32f8d7f..046e728 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = false\135.txt"
@@ -26,11 +26,11 @@
val x = <block>{
val tmp0 = unstable
%composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
+ val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
tmp0::method
}
%composer.endReplaceableGroup()
- tmp0_group
+ tmpCache
}
if (isTraceInProgress()) {
traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 100%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true, intrinsicRemember = true\135.txt"
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
deleted file mode 100644
index 32f8d7f..0000000
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// Source
-// ------------------------------------------
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.NonRestartableComposable
-import androidx.compose.runtime.ReadOnlyComposable
-
-
-@Composable
-fun Something() {
- val x = unstable::method
-}
-
-//
-// Transformed IR
-// ------------------------------------------
-
-@Composable
-fun Something(%composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- if (%changed != 0 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %changed, -1, <>)
- }
- val x = <block>{
- val tmp0 = unstable
- %composer.startReplaceableGroup(<>)
- val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
- tmp0::method
- }
- %composer.endReplaceableGroup()
- tmp0_group
- }
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- Something(%composer, updateChangedFlags(%changed or 0b0001))
- }
-}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false, intrinsicRemember = false\135.txt"
similarity index 89%
rename from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt"
rename to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false, intrinsicRemember = false\135.txt"
index d398e4a..4fbeb8d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false, intrinsicRemember = false\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable fun Varargs(vararg ints: Int) {
+
+@Composable fun Varargs(vararg ints: Int) {
}
@Composable fun Test() {
Varargs(1, 2, 3)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false, intrinsicRemember = true\135.txt"
similarity index 89%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false, intrinsicRemember = true\135.txt"
index d398e4a..4fbeb8d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false, intrinsicRemember = true\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable fun Varargs(vararg ints: Int) {
+
+@Composable fun Varargs(vararg ints: Int) {
}
@Composable fun Test() {
Varargs(1, 2, 3)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true, intrinsicRemember = false\135.txt"
similarity index 89%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true, intrinsicRemember = false\135.txt"
index d398e4a..4fbeb8d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true, intrinsicRemember = false\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable fun Varargs(vararg ints: Int) {
+
+@Composable fun Varargs(vararg ints: Int) {
}
@Composable fun Test() {
Varargs(1, 2, 3)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true, intrinsicRemember = true\135.txt"
similarity index 89%
copy from "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt"
copy to "compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true, intrinsicRemember = true\135.txt"
index d398e4a..4fbeb8d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true, intrinsicRemember = true\135.txt"
@@ -3,10 +3,11 @@
// ------------------------------------------
import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReadOnlyComposable
- @Composable fun Varargs(vararg ints: Int) {
+
+@Composable fun Varargs(vararg ints: Int) {
}
@Composable fun Test() {
Varargs(1, 2, 3)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true\135.txt"
deleted file mode 100644
index d398e4a..0000000
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testVarArgs\133useFir = true\135.txt"
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// Source
-// ------------------------------------------
-
-import androidx.compose.runtime.Composable
- import androidx.compose.runtime.NonRestartableComposable
- import androidx.compose.runtime.ReadOnlyComposable
-
- @Composable fun Varargs(vararg ints: Int) {
-}
-@Composable fun Test() {
- Varargs(1, 2, 3)
-}
-
-//
-// Transformed IR
-// ------------------------------------------
-
-@Composable
-fun Varargs(ints: IntArray, %composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- val %dirty = %changed
- %composer.startMovableGroup(<>, ints.size)
- val <iterator> = ints.iterator()
- while (<iterator>.hasNext()) {
- val value = <iterator>.next()
- %dirty = %dirty or if (%composer.changed(value)) 0b0100 else 0
- }
- %composer.endMovableGroup()
- if (%dirty and 0b1110 == 0) {
- %dirty = %dirty or 0b0010
- }
- if (%dirty and 0b0001 != 0 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %dirty, -1, <>)
- }
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- Varargs(*ints, %composer, updateChangedFlags(%changed or 0b0001))
- }
-}
-@Composable
-fun Test(%composer: Composer?, %changed: Int) {
- %composer = %composer.startRestartGroup(<>)
- if (%changed != 0 || !%composer.skipping) {
- if (isTraceInProgress()) {
- traceEventStart(<>, %changed, -1, <>)
- }
- Varargs(1, 2, 3, %composer, 0)
- if (isTraceInProgress()) {
- traceEventEnd()
- }
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- Test(%composer, updateChangedFlags(%changed or 0b0001))
- }
-}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
index 90acac0..99cacc5 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
@@ -91,6 +91,7 @@
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isFunctionOrKFunction
import org.jetbrains.kotlin.ir.util.isLocal
+import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -991,12 +992,8 @@
calculation
)
- val fileName = currentFile
- ?.fileEntry
- ?.name
- ?.let { PackagePartClassUtils.getFilePartShortName(it) }
- ?: ""
- val key = fileName.hashCode() + expression.startOffset
+ val fqName = currentFunctionContext?.declaration?.kotlinFqName?.asString()
+ val key = fqName.hashCode() + expression.startOffset
// Wrap the cached expression in a replaceable group
val cacheTmpVar = irTemporary(cache, "tmpCache")
return cacheTmpVar.wrap(
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index 55ab96d..2f822aa 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -115,6 +115,7 @@
}
@SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+ method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
}
public final class FlowLayoutKt {
@@ -123,6 +124,7 @@
}
@SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
+ method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
}
public final class IntrinsicKt {
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index c0844d9..cf9513b 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -118,6 +118,7 @@
}
@SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+ method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
}
public final class FlowLayoutKt {
@@ -128,6 +129,7 @@
}
@SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
+ method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
}
public final class IntrinsicKt {
diff --git a/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/SimpleFlowColumnDemo.kt b/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/SimpleFlowColumnDemo.kt
index 3afa90f..eb0867f 100644
--- a/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/SimpleFlowColumnDemo.kt
+++ b/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/SimpleFlowColumnDemo.kt
@@ -20,13 +20,18 @@
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.samples.SimpleFlowColumn
import androidx.compose.foundation.layout.samples.SimpleFlowColumnWithWeights
+import androidx.compose.foundation.layout.samples.SimpleFlowColumn_EqualWidth
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun SimpleFlowColumnDemo() {
- Column() {
+ Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
SimpleFlowColumn()
SimpleFlowColumnWithWeights()
+ SimpleFlowColumn_EqualWidth()
}
}
diff --git a/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/SimpleFlowRowDemo.kt b/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/SimpleFlowRowDemo.kt
index 447fc06..abc3b38 100644
--- a/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/SimpleFlowRowDemo.kt
+++ b/compose/foundation/foundation-layout/integration-tests/layout-demos/src/main/java/androidx/compose/foundation/layout/demos/SimpleFlowRowDemo.kt
@@ -20,13 +20,18 @@
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.samples.SimpleFlowRow
import androidx.compose.foundation.layout.samples.SimpleFlowRowWithWeights
+import androidx.compose.foundation.layout.samples.SimpleFlowRow_EqualHeight
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun SimpleFlowRowDemo() {
- Column {
+ Column(Modifier.verticalScroll(rememberScrollState())) {
SimpleFlowRow()
SimpleFlowRowWithWeights()
+ SimpleFlowRow_EqualHeight()
}
}
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowColumnSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowColumnSample.kt
index b5472254..cce4c34 100644
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowColumnSample.kt
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowColumnSample.kt
@@ -30,6 +30,7 @@
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -44,6 +45,7 @@
fun SimpleFlowColumn() {
FlowColumn(
Modifier
+ .padding(20.dp)
.fillMaxWidth()
.wrapContentHeight(align = Alignment.Top)
.requiredHeight(200.dp)
@@ -73,6 +75,7 @@
fun SimpleFlowColumnWithWeights() {
FlowColumn(
Modifier
+ .padding(20.dp)
.fillMaxWidth()
.wrapContentHeight(align = Alignment.Top)
.requiredHeight(200.dp)
@@ -94,3 +97,34 @@
}
}
}
+
+@OptIn(ExperimentalLayoutApi::class)
+@Sampled
+@Composable
+fun SimpleFlowColumn_EqualWidth() {
+ FlowColumn(
+ Modifier
+ .padding(20.dp)
+ .wrapContentHeight(align = Alignment.Top)
+ .wrapContentWidth(align = Alignment.Start),
+ horizontalArrangement = Arrangement.spacedBy(10.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ maxItemsInEachColumn = 3,
+ ) {
+ repeat(9) {
+ Box(
+ Modifier
+ .height(100.dp)
+ .fillMaxColumnWidth(1f)
+ .background(Color.Green)
+ ) {
+ val text = generateRandomString(IntRange(1, 5).random())
+ Text(
+ text = text,
+ fontSize = 18.sp,
+ modifier = Modifier.padding(3.dp)
+ )
+ }
+ }
+ }
+}
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowRowSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowRowSample.kt
index 7af3c27..3529ff8 100644
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowRowSample.kt
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowRowSample.kt
@@ -34,6 +34,7 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import kotlin.random.Random
@OptIn(ExperimentalLayoutApi::class)
@Sampled
@@ -42,6 +43,7 @@
FlowRow(
Modifier
.fillMaxWidth(1f)
+ .padding(20.dp)
.wrapContentHeight(align = Alignment.Top),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalArrangement = Arrangement.spacedBy(20.dp),
@@ -68,6 +70,7 @@
FlowRow(
Modifier
.wrapContentHeight()
+ .padding(20.dp)
.fillMaxWidth(1f),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalArrangement = Arrangement.spacedBy(20.dp),
@@ -85,3 +88,47 @@
}
}
}
+
+@OptIn(ExperimentalLayoutApi::class)
+@Sampled
+@Composable
+fun SimpleFlowRow_EqualHeight() {
+ FlowRow(
+ Modifier
+ .fillMaxWidth(1f)
+ .padding(20.dp)
+ .wrapContentHeight(align = Alignment.Top),
+ horizontalArrangement = Arrangement.spacedBy(10.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ maxItemsInEachRow = 3,
+ ) {
+ repeat(9) {
+ Box(
+ Modifier
+ .width(100.dp)
+ .background(Color.Green)
+ .fillMaxRowHeight(1f)
+ ) {
+ val text = generateRandomString(IntRange(10, 50).random())
+ Text(
+ text = text,
+ fontSize = 18.sp,
+ modifier = Modifier.padding(3.dp)
+ )
+ }
+ }
+ }
+}
+
+fun generateRandomString(length: Int): String {
+ val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
+ val random = Random.Default
+
+ val randomString = StringBuilder(length)
+ repeat(length) {
+ val randomIndex = random.nextInt(0, charPool.size)
+ val randomChar = charPool[randomIndex]
+ randomString.append(randomChar)
+ }
+ return randomString.toString()
+}
diff --git a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
index 3eeedac..1afc792 100644
--- a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
@@ -16,21 +16,25 @@
package androidx.compose.foundation.layout
+import androidx.compose.foundation.background
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth
import kotlin.math.roundToInt
+import kotlin.random.Random
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -320,6 +324,295 @@
}
@Test
+ fun testFlowRow_equalHeight() {
+ val listOfHeights = mutableListOf<Int>()
+
+ rule.setContent {
+ with(LocalDensity.current) {
+ FlowRow(
+ Modifier
+ .fillMaxWidth(1f)
+ .padding(20.dp)
+ .wrapContentHeight(align = Alignment.Top),
+ horizontalArrangement = Arrangement.spacedBy(10.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ maxItemsInEachRow = 3,
+ ) {
+ repeat(9) {
+ Box(
+ Modifier
+ .onSizeChanged {
+ listOfHeights.add(it.height)
+ }
+ .width(100.dp)
+ .background(Color.Green)
+ .fillMaxRowHeight()
+ ) {
+ val height = it * Random.Default.nextInt(0, 200)
+ Box(modifier = Modifier.height(height.dp))
+ }
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ Truth.assertThat(listOfHeights[0]).isEqualTo(listOfHeights[1])
+ Truth.assertThat(listOfHeights[1]).isEqualTo(listOfHeights[2])
+ Truth.assertThat(listOfHeights[2]).isNotEqualTo(listOfHeights[3])
+ Truth.assertThat(listOfHeights[3]).isEqualTo(listOfHeights[4])
+ Truth.assertThat(listOfHeights[4]).isEqualTo(listOfHeights[5])
+ Truth.assertThat(listOfHeights[5]).isNotEqualTo(listOfHeights[6])
+ Truth.assertThat(listOfHeights[6]).isEqualTo(listOfHeights[7])
+ Truth.assertThat(listOfHeights[7]).isEqualTo(listOfHeights[8])
+ }
+
+ @Test
+ fun testFlowRow_equalHeight_worksWithWeight() {
+ val listOfHeights = mutableListOf<Int>()
+
+ rule.setContent {
+ with(LocalDensity.current) {
+ FlowRow(
+ Modifier
+ .fillMaxWidth(1f)
+ .padding(20.dp)
+ .wrapContentHeight(align = Alignment.Top),
+ horizontalArrangement = Arrangement.spacedBy(10.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ maxItemsInEachRow = 3,
+ ) {
+ repeat(9) {
+ Box(
+ Modifier
+ .onSizeChanged {
+ listOfHeights.add(it.height)
+ }
+ .width(100.dp)
+ .weight(1f, true)
+ .background(Color.Green)
+ .fillMaxRowHeight()
+ ) {
+ val height = it * Random.Default.nextInt(0, 200)
+ Box(modifier = Modifier.height(height.dp))
+ }
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ Truth.assertThat(listOfHeights[0]).isEqualTo(listOfHeights[1])
+ Truth.assertThat(listOfHeights[1]).isEqualTo(listOfHeights[2])
+ Truth.assertThat(listOfHeights[2]).isNotEqualTo(listOfHeights[3])
+ Truth.assertThat(listOfHeights[3]).isEqualTo(listOfHeights[4])
+ Truth.assertThat(listOfHeights[4]).isEqualTo(listOfHeights[5])
+ Truth.assertThat(listOfHeights[5]).isNotEqualTo(listOfHeights[6])
+ Truth.assertThat(listOfHeights[6]).isEqualTo(listOfHeights[7])
+ Truth.assertThat(listOfHeights[7]).isEqualTo(listOfHeights[8])
+ }
+
+ @Test
+ fun testFlowRow_equalHeight_WithFraction() {
+ val listOfHeights = mutableListOf<Int>()
+
+ rule.setContent {
+ CompositionLocalProvider(
+ LocalDensity provides NoOpDensity
+ ) {
+ with(LocalDensity.current) {
+ FlowRow(
+ Modifier
+ .fillMaxWidth(1f)
+ .padding(20.dp)
+ .wrapContentHeight(align = Alignment.Top),
+ horizontalArrangement = Arrangement.spacedBy(10.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ maxItemsInEachRow = 3,
+ ) {
+ repeat(9) {
+ Box(
+ Modifier
+ .onSizeChanged {
+ listOfHeights.add(it.height)
+ }
+ .width(100.dp)
+ .background(Color.Green)
+ .run {
+ if (it == 0 || it == 3 || it == 6) {
+ fillMaxRowHeight(0.5f)
+ } else {
+ this
+ }
+ }
+ ) {
+ val height = it * 400
+ Box(modifier = Modifier.height(height.dp))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ Truth.assertThat(listOfHeights[0]).isEqualTo((.5 * listOfHeights[2]).roundToInt())
+ Truth.assertThat(listOfHeights[1]).isNotEqualTo(listOfHeights[2])
+ Truth.assertThat(listOfHeights[2]).isEqualTo(800)
+ Truth.assertThat(listOfHeights[2]).isNotEqualTo(listOfHeights[3])
+ Truth.assertThat(listOfHeights[3]).isEqualTo((.5 * listOfHeights[5]).roundToInt())
+ Truth.assertThat(listOfHeights[4]).isNotEqualTo(listOfHeights[5])
+ Truth.assertThat(listOfHeights[5]).isEqualTo(2000)
+ Truth.assertThat(listOfHeights[5]).isNotEqualTo(listOfHeights[6])
+ Truth.assertThat(listOfHeights[6]).isEqualTo((.5 * listOfHeights[8]).roundToInt())
+ Truth.assertThat(listOfHeights[7]).isNotEqualTo(listOfHeights[8])
+ Truth.assertThat(listOfHeights[8]).isEqualTo(3200)
+ }
+
+ @Test
+ fun testFlowColumn_equalWidth() {
+ val listOfWidths = mutableListOf<Int>()
+
+ rule.setContent {
+ with(LocalDensity.current) {
+ FlowColumn(
+ Modifier
+ .fillMaxWidth(1f)
+ .padding(20.dp)
+ .wrapContentHeight(align = Alignment.Top),
+ horizontalArrangement = Arrangement.spacedBy(20.dp),
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ maxItemsInEachColumn = 3,
+ ) {
+ repeat(9) {
+ Box(
+ Modifier
+ .onSizeChanged {
+ listOfWidths.add(it.width)
+ }
+ .height(100.dp)
+ .background(Color.Green)
+ .fillMaxColumnWidth()
+ ) {
+ val width = it * Random.Default.nextInt(0, 500)
+ Box(modifier = Modifier.width(width.dp))
+ }
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ Truth.assertThat(listOfWidths[0]).isEqualTo(listOfWidths[1])
+ Truth.assertThat(listOfWidths[1]).isEqualTo(listOfWidths[2])
+ Truth.assertThat(listOfWidths[2]).isNotEqualTo(listOfWidths[3])
+ Truth.assertThat(listOfWidths[3]).isEqualTo(listOfWidths[4])
+ Truth.assertThat(listOfWidths[4]).isEqualTo(listOfWidths[5])
+ Truth.assertThat(listOfWidths[5]).isNotEqualTo(listOfWidths[6])
+ Truth.assertThat(listOfWidths[6]).isEqualTo(listOfWidths[7])
+ Truth.assertThat(listOfWidths[7]).isEqualTo(listOfWidths[8])
+ }
+
+ @Test
+ fun testFlowColumn_equalWidth_worksWithWeight() {
+ val listOfWidths = mutableListOf<Int>()
+
+ rule.setContent {
+ with(LocalDensity.current) {
+ FlowColumn(
+ Modifier
+ .fillMaxHeight(1f)
+ .padding(20.dp),
+ horizontalArrangement = Arrangement.spacedBy(20.dp),
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ maxItemsInEachColumn = 3,
+ ) {
+ repeat(9) {
+ Box(
+ Modifier
+ .onSizeChanged {
+ listOfWidths.add(it.width)
+ }
+ .height(100.dp)
+ .weight(1f, true)
+ .background(Color.Green)
+ .fillMaxColumnWidth()
+ ) {
+ val width = it * Random.Default.nextInt(0, 500)
+ Box(modifier = Modifier.width(width.dp))
+ }
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ Truth.assertThat(listOfWidths[0]).isEqualTo(listOfWidths[1])
+ Truth.assertThat(listOfWidths[1]).isEqualTo(listOfWidths[2])
+ Truth.assertThat(listOfWidths[2]).isNotEqualTo(listOfWidths[3])
+ Truth.assertThat(listOfWidths[3]).isEqualTo(listOfWidths[4])
+ Truth.assertThat(listOfWidths[4]).isEqualTo(listOfWidths[5])
+ Truth.assertThat(listOfWidths[5]).isNotEqualTo(listOfWidths[6])
+ Truth.assertThat(listOfWidths[6]).isEqualTo(listOfWidths[7])
+ Truth.assertThat(listOfWidths[7]).isEqualTo(listOfWidths[8])
+ }
+
+ @Test
+ fun testFlowColumn_equalWidth_fraction() {
+ val listOfWidths = mutableListOf<Int>()
+
+ rule.setContent {
+ CompositionLocalProvider(
+ LocalDensity provides NoOpDensity
+ ) {
+ FlowColumn(
+ Modifier
+ .fillMaxWidth(1f)
+ .padding(20.dp)
+ .wrapContentHeight(align = Alignment.Top),
+ horizontalArrangement = Arrangement.spacedBy(20.dp),
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ maxItemsInEachColumn = 3,
+ ) {
+ repeat(9) {
+ Box(
+ Modifier
+ .onSizeChanged {
+ listOfWidths.add(it.width)
+ }
+ .height(100.dp)
+ .background(Color.Green)
+ .run {
+ if (it == 0 || it == 3 || it == 6) {
+ fillMaxColumnWidth(0.5f)
+ } else {
+ this
+ }
+ }
+ ) {
+ val width = it * 400
+ Box(modifier = Modifier.width(width.dp))
+ }
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ Truth.assertThat(listOfWidths[0]).isEqualTo((.5 * listOfWidths[2]).roundToInt())
+ Truth.assertThat(listOfWidths[1]).isNotEqualTo(listOfWidths[2])
+ Truth.assertThat(listOfWidths[2]).isEqualTo(800)
+ Truth.assertThat(listOfWidths[2]).isNotEqualTo(listOfWidths[3])
+ Truth.assertThat(listOfWidths[3]).isEqualTo((.5 * listOfWidths[5]).roundToInt())
+ Truth.assertThat(listOfWidths[4]).isNotEqualTo(listOfWidths[5])
+ Truth.assertThat(listOfWidths[5]).isEqualTo(2000)
+ Truth.assertThat(listOfWidths[5]).isNotEqualTo(listOfWidths[6])
+ Truth.assertThat(listOfWidths[6]).isEqualTo((.5 * listOfWidths[8]).roundToInt())
+ Truth.assertThat(listOfWidths[7]).isNotEqualTo(listOfWidths[8])
+ Truth.assertThat(listOfWidths[8]).isEqualTo(3200)
+ }
+
+ @Test
fun testFlowColumn_staysInOneRow() {
var width = 0
@@ -2539,3 +2832,8 @@
Truth.assertThat(height).isEqualTo(200)
}
}
+
+private val NoOpDensity = object : Density {
+ override val density = 1f
+ override val fontScale = 1f
+}
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
index e091b8b..3993165 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
@@ -1,5 +1,6 @@
package androidx.compose.foundation.layout
+import androidx.annotation.FloatRange
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collection.MutableVector
@@ -15,7 +16,11 @@
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ParentDataModifierNode
+import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.constrainWidth
@@ -132,7 +137,23 @@
@LayoutScopeMarker
@Immutable
@ExperimentalLayoutApi
-interface FlowRowScope : RowScope
+interface FlowRowScope : RowScope {
+ /**
+ * Have the item fill (possibly only partially) the max height of the tallest item in the
+ * row it was placed in, within the [FlowRow].
+ *
+ * @param fraction The fraction of the max height of the tallest item
+ * between `0` and `1`, inclusive.
+ *
+ * Example usage:
+ * @sample androidx.compose.foundation.layout.samples.SimpleFlowRow_EqualHeight
+ */
+ @ExperimentalLayoutApi
+ fun Modifier.fillMaxRowHeight(
+ @FloatRange(from = 0.0, to = 1.0)
+ fraction: Float = 1f,
+ ): Modifier
+}
/**
* Scope for the children of [FlowColumn].
@@ -140,13 +161,93 @@
@LayoutScopeMarker
@Immutable
@ExperimentalLayoutApi
-interface FlowColumnScope : ColumnScope
+interface FlowColumnScope : ColumnScope {
+ /**
+ * Have the item fill (possibly only partially) the max width of the tallest item in the
+ * column it was placed in, within the [FlowColumn].
+ *
+ * @param fraction The fraction of the max width of the tallest item
+ * between `0` and `1`, inclusive.
+ *
+ * Example usage:
+ * @sample androidx.compose.foundation.layout.samples.SimpleFlowColumn_EqualWidth
+ */
+ @ExperimentalLayoutApi
+ fun Modifier.fillMaxColumnWidth(
+ @FloatRange(from = 0.0, to = 1.0)
+ fraction: Float = 1f,
+ ): Modifier
+}
@OptIn(ExperimentalLayoutApi::class)
-internal object FlowRowScopeInstance : RowScope by RowScopeInstance, FlowRowScope
+internal object FlowRowScopeInstance : RowScope by RowScopeInstance, FlowRowScope {
+ override fun Modifier.fillMaxRowHeight(fraction: Float): Modifier {
+ require(fraction > 0.0) { "invalid fraction $fraction; must be greater than zero" }
+ require(fraction <= 1.0) { "invalid fraction $fraction; must not be greater than 1.0" }
+ return this.then(
+ FillCrossAxisSizeElement(
+ fraction = fraction,
+ )
+ )
+ }
+}
@OptIn(ExperimentalLayoutApi::class)
-internal object FlowColumnScopeInstance : ColumnScope by ColumnScopeInstance, FlowColumnScope
+internal object FlowColumnScopeInstance : ColumnScope by ColumnScopeInstance, FlowColumnScope {
+ override fun Modifier.fillMaxColumnWidth(fraction: Float): Modifier {
+ require(fraction > 0.0) { "invalid fraction $fraction; must be greater than zero" }
+ require(fraction <= 1.0) { "invalid fraction $fraction; must not be greater than 1.0" }
+ return this.then(
+ FillCrossAxisSizeElement(
+ fraction = fraction,
+ )
+ )
+ }
+}
+
+internal data class FlowLayoutData(
+ var fillCrossAxisFraction: Float
+)
+
+internal class FillCrossAxisSizeNode(
+ var fraction: Float,
+) : ParentDataModifierNode, Modifier.Node() {
+ override fun Density.modifyParentData(parentData: Any?) =
+ ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
+ it.flowLayoutData = it.flowLayoutData ?: FlowLayoutData(fraction)
+ it.flowLayoutData!!.fillCrossAxisFraction = fraction
+ }
+}
+
+internal class FillCrossAxisSizeElement(
+ val fraction: Float
+) : ModifierNodeElement<FillCrossAxisSizeNode>() {
+ override fun create(): FillCrossAxisSizeNode {
+ return FillCrossAxisSizeNode(fraction)
+ }
+
+ override fun update(node: FillCrossAxisSizeNode) {
+ node.fraction = fraction
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ name = "fraction"
+ value = fraction
+ properties["fraction"] = fraction
+ }
+
+ override fun hashCode(): Int {
+ var result = fraction.hashCode()
+ result *= 31
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ val otherModifier = other as? FillCrossAxisSizeNode ?: return false
+ return fraction == otherModifier.fraction
+ }
+}
@PublishedApi
@Composable
@@ -620,48 +721,58 @@
crossAxisMax
)
// nextSize of the list, pre-calculated
- var nextSize: Int? = measurables.getOrNull(0)?.measureAndCache(
+ var nextSize: Pair<Int, Int>? = measurables.getOrNull(0)?.measureAndCache(
subsetConstraints, orientation
) { placeable ->
placeables[0] = placeable
}
+ var nextMainAxisSize: Int? = nextSize?.first
+ var nextCrossAxisSize: Int? = nextSize?.second
var startBreakLineIndex = 0
val endBreakLineList = arrayOfNulls<Int>(measurables.size)
+ val crossAxisSizes = arrayOfNulls<Int>(measurables.size)
var endBreakLineIndex = 0
var leftOver = mainAxisMax
// figure out the mainAxisTotalSize which will be minMainAxis when measuring the row/column
var mainAxisTotalSize = mainAxisMin
var currentLineMainAxisSize = 0
+ var currentLineCrossAxisSize = 0
for (index in measurables.indices) {
- val itemMainAxisSize = nextSize!!
+ val itemMainAxisSize = nextMainAxisSize!!
+ val itemCrossAxisSize = nextCrossAxisSize!!
currentLineMainAxisSize += itemMainAxisSize
+ currentLineCrossAxisSize = maxOf(currentLineCrossAxisSize, itemCrossAxisSize)
leftOver -= itemMainAxisSize
nextSize = measurables.getOrNull(index + 1)?.measureAndCache(
subsetConstraints, orientation
) { placeable ->
placeables[index + 1] = placeable
- }?.plus(spacing)
+ }
+ nextMainAxisSize = nextSize?.first?.plus(spacing)
+ nextCrossAxisSize = nextSize?.second ?: 0
if (index + 1 >= measurables.size ||
(index + 1) - startBreakLineIndex >= maxItemsInMainAxis ||
- leftOver - (nextSize ?: 0) < 0
+ leftOver - (nextMainAxisSize ?: 0) < 0
) {
mainAxisTotalSize = maxOf(mainAxisTotalSize, currentLineMainAxisSize)
mainAxisTotalSize = minOf(mainAxisTotalSize, mainAxisMax)
- currentLineMainAxisSize = 0
- leftOver = mainAxisMax
startBreakLineIndex = index + 1
endBreakLineList[endBreakLineIndex] = index + 1
+ crossAxisSizes[endBreakLineIndex] = currentLineCrossAxisSize
endBreakLineIndex++
+ currentLineMainAxisSize = 0
+ currentLineCrossAxisSize = 0
+ leftOver = mainAxisMax
// only add spacing for next items in the row or column, not the starting indexes
- nextSize = nextSize?.minus(spacing)
+ nextMainAxisSize = nextMainAxisSize?.minus(spacing)
}
}
val subsetBoxConstraints = subsetConstraints.copy(
mainAxisMin = mainAxisTotalSize
- ).toBoxConstraints(orientation)
+ )
startBreakLineIndex = 0
var crossAxisTotalSize = 0
@@ -669,9 +780,12 @@
endBreakLineIndex = 0
var endIndex = endBreakLineList.getOrNull(endBreakLineIndex)
while (endIndex != null) {
+ var crossAxisSize = crossAxisSizes[endBreakLineIndex]
val result = measureHelper.measureWithoutPlacing(
this,
- subsetBoxConstraints,
+ subsetBoxConstraints.copy(
+ crossAxisMax = crossAxisSize!!
+ ).toBoxConstraints(orientation),
startBreakLineIndex,
endIndex
)
@@ -726,17 +840,24 @@
constraints: OrientationIndependentConstraints,
orientation: LayoutOrientation,
storePlaceable: (Placeable?) -> Unit
-): Int {
- val itemSize: Int = if (rowColumnParentData.weight == 0f) {
+): Pair<Int, Int> {
+ val itemSize: Pair<Int, Int> = if (
+ rowColumnParentData.weight == 0f &&
+ rowColumnParentData?.flowLayoutData?.fillCrossAxisFraction == 0f
+ ) {
// fixed sizes: measure once
val placeable = measure(
constraints.copy(
mainAxisMin = 0,
).toBoxConstraints(orientation)
).also(storePlaceable)
- placeable.mainAxisSize(orientation)
+ val mainAxis = placeable.mainAxisSize(orientation)
+ val crossAxis = placeable.crossAxisSize(orientation)
+ Pair(mainAxis, crossAxis)
} else {
- mainAxisMin(orientation, Constraints.Infinity)
+ val mainAxis = mainAxisMin(orientation, Constraints.Infinity)
+ val crossAxis = crossAxisMin(orientation, mainAxis)
+ Pair(mainAxis, crossAxis)
}
return itemSize
}
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
index 82150c7..cf7d6d1 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
@@ -830,7 +830,8 @@
internal data class RowColumnParentData(
var weight: Float = 0f,
var fill: Boolean = true,
- var crossAxisAlignment: CrossAxisAlignment? = null
+ var crossAxisAlignment: CrossAxisAlignment? = null,
+ var flowLayoutData: FlowLayoutData? = null,
)
/**
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt
index 0b54663..5c7d4d4 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt
@@ -109,6 +109,10 @@
++weightChildrenCount
} else {
val mainAxisMax = constraints.mainAxisMax
+ val crossAxisMax = constraints.crossAxisMax
+ val crossAxisDesiredSize = if (crossAxisMax == Constraints.Infinity) 0 else ((
+ parentData?.flowLayoutData?.fillCrossAxisFraction ?: 0f
+ ) * crossAxisMax).toInt()
val placeable = placeables[i] ?: child.measure(
// Ask for preferred main axis size.
constraints.copy(
@@ -118,7 +122,9 @@
} else {
(mainAxisMax - fixedSpace).coerceAtLeast(0).toInt()
},
- crossAxisMin = 0
+ crossAxisMin = crossAxisDesiredSize,
+ crossAxisMax = if (crossAxisDesiredSize != 0)
+ crossAxisDesiredSize else constraints.crossAxisMax
).toBoxConstraints(orientation)
)
spaceAfterLastNoWeight = min(
@@ -159,6 +165,11 @@
val child = measurables[i]
val parentData = rowColumnParentData[i]
val weight = parentData.weight
+ val crossAxisMax = constraints.crossAxisMax
+ val crossAxisDesiredSize = if (crossAxisMax == Constraints.Infinity) 0 else
+ ((parentData?.flowLayoutData?.fillCrossAxisFraction ?: 0f) *
+ crossAxisMax
+ ).toInt()
check(weight > 0) { "All weights <= 0 should have placeables" }
// After the weightUnitSpace rounding, the total space going to be occupied
// can be smaller or larger than remainingToTarget. Here we distribute the
@@ -177,8 +188,9 @@
0
},
childMainAxisSize,
- 0,
- constraints.crossAxisMax
+ crossAxisMin = crossAxisDesiredSize,
+ crossAxisMax = if (crossAxisDesiredSize != 0)
+ crossAxisDesiredSize else constraints.crossAxisMax
).toBoxConstraints(orientation)
)
weightedSpace += placeable.mainAxisSize()
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
index 4c72289..b7a613f 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
@@ -32,6 +32,7 @@
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -43,8 +44,12 @@
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.InputMode
import androidx.compose.ui.input.InputMode.Companion.Keyboard
import androidx.compose.ui.input.InputMode.Companion.Touch
import androidx.compose.ui.input.InputModeManager
@@ -165,7 +170,9 @@
Box {
BasicText(
"ClickableText",
- modifier = Modifier.testTag("myClickable").clickable(onClick = onClick)
+ modifier = Modifier
+ .testTag("myClickable")
+ .clickable(onClick = onClick)
)
}
}
@@ -1153,6 +1160,178 @@
}
}
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ @LargeTest
+ fun noHover_whenDisabled() {
+ val interactionSource = MutableInteractionSource()
+
+ lateinit var scope: CoroutineScope
+ val enabled = mutableStateOf(true)
+
+ rule.setContent {
+ scope = rememberCoroutineScope()
+ BasicText(
+ "ClickableText",
+ modifier = Modifier
+ .testTag("myClickable")
+ .clickable(
+ enabled = enabled.value,
+ onClick = {},
+ interactionSource = interactionSource,
+ indication = null
+ )
+ )
+ }
+
+ val interactions = mutableListOf<Interaction>()
+
+ scope.launch {
+ interactionSource.interactions.collect { interactions.add(it) }
+ }
+
+ rule.runOnIdle {
+ assertThat(interactions).isEmpty()
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { enter(center) }
+
+ rule.runOnIdle {
+ assertThat(interactions).hasSize(1)
+ assertThat(interactions.first()).isInstanceOf(HoverInteraction.Enter::class.java)
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { exit(Offset(-1f, -1f)) }
+
+ rule.runOnIdle {
+ interactions.clear()
+ enabled.value = false
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { enter(center) }
+
+ rule.runOnIdle {
+ assertThat(interactions).isEmpty()
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { exit(Offset(-1f, -1f)) }
+
+ rule.runOnIdle {
+ enabled.value = true
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { enter(center) }
+
+ rule.runOnIdle {
+ assertThat(interactions).hasSize(1)
+ assertThat(interactions.first()).isInstanceOf(HoverInteraction.Enter::class.java)
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { exit(Offset(-1f, -1f)) }
+ }
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ @Test
+ fun noFocus_whenDisabled() {
+ val requester = FocusRequester()
+ // Force clickable to always be in non-touch mode, so it should be focusable
+ val keyboardMockManager = object : InputModeManager {
+ override val inputMode = Keyboard
+ override fun requestInputMode(inputMode: InputMode) = true
+ }
+
+ val enabled = mutableStateOf(true)
+ lateinit var focusState: FocusState
+
+ rule.setContent {
+ CompositionLocalProvider(LocalInputModeManager provides keyboardMockManager) {
+ Box {
+ BasicText(
+ "ClickableText",
+ modifier = Modifier
+ .testTag("myClickable")
+ .focusRequester(requester)
+ .onFocusEvent { focusState = it }
+ .clickable(enabled = enabled.value) {}
+ )
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ requester.requestFocus()
+ assertThat(focusState.isFocused).isTrue()
+ }
+
+ rule.runOnIdle {
+ enabled.value = false
+ }
+
+ rule.runOnIdle {
+ assertThat(focusState.isFocused).isFalse()
+ requester.requestFocus()
+ assertThat(focusState.isFocused).isFalse()
+ }
+ }
+
+ /**
+ * Test for b/269319898
+ */
+ @OptIn(ExperimentalComposeUiApi::class)
+ @Test
+ fun noFocusPropertiesSet_whenDisabled() {
+ val requester = FocusRequester()
+ // Force clickable to always be in non-touch mode, so it should be focusable
+ val keyboardMockManager = object : InputModeManager {
+ override val inputMode = Keyboard
+ override fun requestInputMode(inputMode: InputMode) = true
+ }
+
+ val enabled = mutableStateOf(true)
+ lateinit var focusState: FocusState
+
+ rule.setContent {
+ CompositionLocalProvider(LocalInputModeManager provides keyboardMockManager) {
+ Box(Modifier.clickable(enabled = enabled.value, onClick = {})) {
+ Box(
+ Modifier
+ .size(10.dp)
+ // If clickable is setting canFocus to true without a focus target, then
+ // that would override this property
+ .focusProperties { canFocus = false }
+ .focusRequester(requester)
+ .onFocusEvent { focusState = it }
+ .focusable()
+ )
+ }
+ }
+ }
+
+ // b/314129026 we can't read canFocus, so instead try and request focus and make sure
+ // that we are not focused
+ rule.runOnIdle {
+ // Clickable is enabled, it should correctly apply properties to its focus node
+ requester.requestFocus()
+ assertThat(focusState.isFocused).isFalse()
+ }
+
+ rule.runOnIdle {
+ enabled.value = false
+ }
+
+ rule.runOnIdle {
+ // Clickable is disabled, it should not apply properties down the tree
+ requester.requestFocus()
+ assertThat(focusState.isFocused).isFalse()
+ }
+ }
+
@Test
fun testInspectorValue_noIndicationOverload() {
val onClick: () -> Unit = { }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
index 0defe94..5e233f4 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
@@ -29,7 +29,9 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -41,8 +43,12 @@
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.InputMode
import androidx.compose.ui.input.InputMode.Companion.Keyboard
import androidx.compose.ui.input.InputMode.Companion.Touch
import androidx.compose.ui.input.InputModeManager
@@ -1751,6 +1757,178 @@
}
}
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ @LargeTest
+ fun noHover_whenDisabled() {
+ val interactionSource = MutableInteractionSource()
+
+ lateinit var scope: CoroutineScope
+ val enabled = mutableStateOf(true)
+
+ rule.setContent {
+ scope = rememberCoroutineScope()
+ BasicText(
+ "ClickableText",
+ modifier = Modifier
+ .testTag("myClickable")
+ .combinedClickable(
+ enabled = enabled.value,
+ onClick = {},
+ interactionSource = interactionSource,
+ indication = null
+ )
+ )
+ }
+
+ val interactions = mutableListOf<Interaction>()
+
+ scope.launch {
+ interactionSource.interactions.collect { interactions.add(it) }
+ }
+
+ rule.runOnIdle {
+ assertThat(interactions).isEmpty()
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { enter(center) }
+
+ rule.runOnIdle {
+ assertThat(interactions).hasSize(1)
+ assertThat(interactions.first()).isInstanceOf(HoverInteraction.Enter::class.java)
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { exit(Offset(-1f, -1f)) }
+
+ rule.runOnIdle {
+ interactions.clear()
+ enabled.value = false
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { enter(center) }
+
+ rule.runOnIdle {
+ assertThat(interactions).isEmpty()
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { exit(Offset(-1f, -1f)) }
+
+ rule.runOnIdle {
+ enabled.value = true
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { enter(center) }
+
+ rule.runOnIdle {
+ assertThat(interactions).hasSize(1)
+ assertThat(interactions.first()).isInstanceOf(HoverInteraction.Enter::class.java)
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performMouseInput { exit(Offset(-1f, -1f)) }
+ }
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ @Test
+ fun noFocus_whenDisabled() {
+ val requester = FocusRequester()
+ // Force clickable to always be in non-touch mode, so it should be focusable
+ val keyboardMockManager = object : InputModeManager {
+ override val inputMode = Keyboard
+ override fun requestInputMode(inputMode: InputMode) = true
+ }
+
+ val enabled = mutableStateOf(true)
+ lateinit var focusState: FocusState
+
+ rule.setContent {
+ CompositionLocalProvider(LocalInputModeManager provides keyboardMockManager) {
+ Box {
+ BasicText(
+ "ClickableText",
+ modifier = Modifier
+ .testTag("myClickable")
+ .focusRequester(requester)
+ .onFocusEvent { focusState = it }
+ .combinedClickable(enabled = enabled.value) {}
+ )
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ requester.requestFocus()
+ assertThat(focusState.isFocused).isTrue()
+ }
+
+ rule.runOnIdle {
+ enabled.value = false
+ }
+
+ rule.runOnIdle {
+ assertThat(focusState.isFocused).isFalse()
+ requester.requestFocus()
+ assertThat(focusState.isFocused).isFalse()
+ }
+ }
+
+ /**
+ * Test for b/269319898
+ */
+ @OptIn(ExperimentalComposeUiApi::class)
+ @Test
+ fun noFocusPropertiesSet_whenDisabled() {
+ val requester = FocusRequester()
+ // Force clickable to always be in non-touch mode, so it should be focusable
+ val keyboardMockManager = object : InputModeManager {
+ override val inputMode = Keyboard
+ override fun requestInputMode(inputMode: InputMode) = true
+ }
+
+ val enabled = mutableStateOf(true)
+ lateinit var focusState: FocusState
+
+ rule.setContent {
+ CompositionLocalProvider(LocalInputModeManager provides keyboardMockManager) {
+ Box(Modifier.combinedClickable(enabled = enabled.value, onClick = {})) {
+ Box(
+ Modifier
+ .size(10.dp)
+ // If clickable is setting canFocus to true without a focus target, then
+ // that would override this property
+ .focusProperties { canFocus = false }
+ .focusRequester(requester)
+ .onFocusEvent { focusState = it }
+ .focusable()
+ )
+ }
+ }
+ }
+
+ // b/314129026 we can't read canFocus, so instead try and request focus and make sure
+ // that we are not focused
+ rule.runOnIdle {
+ // Clickable is enabled, it should correctly apply properties to its focus node
+ requester.requestFocus()
+ assertThat(focusState.isFocused).isFalse()
+ }
+
+ rule.runOnIdle {
+ enabled.value = false
+ }
+
+ rule.runOnIdle {
+ // Clickable is disabled, it should not apply properties down the tree
+ requester.requestFocus()
+ assertThat(focusState.isFocused).isFalse()
+ }
+ }
+
@Test
fun testInspectorValue_noIndicationOverload() {
val onClick: () -> Unit = { }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index 3af83ab..e9722775 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -26,6 +26,7 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
+import androidx.compose.ui.focus.focusTarget
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEvent
@@ -73,6 +74,11 @@
* If you need to support double click or long click alongside the single click, consider
* using [combinedClickable].
*
+ * ***Note*** Any removal operations on Android Views from `clickable` should wrap `onClick` in a
+ * `post { }` block to guarantee the event dispatch completes before executing the removal. (You
+ * do not need to do this when removing a composable because Compose guarantees it completes via the
+ * snapshot state system.)
+ *
* @sample androidx.compose.foundation.samples.ClickableSample
*
* @param enabled Controls the enabled state. When `false`, [onClick], and this modifier will
@@ -150,9 +156,8 @@
) {
Modifier
.indication(interactionSource, indication)
- .hoverable(enabled = enabled, interactionSource = interactionSource)
- .focusableInNonTouchMode(enabled = enabled, interactionSource = interactionSource)
.then(ClickableElement(interactionSource, enabled, onClickLabel, role, onClick))
+ .then(if (enabled) Modifier.focusTarget() else Modifier)
}
/**
* Configure component to receive clicks, double clicks and long clicks via input or accessibility
@@ -273,8 +278,6 @@
) {
Modifier
.indication(interactionSource, indication)
- .hoverable(enabled = enabled, interactionSource = interactionSource)
- .focusableInNonTouchMode(enabled = enabled, interactionSource = interactionSource)
.then(
CombinedClickableElement(
interactionSource,
@@ -287,6 +290,7 @@
onDoubleClick
)
)
+ .then(if (enabled) Modifier.focusTarget() else Modifier)
}
private suspend fun PressGestureScope.handlePressInteraction(
@@ -740,6 +744,9 @@
) : DelegatingNode(), PointerInputModifierNode, KeyInputModifierNode {
abstract val clickablePointerInputNode: AbstractClickablePointerInputNode
abstract val clickableSemanticsNode: ClickableSemanticsNode
+ private val hoverableNode: HoverableNode = HoverableNode(interactionSource)
+ private val focusableInNonTouchMode: FocusableInNonTouchMode = FocusableInNonTouchMode()
+ private val focusableNode: FocusableNode = FocusableNode(interactionSource)
class InteractionData {
val currentKeyPressInteractions = mutableMapOf<Key, PressInteraction.Press>()
@@ -761,7 +768,14 @@
this.interactionSource = interactionSource
}
if (this.enabled != enabled) {
- if (!enabled) {
+ if (enabled) {
+ delegate(hoverableNode)
+ delegate(focusableInNonTouchMode)
+ delegate(focusableNode)
+ } else {
+ undelegate(hoverableNode)
+ undelegate(focusableInNonTouchMode)
+ undelegate(focusableNode)
disposeInteractionSource()
}
this.enabled = enabled
@@ -769,6 +783,16 @@
this.onClickLabel = onClickLabel
this.role = role
this.onClick = onClick
+ hoverableNode.updateInteractionSource(interactionSource)
+ focusableNode.update(interactionSource)
+ }
+
+ override fun onAttach() {
+ if (enabled) {
+ delegate(hoverableNode)
+ delegate(focusableInNonTouchMode)
+ delegate(focusableNode)
+ }
}
override fun onDetach() {
@@ -792,10 +816,16 @@
pass: PointerEventPass,
bounds: IntSize
) {
+ if (hoverableNode.isAttached) {
+ hoverableNode.onPointerEvent(pointerEvent, pass, bounds)
+ }
clickablePointerInputNode.onPointerEvent(pointerEvent, pass, bounds)
}
override fun onCancelPointerInput() {
+ if (hoverableNode.isAttached) {
+ hoverableNode.onCancelPointerInput()
+ }
clickablePointerInputNode.onCancelPointerInput()
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
index 88c8618..012ac24 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
@@ -130,7 +130,7 @@
},
factory = {
Modifier
- .then(FocusableInNonTouchModeElement)
+ .then(if (enabled) FocusableInNonTouchModeElement else Modifier)
.focusable(enabled, interactionSource)
})
@@ -149,7 +149,7 @@
}
}
-private class FocusableInNonTouchMode : Modifier.Node(), CompositionLocalConsumerModifierNode,
+internal class FocusableInNonTouchMode : Modifier.Node(), CompositionLocalConsumerModifierNode,
FocusPropertiesModifierNode {
private val inputModeManager: InputModeManager
@@ -193,7 +193,7 @@
}
@OptIn(ExperimentalFoundationApi::class)
-private class FocusableNode(
+internal class FocusableNode(
interactionSource: MutableInteractionSource?
) : DelegatingNode(), FocusEventModifierNode, LayoutAwareModifierNode, SemanticsModifierNode,
GlobalPositionAwareModifierNode {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
index cfc7267..9ae866f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
@@ -68,7 +68,7 @@
}
}
-private class HoverableNode(
+internal class HoverableNode(
private var interactionSource: MutableInteractionSource
) : PointerInputModifierNode, Modifier.Node() {
private var hoverInteraction: HoverInteraction.Enter? = null
diff --git a/compose/material3/material3-adaptive-navigation-suite/api/current.txt b/compose/material3/material3-adaptive-navigation-suite/api/current.txt
index db286e5..131dea0 100644
--- a/compose/material3/material3-adaptive-navigation-suite/api/current.txt
+++ b/compose/material3/material3-adaptive-navigation-suite/api/current.txt
@@ -56,9 +56,11 @@
method public String getNavigationBar();
method public String getNavigationDrawer();
method public String getNavigationRail();
+ method public String getNone();
property public final String NavigationBar;
property public final String NavigationDrawer;
property public final String NavigationRail;
+ property public final String None;
}
}
diff --git a/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt b/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
index db286e5..131dea0 100644
--- a/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
+++ b/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
@@ -56,9 +56,11 @@
method public String getNavigationBar();
method public String getNavigationDrawer();
method public String getNavigationRail();
+ method public String getNone();
property public final String NavigationBar;
property public final String NavigationDrawer;
property public final String NavigationRail;
+ property public final String None;
}
}
diff --git a/compose/material3/material3-adaptive-navigation-suite/build.gradle b/compose/material3/material3-adaptive-navigation-suite/build.gradle
index 93430ff2..67b8c3b 100644
--- a/compose/material3/material3-adaptive-navigation-suite/build.gradle
+++ b/compose/material3/material3-adaptive-navigation-suite/build.gradle
@@ -59,8 +59,6 @@
dependsOn(jvmMain)
dependencies {
api("androidx.annotation:annotation:1.1.0")
- implementation(project(":lifecycle:lifecycle-runtime-compose"))
- implementation(project(":window:window"))
}
}
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive-navigation-suite/NavigationSuiteTest.kt b/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive-navigation-suite/NavigationSuiteTest.kt
new file mode 100644
index 0000000..3dffee7
--- /dev/null
+++ b/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive-navigation-suite/NavigationSuiteTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 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.material3.adaptive.navigation.suite
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavigationSuiteTest {
+ @get:Rule
+ val rule = createComposeRule()
+
+ @Test
+ fun navigationLayoutTypeTest_None() {
+ val layoutType = NavigationSuiteType.None
+
+ rule.setContent {
+ SampleNavigationSuite(layoutType = layoutType)
+ }
+
+ rule.onNodeWithTag("NavigationSuite").assertIsNotDisplayed()
+ }
+}
+
+@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
+@Composable
+private fun SampleNavigationSuite(
+ layoutType: NavigationSuiteType
+) {
+ NavigationSuite(
+ modifier = Modifier.testTag("NavigationSuite"),
+ layoutType = layoutType
+ ) {}
+}
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation-suite/NavigationSuiteScaffold.kt b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation-suite/NavigationSuiteScaffold.kt
index 8686392..aa5fae8 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation-suite/NavigationSuiteScaffold.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation-suite/NavigationSuiteScaffold.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.DrawerDefaults
import androidx.compose.material3.Icon
@@ -56,9 +57,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMap
-import androidx.compose.ui.util.fastMaxOfOrNull
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.util.fastFirst
/**
* The Navigation Suite Scaffold wraps the provided content and places the adequate provided
@@ -129,52 +129,55 @@
NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(WindowAdaptiveInfoDefault),
content: @Composable () -> Unit = {}
) {
- Layout(
- contents = listOf(navigationSuite, content)
- ) { (navigationMeasurables, contentMeasurables), constraints ->
- val navigationPlaceables = navigationMeasurables.fastMap { it.measure(constraints) }
+ Layout({
+ // Wrap the navigation suite and content composables each in a Box to not propagate the
+ // parent's (Surface) min constraints to its children (see b/312664933).
+ Box(Modifier.layoutId(NavigationSuiteLayoutIdTag)) {
+ navigationSuite()
+ }
+ Box(Modifier.layoutId(ContentLayoutIdTag)) {
+ content()
+ }
+ }
+ ) { measurables, constraints ->
+ val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
+ // Find the navigation suite composable through it's layoutId tag
+ val navigationPlaceable =
+ measurables.fastFirst { it.layoutId == NavigationSuiteLayoutIdTag }
+ .measure(looseConstraints)
val isNavigationBar = layoutType == NavigationSuiteType.NavigationBar
val layoutHeight = constraints.maxHeight
val layoutWidth = constraints.maxWidth
- val contentPlaceables = contentMeasurables.fastMap { it.measure(
- if (isNavigationBar) {
- constraints.copy(
- minHeight = layoutHeight - (navigationPlaceables.fastMaxOfOrNull { it.height }
- ?: 0),
- maxHeight = layoutHeight - (navigationPlaceables.fastMaxOfOrNull { it.height }
- ?: 0)
- )
- } else {
- constraints.copy(
- minWidth = layoutWidth - (navigationPlaceables.fastMaxOfOrNull { it.width }
- ?: 0),
- maxWidth = layoutWidth - (navigationPlaceables.fastMaxOfOrNull { it.width }
- ?: 0)
- )
- }
- ) }
+ // Find the content composable through it's layoutId tag
+ val contentPlaceable =
+ measurables.fastFirst { it.layoutId == ContentLayoutIdTag }.measure(
+ if (isNavigationBar) {
+ constraints.copy(
+ minHeight = layoutHeight - navigationPlaceable.height,
+ maxHeight = layoutHeight - navigationPlaceable.height
+ )
+ } else {
+ constraints.copy(
+ minWidth = layoutWidth - navigationPlaceable.width,
+ maxWidth = layoutWidth - navigationPlaceable.width
+ )
+ }
+ )
layout(layoutWidth, layoutHeight) {
if (isNavigationBar) {
// Place content above the navigation component.
- contentPlaceables.fastForEach {
- it.placeRelative(0, 0)
- }
+ contentPlaceable.placeRelative(0, 0)
// Place the navigation component at the bottom of the screen.
- navigationPlaceables.fastForEach {
- it.placeRelative(
- 0,
- layoutHeight - (navigationPlaceables.fastMaxOfOrNull { it.height } ?: 0))
- }
+ navigationPlaceable.placeRelative(
+ 0,
+ layoutHeight - (navigationPlaceable.height)
+ )
} else {
// Place the navigation component at the start of the screen.
- navigationPlaceables.fastForEach {
- it.placeRelative(0, 0)
- }
+ navigationPlaceable.placeRelative(0, 0)
// Place content to the side of the navigation component.
- contentPlaceables.fastForEach {
- it.placeRelative((navigationPlaceables.fastMaxOfOrNull { it.width } ?: 0), 0)
- }
+ contentPlaceable.placeRelative((navigationPlaceable.width), 0)
}
}
}
@@ -276,6 +279,8 @@
}
}
}
+
+ NavigationSuiteType.None -> { /* Do nothing. */ }
}
}
@@ -358,6 +363,12 @@
* @see PermanentDrawerSheet
*/
val NavigationDrawer = NavigationSuiteType(description = "NavigationDrawer")
+
+ /**
+ * A navigation suite type that instructs the [NavigationSuite] to not display any
+ * navigation components on the screen.
+ */
+ val None = NavigationSuiteType(description = "None")
}
}
@@ -563,3 +574,6 @@
icon()
}
}
+
+private const val NavigationSuiteLayoutIdTag = "navigationSuite"
+private const val ContentLayoutIdTag = "content"
diff --git a/compose/material3/material3-adaptive/api/current.txt b/compose/material3/material3-adaptive/api/current.txt
index ed7a5d9..9183d40 100644
--- a/compose/material3/material3-adaptive/api/current.txt
+++ b/compose/material3/material3-adaptive/api/current.txt
@@ -44,17 +44,19 @@
field public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldDefaults INSTANCE;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ListDetailPaneScaffoldRole {
- method public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole[] values();
- enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole Detail;
- enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole Extra;
- enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole List;
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ListDetailPaneScaffoldRole {
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getDetail();
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getExtra();
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getList();
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Detail;
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Extra;
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole List;
+ field public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole INSTANCE;
}
public final class ListDetailPaneScaffold_androidKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> listPane, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.adaptive.ThreePaneScaffoldState scaffoldState, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> detailPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldState calculateListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole currentPaneDestination);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldState calculateListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ThreePaneScaffoldRole currentPaneDestination);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class PaneAdaptedValue {
@@ -116,17 +118,19 @@
field public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldDefaults INSTANCE;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum SupportingPaneScaffoldRole {
- method public static androidx.compose.material3.adaptive.SupportingPaneScaffoldRole valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material3.adaptive.SupportingPaneScaffoldRole[] values();
- enum_constant public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldRole Extra;
- enum_constant public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldRole Main;
- enum_constant public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldRole Supporting;
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class SupportingPaneScaffoldRole {
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getExtra();
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getMain();
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getSupporting();
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Extra;
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Main;
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Supporting;
+ field public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldRole INSTANCE;
}
public final class SupportingPaneScaffold_androidKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.adaptive.ThreePaneScaffoldState scaffoldState, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> mainPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldState calculateSupportingPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.SupportingPaneScaffoldRole currentPaneDestination);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldState calculateSupportingPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ThreePaneScaffoldRole currentPaneDestination);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldAdaptStrategies {
@@ -138,17 +142,17 @@
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void AnimatedPane(androidx.compose.material3.adaptive.ThreePaneScaffoldScope, androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ThreePaneScaffoldNavigator<T> {
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ThreePaneScaffoldNavigator {
method public boolean canNavigateBack(optional boolean scaffoldValueMustChange);
method public androidx.compose.material3.adaptive.ThreePaneScaffoldState getScaffoldState();
method public boolean navigateBack(optional boolean popUntilScaffoldValueChange);
- method public void navigateTo(T pane);
+ method public void navigateTo(androidx.compose.material3.adaptive.ThreePaneScaffoldRole pane);
property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldState scaffoldState;
}
public final class ThreePaneScaffoldNavigator_androidKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator<androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole> initialDestinationHistory);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator<androidx.compose.material3.adaptive.SupportingPaneScaffoldRole> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.SupportingPaneScaffoldRole> initialDestinationHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ThreePaneScaffoldRole> initialDestinationHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ThreePaneScaffoldRole> initialDestinationHistory);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ThreePaneScaffoldRole {
@@ -191,7 +195,7 @@
}
public final class ThreePaneScaffoldValueKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.ThreePaneScaffoldValue calculateThreePaneScaffoldValue(int maxHorizontalPartitions, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ThreePaneScaffoldRole? currentFocus);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.ThreePaneScaffoldValue calculateThreePaneScaffoldValue(int maxHorizontalPartitions, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ThreePaneScaffoldRole? currentDestination);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class WindowAdaptiveInfo {
diff --git a/compose/material3/material3-adaptive/api/restricted_current.txt b/compose/material3/material3-adaptive/api/restricted_current.txt
index ed7a5d9..9183d40 100644
--- a/compose/material3/material3-adaptive/api/restricted_current.txt
+++ b/compose/material3/material3-adaptive/api/restricted_current.txt
@@ -44,17 +44,19 @@
field public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldDefaults INSTANCE;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ListDetailPaneScaffoldRole {
- method public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole[] values();
- enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole Detail;
- enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole Extra;
- enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole List;
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ListDetailPaneScaffoldRole {
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getDetail();
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getExtra();
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getList();
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Detail;
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Extra;
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole List;
+ field public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole INSTANCE;
}
public final class ListDetailPaneScaffold_androidKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> listPane, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.adaptive.ThreePaneScaffoldState scaffoldState, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> detailPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldState calculateListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole currentPaneDestination);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldState calculateListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ThreePaneScaffoldRole currentPaneDestination);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class PaneAdaptedValue {
@@ -116,17 +118,19 @@
field public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldDefaults INSTANCE;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum SupportingPaneScaffoldRole {
- method public static androidx.compose.material3.adaptive.SupportingPaneScaffoldRole valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material3.adaptive.SupportingPaneScaffoldRole[] values();
- enum_constant public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldRole Extra;
- enum_constant public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldRole Main;
- enum_constant public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldRole Supporting;
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class SupportingPaneScaffoldRole {
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getExtra();
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getMain();
+ method public androidx.compose.material3.adaptive.ThreePaneScaffoldRole getSupporting();
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Extra;
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Main;
+ property public final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Supporting;
+ field public static final androidx.compose.material3.adaptive.SupportingPaneScaffoldRole INSTANCE;
}
public final class SupportingPaneScaffold_androidKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.adaptive.ThreePaneScaffoldState scaffoldState, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> mainPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldState calculateSupportingPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.SupportingPaneScaffoldRole currentPaneDestination);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldState calculateSupportingPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ThreePaneScaffoldRole currentPaneDestination);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldAdaptStrategies {
@@ -138,17 +142,17 @@
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void AnimatedPane(androidx.compose.material3.adaptive.ThreePaneScaffoldScope, androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ThreePaneScaffoldNavigator<T> {
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ThreePaneScaffoldNavigator {
method public boolean canNavigateBack(optional boolean scaffoldValueMustChange);
method public androidx.compose.material3.adaptive.ThreePaneScaffoldState getScaffoldState();
method public boolean navigateBack(optional boolean popUntilScaffoldValueChange);
- method public void navigateTo(T pane);
+ method public void navigateTo(androidx.compose.material3.adaptive.ThreePaneScaffoldRole pane);
property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldState scaffoldState;
}
public final class ThreePaneScaffoldNavigator_androidKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator<androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole> initialDestinationHistory);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator<androidx.compose.material3.adaptive.SupportingPaneScaffoldRole> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.SupportingPaneScaffoldRole> initialDestinationHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ThreePaneScaffoldRole> initialDestinationHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ThreePaneScaffoldNavigator rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ThreePaneScaffoldRole> initialDestinationHistory);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ThreePaneScaffoldRole {
@@ -191,7 +195,7 @@
}
public final class ThreePaneScaffoldValueKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.ThreePaneScaffoldValue calculateThreePaneScaffoldValue(int maxHorizontalPartitions, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ThreePaneScaffoldRole? currentFocus);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.ThreePaneScaffoldValue calculateThreePaneScaffoldValue(int maxHorizontalPartitions, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ThreePaneScaffoldRole? currentDestination);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class WindowAdaptiveInfo {
diff --git a/compose/material3/material3-adaptive/benchmark/build.gradle b/compose/material3/material3-adaptive/benchmark/build.gradle
deleted file mode 100644
index 48f6444..0000000
--- a/compose/material3/material3-adaptive/benchmark/build.gradle
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2023 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.
- */
-
-plugins {
- id("AndroidXPlugin")
- id("com.android.library")
- id("AndroidXComposePlugin")
- id("org.jetbrains.kotlin.android")
- id("androidx.benchmark")
-}
-
-dependencies {
- androidTestImplementation(project(":compose:material3:material3-adaptive"))
- androidTestImplementation(project(":benchmark:benchmark-junit4"))
- androidTestImplementation(project(":compose:runtime:runtime"))
- androidTestImplementation(project(":compose:benchmark-utils"))
- androidTestImplementation(libs.testRules)
- androidTestImplementation(libs.kotlinStdlib)
- androidTestImplementation(libs.kotlinTestCommon)
- androidTestImplementation(libs.junit)
-}
-
-android {
- namespace "androidx.compose.material3.adaptive.benchmark"
-}
diff --git a/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/ListDetailPaneScaffoldBenchmark.kt b/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/ListDetailPaneScaffoldBenchmark.kt
deleted file mode 100644
index b2e7be5..0000000
--- a/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/ListDetailPaneScaffoldBenchmark.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2023 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.material3.adaptive.benchmark
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.adaptive.AnimatedPane
-import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
-import androidx.compose.material3.adaptive.ListDetailPaneScaffold
-import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
-import androidx.compose.material3.adaptive.calculateListDetailPaneScaffoldState
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.testutils.LayeredComposeTestCase
-import androidx.compose.testutils.ToggleableTestCase
-import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
-import androidx.compose.testutils.benchmark.benchmarkFirstCompose
-import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
-import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.testTag
-import org.junit.Rule
-import org.junit.Test
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-class ListDetailPaneScaffoldBenchmark {
- @get:Rule
- val benchmarkRule = ComposeBenchmarkRule()
-
- @Test
- fun singlePane_firstPixel() {
- benchmarkRule.benchmarkToFirstPixel {
- ListDetailPaneScaffoldTestCase().apply {
- currentScaffoldDirective = singlePaneDirective
- }
- }
- }
-
- @Test
- fun dualPane_firstPixel() {
- benchmarkRule.benchmarkToFirstPixel {
- ListDetailPaneScaffoldTestCase().apply {
- currentScaffoldDirective = dualPaneDirective
- }
- }
- }
-
- @Test
- fun singlePane_firstCompose() {
- benchmarkRule.benchmarkFirstCompose {
- ListDetailPaneScaffoldTestCase().apply {
- currentScaffoldDirective = singlePaneDirective
- }
- }
- }
-
- @Test
- fun dualPane_firstCompose() {
- benchmarkRule.benchmarkFirstCompose {
- ListDetailPaneScaffoldTestCase().apply {
- currentScaffoldDirective = dualPaneDirective
- }
- }
- }
-
- @Test
- fun singlePane_navigateToDetail() {
- benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
- {
- object : ListDetailPaneScaffoldTestCase() {
- override fun toggleState() {
- currentDestination = ListDetailPaneScaffoldRole.Detail
- }
- }.apply {
- currentScaffoldDirective = singlePaneDirective
- }
- },
- assertOneRecomposition = false,
- )
- }
-
- @Test
- fun dualPane_navigateToExtra() {
- benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
- {
- object : ListDetailPaneScaffoldTestCase() {
- override fun toggleState() {
- currentDestination = ListDetailPaneScaffoldRole.Extra
- }
- }.apply {
- currentScaffoldDirective = dualPaneDirective
- }
- },
- assertOneRecomposition = false,
- )
- }
-}
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-internal open class ListDetailPaneScaffoldTestCase : LayeredComposeTestCase(), ToggleableTestCase {
- var currentScaffoldDirective by mutableStateOf(singlePaneDirective)
- var currentDestination by mutableStateOf(ListDetailPaneScaffoldRole.List)
-
- @Composable
- override fun MeasuredContent() {
- ListDetailPaneScaffold(
- scaffoldState = calculateListDetailPaneScaffoldState(
- scaffoldDirective = currentScaffoldDirective,
- currentPaneDestination = currentDestination
- ),
- listPane = {
- AnimatedPane(
- modifier = Modifier.testTag(tag = "ListPane")
- ) {
- Box(modifier = Modifier.fillMaxSize().background(Color.Red))
- }
- },
- extraPane = {
- AnimatedPane(
- modifier = Modifier.testTag(tag = "ExtraPane")
- ) {
- Box(modifier = Modifier.fillMaxSize().background(Color.Blue))
- }
- }
- ) {
- AnimatedPane(
- modifier = Modifier.testTag(tag = "DetailPane")
- ) {
- Box(modifier = Modifier.fillMaxSize().background(Color.Yellow))
- }
- }
- }
-
- override fun toggleState() {}
-}
diff --git a/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/SupportingPaneScaffoldBenchmark.kt b/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/SupportingPaneScaffoldBenchmark.kt
deleted file mode 100644
index 932422a..0000000
--- a/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/SupportingPaneScaffoldBenchmark.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2023 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.material3.adaptive.benchmark
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.adaptive.AnimatedPane
-import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
-import androidx.compose.material3.adaptive.SupportingPaneScaffold
-import androidx.compose.material3.adaptive.SupportingPaneScaffoldRole
-import androidx.compose.material3.adaptive.calculateSupportingPaneScaffoldState
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.testutils.LayeredComposeTestCase
-import androidx.compose.testutils.ToggleableTestCase
-import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
-import androidx.compose.testutils.benchmark.benchmarkFirstCompose
-import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
-import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.testTag
-import org.junit.Rule
-import org.junit.Test
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-class SupportingPaneScaffoldBenchmark {
- @get:Rule
- val benchmarkRule = ComposeBenchmarkRule()
-
- @Test
- fun singlePane_firstPixel() {
- benchmarkRule.benchmarkToFirstPixel {
- SupportingPaneScaffoldTestCase().apply {
- currentScaffoldDirective = singlePaneDirective
- }
- }
- }
-
- @Test
- fun dualPane_firstPixel() {
- benchmarkRule.benchmarkToFirstPixel {
- SupportingPaneScaffoldTestCase().apply {
- currentScaffoldDirective = dualPaneDirective
- }
- }
- }
-
- @Test
- fun singlePane_firstCompose() {
- benchmarkRule.benchmarkFirstCompose {
- SupportingPaneScaffoldTestCase().apply {
- currentScaffoldDirective = singlePaneDirective
- }
- }
- }
-
- @Test
- fun dualPane_firstCompose() {
- benchmarkRule.benchmarkFirstCompose {
- SupportingPaneScaffoldTestCase().apply {
- currentScaffoldDirective = dualPaneDirective
- }
- }
- }
-
- @Test
- fun singlePane_navigateToSupporting() {
- benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
- {
- object : SupportingPaneScaffoldTestCase() {
- override fun toggleState() {
- currentDestination = SupportingPaneScaffoldRole.Supporting
- }
- }.apply {
- currentScaffoldDirective = singlePaneDirective
- }
- },
- assertOneRecomposition = false,
- )
- }
-
- @Test
- fun dualPane_navigateToExtra() {
- benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
- {
- object : SupportingPaneScaffoldTestCase() {
- override fun toggleState() {
- currentDestination = SupportingPaneScaffoldRole.Extra
- }
- }.apply {
- currentScaffoldDirective = dualPaneDirective
- }
- },
- assertOneRecomposition = false,
- )
- }
-}
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-internal open class SupportingPaneScaffoldTestCase : LayeredComposeTestCase(), ToggleableTestCase {
- var currentScaffoldDirective by mutableStateOf(singlePaneDirective)
- var currentDestination by mutableStateOf(SupportingPaneScaffoldRole.Main)
-
- @Composable
- override fun MeasuredContent() {
- SupportingPaneScaffold(
- scaffoldState = calculateSupportingPaneScaffoldState(
- scaffoldDirective = currentScaffoldDirective,
- currentPaneDestination = currentDestination
- ),
- supportingPane = {
- AnimatedPane(
- modifier = Modifier.testTag(tag = "SupportingPane")
- ) {
- Box(modifier = Modifier.fillMaxSize().background(Color.Red))
- }
- },
- extraPane = {
- AnimatedPane(
- modifier = Modifier.testTag(tag = "ExtraPane")
- ) {
- Box(modifier = Modifier.fillMaxSize().background(Color.Blue))
- }
- }
- ) {
- AnimatedPane(
- modifier = Modifier.testTag(tag = "MainPane")
- ) {
- Box(modifier = Modifier.fillMaxSize().background(Color.Yellow))
- }
- }
- }
-
- override fun toggleState() {}
-}
diff --git a/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/TestUtils.kt b/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/TestUtils.kt
deleted file mode 100644
index 4b2e283d..0000000
--- a/compose/material3/material3-adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/TestUtils.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2023 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.material3.adaptive.benchmark
-
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
-import androidx.compose.material3.adaptive.PaneScaffoldDirective
-import androidx.compose.ui.unit.dp
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-val singlePaneDirective = PaneScaffoldDirective(
- contentPadding = PaddingValues(16.dp),
- maxHorizontalPartitions = 1,
- horizontalPartitionSpacerSize = 0.dp,
- maxVerticalPartitions = 1,
- verticalPartitionSpacerSize = 0.dp,
- excludedBounds = emptyList()
-)
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-val dualPaneDirective = PaneScaffoldDirective(
- contentPadding = PaddingValues(24.dp),
- maxHorizontalPartitions = 2,
- horizontalPartitionSpacerSize = 24.dp,
- maxVerticalPartitions = 1,
- verticalPartitionSpacerSize = 0.dp,
- excludedBounds = emptyList()
-)
diff --git a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldNavigatorTest.kt b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldNavigatorTest.kt
index 452f698..83c85bd 100644
--- a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldNavigatorTest.kt
+++ b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldNavigatorTest.kt
@@ -37,7 +37,7 @@
@Test
fun singlePaneLayout_navigateTo_makeFocusPaneExpanded() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<ListDetailPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
var canNavigateBack by Delegates.notNull<Boolean>()
composeRule.setContent {
@@ -64,7 +64,7 @@
@Test
fun dualPaneLayout_navigateTo_keepFocusPaneExpanded() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<ListDetailPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
var canNavigateBack by Delegates.notNull<Boolean>()
composeRule.setContent {
@@ -91,7 +91,7 @@
@Test
fun singlePaneLayout_navigateBack_makeFocusPaneHidden() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<ListDetailPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
var canNavigateBack by Delegates.notNull<Boolean>()
composeRule.setContent {
@@ -123,7 +123,7 @@
@Test
fun dualPaneLayout_enforceScaffoldValueChange_cannotNavigateBack() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<ListDetailPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
composeRule.setContent {
scaffoldNavigator = rememberListDetailPaneScaffoldNavigator(
@@ -142,7 +142,7 @@
@Test
fun dualPaneLayout_notEnforceScaffoldValueChange_canNavigateBack() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<ListDetailPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
composeRule.setContent {
scaffoldNavigator = rememberListDetailPaneScaffoldNavigator(
@@ -171,7 +171,7 @@
@Test
fun singlePaneToDualPaneLayout_enforceScaffoldValueChange_cannotNavigateBack() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<ListDetailPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
val mockCurrentScaffoldDirective = mutableStateOf(MockSinglePaneScaffoldDirective)
composeRule.setContent {
diff --git a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffoldNavigatorTest.kt b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffoldNavigatorTest.kt
index a3ad7b67..34d2315 100644
--- a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffoldNavigatorTest.kt
+++ b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffoldNavigatorTest.kt
@@ -37,7 +37,7 @@
@Test
fun singlePaneLayout_navigateTo_makeFocusPaneExpanded() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<SupportingPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
var canNavigateBack by Delegates.notNull<Boolean>()
composeRule.setContent {
@@ -64,7 +64,7 @@
@Test
fun dualPaneLayout_navigateTo_keepFocusPaneExpanded() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<SupportingPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
var canNavigateBack by Delegates.notNull<Boolean>()
composeRule.setContent {
@@ -91,7 +91,7 @@
@Test
fun singlePaneLayout_navigateBack_makeFocusPaneHidden() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<SupportingPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
var canNavigateBack by Delegates.notNull<Boolean>()
composeRule.setContent {
@@ -123,7 +123,7 @@
@Test
fun dualPaneLayout_enforceScaffoldChange_cannotNavigateBack() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<SupportingPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
composeRule.setContent {
scaffoldNavigator = rememberSupportingPaneScaffoldNavigator(
@@ -142,7 +142,7 @@
@Test
fun dualPaneLayout_notEnforceScaffoldValueChange_canNavigateBack() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<SupportingPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
composeRule.setContent {
scaffoldNavigator = rememberSupportingPaneScaffoldNavigator(
@@ -171,7 +171,7 @@
@Test
fun singlePaneToDualPaneLayout_enforceScaffoldValueChange_cannotNavigateBack() {
- lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator<SupportingPaneScaffoldRole>
+ lateinit var scaffoldNavigator: ThreePaneScaffoldNavigator
val mockCurrentScaffoldDirective = mutableStateOf(MockSinglePaneScaffoldDirective)
composeRule.setContent {
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.android.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.android.kt
index 68f178a..ef08697 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.android.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.android.kt
@@ -71,13 +71,13 @@
calculateStandardPaneScaffoldDirective(currentWindowAdaptiveInfo()),
adaptStrategies: ThreePaneScaffoldAdaptStrategies =
ListDetailPaneScaffoldDefaults.adaptStrategies(),
- currentPaneDestination: ListDetailPaneScaffoldRole = ListDetailPaneScaffoldRole.List
+ currentPaneDestination: ThreePaneScaffoldRole = ListDetailPaneScaffoldRole.List
): ThreePaneScaffoldState = ThreePaneScaffoldStateImpl(
scaffoldDirective,
calculateThreePaneScaffoldValue(
scaffoldDirective.maxHorizontalPartitions,
adaptStrategies,
- currentPaneDestination.threePaneScaffoldRole
+ currentPaneDestination
)
)
@@ -106,22 +106,28 @@
}
/**
- * The set of the available pane roles of [ListDetailPaneScaffold].
+ * The set of the available pane roles of [ListDetailPaneScaffold]. Basically those values are
+ * aliases of [ThreePaneScaffoldRole]. We suggest you to use the values defined here instead of
+ * the raw [ThreePaneScaffoldRole] under the context of [ListDetailPaneScaffold] for better
+ * code clarity.
*/
@ExperimentalMaterial3AdaptiveApi
-enum class ListDetailPaneScaffoldRole(internal val threePaneScaffoldRole: ThreePaneScaffoldRole) {
+object ListDetailPaneScaffoldRole {
/**
- * The list pane of [ListDetailPaneScaffold]. It is mapped to [ThreePaneScaffoldRole.Secondary].
+ * The list pane of [ListDetailPaneScaffold]. It is an alias of
+ * [ThreePaneScaffoldRole.Secondary].
*/
- List(ThreePaneScaffoldRole.Secondary),
+ val List = ThreePaneScaffoldRole.Secondary
/**
- * The detail pane of [ListDetailPaneScaffold]. It is mapped to [ThreePaneScaffoldRole.Primary].
+ * The detail pane of [ListDetailPaneScaffold]. It is an alias of
+ * [ThreePaneScaffoldRole.Primary].
*/
- Detail(ThreePaneScaffoldRole.Primary),
+ val Detail = ThreePaneScaffoldRole.Primary
/**
- * The extra pane of [ListDetailPaneScaffold]. It is mapped to [ThreePaneScaffoldRole.Tertiary].
+ * The extra pane of [ListDetailPaneScaffold]. It is an alias of
+ * [ThreePaneScaffoldRole.Tertiary].
*/
- Extra(ThreePaneScaffoldRole.Tertiary);
+ val Extra = ThreePaneScaffoldRole.Tertiary
}
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffold.android.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffold.android.kt
index 42535b8..b6020ce 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffold.android.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffold.android.kt
@@ -56,13 +56,13 @@
calculateStandardPaneScaffoldDirective(currentWindowAdaptiveInfo()),
adaptStrategies: ThreePaneScaffoldAdaptStrategies =
SupportingPaneScaffoldDefaults.adaptStrategies(),
- currentPaneDestination: SupportingPaneScaffoldRole = SupportingPaneScaffoldRole.Main
+ currentPaneDestination: ThreePaneScaffoldRole = SupportingPaneScaffoldRole.Main
): ThreePaneScaffoldState = ThreePaneScaffoldStateImpl(
scaffoldDirective,
calculateThreePaneScaffoldValue(
scaffoldDirective.maxHorizontalPartitions,
adaptStrategies,
- currentPaneDestination.threePaneScaffoldRole
+ currentPaneDestination
)
)
@@ -91,21 +91,28 @@
}
/**
- * The set of the available pane roles of [SupportingPaneScaffold].
+ * The set of the available pane roles of [SupportingPaneScaffold]. Basically those values are
+ * aliases of [ThreePaneScaffoldRole]. We suggest you to use the values defined here instead of
+ * the raw [ThreePaneScaffoldRole] under the context of [SupportingPaneScaffold] for better
+ * code clarity.
*/
@ExperimentalMaterial3AdaptiveApi
-enum class SupportingPaneScaffoldRole(internal val threePaneScaffoldRole: ThreePaneScaffoldRole) {
+object SupportingPaneScaffoldRole {
/**
- * The main pane of [SupportingPaneScaffold]. It is mapped to [ThreePaneScaffoldRole.Primary].
+ * The main pane of [SupportingPaneScaffold]. It is an alias of
+ * [ThreePaneScaffoldRole.Primary].
*/
- Main(ThreePaneScaffoldRole.Primary),
+ val Main = ThreePaneScaffoldRole.Primary
+
/**
- * The supporting pane of [SupportingPaneScaffold]. It is mapped to
+ * The supporting pane of [SupportingPaneScaffold]. It is an alias of
* [ThreePaneScaffoldRole.Secondary].
*/
- Supporting(ThreePaneScaffoldRole.Secondary),
+ val Supporting = ThreePaneScaffoldRole.Secondary
+
/**
- * The extra pane of [SupportingPaneScaffold]. It is mapped to [ThreePaneScaffoldRole.Tertiary].
+ * The extra pane of [SupportingPaneScaffold]. It is an alias of
+ * [ThreePaneScaffoldRole.Tertiary].
*/
- Extra(ThreePaneScaffoldRole.Tertiary)
+ val Extra = ThreePaneScaffoldRole.Tertiary
}
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldNavigator.android.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldNavigator.android.kt
index 0e269d3..5759fb7 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldNavigator.android.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldNavigator.android.kt
@@ -22,12 +22,10 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
-import androidx.compose.ui.util.fastMap
/**
* The navigation integration entry point of [ThreePaneScaffold] implementations.
@@ -40,7 +38,7 @@
*/
@ExperimentalMaterial3AdaptiveApi
@Stable
-interface ThreePaneScaffoldNavigator<T> {
+interface ThreePaneScaffoldNavigator {
/**
* The current scaffold state provided by the navigator. It's supposed to be auto-updated
* whenever a navigation operation is performed.
@@ -50,7 +48,7 @@
/**
* Navigates to a new pane destination.
*/
- fun navigateTo(pane: T)
+ fun navigateTo(pane: ThreePaneScaffoldRole)
/**
* Returns `true` if there is a previous destination to navigate back to.
@@ -89,18 +87,13 @@
calculateStandardPaneScaffoldDirective(currentWindowAdaptiveInfo()),
adaptStrategies: ThreePaneScaffoldAdaptStrategies =
ListDetailPaneScaffoldDefaults.adaptStrategies(),
- initialDestinationHistory: List<ListDetailPaneScaffoldRole> =
- listOf(ListDetailPaneScaffoldRole.List)
-): ThreePaneScaffoldNavigator<ListDetailPaneScaffoldRole> {
- val internalNavigator = rememberDefaultThreePaneScaffoldNavigator(
+ initialDestinationHistory: List<ThreePaneScaffoldRole> = listOf(ListDetailPaneScaffoldRole.List)
+): ThreePaneScaffoldNavigator =
+ rememberThreePaneScaffoldNavigator(
scaffoldDirective,
adaptStrategies,
- initialDestinationHistory.fastMap { it.threePaneScaffoldRole }
+ initialDestinationHistory
)
- return remember(internalNavigator) {
- DefaultListDetailPaneScaffoldNavigator(internalNavigator)
- }
-}
/**
* Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
@@ -122,48 +115,66 @@
calculateStandardPaneScaffoldDirective(currentWindowAdaptiveInfo()),
adaptStrategies: ThreePaneScaffoldAdaptStrategies =
SupportingPaneScaffoldDefaults.adaptStrategies(),
- initialDestinationHistory: List<SupportingPaneScaffoldRole> =
+ initialDestinationHistory: List<ThreePaneScaffoldRole> =
listOf(SupportingPaneScaffoldRole.Main)
-): ThreePaneScaffoldNavigator<SupportingPaneScaffoldRole> {
- val internalNavigator = rememberDefaultThreePaneScaffoldNavigator(
+): ThreePaneScaffoldNavigator =
+ rememberThreePaneScaffoldNavigator(
scaffoldDirective,
adaptStrategies,
- initialDestinationHistory.fastMap { it.threePaneScaffoldRole }
+ initialDestinationHistory
)
- return remember(internalNavigator) {
- DefaultSupportingPaneScaffoldNavigator(internalNavigator)
+
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+internal fun rememberThreePaneScaffoldNavigator(
+ scaffoldDirective: PaneScaffoldDirective,
+ adaptStrategies: ThreePaneScaffoldAdaptStrategies,
+ initialDestinationHistory: List<ThreePaneScaffoldRole>
+): ThreePaneScaffoldNavigator =
+ rememberSaveable(
+ saver = DefaultThreePaneScaffoldNavigator.saver(scaffoldDirective, adaptStrategies)
+ ) {
+ DefaultThreePaneScaffoldNavigator(
+ initialDestinationHistory = initialDestinationHistory,
+ initialScaffoldDirective = scaffoldDirective,
+ initialAdaptStrategies = adaptStrategies
+ )
+ }.apply {
+ this.scaffoldDirective = scaffoldDirective
+ this.adaptStrategies = adaptStrategies
}
-}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
internal class DefaultThreePaneScaffoldNavigator(
initialDestinationHistory: List<ThreePaneScaffoldRole>,
initialScaffoldDirective: PaneScaffoldDirective,
initialAdaptStrategies: ThreePaneScaffoldAdaptStrategies,
-) : ThreePaneScaffoldState {
+) : ThreePaneScaffoldNavigator, ThreePaneScaffoldState {
private val destinationHistory = mutableStateListOf<ThreePaneScaffoldRole>().apply {
addAll(initialDestinationHistory)
}
+ override val scaffoldState = this
+
override var scaffoldDirective by mutableStateOf(initialScaffoldDirective)
+
var adaptStrategies by mutableStateOf(initialAdaptStrategies)
- val currentDestination: ThreePaneScaffoldRole?
- get() = destinationHistory.lastOrNull()
+ val currentDestination: ThreePaneScaffoldRole? get() = destinationHistory.lastOrNull()
override val scaffoldValue by derivedStateOf {
calculateScaffoldValue(currentDestination)
}
- fun navigateTo(pane: ThreePaneScaffoldRole) {
+ override fun navigateTo(pane: ThreePaneScaffoldRole) {
destinationHistory.add(pane)
}
- fun canNavigateBack(scaffoldValueMustChange: Boolean): Boolean =
+ override fun canNavigateBack(scaffoldValueMustChange: Boolean): Boolean =
getPreviousDestinationIndex(scaffoldValueMustChange) >= 0
- fun navigateBack(popUntilScaffoldValueChange: Boolean): Boolean {
+ override fun navigateBack(popUntilScaffoldValueChange: Boolean): Boolean {
val previousDestinationIndex = getPreviousDestinationIndex(popUntilScaffoldValueChange)
if (previousDestinationIndex < 0) {
destinationHistory.clear()
@@ -211,7 +222,7 @@
initialAdaptStrategies: ThreePaneScaffoldAdaptStrategies
): Saver<DefaultThreePaneScaffoldNavigator, *> = listSaver(
save = {
- it.destinationHistory.toList()
+ it.destinationHistory
},
restore = {
DefaultThreePaneScaffoldNavigator(
@@ -223,60 +234,3 @@
)
}
}
-
-@ExperimentalMaterial3AdaptiveApi
-@Composable
-internal fun rememberDefaultThreePaneScaffoldNavigator(
- scaffoldDirective: PaneScaffoldDirective,
- adaptStrategies: ThreePaneScaffoldAdaptStrategies,
- initialDestinationHistory: List<ThreePaneScaffoldRole>
-): DefaultThreePaneScaffoldNavigator =
- rememberSaveable(
- saver = DefaultThreePaneScaffoldNavigator.saver(
- scaffoldDirective,
- adaptStrategies,
- )
- ) {
- DefaultThreePaneScaffoldNavigator(
- initialDestinationHistory = initialDestinationHistory,
- initialScaffoldDirective = scaffoldDirective,
- initialAdaptStrategies = adaptStrategies
- )
- }.apply {
- this.scaffoldDirective = scaffoldDirective
- this.adaptStrategies = adaptStrategies
- }
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-private class DefaultListDetailPaneScaffoldNavigator(
- val internalNavigator: DefaultThreePaneScaffoldNavigator
-) : ThreePaneScaffoldNavigator<ListDetailPaneScaffoldRole> {
- override val scaffoldState get() = internalNavigator
-
- override fun navigateTo(pane: ListDetailPaneScaffoldRole) {
- internalNavigator.navigateTo(pane.threePaneScaffoldRole)
- }
-
- override fun canNavigateBack(scaffoldValueMustChange: Boolean): Boolean =
- internalNavigator.canNavigateBack(scaffoldValueMustChange)
-
- override fun navigateBack(popUntilScaffoldValueChange: Boolean): Boolean =
- internalNavigator.navigateBack(popUntilScaffoldValueChange)
-}
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-private class DefaultSupportingPaneScaffoldNavigator(
- val internalNavigator: DefaultThreePaneScaffoldNavigator
-) : ThreePaneScaffoldNavigator<SupportingPaneScaffoldRole> {
- override val scaffoldState get() = internalNavigator
-
- override fun navigateTo(pane: SupportingPaneScaffoldRole) {
- internalNavigator.navigateTo(pane.threePaneScaffoldRole)
- }
-
- override fun canNavigateBack(scaffoldValueMustChange: Boolean): Boolean =
- internalNavigator.canNavigateBack(scaffoldValueMustChange)
-
- override fun navigateBack(popUntilScaffoldValueChange: Boolean): Boolean =
- internalNavigator.navigateBack(popUntilScaffoldValueChange)
-}
diff --git a/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValueTest.kt b/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValueTest.kt
index 9ece26c..32cfb93 100644
--- a/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValueTest.kt
+++ b/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValueTest.kt
@@ -31,7 +31,10 @@
adaptStrategies = MockAdaptStrategies
)
scaffoldState.assertState(ThreePaneScaffoldRole.Primary, PaneAdaptedValue.Expanded)
- scaffoldState.assertState(ThreePaneScaffoldRole.Secondary, SecondaryPaneAdaptedState)
+ scaffoldState.assertState(
+ ThreePaneScaffoldRole.Secondary,
+ SecondaryPaneAdaptedState
+ )
scaffoldState.assertState(ThreePaneScaffoldRole.Tertiary, TertiaryPaneAdaptedState)
}
@@ -40,10 +43,12 @@
val scaffoldState = calculateThreePaneScaffoldValue(
maxHorizontalPartitions = 1,
adaptStrategies = MockAdaptStrategies,
- currentFocus = ThreePaneScaffoldRole.Secondary
+ currentDestination = ThreePaneScaffoldRole.Secondary
)
scaffoldState.assertState(ThreePaneScaffoldRole.Primary, PrimaryPaneAdaptedState)
- scaffoldState.assertState(ThreePaneScaffoldRole.Secondary, PaneAdaptedValue.Expanded)
+ scaffoldState.assertState(
+ ThreePaneScaffoldRole.Secondary,
+ PaneAdaptedValue.Expanded)
scaffoldState.assertState(ThreePaneScaffoldRole.Tertiary, TertiaryPaneAdaptedState)
}
@@ -54,7 +59,9 @@
adaptStrategies = MockAdaptStrategies
)
scaffoldState.assertState(ThreePaneScaffoldRole.Primary, PaneAdaptedValue.Expanded)
- scaffoldState.assertState(ThreePaneScaffoldRole.Secondary, PaneAdaptedValue.Expanded)
+ scaffoldState.assertState(
+ ThreePaneScaffoldRole.Secondary, PaneAdaptedValue.Expanded
+ )
scaffoldState.assertState(ThreePaneScaffoldRole.Tertiary, TertiaryPaneAdaptedState)
}
@@ -63,10 +70,12 @@
val scaffoldState = calculateThreePaneScaffoldValue(
maxHorizontalPartitions = 2,
adaptStrategies = MockAdaptStrategies,
- currentFocus = ThreePaneScaffoldRole.Tertiary
+ currentDestination = ThreePaneScaffoldRole.Tertiary
)
scaffoldState.assertState(ThreePaneScaffoldRole.Primary, PaneAdaptedValue.Expanded)
- scaffoldState.assertState(ThreePaneScaffoldRole.Secondary, SecondaryPaneAdaptedState)
+ scaffoldState.assertState(
+ ThreePaneScaffoldRole.Secondary, SecondaryPaneAdaptedState
+ )
scaffoldState.assertState(ThreePaneScaffoldRole.Tertiary, PaneAdaptedValue.Expanded)
}
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
index 4e0abed..134cfcf 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
@@ -185,8 +185,8 @@
) {
/**
- * Resolves and returns the [EnterTransition] for the given [ThreePaneScaffoldRole] at the given
- * [ThreePaneScaffoldHorizontalOrder].
+ * Resolves and returns the [EnterTransition] for the given [ThreePaneScaffoldRole]
+ * at the given [ThreePaneScaffoldHorizontalOrder].
*/
fun enterTransition(
role: ThreePaneScaffoldRole,
@@ -203,8 +203,8 @@
}
/**
- * Resolves and returns the [ExitTransition] for the given [ThreePaneScaffoldRole] at the given
- * [ThreePaneScaffoldHorizontalOrder].
+ * Resolves and returns the [ExitTransition] for the given [ThreePaneScaffoldRole]
+ * at the given [ThreePaneScaffoldHorizontalOrder].
*/
fun exitTransition(
role: ThreePaneScaffoldRole,
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValue.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValue.kt
index f1e1931..30f18cb 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValue.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValue.kt
@@ -31,33 +31,33 @@
/**
* Calculates the current adapted value of [ThreePaneScaffold] according to the given
- * [maxHorizontalPartitions], [adaptStrategies] and [currentFocus]. The returned value can be used
- * as a unique representation of the current layout structure.
+ * [maxHorizontalPartitions], [adaptStrategies] and [currentDestination]. The returned value can be
+ * used as a unique representation of the current layout structure.
*
- * The function will treat the current focus as the highest priority and then adapt the rest
+ * The function will treat the current destination as the highest priority and then adapt the rest
* panes according to the order of [ThreePaneScaffoldRole.Primary],
- * [ThreePaneScaffoldRole.Secondary] and [ThreePaneScaffoldRole.Tertiary]. If there are still
- * remaining partitions to put the pane, the pane will be set as [PaneAdaptedValue.Expanded],
- * otherwise it will be adapted according to its associated [AdaptStrategy].
+ * [ThreePaneScaffoldRole.Secondary] and [ThreePaneScaffoldRole.Tertiary]. If there
+ * are still remaining partitions to put the pane, the pane will be set as
+ * [PaneAdaptedValue.Expanded], otherwise it will be adapted according to its associated
+ * [AdaptStrategy].
*
* @param maxHorizontalPartitions The maximum allowed partitions along the horizontal axis, i.e.
- * how many expanded panes can be shown at the same time.
+ * how many expanded panes can be shown at the same time.
* @param adaptStrategies The adapt strategies of each pane role that [ThreePaneScaffold] supports,
- * the default value will be
- * [ThreePaneScaffoldDefaults.threePaneScaffoldAdaptStrategies].
- * @param currentFocus The current focused pane, which will be treated as the highest priority, can
- * be `null`.
+ * the default value will be [ThreePaneScaffoldDefaults.threePaneScaffoldAdaptStrategies].
+ * @param currentDestination The current pane destination, which will be treated as having
+ * the highest priority, can be `null`.
*/
@ExperimentalMaterial3AdaptiveApi
fun calculateThreePaneScaffoldValue(
maxHorizontalPartitions: Int,
adaptStrategies: ThreePaneScaffoldAdaptStrategies = ThreePaneScaffoldDefaults.adaptStrategies(),
- currentFocus: ThreePaneScaffoldRole? = null,
+ currentDestination: ThreePaneScaffoldRole? = null,
): ThreePaneScaffoldValue {
- var expandedCount = if (currentFocus != null) 1 else 0
+ var expandedCount = if (currentDestination != null) 1 else 0
return buildThreePaneScaffoldValue { role ->
when {
- role == currentFocus -> PaneAdaptedValue.Expanded
+ role == currentDestination -> PaneAdaptedValue.Expanded
expandedCount < maxHorizontalPartitions -> {
expandedCount++
PaneAdaptedValue.Expanded
diff --git a/compose/material3/material3/api/1.2.0-beta01.txt b/compose/material3/material3/api/1.2.0-beta01.txt
index 7138834..4a436c2 100644
--- a/compose/material3/material3/api/1.2.0-beta01.txt
+++ b/compose/material3/material3/api/1.2.0-beta01.txt
@@ -1617,7 +1617,7 @@
method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.material3.SwitchColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
- public interface TabIndicatorScope {
+ @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public interface TabIndicatorScope {
method public androidx.compose.ui.Modifier tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult> measure);
method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, int selectedTabIndex, optional boolean matchContentSize);
}
diff --git a/compose/material3/material3/api/current.ignore b/compose/material3/material3/api/current.ignore
index 78a559f..5240c40 100644
--- a/compose/material3/material3/api/current.ignore
+++ b/compose/material3/material3/api/current.ignore
@@ -1,45 +1,17 @@
// Baseline format: 1.0
-BecameUnchecked: Field TimePickerState.hour:
- Removed Field TimePickerState.hour from compatibility checked API surface
-BecameUnchecked: Field TimePickerState.is24hour:
- Removed Field TimePickerState.is24hour from compatibility checked API surface
-BecameUnchecked: Field TimePickerState.minute:
- Removed Field TimePickerState.minute from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState:
- Removed class androidx.compose.material3.TimePickerState from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#Companion:
- Removed field androidx.compose.material3.TimePickerState.Companion from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#TimePickerState(int, int, boolean):
- Removed constructor androidx.compose.material3.TimePickerState(int,int,boolean) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#TimePickerState(int, int, boolean) parameter #0:
- Removed parameter initialHour in androidx.compose.material3.TimePickerState(int initialHour, int initialMinute, boolean is24Hour) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#TimePickerState(int, int, boolean) parameter #1:
- Removed parameter initialMinute in androidx.compose.material3.TimePickerState(int initialHour, int initialMinute, boolean is24Hour) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#TimePickerState(int, int, boolean) parameter #2:
- Removed parameter is24Hour in androidx.compose.material3.TimePickerState(int initialHour, int initialMinute, boolean is24Hour) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#getHour():
- Removed method androidx.compose.material3.TimePickerState.getHour() from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#getMinute():
- Removed method androidx.compose.material3.TimePickerState.getMinute() from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#is24hour():
- Removed method androidx.compose.material3.TimePickerState.is24hour() from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#settle(kotlin.coroutines.Continuation<? super kotlin.Unit>):
- Removed method androidx.compose.material3.TimePickerState.settle(kotlin.coroutines.Continuation<? super kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#settle(kotlin.coroutines.Continuation<? super kotlin.Unit>) parameter #0:
- Removed parameter arg1 in androidx.compose.material3.TimePickerState.settle(kotlin.coroutines.Continuation<? super kotlin.Unit> arg1) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState.Companion:
- Removed class androidx.compose.material3.TimePickerState.Companion from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState.Companion#Saver():
- Removed method androidx.compose.material3.TimePickerState.Companion.Saver() from compatibility checked API surface
-
-
-DefaultValueChange: androidx.compose.material3.SnackbarHostState#showSnackbar(String, String, boolean, androidx.compose.material3.SnackbarDuration, kotlin.coroutines.Continuation<? super androidx.compose.material3.SnackbarResult>) parameter #4:
- Attempted to remove default value from parameter arg5 in androidx.compose.material3.SnackbarHostState.showSnackbar
-
-
-RemovedClass: androidx.compose.material3.ExposedDropdownMenuKt:
- Removed class androidx.compose.material3.ExposedDropdownMenuKt
-RemovedClass: androidx.compose.material3.SearchBarKt:
- Removed class androidx.compose.material3.SearchBarKt
-RemovedClass: androidx.compose.material3.SwipeToDismissKt:
- Removed class androidx.compose.material3.SwipeToDismissKt
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope:
+ Removed class androidx.compose.material3.TabIndicatorScope from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult>):
+ Removed method androidx.compose.material3.TabIndicatorScope.tabIndicatorLayout(androidx.compose.ui.Modifier,kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult>) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult>) parameter #0:
+ Removed parameter arg1 in androidx.compose.material3.TabIndicatorScope.tabIndicatorLayout(androidx.compose.ui.Modifier arg1, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult> measure) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult>) parameter #1:
+ Removed parameter measure in androidx.compose.material3.TabIndicatorScope.tabIndicatorLayout(androidx.compose.ui.Modifier arg1, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult> measure) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorOffset(androidx.compose.ui.Modifier, int, boolean):
+ Removed method androidx.compose.material3.TabIndicatorScope.tabIndicatorOffset(androidx.compose.ui.Modifier,int,boolean) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorOffset(androidx.compose.ui.Modifier, int, boolean) parameter #0:
+ Removed parameter arg1 in androidx.compose.material3.TabIndicatorScope.tabIndicatorOffset(androidx.compose.ui.Modifier arg1, int selectedTabIndex, boolean matchContentSize) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorOffset(androidx.compose.ui.Modifier, int, boolean) parameter #1:
+ Removed parameter selectedTabIndex in androidx.compose.material3.TabIndicatorScope.tabIndicatorOffset(androidx.compose.ui.Modifier arg1, int selectedTabIndex, boolean matchContentSize) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorOffset(androidx.compose.ui.Modifier, int, boolean) parameter #2:
+ Removed parameter matchContentSize in androidx.compose.material3.TabIndicatorScope.tabIndicatorOffset(androidx.compose.ui.Modifier arg1, int selectedTabIndex, boolean matchContentSize) from compatibility checked API surface
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 7138834..4a436c2 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -1617,7 +1617,7 @@
method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.material3.SwitchColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
- public interface TabIndicatorScope {
+ @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public interface TabIndicatorScope {
method public androidx.compose.ui.Modifier tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult> measure);
method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, int selectedTabIndex, optional boolean matchContentSize);
}
diff --git a/compose/material3/material3/api/restricted_1.2.0-beta01.txt b/compose/material3/material3/api/restricted_1.2.0-beta01.txt
index 7138834..4a436c2 100644
--- a/compose/material3/material3/api/restricted_1.2.0-beta01.txt
+++ b/compose/material3/material3/api/restricted_1.2.0-beta01.txt
@@ -1617,7 +1617,7 @@
method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.material3.SwitchColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
- public interface TabIndicatorScope {
+ @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public interface TabIndicatorScope {
method public androidx.compose.ui.Modifier tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult> measure);
method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, int selectedTabIndex, optional boolean matchContentSize);
}
diff --git a/compose/material3/material3/api/restricted_current.ignore b/compose/material3/material3/api/restricted_current.ignore
index 65af023..5240c40 100644
--- a/compose/material3/material3/api/restricted_current.ignore
+++ b/compose/material3/material3/api/restricted_current.ignore
@@ -1,37 +1,17 @@
// Baseline format: 1.0
-BecameUnchecked: Field TimePickerState.hour:
- Removed Field TimePickerState.hour from compatibility checked API surface
-BecameUnchecked: Field TimePickerState.is24hour:
- Removed Field TimePickerState.is24hour from compatibility checked API surface
-BecameUnchecked: Field TimePickerState.minute:
- Removed Field TimePickerState.minute from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState:
- Removed class androidx.compose.material3.TimePickerState from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#Companion:
- Removed field androidx.compose.material3.TimePickerState.Companion from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#TimePickerState(int, int, boolean):
- Removed constructor androidx.compose.material3.TimePickerState(int,int,boolean) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#TimePickerState(int, int, boolean) parameter #0:
- Removed parameter initialHour in androidx.compose.material3.TimePickerState(int initialHour, int initialMinute, boolean is24Hour) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#TimePickerState(int, int, boolean) parameter #1:
- Removed parameter initialMinute in androidx.compose.material3.TimePickerState(int initialHour, int initialMinute, boolean is24Hour) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#TimePickerState(int, int, boolean) parameter #2:
- Removed parameter is24Hour in androidx.compose.material3.TimePickerState(int initialHour, int initialMinute, boolean is24Hour) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#getHour():
- Removed method androidx.compose.material3.TimePickerState.getHour() from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#getMinute():
- Removed method androidx.compose.material3.TimePickerState.getMinute() from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#is24hour():
- Removed method androidx.compose.material3.TimePickerState.is24hour() from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#settle(kotlin.coroutines.Continuation<? super kotlin.Unit>):
- Removed method androidx.compose.material3.TimePickerState.settle(kotlin.coroutines.Continuation<? super kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState#settle(kotlin.coroutines.Continuation<? super kotlin.Unit>) parameter #0:
- Removed parameter arg1 in androidx.compose.material3.TimePickerState.settle(kotlin.coroutines.Continuation<? super kotlin.Unit> arg1) from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState.Companion:
- Removed class androidx.compose.material3.TimePickerState.Companion from compatibility checked API surface
-BecameUnchecked: androidx.compose.material3.TimePickerState.Companion#Saver():
- Removed method androidx.compose.material3.TimePickerState.Companion.Saver() from compatibility checked API surface
-
-
-DefaultValueChange: androidx.compose.material3.SnackbarHostState#showSnackbar(String, String, boolean, androidx.compose.material3.SnackbarDuration, kotlin.coroutines.Continuation<? super androidx.compose.material3.SnackbarResult>) parameter #4:
- Attempted to remove default value from parameter arg5 in androidx.compose.material3.SnackbarHostState.showSnackbar
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope:
+ Removed class androidx.compose.material3.TabIndicatorScope from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult>):
+ Removed method androidx.compose.material3.TabIndicatorScope.tabIndicatorLayout(androidx.compose.ui.Modifier,kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult>) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult>) parameter #0:
+ Removed parameter arg1 in androidx.compose.material3.TabIndicatorScope.tabIndicatorLayout(androidx.compose.ui.Modifier arg1, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult> measure) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult>) parameter #1:
+ Removed parameter measure in androidx.compose.material3.TabIndicatorScope.tabIndicatorLayout(androidx.compose.ui.Modifier arg1, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult> measure) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorOffset(androidx.compose.ui.Modifier, int, boolean):
+ Removed method androidx.compose.material3.TabIndicatorScope.tabIndicatorOffset(androidx.compose.ui.Modifier,int,boolean) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorOffset(androidx.compose.ui.Modifier, int, boolean) parameter #0:
+ Removed parameter arg1 in androidx.compose.material3.TabIndicatorScope.tabIndicatorOffset(androidx.compose.ui.Modifier arg1, int selectedTabIndex, boolean matchContentSize) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorOffset(androidx.compose.ui.Modifier, int, boolean) parameter #1:
+ Removed parameter selectedTabIndex in androidx.compose.material3.TabIndicatorScope.tabIndicatorOffset(androidx.compose.ui.Modifier arg1, int selectedTabIndex, boolean matchContentSize) from compatibility checked API surface
+BecameUnchecked: androidx.compose.material3.TabIndicatorScope#tabIndicatorOffset(androidx.compose.ui.Modifier, int, boolean) parameter #2:
+ Removed parameter matchContentSize in androidx.compose.material3.TabIndicatorScope.tabIndicatorOffset(androidx.compose.ui.Modifier arg1, int selectedTabIndex, boolean matchContentSize) from compatibility checked API surface
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 7138834..4a436c2 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -1617,7 +1617,7 @@
method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.material3.SwitchColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
- public interface TabIndicatorScope {
+ @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public interface TabIndicatorScope {
method public androidx.compose.ui.Modifier tabIndicatorLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super java.util.List<androidx.compose.material3.TabPosition>,? extends androidx.compose.ui.layout.MeasureResult> measure);
method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, int selectedTabIndex, optional boolean matchContentSize);
}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt
index 3ee638f..50aa382 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt
@@ -467,6 +467,7 @@
)
}
+@OptIn(ExperimentalMaterial3Api::class)
@Sampled
@Composable
fun TabIndicatorScope.FancyAnimatedIndicatorWithModifier(index: Int) {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
index 83b9163..d883f9e 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
@@ -376,11 +376,11 @@
)
}
}
-
+ rule.waitForIdle()
screenHeightPx = with(rule.density) {
rule.onNode(isPopup()).getUnclippedBoundsInRoot().height.toPx()
}
- assertThat(sheetState.currentValue).isEqualTo(SheetValue.PartiallyExpanded)
+ assertThat(sheetState.targetValue).isEqualTo(SheetValue.PartiallyExpanded)
assertThat(sheetState.requireOffset())
.isWithin(1f)
.of(screenHeightPx / 2f)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
index e9c9f99..a251a76 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
@@ -319,6 +319,7 @@
* this can be used for more complex indicators requiring layout information about the tabs
* like [TabRowDefaults.PrimaryIndicator] and [TabRowDefaults.SecondaryIndicator]
*/
+@ExperimentalMaterial3Api
interface TabIndicatorScope {
/**
@@ -353,6 +354,7 @@
fun setTabPositions(positions: List<TabPosition>)
}
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TabRowImpl(
modifier: Modifier,
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt
index 4fc7275..b3763a5 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt
@@ -40,6 +40,7 @@
import org.jetbrains.kotlin.psi.KtValueArgumentList
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.java.JavaUCallExpression
import org.jetbrains.uast.skipParenthesizedExprDown
/**
@@ -47,6 +48,12 @@
* - a snapshot mutation policy argument is not specified (or it is structural equivalent policy)
* - `T` is in the [replacements] map
* - `T` is a non-nullable type
+ *
+ * This check only runs over Kotlin code, despite the possibility of calling mutableStateOf in
+ * Java. It's not possible to annotate a generic type in Java with @Nullable or @NonNull,
+ * so we will never have enough information to make the right call about whether you can make
+ * a suggested replacement or not. We therefore skip this check in all Java files to err on
+ * the side of underreporting.
*/
class AutoboxingStateCreationDetector : Detector(), SourceCodeScanner {
@@ -64,6 +71,7 @@
override fun getApplicableMethodNames() = listOf(Names.Runtime.MutableStateOf.shortName)
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ if (node is JavaUCallExpression) return
if (!method.isInPackageName(Names.Runtime.PackageName)) return
val replacement = getSuggestedReplacementName(node) ?: return
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetectorTest.kt
index 1c36c42..1eba489 100644
--- a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetectorTest.kt
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetectorTest.kt
@@ -33,6 +33,8 @@
private val fqType = typeUnderTest.fqName
private val type = typeUnderTest.typeName
+ private val jvmType = typeUnderTest.jvmClassName
+ private val fqJvmClass = typeUnderTest.fqJvmName
private val stateValue = typeUnderTest.sampleValue
private val primitiveStateStub = kotlin(
@@ -110,6 +112,41 @@
)
}
+ /**
+ * Regression test for b/314093514. Java doesn't allow specifying nullity of the generic type
+ * with either the AndroidX or JetBrains nullity annotations, so we never have enough
+ * information to know whether a mutableState created in Java is capable of being refactored
+ * into the specialized primitive version. Therefore, this inspection should never report
+ * for Java callers.
+ */
+ @Test
+ fun testTrivialMutableStateOf_notReportedInJava() {
+ lint().files(
+ primitiveStateStub,
+ Stubs.Composable,
+ Stubs.SnapshotState,
+ Stubs.StateFactoryMarker,
+ java(
+ """
+ package androidx.compose.runtime.lint.test;
+
+ import static androidx.compose.runtime.SnapshotStateKt.mutableStateOf;
+ import static androidx.compose.runtime.SnapshotStateKt.structuralEqualityPolicy;
+
+ import androidx.compose.runtime.*;
+ import $fqJvmClass;
+
+ class Test {
+ public void valueAssignment() {
+ MutableState<$jvmType> state = mutableStateOf($stateValue, structuralEqualityPolicy());
+ state.setValue($stateValue);
+ }
+ }
+ """
+ )
+ ).run().expectClean()
+ }
+
@Test
fun testInferredMutableStateOf_thatCouldBeMutablePrimitiveStateOf() {
lint().files(
@@ -488,27 +525,30 @@
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun initParameters() = listOf(
- testCase("kotlin.Int", "42"),
- testCase("kotlin.Long", "0xABCDEF1234"),
- testCase("kotlin.Float", "1.5f"),
- testCase("kotlin.Double", "1.024")
+ testCase("kotlin.Int", "java.lang.Integer", "42"),
+ testCase("kotlin.Long", "java.lang.Long", "0xABCDEF1234"),
+ testCase("kotlin.Float", "java.lang.Float", "1.5f"),
+ testCase("kotlin.Double", "java.lang.Double", "1.024")
)
- private fun testCase(fqName: String, value: String): TypeUnderTest {
- val parts = fqName.split('.')
- return TypeUnderTest(
- fqName = fqName,
- typeName = parts.last(),
- packageName = parts.dropLast(1).joinToString(separator = "."),
- sampleValue = value
- )
- }
+ private fun testCase(
+ fqName: String,
+ jvmFqName: String,
+ value: String
+ ) = TypeUnderTest(
+ fqName = fqName,
+ typeName = fqName.split('.').last(),
+ fqJvmName = jvmFqName,
+ jvmClassName = jvmFqName.split('.').last(),
+ sampleValue = value
+ )
}
data class TypeUnderTest(
val fqName: String,
val typeName: String,
- val packageName: String,
+ val fqJvmName: String,
+ val jvmClassName: String,
val sampleValue: String,
) {
// Formatting for test parameter list.
diff --git a/compose/ui/ui-test-junit4/api/current.ignore b/compose/ui/ui-test-junit4/api/current.ignore
index 8513023..c21570e 100644
--- a/compose/ui/ui-test-junit4/api/current.ignore
+++ b/compose/ui/ui-test-junit4/api/current.ignore
@@ -1,227 +1,5 @@
// Baseline format: 1.0
-BecameUnchecked: Field AndroidComposeUiTest.activity:
- Removed Field AndroidComposeUiTest.activity from compatibility checked API surface
-BecameUnchecked: Field AndroidComposeUiTestEnvironment.activity:
- Removed Field AndroidComposeUiTestEnvironment.activity from compatibility checked API surface
-BecameUnchecked: Field AndroidComposeUiTestEnvironment.test:
- Removed Field AndroidComposeUiTestEnvironment.test from compatibility checked API surface
-BecameUnchecked: Field ComposeUiTest.density:
- Removed Field ComposeUiTest.density from compatibility checked API surface
-BecameUnchecked: Field ComposeUiTest.mainClock:
- Removed Field ComposeUiTest.mainClock from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTest:
- Removed class androidx.compose.ui.test.AndroidComposeUiTest from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTest#getActivity():
- Removed method androidx.compose.ui.test.AndroidComposeUiTest.getActivity() from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTestEnvironment:
- Removed class androidx.compose.ui.test.AndroidComposeUiTestEnvironment from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTestEnvironment#AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext):
- Removed constructor androidx.compose.ui.test.AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTestEnvironment#AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext) parameter #0:
- Removed parameter effectContext in androidx.compose.ui.test.AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext effectContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTestEnvironment#getActivity():
- Removed method androidx.compose.ui.test.AndroidComposeUiTestEnvironment.getActivity() from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTestEnvironment#getTest():
- Removed method androidx.compose.ui.test.AndroidComposeUiTestEnvironment.getTest() from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTestEnvironment#runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R>):
- Removed method androidx.compose.ui.test.AndroidComposeUiTestEnvironment.runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.AndroidComposeUiTestEnvironment#runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R>) parameter #0:
- Removed parameter block in androidx.compose.ui.test.AndroidComposeUiTestEnvironment.runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest:
- Removed class androidx.compose.ui.test.ComposeUiTest from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>):
- Removed method androidx.compose.ui.test.ComposeUiTest.awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>) parameter #0:
- Removed parameter arg1 in androidx.compose.ui.test.ComposeUiTest.awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> arg1) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#getDensity():
- Removed method androidx.compose.ui.test.ComposeUiTest.getDensity() from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#getMainClock():
- Removed method androidx.compose.ui.test.ComposeUiTest.getMainClock() from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#registerIdlingResource(androidx.compose.ui.test.IdlingResource):
- Removed method androidx.compose.ui.test.ComposeUiTest.registerIdlingResource(androidx.compose.ui.test.IdlingResource) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#registerIdlingResource(androidx.compose.ui.test.IdlingResource) parameter #0:
- Removed parameter idlingResource in androidx.compose.ui.test.ComposeUiTest.registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#runOnIdle(kotlin.jvm.functions.Function0<? extends T>):
- Removed method androidx.compose.ui.test.ComposeUiTest.runOnIdle(kotlin.jvm.functions.Function0<? extends T>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#runOnIdle(kotlin.jvm.functions.Function0<? extends T>) parameter #0:
- Removed parameter action in androidx.compose.ui.test.ComposeUiTest.runOnIdle(kotlin.jvm.functions.Function0<? extends T> action) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#runOnUiThread(kotlin.jvm.functions.Function0<? extends T>):
- Removed method androidx.compose.ui.test.ComposeUiTest.runOnUiThread(kotlin.jvm.functions.Function0<? extends T>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#runOnUiThread(kotlin.jvm.functions.Function0<? extends T>) parameter #0:
- Removed parameter action in androidx.compose.ui.test.ComposeUiTest.runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#setContent(kotlin.jvm.functions.Function0<kotlin.Unit>):
- Removed method androidx.compose.ui.test.ComposeUiTest.setContent(kotlin.jvm.functions.Function0<kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#setContent(kotlin.jvm.functions.Function0<kotlin.Unit>) parameter #0:
- Removed parameter composable in androidx.compose.ui.test.ComposeUiTest.setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#unregisterIdlingResource(androidx.compose.ui.test.IdlingResource):
- Removed method androidx.compose.ui.test.ComposeUiTest.unregisterIdlingResource(androidx.compose.ui.test.IdlingResource) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#unregisterIdlingResource(androidx.compose.ui.test.IdlingResource) parameter #0:
- Removed parameter idlingResource in androidx.compose.ui.test.ComposeUiTest.unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#waitForIdle():
- Removed method androidx.compose.ui.test.ComposeUiTest.waitForIdle() from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#waitUntil(long, kotlin.jvm.functions.Function0<java.lang.Boolean>):
- Removed method androidx.compose.ui.test.ComposeUiTest.waitUntil(long,kotlin.jvm.functions.Function0<java.lang.Boolean>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#waitUntil(long, kotlin.jvm.functions.Function0<java.lang.Boolean>) parameter #0:
- Removed parameter timeoutMillis in androidx.compose.ui.test.ComposeUiTest.waitUntil(long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest#waitUntil(long, kotlin.jvm.functions.Function0<java.lang.Boolean>) parameter #1:
- Removed parameter condition in androidx.compose.ui.test.ComposeUiTest.waitUntil(long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#runComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>):
- Removed method androidx.compose.ui.test.ComposeUiTestKt.runComposeUiTest(kotlin.coroutines.CoroutineContext,kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#runComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>) parameter #0:
- Removed parameter effectContext in androidx.compose.ui.test.ComposeUiTestKt.runComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#runComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>) parameter #1:
- Removed parameter block in androidx.compose.ui.test.ComposeUiTestKt.runComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.ComposeUiTestKt.waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest,androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter arg1 in androidx.compose.ui.test.ComposeUiTestKt.waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter matcher in androidx.compose.ui.test.ComposeUiTestKt.waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #2:
- Removed parameter timeoutMillis in androidx.compose.ui.test.ComposeUiTestKt.waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.ComposeUiTestKt.waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest,androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter arg1 in androidx.compose.ui.test.ComposeUiTestKt.waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter matcher in androidx.compose.ui.test.ComposeUiTestKt.waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #2:
- Removed parameter timeoutMillis in androidx.compose.ui.test.ComposeUiTestKt.waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.ComposeUiTestKt.waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest,androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter arg1 in androidx.compose.ui.test.ComposeUiTestKt.waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter matcher in androidx.compose.ui.test.ComposeUiTestKt.waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, long) parameter #2:
- Removed parameter timeoutMillis in androidx.compose.ui.test.ComposeUiTestKt.waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, int, long):
- Removed method androidx.compose.ui.test.ComposeUiTestKt.waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest,androidx.compose.ui.test.SemanticsMatcher,int,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #0:
- Removed parameter arg1 in androidx.compose.ui.test.ComposeUiTestKt.waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #1:
- Removed parameter matcher in androidx.compose.ui.test.ComposeUiTestKt.waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #2:
- Removed parameter count in androidx.compose.ui.test.ComposeUiTestKt.waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTestKt#waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #3:
- Removed parameter timeoutMillis in androidx.compose.ui.test.ComposeUiTestKt.waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest arg1, androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function0<? extends A>):
- Removed method androidx.compose.ui.test.ComposeUiTest_androidKt.AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext,kotlin.jvm.functions.Function0<? extends A>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function0<? extends A>) parameter #0:
- Removed parameter effectContext in androidx.compose.ui.test.ComposeUiTest_androidKt.AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function0<? extends A>) parameter #1:
- Removed parameter activityProvider in androidx.compose.ui.test.ComposeUiTest_androidKt.AndroidComposeUiTestEnvironment(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runAndroidComposeUiTest(Class<A>, kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>):
- Removed method androidx.compose.ui.test.ComposeUiTest_androidKt.runAndroidComposeUiTest(Class<A>,kotlin.coroutines.CoroutineContext,kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runAndroidComposeUiTest(Class<A>, kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>) parameter #0:
- Removed parameter activityClass in androidx.compose.ui.test.ComposeUiTest_androidKt.runAndroidComposeUiTest(Class<A> activityClass, kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runAndroidComposeUiTest(Class<A>, kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>) parameter #1:
- Removed parameter effectContext in androidx.compose.ui.test.ComposeUiTest_androidKt.runAndroidComposeUiTest(Class<A> activityClass, kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runAndroidComposeUiTest(Class<A>, kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>) parameter #2:
- Removed parameter block in androidx.compose.ui.test.ComposeUiTest_androidKt.runAndroidComposeUiTest(Class<A> activityClass, kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runAndroidComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>):
- Removed method androidx.compose.ui.test.ComposeUiTest_androidKt.runAndroidComposeUiTest(kotlin.coroutines.CoroutineContext,kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runAndroidComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>) parameter #0:
- Removed parameter effectContext in androidx.compose.ui.test.ComposeUiTest_androidKt.runAndroidComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runAndroidComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit>) parameter #1:
- Removed parameter block in androidx.compose.ui.test.ComposeUiTest_androidKt.runAndroidComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>):
- Removed method androidx.compose.ui.test.ComposeUiTest_androidKt.runComposeUiTest(kotlin.coroutines.CoroutineContext,kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>) parameter #0:
- Removed parameter effectContext in androidx.compose.ui.test.ComposeUiTest_androidKt.runComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runComposeUiTest(kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>) parameter #1:
- Removed parameter block in androidx.compose.ui.test.ComposeUiTest_androidKt.runComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>):
- Removed method androidx.compose.ui.test.ComposeUiTest_androidKt.runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.ComposeUiTest_androidKt#runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit>) parameter #0:
- Removed parameter block in androidx.compose.ui.test.ComposeUiTest_androidKt.runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.StateRestorationTester:
- Removed class androidx.compose.ui.test.StateRestorationTester from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.StateRestorationTester#StateRestorationTester(androidx.compose.ui.test.ComposeUiTest):
- Removed constructor androidx.compose.ui.test.StateRestorationTester(androidx.compose.ui.test.ComposeUiTest) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.StateRestorationTester#StateRestorationTester(androidx.compose.ui.test.ComposeUiTest) parameter #0:
- Removed parameter composeTest in androidx.compose.ui.test.StateRestorationTester(androidx.compose.ui.test.ComposeUiTest composeTest) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.StateRestorationTester#emulateSaveAndRestore():
- Removed method androidx.compose.ui.test.StateRestorationTester.emulateSaveAndRestore() from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.StateRestorationTester#setContent(kotlin.jvm.functions.Function0<kotlin.Unit>):
- Removed method androidx.compose.ui.test.StateRestorationTester.setContent(kotlin.jvm.functions.Function0<kotlin.Unit>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.StateRestorationTester#setContent(kotlin.jvm.functions.Function0<kotlin.Unit>) parameter #0:
- Removed parameter composable in androidx.compose.ui.test.StateRestorationTester.setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#AndroidComposeTestRule(R, kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super R,? extends A>):
- Removed constructor androidx.compose.ui.test.junit4.AndroidComposeTestRule(R,kotlin.coroutines.CoroutineContext,kotlin.jvm.functions.Function1<? super R,? extends A>) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#AndroidComposeTestRule(R, kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super R,? extends A>) parameter #0:
- Removed parameter activityRule in androidx.compose.ui.test.junit4.AndroidComposeTestRule(R activityRule, kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#AndroidComposeTestRule(R, kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super R,? extends A>) parameter #1:
- Removed parameter effectContext in androidx.compose.ui.test.junit4.AndroidComposeTestRule(R activityRule, kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#AndroidComposeTestRule(R, kotlin.coroutines.CoroutineContext, kotlin.jvm.functions.Function1<? super R,? extends A>) parameter #2:
- Removed parameter activityProvider in androidx.compose.ui.test.junit4.AndroidComposeTestRule(R activityRule, kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter matcher in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter timeoutMillis in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter matcher in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter timeoutMillis in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter matcher in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter timeoutMillis in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher, int, long):
- Removed method androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher,int,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #0:
- Removed parameter matcher in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #1:
- Removed parameter count in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule#waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #2:
- Removed parameter timeoutMillis in androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createAndroidComposeRule(Class<A>, kotlin.coroutines.CoroutineContext):
- Removed method androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createAndroidComposeRule(Class<A>,kotlin.coroutines.CoroutineContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createAndroidComposeRule(Class<A>, kotlin.coroutines.CoroutineContext) parameter #0:
- Removed parameter activityClass in androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createAndroidComposeRule(Class<A> activityClass, kotlin.coroutines.CoroutineContext effectContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createAndroidComposeRule(Class<A>, kotlin.coroutines.CoroutineContext) parameter #1:
- Removed parameter effectContext in androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createAndroidComposeRule(Class<A> activityClass, kotlin.coroutines.CoroutineContext effectContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createAndroidComposeRule(kotlin.coroutines.CoroutineContext):
- Removed method androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createAndroidComposeRule(kotlin.coroutines.CoroutineContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createAndroidComposeRule(kotlin.coroutines.CoroutineContext) parameter #0:
- Removed parameter effectContext in androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createAndroidComposeRule(kotlin.coroutines.CoroutineContext effectContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createComposeRule(kotlin.coroutines.CoroutineContext):
- Removed method androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createComposeRule(kotlin.coroutines.CoroutineContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createComposeRule(kotlin.coroutines.CoroutineContext) parameter #0:
- Removed parameter effectContext in androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createComposeRule(kotlin.coroutines.CoroutineContext effectContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createEmptyComposeRule(kotlin.coroutines.CoroutineContext):
- Removed method androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createEmptyComposeRule(kotlin.coroutines.CoroutineContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt#createEmptyComposeRule(kotlin.coroutines.CoroutineContext) parameter #0:
- Removed parameter effectContext in androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createEmptyComposeRule(kotlin.coroutines.CoroutineContext effectContext) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter matcher in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter timeoutMillis in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter matcher in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter timeoutMillis in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher, long):
- Removed method androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher, long) parameter #0:
- Removed parameter matcher in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher, long) parameter #1:
- Removed parameter timeoutMillis in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher, int, long):
- Removed method androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher,int,long) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #0:
- Removed parameter matcher in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #1:
- Removed parameter count in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
-BecameUnchecked: androidx.compose.ui.test.junit4.ComposeTestRule#waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher, int, long) parameter #2:
- Removed parameter timeoutMillis in androidx.compose.ui.test.junit4.ComposeTestRule.waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis) from compatibility checked API surface
+RemovedPackage: androidx.compose.ui.test:
+ Removed package androidx.compose.ui.test
+RemovedPackage: androidx.compose.ui.test.junit4.android:
+ Removed package androidx.compose.ui.test.junit4.android
diff --git a/compose/ui/ui-test-junit4/api/current.txt b/compose/ui/ui-test-junit4/api/current.txt
index 06d2062..8f15c3f 100644
--- a/compose/ui/ui-test-junit4/api/current.txt
+++ b/compose/ui/ui-test-junit4/api/current.txt
@@ -1,59 +1,4 @@
// Signature format: 4.0
-package androidx.compose.ui.test {
-
- @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public sealed interface AndroidComposeUiTest<A extends androidx.activity.ComponentActivity> extends androidx.compose.ui.test.ComposeUiTest {
- method public A? getActivity();
- property public abstract A? activity;
- }
-
- @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
- ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
- method protected abstract A? getActivity();
- method public final androidx.compose.ui.test.AndroidComposeUiTest<A> getTest();
- method public final <R> R runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R> block);
- property protected abstract A? activity;
- property public final androidx.compose.ui.test.AndroidComposeUiTest<A> test;
- }
-
- @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public sealed interface ComposeUiTest extends androidx.compose.ui.test.SemanticsNodeInteractionsProvider {
- method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.ui.unit.Density getDensity();
- method public androidx.compose.ui.test.MainTestClock getMainClock();
- method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
- method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
- method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
- method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
- method public void waitForIdle();
- method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
- property public abstract androidx.compose.ui.unit.Density density;
- property public abstract androidx.compose.ui.test.MainTestClock mainClock;
- }
-
- public final class ComposeUiTestKt {
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
- }
-
- public final class ComposeUiTest_androidKt {
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static inline <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.AndroidComposeUiTestEnvironment<A> AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- }
-
- @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public final class StateRestorationTester {
- ctor public StateRestorationTester(androidx.compose.ui.test.ComposeUiTest composeTest);
- method public void emulateSaveAndRestore();
- method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- }
-
-}
-
package androidx.compose.ui.test.junit4 {
public final class AndroidComposeTestRule<R extends org.junit.rules.TestRule, A extends androidx.activity.ComponentActivity> implements androidx.compose.ui.test.junit4.ComposeContentTestRule {
@@ -130,11 +75,3 @@
}
-package androidx.compose.ui.test.junit4.android {
-
- public final class ComposeNotIdleException extends java.lang.Exception {
- ctor public ComposeNotIdleException(String? message, Throwable? cause);
- }
-
-}
-
diff --git a/compose/ui/ui-test-junit4/api/restricted_current.ignore b/compose/ui/ui-test-junit4/api/restricted_current.ignore
new file mode 100644
index 0000000..c21570e
--- /dev/null
+++ b/compose/ui/ui-test-junit4/api/restricted_current.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+RemovedPackage: androidx.compose.ui.test:
+ Removed package androidx.compose.ui.test
+RemovedPackage: androidx.compose.ui.test.junit4.android:
+ Removed package androidx.compose.ui.test.junit4.android
diff --git a/compose/ui/ui-test-junit4/api/restricted_current.txt b/compose/ui/ui-test-junit4/api/restricted_current.txt
index 06d2062..8f15c3f 100644
--- a/compose/ui/ui-test-junit4/api/restricted_current.txt
+++ b/compose/ui/ui-test-junit4/api/restricted_current.txt
@@ -1,59 +1,4 @@
// Signature format: 4.0
-package androidx.compose.ui.test {
-
- @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public sealed interface AndroidComposeUiTest<A extends androidx.activity.ComponentActivity> extends androidx.compose.ui.test.ComposeUiTest {
- method public A? getActivity();
- property public abstract A? activity;
- }
-
- @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
- ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
- method protected abstract A? getActivity();
- method public final androidx.compose.ui.test.AndroidComposeUiTest<A> getTest();
- method public final <R> R runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R> block);
- property protected abstract A? activity;
- property public final androidx.compose.ui.test.AndroidComposeUiTest<A> test;
- }
-
- @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public sealed interface ComposeUiTest extends androidx.compose.ui.test.SemanticsNodeInteractionsProvider {
- method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.ui.unit.Density getDensity();
- method public androidx.compose.ui.test.MainTestClock getMainClock();
- method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
- method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
- method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
- method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
- method public void waitForIdle();
- method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
- property public abstract androidx.compose.ui.unit.Density density;
- property public abstract androidx.compose.ui.test.MainTestClock mainClock;
- }
-
- public final class ComposeUiTestKt {
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
- }
-
- public final class ComposeUiTest_androidKt {
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static inline <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.AndroidComposeUiTestEnvironment<A> AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- }
-
- @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public final class StateRestorationTester {
- ctor public StateRestorationTester(androidx.compose.ui.test.ComposeUiTest composeTest);
- method public void emulateSaveAndRestore();
- method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- }
-
-}
-
package androidx.compose.ui.test.junit4 {
public final class AndroidComposeTestRule<R extends org.junit.rules.TestRule, A extends androidx.activity.ComponentActivity> implements androidx.compose.ui.test.junit4.ComposeContentTestRule {
@@ -130,11 +75,3 @@
}
-package androidx.compose.ui.test.junit4.android {
-
- public final class ComposeNotIdleException extends java.lang.Exception {
- ctor public ComposeNotIdleException(String? message, Throwable? cause);
- }
-
-}
-
diff --git a/compose/ui/ui-test-junit4/build.gradle b/compose/ui/ui-test-junit4/build.gradle
index 668b67d..f185596 100644
--- a/compose/ui/ui-test-junit4/build.gradle
+++ b/compose/ui/ui-test-junit4/build.gradle
@@ -69,8 +69,6 @@
implementation("androidx.lifecycle:lifecycle-runtime:2.5.1")
implementation("androidx.test:core:1.5.0")
implementation("androidx.test:monitor:1.6.1")
- implementation("androidx.test.espresso:espresso-core:3.3.0")
- implementation("androidx.test.espresso:espresso-idling-resource:3.5.0")
}
}
diff --git a/compose/ui/ui-test-junit4/lint-baseline.xml b/compose/ui/ui-test-junit4/lint-baseline.xml
deleted file mode 100644
index 5b697f0..0000000
--- a/compose/ui/ui-test-junit4/lint-baseline.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
-
- <issue
- id="BanThreadSleep"
- message="Uses Thread.sleep()"
- errorLine1=" Thread.sleep(10)"
- errorLine2=" ~~~~~">
- <location
- file="src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt"/>
- </issue>
-
-</issues>
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/AndroidManifest.xml b/compose/ui/ui-test-junit4/src/androidInstrumentedTest/AndroidManifest.xml
deleted file mode 100644
index 2cc9c44..0000000
--- a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <application>
- <activity android:name="androidx.compose.ui.test.junit4.CustomActivity"
- android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFindTest$Activity1" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFindTest$Activity2" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesClickTest$Activity1" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesClickTest$Activity2" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFirstDrawTest$Activity1" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFirstDrawTest$Activity2" />
- <activity android:name="androidx.compose.ui.test.junit4.LateSetContentTest$Activity" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity1" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity2" />
- <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity3" />
- </application>
-</manifest>
diff --git a/compose/ui/ui-test-junit4/src/desktopTest/kotlin/androidx/compose/ui/test/DesktopComposeUiTestTest.kt b/compose/ui/ui-test-junit4/src/desktopTest/kotlin/androidx/compose/ui/test/DesktopComposeUiTestTest.kt
new file mode 100644
index 0000000..1f55a51
--- /dev/null
+++ b/compose/ui/ui-test-junit4/src/desktopTest/kotlin/androidx/compose/ui/test/DesktopComposeUiTestTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 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.ui.test
+
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.test.junit4.createComposeRule
+import com.google.common.truth.Truth.assertThat
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.runners.model.Statement
+
+@RunWith(JUnit4::class)
+@OptIn(ExperimentalTestApi::class)
+class DesktopComposeUiTestTest {
+
+ private lateinit var testDescription: Description
+
+ /**
+ * Records the current [testDescription] for tests that need to invoke the compose test rule
+ * directly.
+ */
+ @get:Rule
+ val testWatcher = object : TestWatcher() {
+ override fun starting(description: Description) {
+ testDescription = description
+ }
+ }
+
+ @Test
+ fun effectContextPropagatedToComposition_createComposeRule() {
+ val testElement = TestCoroutineContextElement()
+ lateinit var compositionScope: CoroutineScope
+ val rule = createComposeRule(testElement)
+ val baseStatement = object : Statement() {
+ override fun evaluate() {
+ rule.setContent {
+ compositionScope = rememberCoroutineScope()
+ }
+ rule.waitForIdle()
+ }
+ }
+ rule.apply(baseStatement, testDescription)
+ .evaluate()
+
+ val elementFromComposition =
+ compositionScope.coroutineContext[TestCoroutineContextElement]
+ assertThat(elementFromComposition).isSameInstanceAs(testElement)
+ }
+
+ private class TestCoroutineContextElement : CoroutineContext.Element {
+ override val key: CoroutineContext.Key<*> get() = Key
+
+ companion object Key : CoroutineContext.Key<TestCoroutineContextElement>
+ }
+}
diff --git a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.jvm.kt b/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.jvm.kt
index 08dadac..9ee2023 100644
--- a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.jvm.kt
+++ b/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.jvm.kt
@@ -17,7 +17,6 @@
package androidx.compose.ui.test.junit4
import androidx.compose.runtime.Composable
-import androidx.compose.ui.test.ComposeTimeoutException
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.IdlingResource
import androidx.compose.ui.test.MainTestClock
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index 666a657..ad2a32d 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -22,6 +22,20 @@
method public static androidx.compose.ui.test.SemanticsNodeInteraction requestFocus(androidx.compose.ui.test.SemanticsNodeInteraction);
}
+ @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public sealed interface AndroidComposeUiTest<A extends androidx.activity.ComponentActivity> extends androidx.compose.ui.test.ComposeUiTest {
+ method public A? getActivity();
+ property public abstract A? activity;
+ }
+
+ @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
+ ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
+ method protected abstract A? getActivity();
+ method public final androidx.compose.ui.test.AndroidComposeUiTest<A> getTest();
+ method public final <R> R runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R> block);
+ property protected abstract A? activity;
+ property public final androidx.compose.ui.test.AndroidComposeUiTest<A> test;
+ }
+
public final class AndroidImageHelpers_androidKt {
method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
}
@@ -75,6 +89,37 @@
ctor public ComposeTimeoutException(String? message);
}
+ @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public sealed interface ComposeUiTest extends androidx.compose.ui.test.SemanticsNodeInteractionsProvider {
+ method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public androidx.compose.ui.test.MainTestClock getMainClock();
+ method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
+ method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public void waitForIdle();
+ method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
+ property public abstract androidx.compose.ui.unit.Density density;
+ property public abstract androidx.compose.ui.test.MainTestClock mainClock;
+ }
+
+ public final class ComposeUiTestKt {
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
+ }
+
+ public final class ComposeUiTest_androidKt {
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static inline <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.AndroidComposeUiTestEnvironment<A> AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ }
+
@SuppressCompatibility @kotlin.RequiresOptIn(message="This testing API is experimental and is likely to be changed or removed entirely") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTestApi {
}
@@ -434,6 +479,12 @@
property public final String description;
}
+ @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public final class StateRestorationTester {
+ ctor public StateRestorationTester(androidx.compose.ui.test.ComposeUiTest composeTest);
+ method public void emulateSaveAndRestore();
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ }
+
public final class TestContext {
}
@@ -513,3 +564,11 @@
}
+package androidx.compose.ui.test.junit4.android {
+
+ public final class ComposeNotIdleException extends java.lang.Exception {
+ ctor public ComposeNotIdleException(String? message, Throwable? cause);
+ }
+
+}
+
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index b8f4e37..9f051b8 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -22,6 +22,20 @@
method public static androidx.compose.ui.test.SemanticsNodeInteraction requestFocus(androidx.compose.ui.test.SemanticsNodeInteraction);
}
+ @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public sealed interface AndroidComposeUiTest<A extends androidx.activity.ComponentActivity> extends androidx.compose.ui.test.ComposeUiTest {
+ method public A? getActivity();
+ property public abstract A? activity;
+ }
+
+ @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
+ ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
+ method protected abstract A? getActivity();
+ method public final androidx.compose.ui.test.AndroidComposeUiTest<A> getTest();
+ method public final <R> R runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R> block);
+ property protected abstract A? activity;
+ property public final androidx.compose.ui.test.AndroidComposeUiTest<A> test;
+ }
+
public final class AndroidImageHelpers_androidKt {
method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
}
@@ -75,6 +89,37 @@
ctor public ComposeTimeoutException(String? message);
}
+ @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public sealed interface ComposeUiTest extends androidx.compose.ui.test.SemanticsNodeInteractionsProvider {
+ method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public androidx.compose.ui.test.MainTestClock getMainClock();
+ method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
+ method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public void waitForIdle();
+ method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
+ property public abstract androidx.compose.ui.unit.Density density;
+ property public abstract androidx.compose.ui.test.MainTestClock mainClock;
+ }
+
+ public final class ComposeUiTestKt {
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
+ }
+
+ public final class ComposeUiTest_androidKt {
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static inline <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.AndroidComposeUiTestEnvironment<A> AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ }
+
@SuppressCompatibility @kotlin.RequiresOptIn(message="This testing API is experimental and is likely to be changed or removed entirely") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTestApi {
}
@@ -435,6 +480,12 @@
property public final String description;
}
+ @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public final class StateRestorationTester {
+ ctor public StateRestorationTester(androidx.compose.ui.test.ComposeUiTest composeTest);
+ method public void emulateSaveAndRestore();
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ }
+
public final class TestContext {
}
@@ -514,3 +565,11 @@
}
+package androidx.compose.ui.test.junit4.android {
+
+ public final class ComposeNotIdleException extends java.lang.Exception {
+ ctor public ComposeNotIdleException(String? message, Throwable? cause);
+ }
+
+}
+
diff --git a/compose/ui/ui-test/build.gradle b/compose/ui/ui-test/build.gradle
index d2d0338..3e0c6df 100644
--- a/compose/ui/ui-test/build.gradle
+++ b/compose/ui/ui-test/build.gradle
@@ -60,11 +60,14 @@
androidMain {
dependsOn(jvmMain)
dependencies {
+ implementation "androidx.activity:activity-compose:1.3.0"
+
api(project(":compose:ui:ui-graphics"))
implementation("androidx.annotation:annotation:1.1.0")
implementation("androidx.core:core-ktx:1.2.0")
implementation("androidx.test.espresso:espresso-core:3.5.0")
+ implementation("androidx.test.espresso:espresso-idling-resource:3.5.0")
implementation("androidx.test:monitor:1.6.1")
}
}
@@ -96,12 +99,19 @@
dependsOn(androidCommonTest)
dependencies {
implementation(project(":compose:material:material"))
- implementation(project(":compose:ui:ui-test-junit4"))
+ implementation(project(":compose:animation:animation"))
+ implementation(project(":compose:ui:ui-test"))
implementation("androidx.activity:activity-compose:1.3.1")
+ implementation("androidx.fragment:fragment-testing:1.4.1")
+
implementation(libs.mockitoCore)
implementation(libs.mockitoKotlin)
implementation(libs.dexmakerMockito)
implementation(libs.kotlinTest)
+ implementation(libs.testRules)
+ implementation(libs.testRunner)
+ implementation(libs.truth)
+
}
}
@@ -109,14 +119,27 @@
dependsOn(jvmTest)
dependsOn(androidCommonTest)
dependencies {
+ implementation(libs.truth)
implementation(libs.robolectric)
implementation(libs.mockitoCore4)
implementation(libs.mockitoKotlin4)
+
+ implementation(project(":compose:animation:animation-core"))
+ implementation(project(":compose:material:material"))
+ implementation(project(":compose:test-utils"))
}
}
desktopTest {
dependsOn(jvmTest)
+ dependencies {
+ implementation(libs.truth)
+ implementation(libs.junit)
+ implementation(libs.kotlinTest)
+ implementation(libs.skikoCurrentOs)
+ implementation(project(":compose:foundation:foundation"))
+ implementation(project(":compose:ui:ui-test"))
+ }
}
}
}
diff --git a/compose/ui/ui-test/lint-baseline.xml b/compose/ui/ui-test/lint-baseline.xml
index cfe69ca..ee8ffe4 100644
--- a/compose/ui/ui-test/lint-baseline.xml
+++ b/compose/ui/ui-test/lint-baseline.xml
@@ -11,6 +11,15 @@
</issue>
<issue
+ id="BanThreadSleep"
+ message="Uses Thread.sleep()"
+ errorLine1=" Thread.sleep(10)"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt"/>
+ </issue>
+
+ <issue
id="PrimitiveInCollection"
message="field MouseAsTouchEvents with type List<Integer>: replace with IntList"
errorLine1="private val MouseAsTouchEvents = listOf(ACTION_DOWN, ACTION_MOVE, ACTION_UP)"
diff --git a/compose/ui/ui-test/src/androidInstrumentedTest/AndroidManifest.xml b/compose/ui/ui-test/src/androidInstrumentedTest/AndroidManifest.xml
index 63f7324..d70ca0d 100644
--- a/compose/ui/ui-test/src/androidInstrumentedTest/AndroidManifest.xml
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/AndroidManifest.xml
@@ -19,5 +19,18 @@
<activity android:name="androidx.compose.ui.test.ActivityWithActionBar" />
<activity android:name="androidx.compose.ui.test.ClickCounterActivity" />
<activity android:name="androidx.compose.ui.test.EmptyActivity" />
+
+ <activity android:name="androidx.compose.ui.test.junit4.CustomActivity"
+ android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFindTest$Activity1" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFindTest$Activity2" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesClickTest$Activity1" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesClickTest$Activity2" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFirstDrawTest$Activity1" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesFirstDrawTest$Activity2" />
+ <activity android:name="androidx.compose.ui.test.junit4.LateSetContentTest$Activity" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity1" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity2" />
+ <activity android:name="androidx.compose.ui.test.junit4.MultipleActivitiesWithoutComposeTest$Activity3" />
</application>
</manifest>
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
similarity index 98%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
index 1195ad9..0071554 100644
--- a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 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.
@@ -38,7 +38,6 @@
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
@@ -66,7 +65,6 @@
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.test.StandardTestDispatcher
import org.junit.After
@@ -80,7 +78,7 @@
@LargeTest
@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTestApi::class, ExperimentalComposeApi::class, ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalTestApi::class)
class ComposeUiTestTest {
private var idlingPolicy: IdlingPolicy? = null
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ApplySnapshotImmediatelyTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ApplySnapshotImmediatelyTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ApplySnapshotImmediatelyTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ApplySnapshotImmediatelyTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeInFragmentTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeInFragmentTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeInFragmentTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeInFragmentTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistryTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistryTest.kt
similarity index 98%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistryTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistryTest.kt
index 9e8c31c..8add055 100644
--- a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistryTest.kt
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistryTest.kt
@@ -22,6 +22,7 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.ui.platform.ViewRootForTest
+import androidx.compose.ui.test.ComposeRootRegistry
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.LargeTest
import com.google.common.truth.Truth.assertThat
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeTestRuleWaitUntilTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeTestRuleWaitUntilTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeTestRuleWaitUntilTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ComposeTestRuleWaitUntilTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/CustomActivityTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/CustomActivityTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/CustomActivityTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/CustomActivityTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/EspressoLinkTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/EspressoLinkTest.kt
similarity index 96%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/EspressoLinkTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/EspressoLinkTest.kt
index 44ea9a3..15fb33b 100644
--- a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/EspressoLinkTest.kt
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/EspressoLinkTest.kt
@@ -16,6 +16,8 @@
package androidx.compose.ui.test.junit4
+import androidx.compose.ui.test.EspressoLink
+import androidx.compose.ui.test.IdlingResourceRegistry
import androidx.compose.ui.test.InternalTestApi
import androidx.test.espresso.Espresso
import androidx.test.espresso.IdlingRegistry
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/FirstDrawTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/FirstDrawTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/FirstDrawTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/FirstDrawTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt
similarity index 98%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt
index ea2a8e0..9d4133e 100644
--- a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistryTest.kt
@@ -17,6 +17,7 @@
package androidx.compose.ui.test.junit4
import androidx.compose.ui.test.IdlingResource
+import androidx.compose.ui.test.IdlingResourceRegistry
import androidx.compose.ui.test.InternalTestApi
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LateActivityLaunchTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LateActivityLaunchTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LateActivityLaunchTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LateActivityLaunchTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LateSetContentTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LateSetContentTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LateSetContentTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LateSetContentTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LaunchActivityTooEarlyTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LaunchActivityTooEarlyTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LaunchActivityTooEarlyTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/LaunchActivityTooEarlyTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesClickTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesClickTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesClickTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesClickTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesFindTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesFindTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesFindTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesFindTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesFirstDrawTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesFirstDrawTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesFirstDrawTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesFirstDrawTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesWithoutComposeTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesWithoutComposeTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesWithoutComposeTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleActivitiesWithoutComposeTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleComposeRootsTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleComposeRootsTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleComposeRootsTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/MultipleComposeRootsTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/SynchronizationMethodsTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/SynchronizationMethodsTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/SynchronizationMethodsTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/SynchronizationMethodsTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/TimeOutTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/TimeOutTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/TimeOutTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/TimeOutTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/UncaughtExceptionsInCoroutinesTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/UncaughtExceptionsInCoroutinesTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/UncaughtExceptionsInCoroutinesTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/UncaughtExceptionsInCoroutinesTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitUntilNodeCountTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitUntilNodeCountTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitUntilNodeCountTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitUntilNodeCountTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitingForOnCommitCallbackTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitingForOnCommitCallbackTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitingForOnCommitCallbackTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitingForOnCommitCallbackTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitingForPopupTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitingForPopupTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitingForPopupTest.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/WaitingForPopupTest.kt
diff --git a/compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/util/BoundaryNodes.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/util/BoundaryNodes.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/util/BoundaryNodes.kt
rename to compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/junit4/util/BoundaryNodes.kt
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidSynchronization.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidSynchronization.android.kt
similarity index 94%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidSynchronization.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidSynchronization.android.kt
index 80a758b..2981532 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidSynchronization.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidSynchronization.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
import android.annotation.SuppressLint
import android.os.Looper
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResource.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeIdlingResource.android.kt
similarity index 97%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResource.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeIdlingResource.android.kt
index 26754c7..bab56602 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResource.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeIdlingResource.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
import android.view.View
import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.platform.ViewRootForTest
-import androidx.compose.ui.test.IdlingResource
import kotlin.math.max
/**
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistry.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeRootRegistry.android.kt
similarity index 98%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistry.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeRootRegistry.android.kt
index fd75534..cc24adc 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/ComposeRootRegistry.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeRootRegistry.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
import android.os.Handler
import android.os.Looper
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
similarity index 96%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
index 33af8ea..e8fd13b 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 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.
@@ -28,17 +28,6 @@
import androidx.compose.ui.node.RootForTest
import androidx.compose.ui.platform.InfiniteAnimationPolicy
import androidx.compose.ui.platform.WindowRecomposerPolicy
-import androidx.compose.ui.test.junit4.ComposeIdlingResource
-import androidx.compose.ui.test.junit4.ComposeRootRegistry
-import androidx.compose.ui.test.junit4.EspressoLink
-import androidx.compose.ui.test.junit4.IdlingResourceRegistry
-import androidx.compose.ui.test.junit4.IdlingStrategy
-import androidx.compose.ui.test.junit4.MainTestClockImpl
-import androidx.compose.ui.test.junit4.RobolectricIdlingStrategy
-import androidx.compose.ui.test.junit4.UncaughtExceptionHandler
-import androidx.compose.ui.test.junit4.awaitComposeRoots
-import androidx.compose.ui.test.junit4.isOnUiThread
-import androidx.compose.ui.test.junit4.waitForComposeRoots
import androidx.compose.ui.unit.Density
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
@@ -526,7 +515,7 @@
get() = mainClockImpl
override fun <T> runOnUiThread(action: () -> T): T {
- return androidx.compose.ui.test.junit4.runOnUiThread(action)
+ return androidx.compose.ui.test.runOnUiThread(action)
}
override fun getRoots(atLeastOneRootExpected: Boolean): Set<RootForTest> {
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/EspressoLink.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/EspressoLink.android.kt
similarity index 98%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/EspressoLink.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/EspressoLink.android.kt
index fd9ab6a..0bd0a70 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/EspressoLink.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/EspressoLink.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
import androidx.compose.ui.test.junit4.android.ComposeNotIdleException
import androidx.test.espresso.AppNotIdleException
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/IdlingStrategy.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/IdlingStrategy.android.kt
similarity index 95%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/IdlingStrategy.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/IdlingStrategy.android.kt
index d101330..385cdd4 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/IdlingStrategy.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/IdlingStrategy.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
/**
* A strategy to wait for idleness. This is typically implemented by different test frameworks,
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/MainTestClockImpl.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/MainTestClockImpl.android.kt
similarity index 80%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/MainTestClockImpl.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/MainTestClockImpl.android.kt
index e535136..412a0cd 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/MainTestClockImpl.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/MainTestClockImpl.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
-import androidx.compose.ui.test.ExperimentalTestApi
-import androidx.compose.ui.test.TestMonotonicFrameClock
-import androidx.compose.ui.test.frameDelayMillis
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScheduler
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/RobolectricIdlingStrategy.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/RobolectricIdlingStrategy.android.kt
similarity index 97%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/RobolectricIdlingStrategy.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/RobolectricIdlingStrategy.android.kt
index 5ff5012..adbe96e 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/RobolectricIdlingStrategy.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/RobolectricIdlingStrategy.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
import androidx.test.espresso.AppNotIdleException
import androidx.test.espresso.IdlingPolicies
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeNotIdleException.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeNotIdleException.android.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeNotIdleException.android.kt
rename to compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeNotIdleException.android.kt
diff --git a/compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt
similarity index 97%
rename from compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt
rename to compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt
index 310bfa7..ffa8528 100644
--- a/compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt
+++ b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/InfiniteAnimationPolicyTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 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.
diff --git a/compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt
similarity index 99%
rename from compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt
rename to compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt
index 15df0b3..6fbc231 100644
--- a/compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt
+++ b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 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.
diff --git a/compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityRobolectricTest.kt b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityRobolectricTest.kt
similarity index 98%
rename from compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityRobolectricTest.kt
rename to compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityRobolectricTest.kt
index 15cf902..0ef6250 100644
--- a/compose/ui/ui-test-junit4/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityRobolectricTest.kt
+++ b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/junit4/ViewVisibilityRobolectricTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 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.
diff --git a/compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ApplyingContinuationInterceptor.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/ApplyingContinuationInterceptor.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ApplyingContinuationInterceptor.kt
rename to compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/ApplyingContinuationInterceptor.kt
diff --git a/compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ComposeUiTest.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/ComposeUiTest.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ComposeUiTest.kt
rename to compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/ComposeUiTest.kt
diff --git a/compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/StateRestorationTester.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/StateRestorationTester.kt
similarity index 100%
rename from compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/StateRestorationTester.kt
rename to compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/StateRestorationTester.kt
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
similarity index 94%
rename from compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
rename to compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
index 6977ddf..f619bc3 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
+++ b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 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.
@@ -19,12 +19,8 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.ComposeScene
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.node.RootForTest
import androidx.compose.ui.platform.InfiniteAnimationPolicy
-import androidx.compose.ui.test.junit4.MainTestClockImpl
-import androidx.compose.ui.test.junit4.UncaughtExceptionHandler
-import androidx.compose.ui.test.junit4.isOnUiThread
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import kotlin.coroutines.CoroutineContext
@@ -49,7 +45,7 @@
*/
@InternalTestApi
@ExperimentalTestApi
-@OptIn(ExperimentalComposeUiApi::class, ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalCoroutinesApi::class)
class DesktopComposeUiTest(
effectContext: CoroutineContext = EmptyCoroutineContext
) : ComposeUiTest {
@@ -136,7 +132,7 @@
}
override fun <T> runOnUiThread(action: () -> T): T {
- return androidx.compose.ui.test.junit4.runOnUiThread(action)
+ return androidx.compose.ui.test.runOnUiThread(action)
}
override fun <T> runOnIdle(action: () -> T): T {
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopSynchronization.desktop.kt b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopSynchronization.desktop.kt
similarity index 93%
rename from compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopSynchronization.desktop.kt
rename to compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopSynchronization.desktop.kt
index 9bc7aac..333d62d 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopSynchronization.desktop.kt
+++ b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/DesktopSynchronization.desktop.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
import java.util.concurrent.ExecutionException
import java.util.concurrent.FutureTask
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/MainTestClockImpl.desktop.kt b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/MainTestClockImpl.desktop.kt
similarity index 81%
rename from compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/MainTestClockImpl.desktop.kt
rename to compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/MainTestClockImpl.desktop.kt
index 5b21f1b..9f217ef 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/MainTestClockImpl.desktop.kt
+++ b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/MainTestClockImpl.desktop.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScheduler
-@OptIn(ExperimentalCoroutinesApi::class)
internal class MainTestClockImpl(
testScheduler: TestCoroutineScheduler,
frameDelayMillis: Long
diff --git a/compose/ui/ui-test-junit4/src/desktopTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt b/compose/ui/ui-test/src/desktopTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
similarity index 69%
rename from compose/ui/ui-test-junit4/src/desktopTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
rename to compose/ui/ui-test/src/desktopTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
index 0dd6f32..09593b7 100644
--- a/compose/ui/ui-test-junit4/src/desktopTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
+++ b/compose/ui/ui-test/src/desktopTest/kotlin/androidx/compose/ui/test/ComposeUiTestTest.kt
@@ -18,35 +18,17 @@
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.MotionDurationScale
-import androidx.compose.ui.test.junit4.createComposeRule
import com.google.common.truth.Truth.assertThat
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
-import org.junit.Rule
import org.junit.Test
-import org.junit.rules.TestWatcher
-import org.junit.runner.Description
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.junit.runners.model.Statement
@RunWith(JUnit4::class)
@OptIn(ExperimentalTestApi::class)
class ComposeUiTestTest {
- private lateinit var testDescription: Description
-
- /**
- * Records the current [testDescription] for tests that need to invoke the compose test rule
- * directly.
- */
- @get:Rule
- val testWatcher = object : TestWatcher() {
- override fun starting(description: Description) {
- testDescription = description
- }
- }
-
@Test
fun effectContextPropagatedToComposition_runComposeUiTest() {
val testElement = TestCoroutineContextElement()
@@ -65,27 +47,6 @@
}
@Test
- fun effectContextPropagatedToComposition_createComposeRule() {
- val testElement = TestCoroutineContextElement()
- lateinit var compositionScope: CoroutineScope
- val rule = createComposeRule(testElement)
- val baseStatement = object : Statement() {
- override fun evaluate() {
- rule.setContent {
- compositionScope = rememberCoroutineScope()
- }
- rule.waitForIdle()
- }
- }
- rule.apply(baseStatement, testDescription)
- .evaluate()
-
- val elementFromComposition =
- compositionScope.coroutineContext[TestCoroutineContextElement]
- assertThat(elementFromComposition).isSameInstanceAs(testElement)
- }
-
- @Test
fun motionDurationScale_defaultValue() = runComposeUiTest {
var lastRecordedMotionDurationScale: Float? = null
setContent {
diff --git a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/AbstractMainTestClock.jvm.kt b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/AbstractMainTestClock.jvm.kt
similarity index 92%
rename from compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/AbstractMainTestClock.jvm.kt
rename to compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/AbstractMainTestClock.jvm.kt
index 3557dfa..6e4013c 100644
--- a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/AbstractMainTestClock.jvm.kt
+++ b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/AbstractMainTestClock.jvm.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
-import androidx.compose.ui.test.ComposeTimeoutException
-import androidx.compose.ui.test.MainTestClock
import kotlin.math.ceil
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScheduler
diff --git a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistry.jvm.kt b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/IdlingResourceRegistry.jvm.kt
similarity index 96%
rename from compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistry.jvm.kt
rename to compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/IdlingResourceRegistry.jvm.kt
index 1d7aba8..4cc556b 100644
--- a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/IdlingResourceRegistry.jvm.kt
+++ b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/IdlingResourceRegistry.jvm.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
import androidx.annotation.VisibleForTesting
-import androidx.compose.ui.test.IdlingResource
-import androidx.compose.ui.test.InternalTestApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
diff --git a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/UncaughtExceptionHandler.jvm.kt b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/UncaughtExceptionHandler.jvm.kt
similarity index 96%
rename from compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/UncaughtExceptionHandler.jvm.kt
rename to compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/UncaughtExceptionHandler.jvm.kt
index 5518d55..7504672 100644
--- a/compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/UncaughtExceptionHandler.jvm.kt
+++ b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/UncaughtExceptionHandler.jvm.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.ui.test.junit4
+package androidx.compose.ui.test
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
index b4743ae..40bea10 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
@@ -222,6 +222,11 @@
* that is accessed by [block]:
*
* @sample androidx.compose.ui.samples.rememberedUpdatedParameterPointerInputModifier
+ *
+ * ***Note*** Any removal operations on Android Views from `pointerInput` should wrap the `block`
+ * in a `post { }` block to guarantee the event dispatch completes before executing the removal.
+ * (You do not need to do this when removing a composable because Compose guarantees it completes
+ * via the snapshot state system.)
*/
fun Modifier.pointerInput(
key1: Any?,
@@ -256,6 +261,11 @@
* that is accessed by [block]:
*
* @sample androidx.compose.ui.samples.rememberedUpdatedParameterPointerInputModifier
+ *
+ * ***Note*** Any removal operations on Android Views from `pointerInput` should wrap the `block`
+ * in a `post { }` block to guarantee the event dispatch completes before executing the removal.
+ * (You do not need to do this when removing a composable because Compose guarantees it completes
+ * via the snapshot state system.)
*/
fun Modifier.pointerInput(
key1: Any?,
@@ -291,6 +301,11 @@
* that is accessed by [block]:
*
* @sample androidx.compose.ui.samples.rememberedUpdatedParameterPointerInputModifier
+ *
+ * ***Note*** Any removal operations on Android Views from `pointerInput` should wrap the `block`
+ * in a `post { }` block to guarantee the event dispatch completes before executing the removal.
+ * (You do not need to do this when removing a composable because Compose guarantees it completes
+ * via the snapshot state system.)
*/
fun Modifier.pointerInput(
vararg keys: Any?,
diff --git a/core/core-appdigest/src/main/java/androidx/core/appdigest/ChecksumsApiSImpl.java b/core/core-appdigest/src/main/java/androidx/core/appdigest/ChecksumsApiSImpl.java
index d883448..6106c1d 100644
--- a/core/core-appdigest/src/main/java/androidx/core/appdigest/ChecksumsApiSImpl.java
+++ b/core/core-appdigest/src/main/java/androidx/core/appdigest/ChecksumsApiSImpl.java
@@ -103,6 +103,12 @@
+ "list of certificates.");
}
+ if (Build.VERSION.SDK_INT <= 34) {
+ // On certain U devices, this might throw NameNotFoundException even if package is
+ // present.
+ context.getPackageManager().getInstallSourceInfo(packageName);
+ }
+
context.getPackageManager().requestChecksums(packageName, includeSplits, required,
trustedInstallers, new PackageManager.OnChecksumsReadyListener() {
@SuppressLint({"WrongConstant"})
diff --git a/core/core/src/androidTest/java/androidx/core/location/LocationCompatTest.java b/core/core/src/androidTest/java/androidx/core/location/LocationCompatTest.java
index 6cf5ee3..f26c738 100644
--- a/core/core/src/androidTest/java/androidx/core/location/LocationCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/location/LocationCompatTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -105,8 +104,6 @@
assertEquals(1.0, LocationCompat.getMslAltitudeMeters(location), 0.0);
LocationCompat.removeMslAltitude(location);
assertFalse(LocationCompat.hasMslAltitude(location));
- assertThrows(IllegalStateException.class,
- () -> LocationCompat.getMslAltitudeMeters(location));
}
@Test
@@ -118,8 +115,6 @@
assertEquals(1f, LocationCompat.getMslAltitudeAccuracyMeters(location), 0f);
LocationCompat.removeMslAltitudeAccuracy(location);
assertFalse(LocationCompat.hasMslAltitudeAccuracy(location));
- assertThrows(IllegalStateException.class,
- () -> LocationCompat.getMslAltitudeAccuracyMeters(location));
}
@Test
diff --git a/core/core/src/main/java/androidx/core/location/LocationCompat.java b/core/core/src/main/java/androidx/core/location/LocationCompat.java
index 883ff8a..7d4c123 100644
--- a/core/core/src/main/java/androidx/core/location/LocationCompat.java
+++ b/core/core/src/main/java/androidx/core/location/LocationCompat.java
@@ -28,7 +28,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import androidx.core.util.Preconditions;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@@ -337,19 +336,18 @@
/**
* Returns the Mean Sea Level altitude of the location in meters.
*
+ * <p>This is only valid if {@link #hasMslAltitude(Location)} is true.
+ *
* <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude does not exist. In
* order to allow for backwards compatibility and testing however, this method will attempt
* to read a double extra with the key {@link #EXTRA_MSL_ALTITUDE} and return the result.
*
- * @throws IllegalStateException if the Mean Sea Level altitude of the location is not set
* @see Location#getMslAltitudeMeters()
*/
public static double getMslAltitudeMeters(@NonNull Location location) {
if (VERSION.SDK_INT >= 34) {
return Api34Impl.getMslAltitudeMeters(location);
}
- Preconditions.checkState(hasMslAltitude(location),
- "The Mean Sea Level altitude of the location is not set.");
return getOrCreateExtras(location).getDouble(EXTRA_MSL_ALTITUDE);
}
@@ -411,22 +409,20 @@
* altitude of the location falls within {@link #getMslAltitudeMeters(Location)} +/- this
* uncertainty.
*
+ * <p>This is only valid if {@link #hasMslAltitudeAccuracy(Location)} is true.
+ *
* <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude accuracy does not
* exist. In order to allow for backwards compatibility and testing however, this method will
* attempt to read a float extra with the key {@link #EXTRA_MSL_ALTITUDE_ACCURACY} and return
* the result.
*
- * @throws IllegalStateException if the Mean Sea Level altitude accuracy of the location is not
- * set
- * @see Location#setMslAltitudeAccuracyMeters(float)
+ * @see Location#getMslAltitudeAccuracyMeters()
*/
public static @FloatRange(from = 0.0) float getMslAltitudeAccuracyMeters(
@NonNull Location location) {
if (VERSION.SDK_INT >= 34) {
return Api34Impl.getMslAltitudeAccuracyMeters(location);
}
- Preconditions.checkState(hasMslAltitudeAccuracy(location),
- "The Mean Sea Level altitude accuracy of the location is not set.");
return getOrCreateExtras(location).getFloat(EXTRA_MSL_ALTITUDE_ACCURACY);
}
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt
index 9345855..a8d9bd8 100644
--- a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt
@@ -30,6 +30,7 @@
import androidx.core.uwb.UwbControllerSessionScope
import androidx.core.uwb.UwbManager
import androidx.core.uwb.backend.IUwb
+import androidx.core.uwb.exceptions.UwbServiceNotAvailableException
import androidx.core.uwb.helper.checkSystemFeature
import androidx.core.uwb.helper.handleApiException
import com.google.android.gms.common.ConnectionResult
@@ -88,7 +89,8 @@
Nearby.getUwbControllerClient(context) else Nearby.getUwbControleeClient(context)
if (!uwbClient.isAvailable().await()) {
Log.e(TAG, "Uwb availability : false")
- throw RuntimeException("Cannot start a ranging session when UWB is unavailable")
+ throw UwbServiceNotAvailableException("Cannot start a ranging session when UWB is " +
+ "unavailable")
}
try {
val nearbyLocalAddress = uwbClient.localAddress.await()
diff --git a/development/build_log_simplifier/build_log_simplifier.py b/development/build_log_simplifier/build_log_simplifier.py
index 34432aa..974acd6 100755
--- a/development/build_log_simplifier/build_log_simplifier.py
+++ b/development/build_log_simplifier/build_log_simplifier.py
@@ -275,6 +275,21 @@
def remove_control_characters(line):
return control_character_regex.sub("", line)
+# Removes strings from the input wherever they are found
+# This list is less convenient than the .ignore files:
+# This list doesn't get autosuggested additions
+# This list isn't automatically garbage collected
+# Users interested in seeing the exemption history probably won't think to look here
+# This list does allow removing part of the text from a line and still validating the remainder of the line
+# If this list eventually gets long we might want to make it easier to update
+inline_ignores_regex = re.compile(
+ # b/300072778
+ "Sharing is only supported for boot loader classes because bootstrap classpath has been appended"
+)
+
+def remove_inline_ignores(line):
+ return re.sub(inline_ignores_regex, "", line)
+
# Normalizes some filepaths to more easily simplify/skip some messages
def normalize_paths(lines):
# get OUT_DIR, DIST_DIR, and the path of the root of the checkout
@@ -490,6 +505,7 @@
for log_path in log_paths:
lines = readlines(log_path)
lines = [remove_control_characters(line) for line in lines]
+ lines = [remove_inline_ignores(line) for line in lines]
lines = normalize_paths(lines)
all_lines += lines
# load configuration
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 689a78b..db23bb9 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -71,7 +71,7 @@
samples("androidx.compose.foundation:foundation-layout-samples:1.6.0-beta02")
samples("androidx.compose.foundation:foundation-samples:1.6.0-beta02")
kmpDocs("androidx.compose.material3:material3:1.2.0-alpha12")
- docs("androidx.compose.material3:material3-adaptive:1.0.0-alpha02")
+ kmpDocs("androidx.compose.material3:material3-adaptive:1.0.0-alpha02")
samples("androidx.compose.material3:material3-adaptive-navigation-suite-samples:1.2.0-alpha12")
samples("androidx.compose.material3:material3-adaptive-samples:1.2.0-alpha12")
samples("androidx.compose.material3:material3-samples:1.2.0-alpha12")
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 53db4d4e..d408a73 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -224,6 +224,7 @@
mockitoCore = { module = "org.mockito:mockito-core", version.ref = "mockito" }
mockitoCore4 = { module = "org.mockito:mockito-core", version = "4.8.0" }
mockitoAndroid = { module = "org.mockito:mockito-android", version.ref = "mockito" }
+mockitoAndroid5 = { module = "org.mockito:mockito-android", version = "5.8.0" }
mockitoKotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "2.2.11" }
mockitoKotlin4 = { module = "org.mockito.kotlin:mockito-kotlin", version = "4.0.0" }
moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
diff --git a/graphics/graphics-core/api/1.0.0-beta01.txt b/graphics/graphics-core/api/1.0.0-beta01.txt
index bcf0955..7a22bee 100644
--- a/graphics/graphics-core/api/1.0.0-beta01.txt
+++ b/graphics/graphics-core/api/1.0.0-beta01.txt
@@ -8,7 +8,8 @@
method public long getUsageFlags();
method public boolean isClosed();
method public androidx.graphics.CanvasBufferedRenderer.RenderRequest obtainRenderRequest();
- method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer, androidx.hardware.SyncFenceCompat? fence);
+ method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer);
+ method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer, optional androidx.hardware.SyncFenceCompat? fence);
method public void setContentRoot(android.graphics.RenderNode renderNode);
method public void setLightSourceAlpha(float ambientShadowAlpha, float spotShadowAlpha);
method public void setLightSourceGeometry(float lightX, float lightY, float lightZ, float lightRadius);
diff --git a/graphics/graphics-core/api/current.ignore b/graphics/graphics-core/api/current.ignore
index af77f0d..d2d6395 100644
--- a/graphics/graphics-core/api/current.ignore
+++ b/graphics/graphics-core/api/current.ignore
@@ -1,11 +1,3 @@
// Baseline format: 1.0
-AddedMethod: androidx.graphics.CanvasBufferedRenderer.RenderRequest#draw(boolean, kotlin.coroutines.Continuation<? super androidx.graphics.CanvasBufferedRenderer.RenderResult>):
- Added method androidx.graphics.CanvasBufferedRenderer.RenderRequest.draw(boolean,kotlin.coroutines.Continuation<? super androidx.graphics.CanvasBufferedRenderer.RenderResult>)
-AddedMethod: androidx.graphics.CanvasBufferedRenderer.RenderRequest#drawAsync(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.graphics.CanvasBufferedRenderer.RenderResult>):
- Added method androidx.graphics.CanvasBufferedRenderer.RenderRequest.drawAsync(java.util.concurrent.Executor,androidx.core.util.Consumer<androidx.graphics.CanvasBufferedRenderer.RenderResult>)
-
-
-RemovedMethod: androidx.graphics.CanvasBufferedRenderer.RenderRequest#draw(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.graphics.CanvasBufferedRenderer.RenderResult>):
- Removed method androidx.graphics.CanvasBufferedRenderer.RenderRequest.draw(java.util.concurrent.Executor,androidx.core.util.Consumer<androidx.graphics.CanvasBufferedRenderer.RenderResult>)
-RemovedMethod: androidx.graphics.CanvasBufferedRenderer.RenderRequest#drawSync(boolean):
- Removed method androidx.graphics.CanvasBufferedRenderer.RenderRequest.drawSync(boolean)
+AddedMethod: androidx.graphics.CanvasBufferedRenderer#releaseBuffer(android.hardware.HardwareBuffer):
+ Added method androidx.graphics.CanvasBufferedRenderer.releaseBuffer(android.hardware.HardwareBuffer)
diff --git a/graphics/graphics-core/api/current.txt b/graphics/graphics-core/api/current.txt
index bcf0955..7a22bee 100644
--- a/graphics/graphics-core/api/current.txt
+++ b/graphics/graphics-core/api/current.txt
@@ -8,7 +8,8 @@
method public long getUsageFlags();
method public boolean isClosed();
method public androidx.graphics.CanvasBufferedRenderer.RenderRequest obtainRenderRequest();
- method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer, androidx.hardware.SyncFenceCompat? fence);
+ method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer);
+ method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer, optional androidx.hardware.SyncFenceCompat? fence);
method public void setContentRoot(android.graphics.RenderNode renderNode);
method public void setLightSourceAlpha(float ambientShadowAlpha, float spotShadowAlpha);
method public void setLightSourceGeometry(float lightX, float lightY, float lightZ, float lightRadius);
diff --git a/graphics/graphics-core/api/restricted_1.0.0-beta01.txt b/graphics/graphics-core/api/restricted_1.0.0-beta01.txt
index 0c3a248..b421d28 100644
--- a/graphics/graphics-core/api/restricted_1.0.0-beta01.txt
+++ b/graphics/graphics-core/api/restricted_1.0.0-beta01.txt
@@ -8,7 +8,8 @@
method public long getUsageFlags();
method public boolean isClosed();
method public androidx.graphics.CanvasBufferedRenderer.RenderRequest obtainRenderRequest();
- method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer, androidx.hardware.SyncFenceCompat? fence);
+ method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer);
+ method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer, optional androidx.hardware.SyncFenceCompat? fence);
method public void setContentRoot(android.graphics.RenderNode renderNode);
method public void setLightSourceAlpha(float ambientShadowAlpha, float spotShadowAlpha);
method public void setLightSourceGeometry(float lightX, float lightY, float lightZ, float lightRadius);
diff --git a/graphics/graphics-core/api/restricted_current.ignore b/graphics/graphics-core/api/restricted_current.ignore
index af77f0d..d2d6395 100644
--- a/graphics/graphics-core/api/restricted_current.ignore
+++ b/graphics/graphics-core/api/restricted_current.ignore
@@ -1,11 +1,3 @@
// Baseline format: 1.0
-AddedMethod: androidx.graphics.CanvasBufferedRenderer.RenderRequest#draw(boolean, kotlin.coroutines.Continuation<? super androidx.graphics.CanvasBufferedRenderer.RenderResult>):
- Added method androidx.graphics.CanvasBufferedRenderer.RenderRequest.draw(boolean,kotlin.coroutines.Continuation<? super androidx.graphics.CanvasBufferedRenderer.RenderResult>)
-AddedMethod: androidx.graphics.CanvasBufferedRenderer.RenderRequest#drawAsync(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.graphics.CanvasBufferedRenderer.RenderResult>):
- Added method androidx.graphics.CanvasBufferedRenderer.RenderRequest.drawAsync(java.util.concurrent.Executor,androidx.core.util.Consumer<androidx.graphics.CanvasBufferedRenderer.RenderResult>)
-
-
-RemovedMethod: androidx.graphics.CanvasBufferedRenderer.RenderRequest#draw(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.graphics.CanvasBufferedRenderer.RenderResult>):
- Removed method androidx.graphics.CanvasBufferedRenderer.RenderRequest.draw(java.util.concurrent.Executor,androidx.core.util.Consumer<androidx.graphics.CanvasBufferedRenderer.RenderResult>)
-RemovedMethod: androidx.graphics.CanvasBufferedRenderer.RenderRequest#drawSync(boolean):
- Removed method androidx.graphics.CanvasBufferedRenderer.RenderRequest.drawSync(boolean)
+AddedMethod: androidx.graphics.CanvasBufferedRenderer#releaseBuffer(android.hardware.HardwareBuffer):
+ Added method androidx.graphics.CanvasBufferedRenderer.releaseBuffer(android.hardware.HardwareBuffer)
diff --git a/graphics/graphics-core/api/restricted_current.txt b/graphics/graphics-core/api/restricted_current.txt
index 0c3a248..b421d28 100644
--- a/graphics/graphics-core/api/restricted_current.txt
+++ b/graphics/graphics-core/api/restricted_current.txt
@@ -8,7 +8,8 @@
method public long getUsageFlags();
method public boolean isClosed();
method public androidx.graphics.CanvasBufferedRenderer.RenderRequest obtainRenderRequest();
- method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer, androidx.hardware.SyncFenceCompat? fence);
+ method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer);
+ method public void releaseBuffer(android.hardware.HardwareBuffer hardwareBuffer, optional androidx.hardware.SyncFenceCompat? fence);
method public void setContentRoot(android.graphics.RenderNode renderNode);
method public void setLightSourceAlpha(float ambientShadowAlpha, float spotShadowAlpha);
method public void setLightSourceGeometry(float lightX, float lightY, float lightZ, float lightRadius);
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRenderer.kt
index 7b58718..13ac94f 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRenderer.kt
@@ -430,14 +430,28 @@
* @param fence Optional [SyncFenceCompat] that should be waited upon before the buffer is
* reused.
*/
- fun releaseBuffer(hardwareBuffer: HardwareBuffer, fence: SyncFenceCompat?) {
+ @JvmOverloads
+ fun releaseBuffer(hardwareBuffer: HardwareBuffer, fence: SyncFenceCompat? = null) {
mImpl.releaseBuffer(hardwareBuffer, fence)
}
/**
* Class that contains data regarding the result of the render request. Consumers are to wait
- * on the provided [SyncFenceCompat] before consuming the [HardwareBuffer] provided to as well
- * as verify that the status returned by [RenderResult.status] returns [RenderResult.SUCCESS].
+ * on the provided [SyncFenceCompat] if it is non null before consuming the [HardwareBuffer]
+ * provided to as well as verify that the status returned by [RenderResult.status] returns
+ * [RenderResult.SUCCESS].
+ *
+ * For example:
+ * ```
+ * fun handleRenderResult(result: RenderResult) {
+ * // block on the fence if it is non-null
+ * result.fence?.let { fence ->
+ * fence.awaitForever()
+ * fence.close()
+ * }
+ * // consume contents of RenderResult.hardwareBuffer
+ * }
+ * ```
*/
class RenderResult(
private val buffer: HardwareBuffer,
@@ -448,13 +462,23 @@
/**
* [HardwareBuffer] that contains the result of the render request.
* Consumers should be sure to block on the [SyncFenceCompat] instance
- * provided in [fence] before consuming the contents of this buffer.
+ * provided in [fence] if it is non-null before consuming the contents of this buffer.
+ * If [fence] returns null then this [HardwareBuffer] can be consumed immediately.
*/
val hardwareBuffer: HardwareBuffer
get() = buffer
/**
- * Optional fence that should be waited upon before consuming [hardwareBuffer]
+ * Optional fence that should be waited upon before consuming [hardwareBuffer].
+ * On Android U and above, requests to render will return sooner and include this fence
+ * as a way to signal that the result of the render request is reflected in the contents
+ * of the buffer. This is done to reduce latency and provide opportunities for other systems
+ * to block on the fence on the behalf of the application.
+ * For example, [SurfaceControlCompat.Transaction.setBuffer] can be invoked with
+ * [RenderResult.hardwareBuffer] and [RenderResult.fence] respectively without the
+ * application having to explicitly block on this fence.
+ * For older Android versions, the rendering pipeline will automatically block on this fence
+ * and this value will return null.
*/
val fence: SyncFenceCompat?
get() = mFence
diff --git a/graphics/graphics-shapes/build.gradle b/graphics/graphics-shapes/build.gradle
index 45bc2b8..d7c1342 100644
--- a/graphics/graphics-shapes/build.gradle
+++ b/graphics/graphics-shapes/build.gradle
@@ -38,7 +38,7 @@
commonMain {
dependencies {
implementation(libs.kotlinStdlibCommon)
- implementation project(':collection:collection')
+ implementation("androidx.collection:collection:1.4.0-beta02")
implementation("androidx.annotation:annotation:1.1.0")
}
}
diff --git a/health/health-services-client/api/current.txt b/health/health-services-client/api/current.txt
index b807507..b59d5a2 100644
--- a/health/health-services-client/api/current.txt
+++ b/health/health-services-client/api/current.txt
@@ -260,6 +260,7 @@
field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Long>> STEPS_PER_MINUTE_STATS;
field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> STEPS_TOTAL;
field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT;
+ field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT_TOTAL;
field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_STROKES;
field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_STROKES_TOTAL;
field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> VO2_MAX;
diff --git a/health/health-services-client/api/restricted_current.txt b/health/health-services-client/api/restricted_current.txt
index 53770c5..b777571 100644
--- a/health/health-services-client/api/restricted_current.txt
+++ b/health/health-services-client/api/restricted_current.txt
@@ -260,6 +260,7 @@
field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Long>> STEPS_PER_MINUTE_STATS;
field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> STEPS_TOTAL;
field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT;
+ field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_LAP_COUNT_TOTAL;
field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> SWIMMING_STROKES;
field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> SWIMMING_STROKES_TOTAL;
field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> VO2_MAX;
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
index a3e87c7..7f01d87 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
@@ -573,11 +573,16 @@
val ACTIVE_EXERCISE_DURATION_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
createCumulativeDataType("Active Exercise Duration")
- /** Count of swimming laps since the start of the current active exercise. */
+ /** Count of swimming laps since the last update. */
@JvmField
val SWIMMING_LAP_COUNT: DeltaDataType<Long, IntervalDataPoint<Long>> =
createIntervalDataType("Swim Lap Count")
+ /** Count of swimming laps since the start of the current active exercise. */
+ @JvmField
+ val SWIMMING_LAP_COUNT_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
+ createCumulativeDataType("Swim Lap Count")
+
/** The number of repetitions of an exercise performed since the last update. */
@JvmField
val REP_COUNT: DeltaDataType<Long, IntervalDataPoint<Long>> =
@@ -685,6 +690,7 @@
SPEED_STATS,
STEPS_PER_MINUTE_STATS,
STEPS_TOTAL,
+ SWIMMING_LAP_COUNT_TOTAL,
SWIMMING_STROKES_TOTAL,
VO2_MAX_STATS,
WALKING_STEPS_TOTAL,
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt
index 6ab8729..7625478 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt
@@ -28,7 +28,6 @@
import androidx.health.services.client.data.DataType.Companion.LOCATION
import androidx.health.services.client.data.DataType.Companion.STEPS
import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
-import androidx.health.services.client.data.DataType.Companion.SWIMMING_LAP_COUNT
import androidx.health.services.client.data.DataType.TimeType.Companion.INTERVAL
import androidx.health.services.client.data.DataType.TimeType.Companion.UNKNOWN
import androidx.health.services.client.proto.DataProto
@@ -199,8 +198,6 @@
}.map { it.name }
// Certain deltas are expected to not have aggregates
val deltaNames = DataType.deltaDataTypes.toMutableSet().apply {
- // Swimming lap count is already aggregated
- remove(SWIMMING_LAP_COUNT)
// Aggregate location doesn't make a lot of sense
remove(LOCATION)
// Dailies are used in passive and passive only deals with deltas
diff --git a/hilt/hilt-navigation-compose/api/current.txt b/hilt/hilt-navigation-compose/api/current.txt
index 99cdd72..b34c1d5 100644
--- a/hilt/hilt-navigation-compose/api/current.txt
+++ b/hilt/hilt-navigation-compose/api/current.txt
@@ -3,6 +3,7 @@
public final class HiltViewModelKt {
method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM hiltViewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key);
+ method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel, reified VMF> VM hiltViewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, kotlin.jvm.functions.Function1<? super VMF,? extends VM> creationCallback);
}
}
diff --git a/hilt/hilt-navigation-compose/api/restricted_current.txt b/hilt/hilt-navigation-compose/api/restricted_current.txt
index 40d2a82..8ff83a6 100644
--- a/hilt/hilt-navigation-compose/api/restricted_current.txt
+++ b/hilt/hilt-navigation-compose/api/restricted_current.txt
@@ -4,6 +4,7 @@
public final class HiltViewModelKt {
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.lifecycle.ViewModelProvider.Factory? createHiltViewModelFactory(androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner);
method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM hiltViewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key);
+ method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel, reified VMF> VM hiltViewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, kotlin.jvm.functions.Function1<? super VMF,? extends VM> creationCallback);
}
}
diff --git a/hilt/hilt-navigation-compose/src/androidTest/java/androidx/hilt/navigation/compose/HiltViewModelComposeTest.kt b/hilt/hilt-navigation-compose/src/androidTest/java/androidx/hilt/navigation/compose/HiltViewModelComposeTest.kt
index ad8f010..4b8cbb2 100644
--- a/hilt/hilt-navigation-compose/src/androidTest/java/androidx/hilt/navigation/compose/HiltViewModelComposeTest.kt
+++ b/hilt/hilt-navigation-compose/src/androidTest/java/androidx/hilt/navigation/compose/HiltViewModelComposeTest.kt
@@ -43,6 +43,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.google.common.truth.Truth.assertThat
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
@@ -250,6 +253,27 @@
assertThat(firstFactory).isNotSameInstanceAs(secondFactory)
}
+ @Test
+ fun hiltViewModelAssisted() {
+ lateinit var viewModel: SimpleAssistedViewModel
+ composeTestRule.setContent {
+ val navController = rememberNavController()
+ NavHost(navController, startDestination = "Main") {
+ composable("Main") {
+ viewModel = hiltViewModel<
+ SimpleAssistedViewModel,
+ SimpleAssistedViewModel.Factory> {
+ it.create(42)
+ }
+ }
+ }
+ }
+ composeTestRule.waitForIdle()
+
+ assertThat(viewModel).isNotNull()
+ assertThat(viewModel.i).isEqualTo(42)
+ }
+
@Composable
private fun NavigateButton(text: String, listener: () -> Unit = { }) {
Button(onClick = listener) {
@@ -269,6 +293,21 @@
@ApplicationContext val context: Context
) : ViewModel()
+ @HiltViewModel(assistedFactory = SimpleAssistedViewModel.Factory::class)
+ class SimpleAssistedViewModel @AssistedInject constructor(
+ val handle: SavedStateHandle,
+ val logger: MyLogger,
+ // TODO(kuanyingchou) Remove this after https://github.com/google/dagger/issues/3601 is
+ // resolved.
+ @ApplicationContext val context: Context,
+ @Assisted val i: Int
+ ) : ViewModel() {
+ @AssistedFactory
+ interface Factory {
+ fun create(i: Int): SimpleAssistedViewModel
+ }
+ }
+
class TestFragment(val composable: @Composable (Fragment) -> Unit) : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
diff --git a/hilt/hilt-navigation-compose/src/main/java/androidx/hilt/navigation/compose/HiltViewModel.kt b/hilt/hilt-navigation-compose/src/main/java/androidx/hilt/navigation/compose/HiltViewModel.kt
index 5619fe3..ea36c0d 100644
--- a/hilt/hilt-navigation-compose/src/main/java/androidx/hilt/navigation/compose/HiltViewModel.kt
+++ b/hilt/hilt-navigation-compose/src/main/java/androidx/hilt/navigation/compose/HiltViewModel.kt
@@ -23,8 +23,11 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
+import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel
+import dagger.hilt.android.internal.lifecycle.HiltViewModelFactory
+import dagger.hilt.android.lifecycle.withCreationCallback
/**
* Returns an existing
@@ -49,6 +52,41 @@
return viewModel(viewModelStoreOwner, key, factory = factory)
}
+/**
+ * Returns an existing
+ * [HiltViewModel](https://dagger.dev/api/latest/dagger/hilt/android/lifecycle/HiltViewModel)
+ * -annotated [ViewModel] with an [@AssistedInject]-annotated constructor or creates a new one scoped to the current navigation graph present on
+ * the {@link NavController} back stack.
+ *
+ * If no navigation graph is currently present then the current scope will be used, usually, a
+ * fragment or an activity.
+ *
+ * @sample androidx.hilt.navigation.compose.samples.NavComposable
+ * @sample androidx.hilt.navigation.compose.samples.NestedNavComposable
+ */
+@Composable
+inline fun <reified VM : ViewModel, reified VMF> hiltViewModel(
+ viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
+ "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
+ },
+ key: String? = null,
+ noinline creationCallback: (VMF) -> VM
+): VM {
+ val factory = createHiltViewModelFactory(viewModelStoreOwner)
+ return viewModel(
+ viewModelStoreOwner = viewModelStoreOwner,
+ key = key,
+ factory = factory,
+ extras = viewModelStoreOwner.run {
+ if (this is HasDefaultViewModelProviderFactory) {
+ this.defaultViewModelCreationExtras.withCreationCallback(creationCallback)
+ } else {
+ CreationExtras.Empty.withCreationCallback(creationCallback)
+ }
+ }
+ )
+}
+
@Composable
@PublishedApi
internal fun createHiltViewModelFactory(
diff --git a/hilt/hilt-navigation-fragment/api/current.txt b/hilt/hilt-navigation-fragment/api/current.txt
index 7cbc428..abbed0e 100644
--- a/hilt/hilt-navigation-fragment/api/current.txt
+++ b/hilt/hilt-navigation-fragment/api/current.txt
@@ -3,6 +3,7 @@
public final class HiltNavGraphViewModelLazyKt {
method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> hiltNavGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel, reified VMF> kotlin.Lazy<VM> hiltNavGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, kotlin.jvm.functions.Function1<? super VMF,? extends VM> creationCallback);
}
}
diff --git a/hilt/hilt-navigation-fragment/api/restricted_current.txt b/hilt/hilt-navigation-fragment/api/restricted_current.txt
index 7cbc428..abbed0e 100644
--- a/hilt/hilt-navigation-fragment/api/restricted_current.txt
+++ b/hilt/hilt-navigation-fragment/api/restricted_current.txt
@@ -3,6 +3,7 @@
public final class HiltNavGraphViewModelLazyKt {
method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> hiltNavGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel, reified VMF> kotlin.Lazy<VM> hiltNavGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, kotlin.jvm.functions.Function1<? super VMF,? extends VM> creationCallback);
}
}
diff --git a/hilt/hilt-navigation-fragment/src/androidTest/java/androidx/hilt/navigation/fragment/HiltNavGraphViewModelLazyTest.kt b/hilt/hilt-navigation-fragment/src/androidTest/java/androidx/hilt/navigation/fragment/HiltNavGraphViewModelLazyTest.kt
index f454761..32dd2e6 100644
--- a/hilt/hilt-navigation-fragment/src/androidTest/java/androidx/hilt/navigation/fragment/HiltNavGraphViewModelLazyTest.kt
+++ b/hilt/hilt-navigation-fragment/src/androidTest/java/androidx/hilt/navigation/fragment/HiltNavGraphViewModelLazyTest.kt
@@ -33,6 +33,9 @@
import androidx.test.filters.LargeTest
import androidx.testutils.withActivity
import com.google.common.truth.Truth.assertThat
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
@@ -79,10 +82,15 @@
val viewModel = withActivity { firstFragment.viewModel }
val savedStateViewModel = withActivity { firstFragment.savedStateViewModel }
val hiltSavedStateViewModel = withActivity { firstFragment.hiltSavedStateViewModel }
+ val hiltAssistedInjectViewModel = withActivity {
+ firstFragment.hiltAssistedInjectViewModel
+ }
assertThat(viewModel).isNotNull()
assertThat(savedStateViewModel).isNotNull()
assertThat(hiltSavedStateViewModel).isNotNull()
assertThat(hiltSavedStateViewModel.otherDep).isNotNull()
+ assertThat(hiltAssistedInjectViewModel).isNotNull()
+ assertThat(hiltAssistedInjectViewModel.id).isEqualTo("test")
// First assert that the initial value is null. Note that we won't get the
// default value from nav args passed to the destination as this viewmodel
@@ -112,6 +120,8 @@
.isSameInstanceAs(savedStateViewModel)
assertThat(secondFragment.hiltSavedStateViewModel)
.isSameInstanceAs(hiltSavedStateViewModel)
+ assertThat(secondFragment.hiltAssistedInjectViewModel)
+ .isSameInstanceAs(hiltAssistedInjectViewModel)
val savedValue: String? = secondFragment.savedStateViewModel
.savedStateHandle["test"]
assertThat(savedValue).isEqualTo("test")
@@ -132,6 +142,8 @@
.isSameInstanceAs(savedStateViewModel)
assertThat(recreatedFragment.hiltSavedStateViewModel)
.isSameInstanceAs(hiltSavedStateViewModel)
+ assertThat(recreatedFragment.hiltAssistedInjectViewModel)
+ .isSameInstanceAs(hiltAssistedInjectViewModel)
val recreatedValue: String? = recreatedFragment.savedStateViewModel
.savedStateHandle["test"]
assertThat(recreatedValue).isEqualTo("test")
@@ -148,6 +160,11 @@
val savedStateViewModel: TestSavedStateViewModel by navGraphViewModels(R.id.vm_graph)
val hiltSavedStateViewModel: TestHiltSavedStateViewModel
by hiltNavGraphViewModels(R.id.vm_graph)
+ val hiltAssistedInjectViewModel: TestHiltAssistedInjectViewModel
+ by hiltNavGraphViewModels(R.id.vm_graph) {
+ factory: TestHiltAssistedInjectViewModel.Factory ->
+ factory.create(id = "test")
+ }
// TODO(kuanyingchou) Remove this after https://github.com/google/dagger/issues/3601 is resolved
@Inject @ApplicationContext
lateinit var applicationContext: Context
@@ -174,4 +191,16 @@
val otherDep: OtherDep
) : ViewModel()
+@HiltViewModel(assistedFactory = TestHiltAssistedInjectViewModel.Factory::class)
+class TestHiltAssistedInjectViewModel @AssistedInject constructor(
+ val savedStateHandle: SavedStateHandle,
+ val otherDep: OtherDep,
+ @Assisted val id: String
+) : ViewModel() {
+ @AssistedFactory
+ interface Factory {
+ fun create(id: String): TestHiltAssistedInjectViewModel
+ }
+}
+
class OtherDep @Inject constructor()
diff --git a/hilt/hilt-navigation-fragment/src/main/java/androidx/hilt/navigation/fragment/HiltNavGraphViewModelLazy.kt b/hilt/hilt-navigation-fragment/src/main/java/androidx/hilt/navigation/fragment/HiltNavGraphViewModelLazy.kt
index 099b97c..8d2702e 100644
--- a/hilt/hilt-navigation-fragment/src/main/java/androidx/hilt/navigation/fragment/HiltNavGraphViewModelLazy.kt
+++ b/hilt/hilt-navigation-fragment/src/main/java/androidx/hilt/navigation/fragment/HiltNavGraphViewModelLazy.kt
@@ -24,6 +24,7 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelStore
import androidx.navigation.fragment.findNavController
+import dagger.hilt.android.lifecycle.withCreationCallback
/**
* Returns a property delegate to access a
@@ -31,7 +32,7 @@
* -annotated [ViewModel] scoped to a navigation graph present on the [NavController] back stack:
* ```
* class MyFragment : Fragment() {
- * val viewmodel: MainViewModel by androidx.hilt.navigation.fragment.hiltNavGraphViewModels(R.navigation.main)
+ * val viewmodel: MainViewModel by hiltNavGraphViewModels(R.navigation.main)
* }
* ```
*
@@ -59,3 +60,46 @@
extrasProducer = { backStackEntry.defaultViewModelCreationExtras }
)
}
+
+/**
+ * Returns a property delegate to access a
+ * [HiltViewModel](https://dagger.dev/api/latest/dagger/hilt/android/lifecycle/HiltViewModel)
+ * -annotated [ViewModel] with an [@AssistedInject]-annotated constructor that is scoped to a
+ * navigation graph present on the [NavController] back stack:
+ * ```
+ * class MyFragment : Fragment() {
+ * val viewmodel: MainViewModel by hiltNavGraphViewModels(R.navigation.main) { factory: MainViewModelFactory ->
+ * factory.create(...)
+ * }
+ * }
+ * ```
+ *
+ * This property can be accessed only after this NavGraph is on the NavController back stack,
+ * and an attempt access prior to that will result in an IllegalArgumentException.
+ *
+ * @param navGraphId ID of a NavGraph that exists on the [NavController] back stack
+ * @param creationCallback callback that takes an @AssistedFactory-annotated factory and creates a HiltViewModel using @AssistedInject-annotated constructor.
+ */
+@MainThread
+@Suppress("MissingNullability") // Due to https://youtrack.jetbrains.com/issue/KT-39209
+public inline fun <reified VM : ViewModel, reified VMF : Any> Fragment.hiltNavGraphViewModels(
+ @IdRes navGraphId: Int,
+ noinline creationCallback: (VMF) -> VM
+): Lazy<VM> {
+ val backStackEntry by lazy {
+ findNavController().getBackStackEntry(navGraphId)
+ }
+ val storeProducer: () -> ViewModelStore = {
+ backStackEntry.viewModelStore
+ }
+ return createViewModelLazy(
+ viewModelClass = VM::class,
+ storeProducer = storeProducer,
+ factoryProducer = {
+ HiltViewModelFactory(requireActivity(), backStackEntry.defaultViewModelProviderFactory)
+ },
+ extrasProducer = {
+ backStackEntry.defaultViewModelCreationExtras.withCreationCallback(creationCallback)
+ }
+ )
+}
diff --git a/hilt/hilt-navigation/src/main/java/androidx/hilt/navigation/HiltNavBackStackEntry.kt b/hilt/hilt-navigation/src/main/java/androidx/hilt/navigation/HiltNavBackStackEntry.kt
index bfe393e..fc14f38 100644
--- a/hilt/hilt-navigation/src/main/java/androidx/hilt/navigation/HiltNavBackStackEntry.kt
+++ b/hilt/hilt-navigation/src/main/java/androidx/hilt/navigation/HiltNavBackStackEntry.kt
@@ -72,12 +72,8 @@
"but instead found: $ctx"
)
}
- // TODO(kuanyingchou): The `owner` is actually not used. We pass
- // `activity` here since it's a NonNull parameter. This can be removed with Dagger 2.45.
return HiltViewModelFactory.createInternal(
/* activity = */ activity,
- /* owner = */ activity,
- /* defaultArgs = */ null,
/* delegateFactory = */ delegateFactory
)
}
diff --git a/hilt/integration-tests/viewmodelapp/src/androidTest/java/androidx/hilt/integration/viewmodelapp/ActivityInjectionTest.kt b/hilt/integration-tests/viewmodelapp/src/androidTest/java/androidx/hilt/integration/viewmodelapp/ActivityInjectionTest.kt
index ba64f40..82d890d 100644
--- a/hilt/integration-tests/viewmodelapp/src/androidTest/java/androidx/hilt/integration/viewmodelapp/ActivityInjectionTest.kt
+++ b/hilt/integration-tests/viewmodelapp/src/androidTest/java/androidx/hilt/integration/viewmodelapp/ActivityInjectionTest.kt
@@ -25,6 +25,7 @@
import androidx.test.filters.SdkSuppress
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.lifecycle.withCreationCallback
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Rule
@@ -49,6 +50,8 @@
assertThat(activity.myViewModel).isNotNull()
assertThat(activity.myInjectedViewModel).isNotNull()
assertThat(activity.myNestedInjectedViewModel).isNotNull()
+ assertThat(activity.myAssistedInjectedViewModel).isNotNull()
+ assertThat(activity.myAssistedInjectedViewModel.bar).isEqualTo(42)
}
}
}
@@ -59,5 +62,13 @@
val myViewModel by viewModels<MyViewModel>()
val myInjectedViewModel by viewModels<MyInjectedViewModel>()
val myNestedInjectedViewModel by viewModels<TopClass.MyNestedInjectedViewModel>()
+ val myAssistedInjectedViewModel by viewModels<MyAssistedInjectedViewModel>(
+ extrasProducer = {
+ defaultViewModelCreationExtras.withCreationCallback<
+ MyAssistedInjectedViewModel.Factory> { factory ->
+ factory.create(42)
+ }
+ }
+ )
}
}
diff --git a/hilt/integration-tests/viewmodelapp/src/androidTest/java/androidx/hilt/integration/viewmodelapp/MyViewModels.kt b/hilt/integration-tests/viewmodelapp/src/androidTest/java/androidx/hilt/integration/viewmodelapp/MyViewModels.kt
index 040777a..2b40fe7 100644
--- a/hilt/integration-tests/viewmodelapp/src/androidTest/java/androidx/hilt/integration/viewmodelapp/MyViewModels.kt
+++ b/hilt/integration-tests/viewmodelapp/src/androidTest/java/androidx/hilt/integration/viewmodelapp/MyViewModels.kt
@@ -21,6 +21,9 @@
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@@ -32,6 +35,18 @@
@HiltViewModel
class MyInjectedViewModel @Inject constructor(foo: Foo) : ViewModel()
+@Suppress("UNUSED_PARAMETER")
+@HiltViewModel(assistedFactory = MyAssistedInjectedViewModel.Factory::class)
+class MyAssistedInjectedViewModel @AssistedInject constructor(
+ foo: Foo,
+ @Assisted val bar: Int
+) : ViewModel() {
+ @AssistedFactory
+ interface Factory {
+ fun create(bar: Int): MyAssistedInjectedViewModel
+ }
+}
+
object TopClass {
@Suppress("UNUSED_PARAMETER")
@HiltViewModel
diff --git a/libraryversions.toml b/libraryversions.toml
index d9efb69..17bcc57 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -15,16 +15,19 @@
BROWSER = "1.8.0-beta01"
BUILDSRC_TESTS = "1.0.0-alpha01"
CAMERA = "1.4.0-alpha03"
+CAMERA_EFFECTS = "1.0.0-alpha01"
+CAMERA_MLKIT_VISION = "1.4.0-alpha03"
CAMERA_PIPE = "1.0.0-alpha01"
CAMERA_TESTING = "1.0.0-alpha01"
+CAMERA_VIEWFINDER = "1.4.0-alpha03"
CARDVIEW = "1.1.0-alpha01"
CAR_APP = "1.7.0-alpha01"
COLLECTION = "1.4.0-beta02"
COMPOSE = "1.7.0-alpha01"
COMPOSE_COMPILER = "1.5.6"
COMPOSE_MATERIAL3 = "1.2.0-beta01"
-COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha02"
-COMPOSE_MATERIAL3_ADAPTIVE_NAVIGATION_SUITE = "1.0.0-alpha01"
+COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha03"
+COMPOSE_MATERIAL3_ADAPTIVE_NAVIGATION_SUITE = "1.0.0-alpha02"
COMPOSE_MATERIAL3_COMMON = "1.0.0-alpha01"
COMPOSE_RUNTIME_TRACING = "1.0.0-beta01"
CONSTRAINTLAYOUT = "2.2.0-alpha13"
@@ -96,7 +99,7 @@
MEDIA = "1.7.0-rc01"
MEDIA2 = "1.3.0-rc01"
MEDIAROUTER = "1.7.0-beta01"
-METRICS = "1.0.0-alpha05"
+METRICS = "1.0.0-beta01"
NAVIGATION = "2.8.0-alpha01"
PAGING = "3.3.0-alpha03"
PALETTE = "1.1.0-alpha01"
@@ -186,6 +189,9 @@
BUILDSRC_TESTS_MAX_DEP_VERSIONS_DEP = { group = "androidx.buildSrc-tests-max-dep-versions-dep", atomicGroupVersion = "versions.BUILDSRC_TESTS", overrideInclude = [ ":buildSrc-tests:max-dep-versions:buildSrc-tests-max-dep-versions-dep" ] }
BUILDSRC_TESTS_MAX_DEP_VERSIONS_MAIN = { group = "androidx.buildSrc-tests-max-dep-versions-main", atomicGroupVersion = "versions.BUILDSRC_TESTS", overrideInclude = [ ":buildSrc-tests:max-dep-versions:buildSrc-tests-max-dep-versions-main" ] }
CAMERA = { group = "androidx.camera", atomicGroupVersion = "versions.CAMERA" }
+CAMERA_EFFECTS = { group = "androidx.camera", atomicGroupVersion = "versions.CAMERA_EFFECTS", overrideInclude = [ ":camera:camera-effects" ] }
+CAMERA_MLKIT_VISION = { group = "androidx.camera", atomicGroupVersion = "versions.CAMERA_MLKIT_VISION", overrideInclude = [ ":camera:camera-mlkit-vision" ] }
+CAMERA_VIEWFINDER = { group = "androidx.camera", atomicGroupVersion = "versions.CAMERA_VIEWFINDER", overrideInclude = [ ":camera:camera-viewfinder", ":camera:camera-viewfinder-compose", ":camera:camera-viewfinder-core" ] }
CARDVIEW = { group = "androidx.cardview", atomicGroupVersion = "versions.CARDVIEW" }
CAR_APP = { group = "androidx.car.app", atomicGroupVersion = "versions.CAR_APP" }
COLLECTION = { group = "androidx.collection", atomicGroupVersion = "versions.COLLECTION" }
diff --git a/metrics/metrics-performance/api/1.0.0-beta01.txt b/metrics/metrics-performance/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..319e59a
--- /dev/null
+++ b/metrics/metrics-performance/api/1.0.0-beta01.txt
@@ -0,0 +1,76 @@
+// Signature format: 4.0
+package androidx.metrics.performance {
+
+ public class FrameData {
+ ctor public FrameData(long frameStartNanos, long frameDurationUiNanos, boolean isJank, java.util.List<androidx.metrics.performance.StateInfo> states);
+ method public androidx.metrics.performance.FrameData copy();
+ method public final long getFrameDurationUiNanos();
+ method public final long getFrameStartNanos();
+ method public final java.util.List<androidx.metrics.performance.StateInfo> getStates();
+ method public final boolean isJank();
+ property public final long frameDurationUiNanos;
+ property public final long frameStartNanos;
+ property public final boolean isJank;
+ property public final java.util.List<androidx.metrics.performance.StateInfo> states;
+ }
+
+ public class FrameDataApi24 extends androidx.metrics.performance.FrameData {
+ ctor public FrameDataApi24(long frameStartNanos, long frameDurationUiNanos, long frameDurationCpuNanos, boolean isJank, java.util.List<androidx.metrics.performance.StateInfo> states);
+ method public final long getFrameDurationCpuNanos();
+ property public final long frameDurationCpuNanos;
+ }
+
+ public final class FrameDataApi31 extends androidx.metrics.performance.FrameDataApi24 {
+ ctor public FrameDataApi31(long frameStartNanos, long frameDurationUiNanos, long frameDurationCpuNanos, long frameDurationTotalNanos, long frameOverrunNanos, boolean isJank, java.util.List<androidx.metrics.performance.StateInfo> states);
+ method public long getFrameDurationTotalNanos();
+ method public long getFrameOverrunNanos();
+ property public final long frameDurationTotalNanos;
+ property public final long frameOverrunNanos;
+ }
+
+ public final class JankStats {
+ method @UiThread public static androidx.metrics.performance.JankStats createAndTrack(android.view.Window window, androidx.metrics.performance.JankStats.OnFrameListener frameListener);
+ method public float getJankHeuristicMultiplier();
+ method public boolean isTrackingEnabled();
+ method public void setJankHeuristicMultiplier(float);
+ method @UiThread public void setTrackingEnabled(boolean);
+ property public final boolean isTrackingEnabled;
+ property public final float jankHeuristicMultiplier;
+ field public static final androidx.metrics.performance.JankStats.Companion Companion;
+ }
+
+ public static final class JankStats.Companion {
+ method @UiThread public androidx.metrics.performance.JankStats createAndTrack(android.view.Window window, androidx.metrics.performance.JankStats.OnFrameListener frameListener);
+ }
+
+ public static fun interface JankStats.OnFrameListener {
+ method public void onFrame(androidx.metrics.performance.FrameData volatileFrameData);
+ }
+
+ public final class PerformanceMetricsState {
+ method @UiThread public static androidx.metrics.performance.PerformanceMetricsState.Holder getHolderForHierarchy(android.view.View view);
+ method public void putSingleFrameState(String key, String value);
+ method public void putState(String key, String value);
+ method public void removeState(String key);
+ field public static final androidx.metrics.performance.PerformanceMetricsState.Companion Companion;
+ }
+
+ public static final class PerformanceMetricsState.Companion {
+ method @UiThread public androidx.metrics.performance.PerformanceMetricsState.Holder getHolderForHierarchy(android.view.View view);
+ }
+
+ public static final class PerformanceMetricsState.Holder {
+ method public androidx.metrics.performance.PerformanceMetricsState? getState();
+ property public final androidx.metrics.performance.PerformanceMetricsState? state;
+ }
+
+ public final class StateInfo {
+ ctor public StateInfo(String key, String value);
+ method public String getKey();
+ method public String getValue();
+ property public final String key;
+ property public final String value;
+ }
+
+}
+
diff --git a/metrics/metrics-performance/api/res-1.0.0-beta01.txt b/metrics/metrics-performance/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/metrics/metrics-performance/api/res-1.0.0-beta01.txt
diff --git a/metrics/metrics-performance/api/restricted_1.0.0-beta01.txt b/metrics/metrics-performance/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..319e59a
--- /dev/null
+++ b/metrics/metrics-performance/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,76 @@
+// Signature format: 4.0
+package androidx.metrics.performance {
+
+ public class FrameData {
+ ctor public FrameData(long frameStartNanos, long frameDurationUiNanos, boolean isJank, java.util.List<androidx.metrics.performance.StateInfo> states);
+ method public androidx.metrics.performance.FrameData copy();
+ method public final long getFrameDurationUiNanos();
+ method public final long getFrameStartNanos();
+ method public final java.util.List<androidx.metrics.performance.StateInfo> getStates();
+ method public final boolean isJank();
+ property public final long frameDurationUiNanos;
+ property public final long frameStartNanos;
+ property public final boolean isJank;
+ property public final java.util.List<androidx.metrics.performance.StateInfo> states;
+ }
+
+ public class FrameDataApi24 extends androidx.metrics.performance.FrameData {
+ ctor public FrameDataApi24(long frameStartNanos, long frameDurationUiNanos, long frameDurationCpuNanos, boolean isJank, java.util.List<androidx.metrics.performance.StateInfo> states);
+ method public final long getFrameDurationCpuNanos();
+ property public final long frameDurationCpuNanos;
+ }
+
+ public final class FrameDataApi31 extends androidx.metrics.performance.FrameDataApi24 {
+ ctor public FrameDataApi31(long frameStartNanos, long frameDurationUiNanos, long frameDurationCpuNanos, long frameDurationTotalNanos, long frameOverrunNanos, boolean isJank, java.util.List<androidx.metrics.performance.StateInfo> states);
+ method public long getFrameDurationTotalNanos();
+ method public long getFrameOverrunNanos();
+ property public final long frameDurationTotalNanos;
+ property public final long frameOverrunNanos;
+ }
+
+ public final class JankStats {
+ method @UiThread public static androidx.metrics.performance.JankStats createAndTrack(android.view.Window window, androidx.metrics.performance.JankStats.OnFrameListener frameListener);
+ method public float getJankHeuristicMultiplier();
+ method public boolean isTrackingEnabled();
+ method public void setJankHeuristicMultiplier(float);
+ method @UiThread public void setTrackingEnabled(boolean);
+ property public final boolean isTrackingEnabled;
+ property public final float jankHeuristicMultiplier;
+ field public static final androidx.metrics.performance.JankStats.Companion Companion;
+ }
+
+ public static final class JankStats.Companion {
+ method @UiThread public androidx.metrics.performance.JankStats createAndTrack(android.view.Window window, androidx.metrics.performance.JankStats.OnFrameListener frameListener);
+ }
+
+ public static fun interface JankStats.OnFrameListener {
+ method public void onFrame(androidx.metrics.performance.FrameData volatileFrameData);
+ }
+
+ public final class PerformanceMetricsState {
+ method @UiThread public static androidx.metrics.performance.PerformanceMetricsState.Holder getHolderForHierarchy(android.view.View view);
+ method public void putSingleFrameState(String key, String value);
+ method public void putState(String key, String value);
+ method public void removeState(String key);
+ field public static final androidx.metrics.performance.PerformanceMetricsState.Companion Companion;
+ }
+
+ public static final class PerformanceMetricsState.Companion {
+ method @UiThread public androidx.metrics.performance.PerformanceMetricsState.Holder getHolderForHierarchy(android.view.View view);
+ }
+
+ public static final class PerformanceMetricsState.Holder {
+ method public androidx.metrics.performance.PerformanceMetricsState? getState();
+ property public final androidx.metrics.performance.PerformanceMetricsState? state;
+ }
+
+ public final class StateInfo {
+ ctor public StateInfo(String key, String value);
+ method public String getKey();
+ method public String getValue();
+ property public final String key;
+ property public final String value;
+ }
+
+}
+
diff --git a/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt b/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
index 18928bb..1e9c73d 100644
--- a/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
+++ b/metrics/metrics-performance/src/androidTest/java/androidx/metrics/performance/test/JankStatsTest.kt
@@ -16,7 +16,6 @@
package androidx.metrics.performance.test
import android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
-import android.util.Log
import android.view.Choreographer
import androidx.metrics.performance.FrameData
import androidx.metrics.performance.FrameDataApi24
@@ -188,9 +187,13 @@
initFramePipeline()
var numSecondListenerCalls = 0
- val secondListenerStates = mutableListOf<StateInfo>()
+ val secondListenerFrameData = mutableListOf<FrameData>()
val secondListener = OnFrameListener { volatileFrameData ->
- secondListenerStates.addAll(volatileFrameData.states)
+ // Sometimes we get a late frame arrival while we are checking the data.
+ // This sync call prevents ConcurrentModException
+ synchronized(secondListenerFrameData) {
+ secondListenerFrameData.add(volatileFrameData.copy())
+ }
numSecondListenerCalls++
if (numSecondListenerCalls >= NUM_FRAMES) {
secondListenerLatch.countDown()
@@ -201,19 +204,30 @@
scenario.onActivity { _ ->
jankStats2 = JankStats.createAndTrack(delayedActivity.window, secondListener)
}
- val testState = StateInfo("Testing State", "sampleState")
- metricsState.putSingleFrameState(testState.key, testState.value)
+ resetFrameStates()
+ val testState = StateInfo("Testing State", "sampleState")
+ val insertTime = System.nanoTime()
+ metricsState.putSingleFrameState(testState.key, testState.value)
// in case earlier frames arrive before our test begins
- secondListenerStates.clear()
+ secondListenerFrameData.clear()
secondListenerLatch = CountDownLatch(1)
- latchedListener.reset()
+
runDelayTest(frameDelay, NUM_FRAMES, latchedListener)
secondListenerLatch.await(frameDelay * NUM_FRAMES + 1000L, TimeUnit.MILLISECONDS)
- val jankData: FrameData = latchedListener.jankData[0]
assertTrue("No calls to second listener", numSecondListenerCalls > 0)
- assertEquals(listOf(testState), jankData.states)
- assertEquals(listOf(testState), secondListenerStates)
+
+ // Test in both jankData.states and secondListenerStates:
+ // - Ensure that testState exists in the list of states
+ // - Ensure that frameStart + for that frameData is greater than insertTime
+ // - Ensure that that state exists only once in the list
+
+ assertEquals("Should be exactly one occurrence of SingleFrameState",
+ 1, checkSingleStateExistence(testState, latchedListener.jankData, insertTime))
+ synchronized(secondListenerFrameData) {
+ assertEquals("Should be exactly one occurrence of SingleFrameState", 1,
+ checkSingleStateExistence(testState, secondListenerFrameData, insertTime))
+ }
jankStats2.isTrackingEnabled = false
numSecondListenerCalls = 0
@@ -246,7 +260,31 @@
listenerPostingThread.start()
// add listeners concurrently - no asserts here, just testing whether we
// avoid any concurrency issues with adding and using multiple listeners
- runDelayTest(frameDelay, NUM_FRAMES * 100, latchedListener)
+ runDelayTest(frameDelay, NUM_FRAMES, latchedListener)
+ }
+
+ /**
+ * Ensure that there is only one occurrence of a given StateInfo entry. This is used to
+ * validate that SingleFrameState does the right thing - inserts into the current frame and
+ * removes it immediately.
+ */
+ fun checkSingleStateExistence(
+ singleState: StateInfo,
+ frameData: List<FrameData>,
+ insertionTimeNanos: Long
+ ): Int {
+ var numOccurrences = 0
+ for (item in frameData) {
+ for (state in item.states) {
+ if (state.equals(singleState)) {
+ numOccurrences++
+ assertTrue("State be added before frame end time",
+ (item.frameStartNanos + item.frameDurationUiNanos) >
+ insertionTimeNanos)
+ }
+ }
+ }
+ return numOccurrences
}
@Test
@@ -279,7 +317,7 @@
initFramePipeline()
- resetFrameStateData()
+ resetFrameStates()
val state0 = StateInfo("Testing State 0", "sampleStateA")
val state1 = StateInfo("Testing State 1", "sampleStateB")
@@ -318,12 +356,12 @@
}
// reset and clear states
- resetFrameStateData()
+ resetFrameStates()
latchedListener.reset()
metricsState.removeState(state0.key)
metricsState.removeState(state1.key)
- runDelayTest(frameDelay, 1, latchedListener)
+ syncFrameStates()
item0 = latchedListener.jankData[0]
assertEquals(
"States should be empty after being cleared, but got ${item0.states}",
@@ -335,16 +373,16 @@
val state4 = Pair("Testing State 4", "sampleStateE")
metricsState.putState(state3.first, state3.second)
metricsState.putState(state4.first, state4.second)
- runDelayTest(frameDelay, 1, latchedListener)
+ syncFrameStates()
item0 = latchedListener.jankData[0]
assertEquals("states: ${item0.states}", 2, item0.states.size)
latchedListener.reset()
// Test removal of state3 and replacement of state4
- resetFrameStateData()
+ resetFrameStates()
metricsState.removeState(state3.first)
metricsState.putState(state4.first, "sampleStateF")
- runDelayTest(frameDelay, 1, latchedListener)
+ syncFrameStates()
item0 = latchedListener.jankData[0]
assertEquals("states: ${item0.states}", 1, item0.states.size)
assertEquals(state4.first, item0.states[0].key)
@@ -459,8 +497,23 @@
JankStatsTest.FrameStateInputData(
addStates = listOf("stateNameA" to "0", "stateNameA" to "1"),
),
+ // 12-16: empty, just to allow extra frames to pulse
+ // Run more than the exact number of frames we have states for. Sometimes the system
+ // isn't done running all of the frames in which these states should go by the
+ // time we've run that number of frames.
+ JankStatsTest.FrameStateInputData(),
+ JankStatsTest.FrameStateInputData(),
+ JankStatsTest.FrameStateInputData(),
+ JankStatsTest.FrameStateInputData(),
+ JankStatsTest.FrameStateInputData(),
)
- // testData will hold input (above) plus expected results
+ // expectedResults holds the values of the states which we would expect to see in
+ // a normal test run.
+ // This list is currently unused due to flaky test issues related to race conditions
+ // between the test/UI thread and the FrameMetrics thread. It's difficult to
+ // deterministically insert and then test against data landing in specific frames.
+ // Leaving this here for future reference if we want to make the tests more robust
+ // eventually.
val expectedResults = listOf(
mapOf("stateNameA" to "0"),
mapOf("stateNameA" to "0"),
@@ -476,39 +529,69 @@
mapOf("stateNameA" to "1"),
)
- resetFrameStateData()
+ resetFrameStates()
runDelayTest(frameDelay = 0, numFrames = perFrameStateData.size,
latchedListener, perFrameStateData)
// There might be one or two dropped frames, check that we have nearly the number
// expected
- assertTrue("There should be at least ${expectedResults.size - 2} frames of data",
+ assertTrue("There should be at least ${expectedResults.size - 2} frames of data" +
+ "but there were ${latchedListener.jankData.size}",
(latchedListener.jankData.size > expectedResults.size - 2))
- /*
- Ideally, we would check each frame's result states against the expected results.
- But the system sometimes drops frames, causing the jankData to be a subset of
- the expectedResults set from above. This is fine, for testing purposes, but that
- means we should check the current result against the expected result of this and
- the next frame, to account for these skips. when this happens, we increment the
- expected index since all results will be offset by that skip.
- */
- var expectedIndex = 0
- var resultIndex = 0
- while (expectedIndex < expectedResults.size &&
- resultIndex < latchedListener.jankData.size) {
- val testResultStates = latchedListener.jankData[resultIndex].states
- // Test against this and next expected result, in case system skipped a frame
- var matched = checkFrameStates(expectedResults[expectedIndex], testResultStates)
- if (!matched) {
- expectedIndex++
- matched = checkFrameStates(expectedResults[expectedIndex], testResultStates)
+ // A more flexible way to check for the above, accounting for very minor frame boundary
+ // collisions which could cause states to be off by a frame or so, is to check
+ // the sequence of values that any state goes through in the results:
+ // stateNameA: 0, 1, 2, none, 0, none, 1
+ // stateNameB: none, 10, none
+ // stateNameC: none, 100, none
+ // Even this runs into problems, however, so disabling checks in this test for now.
+
+// checkComplexFrameStates("stateNameA",
+// arrayOf("0", "1", "2", null, "0", null, "1"))
+// checkComplexFrameStates("stateNameB", arrayOf<String?>(null, "10", null))
+// checkComplexFrameStates("stateNameC", arrayOf<String?>(null, "100", null))
+ }
+
+ /**
+ * Currently unused - this function checks the given frame data against a set of known
+ * states, in order. This works in general, but occasionally one of the states is wrong
+ * (due to multi-threaded race conditions with the frameMetrics thread, I suspect), so
+ * not used for now. Leaving it here in case we want more robust testing in the future.
+ */
+ private fun checkComplexFrameStates(stateName: String, stateValues: Array<String?>) {
+ var stateValuesIndex = 0
+ var currStateValue: String? = "placeholder"
+ // Iterating on the frame data has potential ConcurrentModificationException issues since
+ // the thread placing data in that array is running asynchronously. It should be done
+ // by the time we check the data, but may still be running anyway
+ for (frameData in latchedListener.jankData) {
+ val nextStateValue = stateValues[stateValuesIndex]
+ var matched = false
+ for (state in frameData.states) {
+ if (state.key == stateName) {
+ if (nextStateValue == state.value) {
+ matched = true
+ ++stateValuesIndex
+ currStateValue = state.value
+ break
+ } else {
+ assertEquals("Next state value not correct",
+ currStateValue, state.value)
+ matched = true
+ }
+ }
}
- assertTrue("Expected states do not match $testResultStates at frame " +
- "$expectedIndex", matched)
- expectedIndex++
- resultIndex++
+ if (!matched) {
+ if (currStateValue != null) {
+ assertEquals(nextStateValue, null)
+ currStateValue = null
+ ++stateValuesIndex
+ }
+ }
+ if (stateValuesIndex >= stateValues.size) break
}
+ assertEquals(stateValuesIndex, stateValues.size)
}
private fun checkFrameStates(
@@ -530,32 +613,48 @@
* pre-date the current time, which is when we might be setting/removing state.
*
* To ensure that the right thing happens, call this function prior to setting any frame state.
- * It will run frames through the system until the frameData start timeis after the
- * current time when this function is called.
+ * It will run frames through the system until the frameData start time is after the
+ * current time when this function is called. Then it will reset [latchedListener] to clear
+ * it of any state data just processed.
*/
- private fun resetFrameStateData() {
- val currentNanos = System.nanoTime()
- // failsafe - limit the iterations, don't want to loop forever
- var numAttempts = 0
+ private fun resetFrameStates() {
try {
- while (numAttempts < 100) {
- runDelayTest(0, 1, latchedListener)
- if (latchedListener.jankData.size > 0) {
- if (latchedListener.jankData[0].frameStartNanos > currentNanos) {
- return
- }
- }
- Log.d("JankStatsTest", "resetFrameStateData attempt $numAttempts:" +
- "frame start < currentTime: " +
- "${latchedListener.jankData[0].frameStartNanos}, $currentNanos")
- latchedListener.reset()
- numAttempts++
- }
+ syncFrameStates()
} finally {
latchedListener.reset()
}
}
+ /**
+ * When we add or remove a frame state, it records the time for that request, then waits for
+ * a later frame starting after that time to actually add/remove those states. This sometimes
+ * breaks when there is an existing frame still to be processed and we only wait for that
+ * single frame to pulse. Because the frame started before the state request(s), they are not
+ * added/removed as expected and tests can fail.
+ *
+ * The solution is to pulse frames until we see a frame happen after the current time, which
+ * should be sufficient (since this function should only be called after the state requests
+ * have been made, thus before the current time, thus before the frame we are waiting for).
+ * This function does just that; pulses frames until one has a start time after the time
+ * when this function is called. Then we record the state settings for that frame and return.
+ */
+ private fun syncFrameStates() {
+ val currentNanos = System.nanoTime()
+ // failsafe - limit the iterations, don't want to loop forever. Typically we will
+ // only run for one or two frames.
+ var numAttempts = 0
+ while (numAttempts < 100) {
+ runDelayTest(0, 1, latchedListener)
+ if (latchedListener.jankData.size > 0) {
+ if (latchedListener.jankData[0].frameStartNanos > currentNanos) {
+ return
+ }
+ }
+ latchedListener.reset()
+ numAttempts++
+ }
+ }
+
private fun runDelayTest(
frameDelay: Int,
numFrames: Int,
diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt
index 60cfb2c..bb70163 100644
--- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt
+++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt
@@ -338,6 +338,51 @@
)
}
+ @Test
+ fun testPopToInitialInFragmentStarted() = withNavigationActivity {
+ navController.graph = navController.createGraph("first") {
+ fragment<EmptyFragment>("first")
+ fragment<PopToInitialInOnStartedFragment>("second")
+ fragment<EmptyFragment>("third")
+ }
+ navController.navigate("second")
+
+ val fm = supportFragmentManager.findFragmentById(R.id.nav_host)?.childFragmentManager
+ fm?.executePendingTransactions()
+
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("third")
+ assertThat(navController.visibleEntries.value).containsExactly(
+ navController.currentBackStackEntry
+ )
+ }
+
+ @Test
+ fun testPopInitialAndNavigateInitial() = withNavigationActivity {
+ navController.graph = navController.createGraph("first") {
+ fragment<EmptyFragment>("first")
+ fragment<EmptyFragment>("second")
+ }
+ val fm = supportFragmentManager.findFragmentById(R.id.nav_host)?.childFragmentManager
+ fm?.executePendingTransactions()
+
+ // pop first as initial entry, then navigate to second which is the new initial entry
+ navController.navigate(
+ "second",
+ navOptions { popUpTo("first") { inclusive = true } }
+ )
+
+ fm?.executePendingTransactions()
+
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("second")
+ assertThat(navController.visibleEntries.value).containsExactly(
+ navController.currentBackStackEntry
+ )
+ val navigator = navController.navigatorProvider.getNavigator(FragmentNavigator::class.java)
+ // the popUpTo and following navigation to second are both isolated
+ // fragment operations (initial entry) so neither of them would trigger a callback from FM
+ assertThat(navigator.pendingOps).isEmpty()
+ }
+
@LargeTest
@Test
fun testSystemBackPressAfterPopUpToStartDestinationOffBackStack() = withNavigationActivity {
@@ -453,6 +498,17 @@
}
}
+class PopToInitialInOnStartedFragment : Fragment(R.layout.strict_view_fragment) {
+ override fun onStart() {
+ super.onStart()
+ findNavController().navigate("third") {
+ popUpTo("first") {
+ inclusive = true
+ }
+ }
+ }
+}
+
class TestDialogFragment : DialogFragment() {
val dialogs = mutableListOf<Dialog>()
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt
index cbc54c65..f58d911 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt
@@ -338,7 +338,14 @@
addPendingOps(incomingEntry.id)
}
// add pending ops here before any animation (if present) starts
- poppedList.filter { it.id != initialEntry.id }.forEach { entry ->
+ poppedList.filter { entry ->
+ // normally we don't add initialEntry to pending ops because the adding/popping
+ // of an isolated fragment does not trigger onBackStackCommitted. But if initial
+ // entry was already added to pendingOps, it was likely an incomingEntry that now
+ // needs to be popped, so we need to overwrite isPop to true here.
+ pendingOps.asSequence().map { it.first }.contains(entry.id) ||
+ entry.id != initialEntry.id
+ }.forEach { entry ->
addPendingOps(entry.id, isPop = true)
}
diff --git a/playground-common/playground-plugin/build.gradle b/playground-common/playground-plugin/build.gradle
index 0d973f1..cc6735b 100644
--- a/playground-common/playground-plugin/build.gradle
+++ b/playground-common/playground-plugin/build.gradle
@@ -21,7 +21,7 @@
dependencies {
implementation(project(":shared"))
- implementation("com.gradle:gradle-enterprise-gradle-plugin:3.15.1")
+ implementation("com.gradle:gradle-enterprise-gradle-plugin:3.16")
implementation("com.gradle:common-custom-user-data-gradle-plugin:1.12")
implementation("supportBuildSrc:private")
implementation("supportBuildSrc:public")
diff --git a/privacysandbox/ui/ui-client/build.gradle b/privacysandbox/ui/ui-client/build.gradle
index 7c21fd7..d278278 100644
--- a/privacysandbox/ui/ui-client/build.gradle
+++ b/privacysandbox/ui/ui-client/build.gradle
@@ -30,6 +30,7 @@
implementation("androidx.lifecycle:lifecycle-common:2.2.0")
implementation("androidx.privacysandbox.sdkruntime:sdkruntime-client:1.0.0-alpha08")
+ implementation("androidx.customview:customview-poolingcontainer:1.0.0-alpha01")
implementation project(path: ':privacysandbox:ui:ui-core')
androidTestImplementation(project(":internal-testutils-runtime"))
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
index e81928d..2669711 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
@@ -33,6 +33,11 @@
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
+import androidx.customview.poolingcontainer.PoolingContainerListener
+import androidx.customview.poolingcontainer.addPoolingContainerListener
+import androidx.customview.poolingcontainer.isPoolingContainer
+import androidx.customview.poolingcontainer.isWithinPoolingContainer
+import androidx.customview.poolingcontainer.removePoolingContainerListener
import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState.Active
import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState.Idle
import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState.Loading
@@ -99,6 +104,11 @@
class SandboxedSdkView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
ViewGroup(context, attrs) {
+ // TODO(b/284147223): Remove this logic in V+
+ private val surfaceView = SurfaceView(context).apply {
+ visibility = GONE
+ }
+
// This will only be invoked when the content view has been set and the window is attached.
private val surfaceChangedCallback = object : SurfaceHolder.Callback {
override fun surfaceCreated(p0: SurfaceHolder) {
@@ -131,6 +141,8 @@
private var previousHeight = -1
private var currentClippingBounds = Rect()
internal val stateListenerManager: StateListenerManager = StateListenerManager()
+ private var poolingContainerChild: View? = null
+ private var poolingContainerListener = PoolingContainerListener {}
/**
* Adds a state change listener to the UI session and immediately reports the current
@@ -319,6 +331,9 @@
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ if (this.isWithinPoolingContainer) {
+ attachPoolingContainerListener()
+ }
// We will not call client?.notifyResized for the first onLayout call
// and the case in which the width and the height remain unchanged.
if ((previousWidth != (right - left) || previousHeight != (bottom - top)) &&
@@ -337,16 +352,51 @@
checkClientOpenSession()
}
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- CompatImpl.deriveInputTokenAndOpenSession(context, this)
- }
-
- override fun onDetachedFromWindow() {
+ private fun closeClient() {
client?.close()
client = null
windowInputToken = null
removeCallbacks()
+ }
+
+ private fun attachPoolingContainerListener() {
+ val listener = PoolingContainerListener {
+ closeClient()
+ poolingContainerChild
+ ?.removePoolingContainerListener(poolingContainerListener)
+ }
+
+ var currentView = this as View
+ var parentView = parent
+
+ while (parentView != null && !(parentView as View).isPoolingContainer) {
+ currentView = parentView
+ parentView = currentView.parent
+ }
+
+ if (currentView == poolingContainerChild) {
+ return
+ }
+
+ poolingContainerChild
+ ?.removePoolingContainerListener(poolingContainerListener)
+ currentView.addPoolingContainerListener(listener)
+ poolingContainerChild = currentView
+ poolingContainerListener = listener
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ if (this.isWithinPoolingContainer) {
+ attachPoolingContainerListener()
+ }
+ CompatImpl.deriveInputTokenAndOpenSession(context, this)
+ }
+
+ override fun onDetachedFromWindow() {
+ if (!this.isWithinPoolingContainer) {
+ closeClient()
+ }
super.onDetachedFromWindow()
}
diff --git a/privacysandbox/ui/ui-tests/build.gradle b/privacysandbox/ui/ui-tests/build.gradle
index 62b367f..76d7dae 100644
--- a/privacysandbox/ui/ui-tests/build.gradle
+++ b/privacysandbox/ui/ui-tests/build.gradle
@@ -27,6 +27,7 @@
implementation project(path: ':privacysandbox:ui:ui-client')
implementation project(path: ':privacysandbox:ui:ui-provider')
implementation(libs.kotlinStdlib)
+ implementation 'androidx.recyclerview:recyclerview:1.3.2'
androidTestImplementation(project(":internal-testutils-runtime"))
androidTestImplementation(libs.junit)
@@ -38,6 +39,7 @@
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.truth)
androidTestImplementation project(path: ':appcompat:appcompat')
+ androidTestImplementation project(':recyclerview:recyclerview')
def multidex_version = "2.0.1"
implementation "androidx.multidex:multidex:$multidex_version"
}
diff --git a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
index ec0dc24..bdd7717 100644
--- a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
+++ b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
@@ -35,6 +35,8 @@
import androidx.privacysandbox.ui.core.BackwardCompatUtil
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
import androidx.privacysandbox.ui.provider.toCoreLibInfo
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
@@ -77,8 +79,10 @@
private val context = InstrumentationRegistry.getInstrumentation().context
private lateinit var view: SandboxedSdkView
+ private lateinit var recyclerView: RecyclerView
private lateinit var stateChangeListener: TestStateChangeListener
private lateinit var errorLatch: CountDownLatch
+ private lateinit var linearLayout: LinearLayout
@Before
fun setup() {
@@ -89,10 +93,11 @@
activityScenarioRule.withActivity {
view = SandboxedSdkView(context)
+ recyclerView = RecyclerView(context)
errorLatch = CountDownLatch(1)
stateChangeListener = TestStateChangeListener(errorLatch)
view.addStateChangedListener(stateChangeListener)
- val linearLayout = LinearLayout(context)
+ linearLayout = LinearLayout(context)
linearLayout.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
@@ -100,6 +105,8 @@
setContentView(linearLayout)
view.layoutParams = LinearLayout.LayoutParams(INITIAL_WIDTH, INITIAL_HEIGHT)
linearLayout.addView(view)
+ linearLayout.addView(recyclerView)
+ recyclerView.setLayoutManager(LinearLayoutManager(context))
}
}
@@ -309,6 +316,175 @@
assertThat(client.hashCode()).isEqualTo(client.hashCode())
}
+ @Test
+ fun testPoolingContainerListener_AllViewsRemovedFromContainer() {
+ // TODO(b/309848703): Stop skipping this for backwards compat flow
+ assumeTrue(!invokeBackwardsCompatFlow)
+
+ val adapter = createRecyclerViewTestAdapterAndWaitForChildrenToBeActive(
+ isNestedView = false)
+
+ activityScenarioRule.withActivity {
+ recyclerView.layoutManager!!.removeAllViews()
+ }
+
+ adapter.waitForViewsToBeDetached()
+ adapter.ensureChildrenDoNotBecomeIdleFromActive()
+ }
+
+ @Test
+ fun testPoolingContainerListener_ContainerRemovedFromLayout() {
+ // TODO(b/309848703): Stop skipping this for backwards compat flow
+ assumeTrue(!invokeBackwardsCompatFlow)
+
+ val adapter = createRecyclerViewTestAdapterAndWaitForChildrenToBeActive(
+ isNestedView = true)
+
+ activityScenarioRule.withActivity {
+ linearLayout.removeView(recyclerView)
+ }
+
+ adapter.ensureAllChildrenBecomeIdleFromActive()
+ }
+
+ @Test
+ fun testPoolingContainerListener_ViewWithinAnotherView_AllViewsRemovedFromContainer() {
+ // TODO(b/309848703): Stop skipping this for backwards compat flow
+ assumeTrue(!invokeBackwardsCompatFlow)
+
+ val adapter = createRecyclerViewTestAdapterAndWaitForChildrenToBeActive(
+ isNestedView = false)
+
+ activityScenarioRule.withActivity {
+ recyclerView.layoutManager!!.removeAllViews()
+ }
+
+ adapter.waitForViewsToBeDetached()
+ adapter.ensureChildrenDoNotBecomeIdleFromActive()
+ }
+
+ @Test
+ fun testPoolingContainerListener_ViewWithinAnotherView_ContainerRemovedFromLayout() {
+ // TODO(b/309848703): Stop skipping this for backwards compat flow
+ assumeTrue(!invokeBackwardsCompatFlow)
+
+ val adapter = createRecyclerViewTestAdapterAndWaitForChildrenToBeActive(
+ isNestedView = true)
+
+ activityScenarioRule.withActivity {
+ linearLayout.removeView(recyclerView)
+ }
+
+ adapter.ensureAllChildrenBecomeIdleFromActive()
+ }
+
+ fun createRecyclerViewTestAdapterAndWaitForChildrenToBeActive(isNestedView: Boolean):
+ RecyclerViewTestAdapter {
+ val adapter = RecyclerViewTestAdapter(context, isNestedView)
+ activityScenarioRule.withActivity {
+ recyclerView.setAdapter(adapter)
+ }
+
+ adapter.waitForViewsToBeAttached()
+
+ for (i in 0 until recyclerView.childCount) {
+ lateinit var childView: SandboxedSdkView
+ if (isNestedView) {
+ childView = (recyclerView.getChildAt(i) as ViewGroup)
+ .getChildAt(0) as SandboxedSdkView
+ } else {
+ childView = recyclerView.getChildAt(i) as SandboxedSdkView
+ }
+ createAdapterAndWaitToBeActive(true, childView)
+ }
+
+ adapter.ensureAllChildrenBecomeActive()
+ return adapter
+ }
+
+ class RecyclerViewTestAdapter(
+ private val context: Context,
+ val isNestedView: Boolean = false,
+ ) :
+ RecyclerView.Adapter<RecyclerViewTestAdapter.ViewHolder>() {
+ class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
+ var numberOfSandboxedSdkViews = 0
+ val items = 5
+ private val activeLatch = CountDownLatch(items)
+ // The session will first be idle -> active -> idle in
+ // our tests, hence the count is items*2
+ private val idleLatch = CountDownLatch(items * 2)
+ private val attachedLatch = CountDownLatch(items)
+ private val detachedLatch = CountDownLatch(items)
+ val onAttachStateChangeListener = object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ attachedLatch.countDown()
+ }
+
+ override fun onViewDetachedFromWindow(v: View) {
+ if (attachedLatch.count.equals(0.toLong())) {
+ detachedLatch.countDown()
+ }
+ }
+ }
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ if (numberOfSandboxedSdkViews >= items) {
+ // We should return without creating a SandboxedSdkView if the
+ // number of SandboxedSdkViews is already equal to items. Recycler
+ // view will create new ViewHolders once SandboxedSdkViews are
+ // removed. We do not want to count latch down at that point of time.
+ return ViewHolder(View(context))
+ }
+
+ val listener = SandboxedSdkUiSessionStateChangedListener { state ->
+ if (state is SandboxedSdkUiSessionState.Active) {
+ activeLatch.countDown()
+ } else if (state is SandboxedSdkUiSessionState.Idle) {
+ idleLatch.countDown()
+ }
+ }
+
+ numberOfSandboxedSdkViews++
+ var view: View = SandboxedSdkView(context)
+ (view as SandboxedSdkView).addStateChangedListener(listener)
+ if (isNestedView) {
+ val parentView = LinearLayout(context)
+ parentView.addView(view)
+ view = parentView
+ }
+ view.layoutParams =
+ RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1)
+ view.addOnAttachStateChangeListener(onAttachStateChangeListener)
+ return ViewHolder(view)
+ }
+
+ fun waitForViewsToBeAttached() {
+ assertThat(attachedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ fun waitForViewsToBeDetached() {
+ assertThat(detachedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ fun ensureAllChildrenBecomeActive() {
+ assertThat(activeLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ fun ensureAllChildrenBecomeIdleFromActive() {
+ assertThat(idleLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ fun ensureChildrenDoNotBecomeIdleFromActive() {
+ assertThat(idleLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isFalse()
+ assertThat(idleLatch.count).isEqualTo(items)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ }
+
+ override fun getItemCount(): Int = items
+ }
+
private fun getCoreLibInfoFromAdapter(sdkAdapter: SandboxedUiAdapter): Bundle {
val bundle = sdkAdapter.toCoreLibInfo(context)
bundle.putBoolean(TEST_ONLY_USE_REMOTE_ADAPTER, !invokeBackwardsCompatFlow)
@@ -322,10 +498,10 @@
* created adapter is set on [view] to establish session.
*/
private fun createAdapterAndEstablishSession(
- hasFailingTestSession: Boolean = false,
- viewForSession: SandboxedSdkView? = view,
- testSessionClient: TestSessionClient = TestSessionClient()
- ): TestSandboxedUiAdapter {
+ hasFailingTestSession: Boolean = false,
+ viewForSession: SandboxedSdkView? = view,
+ testSessionClient: TestSessionClient = TestSessionClient()
+ ): TestSandboxedUiAdapter {
val adapter = TestSandboxedUiAdapter(hasFailingTestSession)
val adapterFromCoreLibInfo = SandboxedUiAdapterFactory.createFromCoreLibInfo(
@@ -354,14 +530,17 @@
return adapter
}
- private fun createAdapterAndWaitToBeActive(initialZOrder: Boolean = true):
+ private fun createAdapterAndWaitToBeActive(
+ initialZOrder: Boolean = true,
+ viewForSession: SandboxedSdkView = view
+ ):
TestSandboxedUiAdapter {
- view.orderProviderUiAboveClientUi(initialZOrder)
+ viewForSession.orderProviderUiAboveClientUi(initialZOrder)
- val adapter = createAdapterAndEstablishSession()
+ val adapter = createAdapterAndEstablishSession(false, viewForSession)
val activeLatch = CountDownLatch(1)
- view.addStateChangedListener { state ->
+ viewForSession.addStateChangedListener { state ->
if (state is SandboxedSdkUiSessionState.Active) {
activeLatch.countDown()
}
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index d2f0759..cc25642 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -136,11 +136,9 @@
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.espressoCore)
androidTestImplementation(libs.truth)
- androidTestImplementation(libs.mockitoCore4)
- androidTestImplementation(libs.dexmakerMockito)
+ androidTestImplementation(libs.mockitoAndroid5)
androidTestImplementation(project(":internal-testutils-truth"))
-
testImplementation(libs.junit)
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 0f01914..7b32220 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -1904,9 +1904,32 @@
}
""".trimIndent()
)
+ val javaSrc = Source.java(
+ "JavaClass",
+ """
+ import java.util.List;
+ import kotlin.Result;
+ import kotlin.UInt;
+ interface JavaClass {
+ UInt inlineClassDirectUsage();
+ List<UInt> inlineClassIndirectUsage();
+ Result<Integer> genericInlineClassDirectUsage();
+ List<Result<Integer>> genericInlineClassIndirectUsage();
+
+ MyInlineClass customInlineClassDirectUsage();
+ List<MyInlineClass> customInlineClassIndirectUsage();
+ MyGenericInlineClass<Integer> customGenericInlineClassDirectUsage();
+ List<MyGenericInlineClass<Integer>> customGenericInlineClassIndirectUsage();
+ }
+ """.trimIndent()
+ )
runProcessorTest(
- sources = if (isPrecompiled) { emptyList() } else { listOf(kotlinSrc) },
- classpath = if (isPrecompiled) { compileFiles(listOf(kotlinSrc)) } else { emptyList() }
+ sources = if (isPrecompiled) { emptyList() } else { listOf(kotlinSrc, javaSrc) },
+ classpath = if (isPrecompiled) {
+ compileFiles(listOf(kotlinSrc, javaSrc))
+ } else {
+ emptyList()
+ }
) { invocation ->
val kotlinElm = invocation.processingEnv.requireTypeElement("KotlinClass")
kotlinElm.getMethodByJvmName("kotlinValueClassDirectUsage").apply {
@@ -1989,6 +2012,53 @@
.isEqualTo("kotlin.collections.List<MyGenericInlineClass<kotlin.Int>>")
}
}
+
+ val javaElm = invocation.processingEnv.requireTypeElement("JavaClass")
+ javaElm.getMethodByJvmName("inlineClassDirectUsage").apply {
+ if (invocation.isKsp) {
+ // TODO(kuanyingchou): When an inline type is used in Java we shouldn't replace
+ // it with the JVM type.
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("int")
+ } else {
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("kotlin.UInt")
+ }
+ }
+ javaElm.getMethodByJvmName("inlineClassIndirectUsage").apply {
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("java.util.List<kotlin.UInt>")
+ }
+ javaElm.getMethodByJvmName("customInlineClassDirectUsage").apply {
+ if (invocation.isKsp) {
+ // TODO(kuanyingchou): When an inline type is used in Java we shouldn't replace
+ // it with the JVM type.
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("int")
+ } else {
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("MyInlineClass")
+ }
+ }
+ javaElm.getMethodByJvmName("customInlineClassIndirectUsage").apply {
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("java.util.List<MyInlineClass>")
+ }
+ javaElm.getMethodByJvmName("customGenericInlineClassDirectUsage").apply {
+ if (invocation.isKsp) {
+ // TODO(kuanyingchou): When an inline type is used in Java we shouldn't replace
+ // it with the JVM type.
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("java.lang.Number")
+ } else {
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("MyGenericInlineClass<java.lang.Integer>")
+ }
+ }
+ javaElm.getMethodByJvmName("customGenericInlineClassIndirectUsage").apply {
+ assertThat(returnType.asTypeName().java.toString())
+ .isEqualTo("java.util.List<MyGenericInlineClass<java.lang.Integer>>")
+ }
}
}
}
diff --git a/settings.gradle b/settings.gradle
index b60f210..6d5af64 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -30,7 +30,7 @@
classpath("com.google.protobuf:protobuf-java:3.22.3")
// upgrade okio for gcpbuildcache that is compatible with the wire plugin used by androidx
classpath("com.squareup.okio:okio:3.3.0")
- classpath("com.gradle:gradle-enterprise-gradle-plugin:3.15.1")
+ classpath("com.gradle:gradle-enterprise-gradle-plugin:3.16")
classpath("com.gradle:common-custom-user-data-gradle-plugin:1.12")
classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-beta05")
}
@@ -505,7 +505,6 @@
includeProject(":compose:material3:benchmark", [BuildType.COMPOSE])
includeProject(":compose:material3:material3-adaptive", [BuildType.COMPOSE])
includeProject(":compose:material3:material3-adaptive:material3-adaptive-samples", "compose/material3/material3-adaptive/samples", [BuildType.COMPOSE])
-includeProject(":compose:material3:material3-adaptive:material3-adaptive-benchmark", "compose/material3/material3-adaptive/benchmark", [BuildType.COMPOSE])
includeProject(":compose:material3:material3-adaptive-navigation-suite", [BuildType.COMPOSE])
includeProject(":compose:material3:material3-adaptive-navigation-suite:material3-adaptive-navigation-suite-samples", "compose/material3/material3-adaptive-navigation-suite/samples", [BuildType.COMPOSE])
includeProject(":compose:material3:material3-common", [BuildType.COMPOSE])
diff --git a/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.kt b/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.kt
index f5ff489..abea24a 100644
--- a/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.kt
+++ b/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.kt
@@ -48,16 +48,12 @@
import androidx.core.view.AccessibilityDelegateCompat
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
-import androidx.core.view.animation.PathInterpolatorCompat
import androidx.core.view.forEach
import androidx.core.view.forEachIndexed
import androidx.customview.view.AbsSavedState
import androidx.customview.widget.Openable
import androidx.customview.widget.ViewDragHelper
import androidx.slidingpanelayout.R
-import androidx.transition.ChangeBounds
-import androidx.transition.Transition
-import androidx.transition.TransitionManager
import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
import java.util.concurrent.CopyOnWriteArrayList
@@ -342,6 +338,12 @@
internal annotation class LockMode
private var foldingFeature: FoldingFeature? = null
+ set(value) {
+ if (value != field) {
+ field = value
+ requestLayout()
+ }
+ }
/**
* [Job] that tracks the last launched coroutine running [whileAttachedToVisibleWindow].
@@ -780,12 +782,6 @@
.distinctUntilChanged()
.collect { nextFeature ->
foldingFeature = nextFeature
- // Start transition animation when folding feature changed
- val changeBounds: Transition = ChangeBounds()
- changeBounds.duration = 300L
- changeBounds.interpolator = PathInterpolatorCompat.create(0.2f, 0f, 0f, 1f)
- TransitionManager.beginDelayedTransition(this@SlidingPaneLayout, changeBounds)
- requestLayout()
}
}
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
index 94338f3..0ecbc8d 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
@@ -140,7 +140,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressMenu();
- assertEquals("keycode menu pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode menu pressed"), TIMEOUT_MS));
}
@Test
@@ -149,7 +149,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressBack();
- assertEquals("keycode back pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode back pressed"), TIMEOUT_MS));
}
@Test
@@ -158,7 +158,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressSearch();
- assertEquals("keycode search pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode search pressed"), TIMEOUT_MS));
}
@Test
@@ -167,7 +167,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressDPadCenter();
- assertEquals("keycode dpad center pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode dpad center pressed"), TIMEOUT_MS));
}
@Test
@@ -176,7 +176,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressDPadDown();
- assertEquals("keycode dpad down pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode dpad down pressed"), TIMEOUT_MS));
}
@Test
@@ -185,7 +185,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressDPadUp();
- assertEquals("keycode dpad up pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode dpad up pressed"), TIMEOUT_MS));
}
@Test
@@ -194,7 +194,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressDPadLeft();
- assertEquals("keycode dpad left pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode dpad left pressed"), TIMEOUT_MS));
}
@Test
@@ -203,7 +203,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressDPadRight();
- assertEquals("keycode dpad right pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode dpad right pressed"), TIMEOUT_MS));
}
@Test
@@ -212,7 +212,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressDelete();
- assertEquals("keycode delete pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode delete pressed"), TIMEOUT_MS));
}
@Test
@@ -221,7 +221,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressEnter();
- assertEquals("keycode enter pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode enter pressed"), TIMEOUT_MS));
}
@Test
@@ -230,7 +230,7 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
- assertEquals("keycode 0 pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode 0 pressed"), TIMEOUT_MS));
}
@Test
@@ -240,7 +240,8 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressKeyCode(KeyEvent.KEYCODE_Z,
KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
- assertEquals("keycode Z pressed with meta shift left on", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode Z pressed with meta shift left on"),
+ TIMEOUT_MS));
}
@Test
@@ -260,7 +261,8 @@
UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
mDevice.pressKeyCodes(new int[]{KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_B});
- assertEquals("keycode A and keycode B are pressed", textView.getText());
+ assertTrue(textView.wait(Until.textEquals("keycode A and keycode B are pressed"),
+ TIMEOUT_MS));
}
@Test
diff --git a/test/uiautomator/uiautomator/api/2.3.0-beta01.txt b/test/uiautomator/uiautomator/api/2.3.0-beta01.txt
index bfaecd4..d3d9df8 100644
--- a/test/uiautomator/uiautomator/api/2.3.0-beta01.txt
+++ b/test/uiautomator/uiautomator/api/2.3.0-beta01.txt
@@ -345,9 +345,9 @@
method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.Condition<? super androidx.test.uiautomator.UiObject2,U!>);
method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.EventCondition<U!>);
method public void setGestureMargin(int);
- method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=0.5f) float);
- method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+ method public void setGestureMarginPercentage(@FloatRange(from=0.0f, to=0.5f) float);
method public void setGestureMargins(int, int, int, int);
+ method public void setGestureMarginsPercentage(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
method public void setText(String?);
method public void swipe(androidx.test.uiautomator.Direction, float);
method public void swipe(androidx.test.uiautomator.Direction, float, int);
diff --git a/test/uiautomator/uiautomator/api/api_lint.ignore b/test/uiautomator/uiautomator/api/api_lint.ignore
index 1c65a590..a45a44b 100644
--- a/test/uiautomator/uiautomator/api/api_lint.ignore
+++ b/test/uiautomator/uiautomator/api/api_lint.ignore
@@ -35,6 +35,8 @@
Missing nullability on method `getText` return
+PercentageInt: androidx.test.uiautomator.UiObject2#setGestureMarginPercentage(float):
+ Percentage must use ints, was `float` in `setGestureMarginPercentage`
PercentageInt: androidx.test.uiautomator.UiScrollable#getSwipeDeadZonePercentage():
Percentage must use ints, was `double` in `getSwipeDeadZonePercentage`
diff --git a/test/uiautomator/uiautomator/api/current.ignore b/test/uiautomator/uiautomator/api/current.ignore
index 5f145ac..3d43de2 100644
--- a/test/uiautomator/uiautomator/api/current.ignore
+++ b/test/uiautomator/uiautomator/api/current.ignore
@@ -1,7 +1,11 @@
// Baseline format: 1.0
-AddedAbstractMethod: androidx.test.uiautomator.EventCondition#getResult():
- Added method androidx.test.uiautomator.EventCondition.getResult()
+AddedMethod: androidx.test.uiautomator.UiObject2#setGestureMarginPercentage(float):
+ Added method androidx.test.uiautomator.UiObject2.setGestureMarginPercentage(float)
+AddedMethod: androidx.test.uiautomator.UiObject2#setGestureMarginsPercentage(float, float, float, float):
+ Added method androidx.test.uiautomator.UiObject2.setGestureMarginsPercentage(float,float,float,float)
-RemovedMethod: androidx.test.uiautomator.Until#Until():
- Removed constructor androidx.test.uiautomator.Until()
+RemovedMethod: androidx.test.uiautomator.UiObject2#setGestureMarginPercent(float):
+ Removed method androidx.test.uiautomator.UiObject2.setGestureMarginPercent(float)
+RemovedMethod: androidx.test.uiautomator.UiObject2#setGestureMarginPercent(float, float, float, float):
+ Removed method androidx.test.uiautomator.UiObject2.setGestureMarginPercent(float,float,float,float)
diff --git a/test/uiautomator/uiautomator/api/current.txt b/test/uiautomator/uiautomator/api/current.txt
index bfaecd4..d3d9df8 100644
--- a/test/uiautomator/uiautomator/api/current.txt
+++ b/test/uiautomator/uiautomator/api/current.txt
@@ -345,9 +345,9 @@
method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.Condition<? super androidx.test.uiautomator.UiObject2,U!>);
method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.EventCondition<U!>);
method public void setGestureMargin(int);
- method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=0.5f) float);
- method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+ method public void setGestureMarginPercentage(@FloatRange(from=0.0f, to=0.5f) float);
method public void setGestureMargins(int, int, int, int);
+ method public void setGestureMarginsPercentage(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
method public void setText(String?);
method public void swipe(androidx.test.uiautomator.Direction, float);
method public void swipe(androidx.test.uiautomator.Direction, float, int);
diff --git a/test/uiautomator/uiautomator/api/restricted_2.3.0-beta01.txt b/test/uiautomator/uiautomator/api/restricted_2.3.0-beta01.txt
index bfaecd4..d3d9df8 100644
--- a/test/uiautomator/uiautomator/api/restricted_2.3.0-beta01.txt
+++ b/test/uiautomator/uiautomator/api/restricted_2.3.0-beta01.txt
@@ -345,9 +345,9 @@
method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.Condition<? super androidx.test.uiautomator.UiObject2,U!>);
method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.EventCondition<U!>);
method public void setGestureMargin(int);
- method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=0.5f) float);
- method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+ method public void setGestureMarginPercentage(@FloatRange(from=0.0f, to=0.5f) float);
method public void setGestureMargins(int, int, int, int);
+ method public void setGestureMarginsPercentage(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
method public void setText(String?);
method public void swipe(androidx.test.uiautomator.Direction, float);
method public void swipe(androidx.test.uiautomator.Direction, float, int);
diff --git a/test/uiautomator/uiautomator/api/restricted_current.ignore b/test/uiautomator/uiautomator/api/restricted_current.ignore
index 5f145ac..3d43de2 100644
--- a/test/uiautomator/uiautomator/api/restricted_current.ignore
+++ b/test/uiautomator/uiautomator/api/restricted_current.ignore
@@ -1,7 +1,11 @@
// Baseline format: 1.0
-AddedAbstractMethod: androidx.test.uiautomator.EventCondition#getResult():
- Added method androidx.test.uiautomator.EventCondition.getResult()
+AddedMethod: androidx.test.uiautomator.UiObject2#setGestureMarginPercentage(float):
+ Added method androidx.test.uiautomator.UiObject2.setGestureMarginPercentage(float)
+AddedMethod: androidx.test.uiautomator.UiObject2#setGestureMarginsPercentage(float, float, float, float):
+ Added method androidx.test.uiautomator.UiObject2.setGestureMarginsPercentage(float,float,float,float)
-RemovedMethod: androidx.test.uiautomator.Until#Until():
- Removed constructor androidx.test.uiautomator.Until()
+RemovedMethod: androidx.test.uiautomator.UiObject2#setGestureMarginPercent(float):
+ Removed method androidx.test.uiautomator.UiObject2.setGestureMarginPercent(float)
+RemovedMethod: androidx.test.uiautomator.UiObject2#setGestureMarginPercent(float, float, float, float):
+ Removed method androidx.test.uiautomator.UiObject2.setGestureMarginPercent(float,float,float,float)
diff --git a/test/uiautomator/uiautomator/api/restricted_current.txt b/test/uiautomator/uiautomator/api/restricted_current.txt
index bfaecd4..d3d9df8 100644
--- a/test/uiautomator/uiautomator/api/restricted_current.txt
+++ b/test/uiautomator/uiautomator/api/restricted_current.txt
@@ -345,9 +345,9 @@
method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.Condition<? super androidx.test.uiautomator.UiObject2,U!>);
method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.EventCondition<U!>);
method public void setGestureMargin(int);
- method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=0.5f) float);
- method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+ method public void setGestureMarginPercentage(@FloatRange(from=0.0f, to=0.5f) float);
method public void setGestureMargins(int, int, int, int);
+ method public void setGestureMarginsPercentage(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
method public void setText(String?);
method public void swipe(androidx.test.uiautomator.Direction, float);
method public void swipe(androidx.test.uiautomator.Direction, float, int);
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
index 548eee3..74c2c2c 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
@@ -151,26 +151,26 @@
* Sets the percentage of gestures' margins to avoid touching too close to the edges, e.g.
* when scrolling up, phone open quick settings instead if gesture is close to the top.
* The percentage is based on the object's visible size, e.g. to set 20% margins:
- * <pre>mUiObject2.setGestureMarginPercent(0.2f);</pre>
+ * <pre>mUiObject2.setGestureMarginPercentage(0.2f);</pre>
*
* @Param percent Float between [0, 0.5] for four margins: left, top, right, and bottom.
*/
- public void setGestureMarginPercent(@FloatRange(from = 0f, to = 0.5f) float percent) {
- setGestureMarginPercent(percent, percent, percent, percent);
+ public void setGestureMarginPercentage(@FloatRange(from = 0f, to = 0.5f) float percent) {
+ setGestureMarginsPercentage(percent, percent, percent, percent);
}
/**
* Sets the percentage of gestures' margins to avoid touching too close to the edges, e.g.
* when scrolling up, phone open quick settings instead if gesture is close to the top.
* The percentage is based on the object's visible size, e.g. to set 20% bottom margin only:
- * <pre>mUiObject2.setGestureMarginPercent(0f, 0f, 0f, 0.2f);</pre>
+ * <pre>mUiObject2.setGestureMarginsPercentage(0f, 0f, 0f, 0.2f);</pre>
*
* @Param left Float between [0, 1] for left margin
* @Param top Float between [0, 1] for top margin
* @Param right Float between [0, 1] for right margin
* @Param bottom Float between [0, 1] for bottom margin
*/
- public void setGestureMarginPercent(@FloatRange(from = 0f, to = 1f) float left,
+ public void setGestureMarginsPercentage(@FloatRange(from = 0f, to = 1f) float left,
@FloatRange(from = 0f, to = 1f) float top,
@FloatRange(from = 0f, to = 1f) float right,
@FloatRange(from = 0f, to = 1f) float bottom) {
diff --git a/vectordrawable/vectordrawable-animated/build.gradle b/vectordrawable/vectordrawable-animated/build.gradle
index db008c9..32733ad0 100644
--- a/vectordrawable/vectordrawable-animated/build.gradle
+++ b/vectordrawable/vectordrawable-animated/build.gradle
@@ -8,7 +8,7 @@
dependencies {
api("androidx.annotation:annotation:1.2.0")
api(project(":vectordrawable:vectordrawable"))
- implementation(project(":core:core"))
+ implementation("androidx.core:core:1.12.0")
implementation("androidx.interpolator:interpolator:1.0.0")
implementation("androidx.collection:collection:1.1.0")
diff --git a/vectordrawable/vectordrawable-seekable/build.gradle b/vectordrawable/vectordrawable-seekable/build.gradle
index 3516876..928385c 100644
--- a/vectordrawable/vectordrawable-seekable/build.gradle
+++ b/vectordrawable/vectordrawable-seekable/build.gradle
@@ -7,7 +7,7 @@
dependencies {
api(project(":vectordrawable:vectordrawable"))
- api(project(":core:core-animation"))
+ api("androidx.core:core-animation:1.0.0-rc01")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(libs.testExtJunit)
diff --git a/wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/SwipeBenchmark.kt b/wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/SwipeBenchmark.kt
index df1706c..5a47931 100644
--- a/wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/SwipeBenchmark.kt
+++ b/wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/SwipeBenchmark.kt
@@ -27,6 +27,7 @@
import androidx.test.uiautomator.UiDevice
import androidx.testutils.createCompilationParams
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,6 +35,7 @@
@LargeTest
@RunWith(Parameterized::class)
+@Ignore("b/315170517")
class SwipeBenchmark(
private val compilationMode: CompilationMode
) {
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoolNodes.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoolNodes.java
index 2cdde6f..72e0f87 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoolNodes.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/BoolNodes.java
@@ -16,8 +16,6 @@
package androidx.wear.protolayout.expression.pipeline;
-import android.util.Log;
-
import androidx.annotation.UiThread;
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool;
import androidx.wear.protolayout.expression.proto.DynamicProto;
@@ -99,10 +97,13 @@
return unboxedLhs > unboxedRhs;
case COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO:
return unboxedLhs >= unboxedRhs;
- default:
- Log.e(TAG, "Unknown operation type in ComparisonInt32Node");
- return false;
+ case COMPARISON_OP_TYPE_UNDEFINED:
+ case UNRECOGNIZED:
+ break;
}
+ throw new IllegalArgumentException(
+ "Unknown operation type in ComparisonInt32Node: "
+ + protoNode.getOperationType());
});
}
}
@@ -138,10 +139,13 @@
case COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO:
return (unboxedLhs > unboxedRhs)
|| equalFloats(unboxedLhs, unboxedRhs);
- default:
- Log.e(TAG, "Unknown operation type in ComparisonInt32Node");
- return false;
+ case COMPARISON_OP_TYPE_UNDEFINED:
+ case UNRECOGNIZED:
+ break;
}
+ throw new IllegalArgumentException(
+ "Unknown operation type in ComparisonFloatNode: "
+ + protoNode.getOperationType());
});
}
@@ -176,10 +180,13 @@
return a.equals(b);
case LOGICAL_OP_TYPE_NOT_EQUAL:
return !a.equals(b);
- default:
- Log.e(TAG, "Unknown operation type in LogicalBoolOp");
- return false;
+ case LOGICAL_OP_TYPE_UNDEFINED:
+ case UNRECOGNIZED:
+ break;
}
+ throw new IllegalArgumentException(
+ "Unknown operation type in LogicalBoolOp: "
+ + protoNode.getOperationType());
});
}
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeBindingRequest.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeBindingRequest.java
index 1af5943..b805b3f 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeBindingRequest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeBindingRequest.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -19,6 +19,7 @@
import android.icu.util.ULocale;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.protolayout.expression.DynamicBuilders;
@@ -36,6 +37,7 @@
/**
* Holds the parameters needed by {@link DynamicTypeEvaluator#bind}. It can be used as follows:
+ *
* <pre>{@code
* DynamicTypeBindingRequest request = DynamicTypeBindingRequest.forDynamicInt32(source,consumer);
* BoundDynamicType boundType = evaluator.bind(request);
@@ -53,7 +55,7 @@
*
* @param floatSource The given float dynamic type that should be evaluated.
* @param consumer The registered consumer for results of the evaluation. It will be called from
- * UI thread.
+ * UI thread.
*/
@NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -63,11 +65,11 @@
}
/**
- * Creates a {@link DynamicTypeBindingRequest} from the given {@link DynamicBuilders.DynamicFloat}
- * for binding.
+ * Creates a {@link DynamicTypeBindingRequest} from the given {@link
+ * DynamicBuilders.DynamicFloat} for binding.
*
- * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on the
- * given {@link Executor}.
+ * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on
+ * the given {@link Executor}.
*
* @param floatSource The given float dynamic type that should be evaluated.
* @param executor The Executor to run the consumer on.
@@ -78,7 +80,8 @@
@NonNull DynamicBuilders.DynamicFloat floatSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Float> consumer) {
- return new DynamicFloatBindingRequestWithExecutor(floatSource, executor, consumer);
+ return new DynamicFloatBindingRequest(
+ floatSource.toDynamicFloatProto(), executor, consumer);
}
/**
@@ -87,21 +90,22 @@
*
* @param int32Source The given integer dynamic type that should be evaluated.
* @param consumer The registered consumer for results of the evaluation. It will be called from
- * UI thread.
+ * UI thread.
*/
@NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
public static DynamicTypeBindingRequest forDynamicInt32Internal(
- @NonNull DynamicInt32 int32Source, @NonNull DynamicTypeValueReceiver<Integer> consumer) {
+ @NonNull DynamicInt32 int32Source,
+ @NonNull DynamicTypeValueReceiver<Integer> consumer) {
return new DynamicInt32BindingRequest(int32Source, consumer);
}
/**
- * Creates a {@link DynamicTypeBindingRequest} from the given {@link DynamicBuilders.DynamicInt32}
- * for binding.
+ * Creates a {@link DynamicTypeBindingRequest} from the given {@link
+ * DynamicBuilders.DynamicInt32} for binding.
*
- * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on the
- * given {@link Executor}.
+ * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on
+ * the given {@link Executor}.
*
* @param int32Source The given integer dynamic type that should be evaluated.
* @param executor The Executor to run the consumer on.
@@ -112,7 +116,8 @@
@NonNull DynamicBuilders.DynamicInt32 int32Source,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Integer> consumer) {
- return new DynamicInt32BindingRequestWithExecutor(int32Source, executor, consumer);
+ return new DynamicInt32BindingRequest(
+ int32Source.toDynamicInt32Proto(), executor, consumer);
}
/**
@@ -121,21 +126,22 @@
*
* @param colorSource The given color dynamic type that should be evaluated.
* @param consumer The registered consumer for results of the evaluation. It will be called from
- * UI thread.
+ * UI thread.
*/
@NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
public static DynamicTypeBindingRequest forDynamicColorInternal(
- @NonNull DynamicColor colorSource, @NonNull DynamicTypeValueReceiver<Integer> consumer) {
+ @NonNull DynamicColor colorSource,
+ @NonNull DynamicTypeValueReceiver<Integer> consumer) {
return new DynamicColorBindingRequest(colorSource, consumer);
}
/**
- * Creates a {@link DynamicTypeBindingRequest} from the given {@link DynamicBuilders.DynamicColor}
- * for binding.
+ * Creates a {@link DynamicTypeBindingRequest} from the given {@link
+ * DynamicBuilders.DynamicColor} for binding.
*
- * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on the
- * given {@link Executor}.
+ * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on
+ * the given {@link Executor}.
*
* @param colorSource The given color dynamic type that should be evaluated.
* @param executor The Executor to run the consumer on.
@@ -146,7 +152,8 @@
@NonNull DynamicBuilders.DynamicColor colorSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Integer> consumer) {
- return new DynamicColorBindingRequestWithExecutor(colorSource, executor, consumer);
+ return new DynamicColorBindingRequest(
+ colorSource.toDynamicColorProto(), executor, consumer);
}
/**
@@ -155,7 +162,7 @@
*
* @param boolSource The given boolean dynamic type that should be evaluated.
* @param consumer The registered consumer for results of the evaluation. It will be called from
- * UI thread.
+ * UI thread.
*/
@NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -165,11 +172,11 @@
}
/**
- * Creates a {@link DynamicTypeBindingRequest} from the given {@link DynamicBuilders.DynamicBool}
- * for binding.
+ * Creates a {@link DynamicTypeBindingRequest} from the given {@link
+ * DynamicBuilders.DynamicBool} for binding.
*
- * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on the
- * given {@link Executor}.
+ * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on
+ * the given {@link Executor}.
*
* @param boolSource The given boolean dynamic type that should be evaluated.
* @param executor The Executor to run the consumer on.
@@ -180,7 +187,7 @@
@NonNull DynamicBuilders.DynamicBool boolSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Boolean> consumer) {
- return new DynamicBoolBindingRequestWithExecutor(boolSource, executor, consumer);
+ return new DynamicBoolBindingRequest(boolSource.toDynamicBoolProto(), executor, consumer);
}
/**
@@ -189,7 +196,7 @@
*
* @param stringSource The given String dynamic type that should be evaluated.
* @param consumer The registered consumer for results of the evaluation. It will be called from
- * UI thread.
+ * UI thread.
* @param locale The locale used for the given String source.
*/
@NonNull
@@ -205,8 +212,8 @@
* Creates a {@link DynamicTypeBindingRequest} from the given {@link
* DynamicBuilders.DynamicString} for binding.
*
- * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on the
- * given {@link Executor}.
+ * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on
+ * the given {@link Executor}.
*
* @param stringSource The given String dynamic type that should be evaluated.
* @param locale The locale used for the given String source.
@@ -219,7 +226,8 @@
@NonNull ULocale locale,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<String> consumer) {
- return new DynamicStringBindingRequestWithExecutor(stringSource, locale, executor, consumer);
+ return new DynamicStringBindingRequest(
+ stringSource.toDynamicStringProto(), locale, executor, consumer);
}
/**
@@ -228,7 +236,7 @@
*
* @param durationSource The given durations dynamic type that should be evaluated.
* @param consumer The registered consumer for results of the evaluation. It will be called from
- * UI thread.
+ * UI thread.
*/
@NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -242,8 +250,8 @@
* Creates a {@link DynamicTypeBindingRequest} from the given {@link
* DynamicBuilders.DynamicDuration} for binding.
*
- * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on the
- * given {@link Executor}.
+ * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on
+ * the given {@link Executor}.
*
* @param durationSource The given duration dynamic type that should be evaluated.
* @param executor The Executor to run the consumer on.
@@ -254,7 +262,8 @@
@NonNull DynamicBuilders.DynamicDuration durationSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Duration> consumer) {
- return new DynamicDurationBindingRequestWithExecutor(durationSource, executor, consumer);
+ return new DynamicDurationBindingRequest(
+ durationSource.toDynamicDurationProto(), executor, consumer);
}
/**
@@ -263,12 +272,13 @@
*
* @param instantSource The given instant dynamic type that should be evaluated.
* @param consumer The registered consumer for results of the evaluation. It will be called from
- * UI thread.
+ * UI thread.
*/
@NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
public static DynamicTypeBindingRequest forDynamicInstantInternal(
- @NonNull DynamicInstant instantSource, @NonNull DynamicTypeValueReceiver<Instant> consumer) {
+ @NonNull DynamicInstant instantSource,
+ @NonNull DynamicTypeValueReceiver<Instant> consumer) {
return new DynamicInstantBindingRequest(instantSource, consumer);
}
@@ -276,8 +286,8 @@
* Creates a {@link DynamicTypeBindingRequest} from the given {@link
* DynamicBuilders.DynamicInstant} for binding.
*
- * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on the
- * given {@link Executor}.
+ * <p>Results of evaluation will be sent through the given {@link DynamicTypeValueReceiver} on
+ * the given {@link Executor}.
*
* @param instantSource The given instant dynamic type that should be evaluated.
* @param executor The Executor to run the consumer on.
@@ -288,142 +298,28 @@
@NonNull DynamicBuilders.DynamicInstant instantSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Instant> consumer) {
- return new DynamicInstantBindingRequestWithExecutor(instantSource, executor, consumer);
+ return new DynamicInstantBindingRequest(
+ instantSource.toDynamicInstantProto(), executor, consumer);
+ }
+
+ @NonNull
+ private static <T> DynamicTypeValueReceiverOnExecutor<T> createReceiver(
+ @Nullable Executor executor, @NonNull DynamicTypeValueReceiver<T> consumer) {
+ if (executor != null) {
+ return new DynamicTypeValueReceiverOnExecutor<>(executor, consumer);
+ } else {
+ return new DynamicTypeValueReceiverOnExecutor<>(consumer);
+ }
}
private static class DynamicFloatBindingRequest extends DynamicTypeBindingRequest {
@NonNull private final DynamicFloat mFloatSource;
+ @Nullable private final Executor mExecutor;
@NonNull private final DynamicTypeValueReceiver<Float> mConsumer;
DynamicFloatBindingRequest(
- @NonNull DynamicFloat floatSource, @NonNull DynamicTypeValueReceiver<Float> consumer) {
- mFloatSource = floatSource;
- mConsumer = consumer;
- }
-
- @Override
- BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mFloatSource, mConsumer);
- }
- }
-
- private static class DynamicInt32BindingRequest extends DynamicTypeBindingRequest {
-
- @NonNull private final DynamicInt32 mInt32Source;
- @NonNull private final DynamicTypeValueReceiver<Integer> mConsumer;
-
- DynamicInt32BindingRequest(
- @NonNull DynamicInt32 int32Source, @NonNull DynamicTypeValueReceiver<Integer> consumer) {
- mInt32Source = int32Source;
- mConsumer = consumer;
- }
-
- @Override
- BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mInt32Source, mConsumer);
- }
- }
-
- private static class DynamicColorBindingRequest extends DynamicTypeBindingRequest {
-
- @NonNull private final DynamicColor mColorSource;
- @NonNull private final DynamicTypeValueReceiver<Integer> mConsumer;
-
- DynamicColorBindingRequest(
- @NonNull DynamicColor colorSource, @NonNull DynamicTypeValueReceiver<Integer> consumer) {
- mColorSource = colorSource;
- mConsumer = consumer;
- }
-
- @Override
- BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mColorSource, mConsumer);
- }
- }
-
- private static class DynamicBoolBindingRequest extends DynamicTypeBindingRequest {
-
- @NonNull private final DynamicBool mBoolSource;
- @NonNull private final DynamicTypeValueReceiver<Boolean> mConsumer;
-
- DynamicBoolBindingRequest(
- @NonNull DynamicBool boolSource, @NonNull DynamicTypeValueReceiver<Boolean> consumer) {
- mBoolSource = boolSource;
- mConsumer = consumer;
- }
-
- @Override
- BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mBoolSource, mConsumer);
- }
- }
-
- private static class DynamicStringBindingRequest extends DynamicTypeBindingRequest {
-
- @NonNull private final DynamicString mStringSource;
- @NonNull private final ULocale mLocale;
- @NonNull private final DynamicTypeValueReceiver<String> mConsumer;
-
- DynamicStringBindingRequest(
- @NonNull DynamicString stringSource,
- @NonNull ULocale locale,
- @NonNull DynamicTypeValueReceiver<String> consumer) {
- mStringSource = stringSource;
- mLocale = locale;
- mConsumer = consumer;
- }
-
- @Override
- BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mStringSource, mLocale, mConsumer);
- }
- }
-
- private static class DynamicDurationBindingRequest extends DynamicTypeBindingRequest {
-
- @NonNull private final DynamicDuration mDurationSource;
- @NonNull private final DynamicTypeValueReceiver<Duration> mConsumer;
-
- DynamicDurationBindingRequest(
- @NonNull DynamicDuration durationSource,
- @NonNull DynamicTypeValueReceiver<Duration> consumer) {
- mDurationSource = durationSource;
- mConsumer = consumer;
- }
-
- @Override
- BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mDurationSource, mConsumer);
- }
- }
-
- private static class DynamicInstantBindingRequest extends DynamicTypeBindingRequest {
-
- @NonNull private final DynamicInstant mInstantSource;
- @NonNull private final DynamicTypeValueReceiver<Instant> mConsumer;
-
- DynamicInstantBindingRequest(
- @NonNull DynamicInstant instantSource,
- @NonNull DynamicTypeValueReceiver<Instant> consumer) {
- mInstantSource = instantSource;
- mConsumer = consumer;
- }
-
- @Override
- BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mInstantSource, mConsumer);
- }
- }
-
- private static class DynamicFloatBindingRequestWithExecutor extends DynamicTypeBindingRequest {
-
- @NonNull private final DynamicBuilders.DynamicFloat mFloatSource;
- @NonNull private final Executor mExecutor;
- @NonNull private final DynamicTypeValueReceiver<Float> mConsumer;
-
- DynamicFloatBindingRequestWithExecutor(
- @NonNull DynamicBuilders.DynamicFloat floatSource,
+ @NonNull DynamicFloat floatSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Float> consumer) {
mFloatSource = floatSource;
@@ -431,20 +327,28 @@
mConsumer = consumer;
}
+ DynamicFloatBindingRequest(
+ @NonNull DynamicFloat floatSource,
+ @NonNull DynamicTypeValueReceiver<Float> consumer) {
+ mFloatSource = floatSource;
+ mConsumer = consumer;
+ mExecutor = null;
+ }
+
@Override
BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mFloatSource, mExecutor, mConsumer);
+ return evaluator.bindInternal(mFloatSource, createReceiver(mExecutor, mConsumer));
}
}
- private static class DynamicInt32BindingRequestWithExecutor extends DynamicTypeBindingRequest {
+ private static class DynamicInt32BindingRequest extends DynamicTypeBindingRequest {
- @NonNull private final DynamicBuilders.DynamicInt32 mInt32Source;
- @NonNull private final Executor mExecutor;
+ @NonNull private final DynamicInt32 mInt32Source;
+ @Nullable private final Executor mExecutor;
@NonNull private final DynamicTypeValueReceiver<Integer> mConsumer;
- DynamicInt32BindingRequestWithExecutor(
- @NonNull DynamicBuilders.DynamicInt32 int32Source,
+ DynamicInt32BindingRequest(
+ @NonNull DynamicInt32 int32Source,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Integer> consumer) {
mInt32Source = int32Source;
@@ -452,20 +356,28 @@
mConsumer = consumer;
}
+ DynamicInt32BindingRequest(
+ @NonNull DynamicInt32 int32Source,
+ @NonNull DynamicTypeValueReceiver<Integer> consumer) {
+ mInt32Source = int32Source;
+ mConsumer = consumer;
+ mExecutor = null;
+ }
+
@Override
BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mInt32Source, mExecutor, mConsumer);
+ return evaluator.bindInternal(mInt32Source, createReceiver(mExecutor, mConsumer));
}
}
- private static class DynamicColorBindingRequestWithExecutor extends DynamicTypeBindingRequest {
+ private static class DynamicColorBindingRequest extends DynamicTypeBindingRequest {
- @NonNull private final DynamicBuilders.DynamicColor mColorSource;
- @NonNull private final Executor mExecutor;
+ @NonNull private final DynamicColor mColorSource;
+ @Nullable private final Executor mExecutor;
@NonNull private final DynamicTypeValueReceiver<Integer> mConsumer;
- DynamicColorBindingRequestWithExecutor(
- @NonNull DynamicBuilders.DynamicColor colorSource,
+ DynamicColorBindingRequest(
+ @NonNull DynamicColor colorSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Integer> consumer) {
mColorSource = colorSource;
@@ -473,20 +385,28 @@
mConsumer = consumer;
}
+ DynamicColorBindingRequest(
+ @NonNull DynamicColor colorSource,
+ @NonNull DynamicTypeValueReceiver<Integer> consumer) {
+ mColorSource = colorSource;
+ mConsumer = consumer;
+ mExecutor = null;
+ }
+
@Override
BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mColorSource, mExecutor, mConsumer);
+ return evaluator.bindInternal(mColorSource, createReceiver(mExecutor, mConsumer));
}
}
- private static class DynamicBoolBindingRequestWithExecutor extends DynamicTypeBindingRequest {
+ private static class DynamicBoolBindingRequest extends DynamicTypeBindingRequest {
- @NonNull private final DynamicBuilders.DynamicBool mBoolSource;
- @NonNull private final Executor mExecutor;
+ @NonNull private final DynamicBool mBoolSource;
+ @Nullable private final Executor mExecutor;
@NonNull private final DynamicTypeValueReceiver<Boolean> mConsumer;
- DynamicBoolBindingRequestWithExecutor(
- @NonNull DynamicBuilders.DynamicBool boolSource,
+ DynamicBoolBindingRequest(
+ @NonNull DynamicBool boolSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Boolean> consumer) {
mBoolSource = boolSource;
@@ -494,21 +414,29 @@
mConsumer = consumer;
}
+ DynamicBoolBindingRequest(
+ @NonNull DynamicBool boolSource,
+ @NonNull DynamicTypeValueReceiver<Boolean> consumer) {
+ mBoolSource = boolSource;
+ mConsumer = consumer;
+ mExecutor = null;
+ }
+
@Override
BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mBoolSource, mExecutor, mConsumer);
+ return evaluator.bindInternal(mBoolSource, createReceiver(mExecutor, mConsumer));
}
}
- private static class DynamicStringBindingRequestWithExecutor extends DynamicTypeBindingRequest {
+ private static class DynamicStringBindingRequest extends DynamicTypeBindingRequest {
- @NonNull private final DynamicBuilders.DynamicString mStringSource;
+ @NonNull private final DynamicString mStringSource;
@NonNull private final ULocale mLocale;
- @NonNull private final Executor mExecutor;
+ @Nullable private final Executor mExecutor;
@NonNull private final DynamicTypeValueReceiver<String> mConsumer;
- DynamicStringBindingRequestWithExecutor(
- @NonNull DynamicBuilders.DynamicString stringSource,
+ DynamicStringBindingRequest(
+ @NonNull DynamicString stringSource,
@NonNull ULocale locale,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<String> consumer) {
@@ -518,20 +446,31 @@
this.mLocale = locale;
}
+ DynamicStringBindingRequest(
+ @NonNull DynamicString stringSource,
+ @NonNull ULocale locale,
+ @NonNull DynamicTypeValueReceiver<String> consumer) {
+ mStringSource = stringSource;
+ mConsumer = consumer;
+ mLocale = locale;
+ mExecutor = null;
+ }
+
@Override
BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mStringSource, mLocale, mExecutor, mConsumer);
+ return evaluator.bindInternal(
+ mStringSource, mLocale, createReceiver(mExecutor, mConsumer));
}
}
- private static class DynamicDurationBindingRequestWithExecutor extends DynamicTypeBindingRequest {
+ private static class DynamicDurationBindingRequest extends DynamicTypeBindingRequest {
- @NonNull private final DynamicBuilders.DynamicDuration mDurationSource;
- @NonNull private final Executor mExecutor;
+ @NonNull private final DynamicDuration mDurationSource;
+ @Nullable private final Executor mExecutor;
@NonNull private final DynamicTypeValueReceiver<Duration> mConsumer;
- DynamicDurationBindingRequestWithExecutor(
- @NonNull DynamicBuilders.DynamicDuration durationSource,
+ DynamicDurationBindingRequest(
+ @NonNull DynamicDuration durationSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Duration> consumer) {
mDurationSource = durationSource;
@@ -539,20 +478,28 @@
mConsumer = consumer;
}
+ DynamicDurationBindingRequest(
+ @NonNull DynamicDuration durationSource,
+ @NonNull DynamicTypeValueReceiver<Duration> consumer) {
+ mDurationSource = durationSource;
+ mConsumer = consumer;
+ mExecutor = null;
+ }
+
@Override
BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mDurationSource, mExecutor, mConsumer);
+ return evaluator.bindInternal(mDurationSource, createReceiver(mExecutor, mConsumer));
}
}
- private static class DynamicInstantBindingRequestWithExecutor extends DynamicTypeBindingRequest {
+ private static class DynamicInstantBindingRequest extends DynamicTypeBindingRequest {
- @NonNull private final DynamicBuilders.DynamicInstant mInstantSource;
- @NonNull private final Executor mExecutor;
+ @NonNull private final DynamicInstant mInstantSource;
+ @Nullable private final Executor mExecutor;
@NonNull private final DynamicTypeValueReceiver<Instant> mConsumer;
- DynamicInstantBindingRequestWithExecutor(
- @NonNull DynamicBuilders.DynamicInstant instantSource,
+ DynamicInstantBindingRequest(
+ @NonNull DynamicInstant instantSource,
@NonNull Executor executor,
@NonNull DynamicTypeValueReceiver<Instant> consumer) {
mInstantSource = instantSource;
@@ -560,9 +507,55 @@
mConsumer = consumer;
}
+ DynamicInstantBindingRequest(
+ @NonNull DynamicInstant instantSource,
+ @NonNull DynamicTypeValueReceiver<Instant> consumer) {
+ mInstantSource = instantSource;
+ mConsumer = consumer;
+ mExecutor = null;
+ }
+
@Override
BoundDynamicTypeImpl callBindOn(DynamicTypeEvaluator evaluator) {
- return evaluator.bindInternal(mInstantSource, mExecutor, mConsumer);
+ return evaluator.bindInternal(mInstantSource, createReceiver(mExecutor, mConsumer));
+ }
+ }
+
+ /**
+ * Wraps {@link DynamicTypeValueReceiver} and executes its methods on the given {@link
+ * Executor}.
+ */
+ private static class DynamicTypeValueReceiverOnExecutor<T>
+ implements DynamicTypeValueReceiverWithPreUpdate<T> {
+
+ @NonNull private final Executor mExecutor;
+ @NonNull private final DynamicTypeValueReceiver<T> mConsumer;
+
+ DynamicTypeValueReceiverOnExecutor(@NonNull DynamicTypeValueReceiver<T> consumer) {
+ this(Runnable::run, consumer);
+ }
+
+ DynamicTypeValueReceiverOnExecutor(
+ @NonNull Executor executor, @NonNull DynamicTypeValueReceiver<T> consumer) {
+ this.mConsumer = consumer;
+ this.mExecutor = executor;
+ }
+
+ /** This method is noop in this class. */
+ @Override
+ @SuppressWarnings("ExecutorTaskName")
+ public void onPreUpdate() {}
+
+ @Override
+ @SuppressWarnings("ExecutorTaskName")
+ public void onData(@NonNull T newData) {
+ mExecutor.execute(() -> mConsumer.onData(newData));
+ }
+
+ @Override
+ @SuppressWarnings("ExecutorTaskName")
+ public void onInvalidated() {
+ mExecutor.execute(mConsumer::onInvalidated);
}
}
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
index 5dc4a67..5e49966 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
@@ -27,7 +27,6 @@
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
-import androidx.wear.protolayout.expression.DynamicBuilders;
import androidx.wear.protolayout.expression.PlatformDataKey;
import androidx.wear.protolayout.expression.pipeline.BoolNodes.ComparisonFloatNode;
import androidx.wear.protolayout.expression.pipeline.BoolNodes.ComparisonInt32Node;
@@ -92,7 +91,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Executor;
import java.util.function.Supplier;
/**
@@ -392,133 +390,63 @@
}
@NonNull
- BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicBuilders.DynamicString stringSource,
- @NonNull ULocale locale,
- @NonNull Executor executor,
- @NonNull DynamicTypeValueReceiver<String> consumer) {
- return bindInternal(
- stringSource.toDynamicStringProto(),
- locale,
- new DynamicTypeValueReceiverOnExecutor<>(executor, consumer));
- }
-
- @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
BoundDynamicTypeImpl bindInternal(
@NonNull DynamicString stringSource,
@NonNull ULocale locale,
- @NonNull DynamicTypeValueReceiver<String> consumer) {
+ @NonNull DynamicTypeValueReceiverWithPreUpdate<String> consumer) {
List<DynamicDataNode<?>> resultBuilder = new ArrayList<>();
- bindRecursively(
- stringSource,
- new DynamicTypeValueReceiverOnExecutor<>(consumer),
- locale,
- resultBuilder);
+ bindRecursively(stringSource, consumer, locale, resultBuilder);
return new BoundDynamicTypeImpl(resultBuilder, mDynamicTypesQuotaManager);
}
@NonNull
- BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicBuilders.DynamicInt32 int32Source,
- @NonNull Executor executor,
- @NonNull DynamicTypeValueReceiver<Integer> consumer) {
- return bindInternal(
- int32Source.toDynamicInt32Proto(),
- new DynamicTypeValueReceiverOnExecutor<>(executor, consumer));
- }
-
- @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
BoundDynamicTypeImpl bindInternal(
@NonNull DynamicInt32 int32Source,
- @NonNull DynamicTypeValueReceiver<Integer> consumer) {
+ @NonNull DynamicTypeValueReceiverWithPreUpdate<Integer> consumer) {
List<DynamicDataNode<?>> resultBuilder = new ArrayList<>();
- bindRecursively(
- int32Source, new DynamicTypeValueReceiverOnExecutor<>(consumer), resultBuilder);
+ bindRecursively(int32Source, consumer, resultBuilder);
return new BoundDynamicTypeImpl(resultBuilder, mDynamicTypesQuotaManager);
}
@NonNull
- BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicBuilders.DynamicFloat floatSource,
- @NonNull Executor executor,
- @NonNull DynamicTypeValueReceiver<Float> consumer) {
- return bindInternal(
- floatSource.toDynamicFloatProto(),
- new DynamicTypeValueReceiverOnExecutor<>(executor, consumer));
- }
-
- @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicFloat floatSource, @NonNull DynamicTypeValueReceiver<Float> consumer) {
+ @NonNull DynamicFloat floatSource,
+ @NonNull DynamicTypeValueReceiverWithPreUpdate<Float> consumer) {
List<DynamicDataNode<?>> resultBuilder = new ArrayList<>();
- bindRecursively(
- floatSource, new DynamicTypeValueReceiverOnExecutor<>(consumer), resultBuilder);
+ bindRecursively(floatSource, consumer, resultBuilder);
return new BoundDynamicTypeImpl(resultBuilder, mDynamicTypesQuotaManager);
}
@NonNull
- BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicBuilders.DynamicColor colorSource,
- @NonNull Executor executor,
- @NonNull DynamicTypeValueReceiver<Integer> consumer) {
- return bindInternal(
- colorSource.toDynamicColorProto(),
- new DynamicTypeValueReceiverOnExecutor<>(executor, consumer));
- }
-
- @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
BoundDynamicTypeImpl bindInternal(
@NonNull DynamicColor colorSource,
- @NonNull DynamicTypeValueReceiver<Integer> consumer) {
+ @NonNull DynamicTypeValueReceiverWithPreUpdate<Integer> consumer) {
List<DynamicDataNode<?>> resultBuilder = new ArrayList<>();
- bindRecursively(
- colorSource, new DynamicTypeValueReceiverOnExecutor<>(consumer), resultBuilder);
+ bindRecursively(colorSource, consumer, resultBuilder);
return new BoundDynamicTypeImpl(resultBuilder, mDynamicTypesQuotaManager);
}
@NonNull
- BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicBuilders.DynamicDuration durationSource,
- @NonNull Executor executor,
- @NonNull DynamicTypeValueReceiver<Duration> consumer) {
- return bindInternal(
- durationSource.toDynamicDurationProto(),
- new DynamicTypeValueReceiverOnExecutor<>(executor, consumer));
- }
-
- @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
BoundDynamicTypeImpl bindInternal(
@NonNull DynamicDuration durationSource,
- @NonNull DynamicTypeValueReceiver<Duration> consumer) {
+ @NonNull DynamicTypeValueReceiverWithPreUpdate<Duration> consumer) {
List<DynamicDataNode<?>> resultBuilder = new ArrayList<>();
- bindRecursively(
- durationSource, new DynamicTypeValueReceiverOnExecutor<>(consumer), resultBuilder);
+ bindRecursively(durationSource, consumer, resultBuilder);
return new BoundDynamicTypeImpl(resultBuilder, mDynamicTypesQuotaManager);
}
@NonNull
- BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicBuilders.DynamicInstant instantSource,
- @NonNull Executor executor,
- @NonNull DynamicTypeValueReceiver<Instant> consumer) {
- return bindInternal(
- instantSource.toDynamicInstantProto(),
- new DynamicTypeValueReceiverOnExecutor<>(executor, consumer));
- }
-
- @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
BoundDynamicTypeImpl bindInternal(
@NonNull DynamicInstant instantSource,
- @NonNull DynamicTypeValueReceiver<Instant> consumer) {
+ @NonNull DynamicTypeValueReceiverWithPreUpdate<Instant> consumer) {
List<DynamicDataNode<?>> resultBuilder = new ArrayList<>();
- bindRecursively(
- instantSource, new DynamicTypeValueReceiverOnExecutor<>(consumer), resultBuilder);
+ bindRecursively(instantSource, consumer, resultBuilder);
return new BoundDynamicTypeImpl(resultBuilder, mDynamicTypesQuotaManager);
}
@@ -526,30 +454,19 @@
@RestrictTo(Scope.LIBRARY_GROUP)
BoundDynamicTypeImpl bindInternal(
@NonNull DynamicZonedDateTime zdtSource,
- @NonNull DynamicTypeValueReceiver<ZonedDateTime> consumer) {
+ @NonNull DynamicTypeValueReceiverWithPreUpdate<ZonedDateTime> consumer) {
List<DynamicDataNode<?>> resultBuilder = new ArrayList<>();
- bindRecursively(
- zdtSource, new DynamicTypeValueReceiverOnExecutor<>(consumer), resultBuilder);
+ bindRecursively(zdtSource, consumer, resultBuilder);
return new BoundDynamicTypeImpl(resultBuilder, mDynamicTypesQuotaManager);
}
@NonNull
- BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicBuilders.DynamicBool boolSource,
- @NonNull Executor executor,
- @NonNull DynamicTypeValueReceiver<Boolean> consumer) {
- return bindInternal(
- boolSource.toDynamicBoolProto(),
- new DynamicTypeValueReceiverOnExecutor<>(executor, consumer));
- }
-
- @NonNull
@RestrictTo(Scope.LIBRARY_GROUP)
BoundDynamicTypeImpl bindInternal(
- @NonNull DynamicBool boolSource, @NonNull DynamicTypeValueReceiver<Boolean> consumer) {
+ @NonNull DynamicBool boolSource,
+ @NonNull DynamicTypeValueReceiverWithPreUpdate<Boolean> consumer) {
List<DynamicDataNode<?>> resultBuilder = new ArrayList<>();
- bindRecursively(
- boolSource, new DynamicTypeValueReceiverOnExecutor<>(consumer), resultBuilder);
+ bindRecursively(boolSource, consumer, resultBuilder);
return new BoundDynamicTypeImpl(resultBuilder, mDynamicTypesQuotaManager);
}
@@ -1228,42 +1145,4 @@
resultBuilder.add(node);
}
-
- /**
- * Wraps {@link DynamicTypeValueReceiver} and executes its methods on the given {@link
- * Executor}.
- */
- private static class DynamicTypeValueReceiverOnExecutor<T>
- implements DynamicTypeValueReceiverWithPreUpdate<T> {
-
- @NonNull private final Executor mExecutor;
- @NonNull private final DynamicTypeValueReceiver<T> mConsumer;
-
- DynamicTypeValueReceiverOnExecutor(@NonNull DynamicTypeValueReceiver<T> consumer) {
- this(Runnable::run, consumer);
- }
-
- DynamicTypeValueReceiverOnExecutor(
- @NonNull Executor executor, @NonNull DynamicTypeValueReceiver<T> consumer) {
- this.mConsumer = consumer;
- this.mExecutor = executor;
- }
-
- /** This method is noop in this class. */
- @Override
- @SuppressWarnings("ExecutorTaskName")
- public void onPreUpdate() {}
-
- @Override
- @SuppressWarnings("ExecutorTaskName")
- public void onData(@NonNull T newData) {
- mExecutor.execute(() -> mConsumer.onData(newData));
- }
-
- @Override
- @SuppressWarnings("ExecutorTaskName")
- public void onInvalidated() {
- mExecutor.execute(mConsumer::onInvalidated);
- }
- }
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FloatNodes.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FloatNodes.java
index fb45b99..77a8fb1 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FloatNodes.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FloatNodes.java
@@ -96,13 +96,9 @@
}
private static float computeResult(
- DynamicProto.ArithmeticOpType opType, Float lhs, Float rhs) {
+ DynamicProto.ArithmeticOpType opType, float lhs, float rhs) {
try {
switch (opType) {
- case ARITHMETIC_OP_TYPE_UNDEFINED:
- case UNRECOGNIZED:
- Log.e(TAG, "Unknown operation type in ArithmeticFloatNode");
- return Float.NaN;
case ARITHMETIC_OP_TYPE_ADD:
return lhs + rhs;
case ARITHMETIC_OP_TYPE_SUBTRACT:
@@ -113,13 +109,16 @@
return lhs / rhs;
case ARITHMETIC_OP_TYPE_MODULO:
return lhs % rhs;
+ case ARITHMETIC_OP_TYPE_UNDEFINED:
+ case UNRECOGNIZED:
+ break;
}
} catch (ArithmeticException ex) {
Log.e(TAG, "ArithmeticException in ArithmeticFloatNode", ex);
return Float.NaN;
}
- Log.e(TAG, "Unknown operation type in ArithmeticFloatNode");
- return Float.NaN;
+ throw new IllegalArgumentException(
+ "Unknown operation type in ArithmeticFloatNode: " + opType);
}
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/Int32Nodes.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/Int32Nodes.java
index b43d388..6aca826 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/Int32Nodes.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/Int32Nodes.java
@@ -133,10 +133,6 @@
(lhs, rhs) -> {
try {
switch (protoNode.getOperationType()) {
- case ARITHMETIC_OP_TYPE_UNDEFINED:
- case UNRECOGNIZED:
- Log.e(TAG, "Unknown operation type in ArithmeticInt32Node");
- return 0;
case ARITHMETIC_OP_TYPE_ADD:
return lhs + rhs;
case ARITHMETIC_OP_TYPE_SUBTRACT:
@@ -147,14 +143,18 @@
return lhs / rhs;
case ARITHMETIC_OP_TYPE_MODULO:
return lhs % rhs;
+ case ARITHMETIC_OP_TYPE_UNDEFINED:
+ case UNRECOGNIZED:
+ break;
}
} catch (ArithmeticException ex) {
Log.e(TAG, "ArithmeticException in ArithmeticInt32Node", ex);
return null;
}
- Log.e(TAG, "Unknown operation type in ArithmeticInt32Node");
- return 0;
+ throw new IllegalArgumentException(
+ "Unknown operation type in ArithmeticInt32Node: "
+ + protoNode.getOperationType());
});
}
}
@@ -185,16 +185,18 @@
downstream,
x -> {
switch (protoNode.getRoundMode()) {
- case ROUND_MODE_UNDEFINED:
case ROUND_MODE_FLOOR:
return (int) Math.floor(x);
case ROUND_MODE_ROUND:
return Math.round(x);
case ROUND_MODE_CEILING:
return (int) Math.ceil(x);
- default:
- throw new IllegalArgumentException("Unknown rounding mode");
+ case ROUND_MODE_UNDEFINED:
+ case UNRECOGNIZED:
+ break;
}
+ throw new IllegalArgumentException(
+ "Unknown rounding mode:" + protoNode.getRoundMode());
},
x -> x - 1 < Integer.MAX_VALUE && x >= Integer.MIN_VALUE);
}
@@ -214,10 +216,6 @@
private static long getDurationPart(Duration duration, DurationPartType durationPartType) {
switch (durationPartType) {
- case DURATION_PART_TYPE_UNDEFINED:
- case UNRECOGNIZED:
- Log.e(TAG, "Unknown duration part type in GetDurationPartOpNode");
- return 0;
case DURATION_PART_TYPE_DAYS:
return abs(duration.getSeconds() / (3600 * 24));
case DURATION_PART_TYPE_HOURS:
@@ -234,8 +232,11 @@
return duration.toMinutes();
case DURATION_PART_TYPE_TOTAL_SECONDS:
return duration.getSeconds();
+ case DURATION_PART_TYPE_UNDEFINED:
+ case UNRECOGNIZED:
+ break;
}
- throw new IllegalArgumentException("Unknown duration part");
+ throw new IllegalArgumentException("Unknown duration part: " + durationPartType);
}
}
@@ -363,10 +364,6 @@
private static long getZonedDateTimePart(ZonedDateTime zdt, ZonedDateTimePartType type) {
switch (type) {
- case ZONED_DATE_TIME_PART_UNDEFINED:
- case UNRECOGNIZED:
- Log.e(TAG, "Unknown ZonedDateTime part.");
- return 0;
case ZONED_DATE_TIME_PART_SECOND:
return zdt.getSecond();
case ZONED_DATE_TIME_PART_MINUTE:
@@ -381,8 +378,11 @@
return zdt.getMonth().getValue();
case ZONED_DATE_TIME_PART_YEAR:
return zdt.getYear();
+ case ZONED_DATE_TIME_PART_UNDEFINED:
+ case UNRECOGNIZED:
+ break;
}
- throw new IllegalArgumentException("Unknown ZonedDateTime part.");
+ throw new IllegalArgumentException("Unknown ZonedDateTime part: " + type);
}
}
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java
index 176d5bc..7a67bd8e 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java
@@ -16,23 +16,37 @@
package androidx.wear.protolayout.expression.pipeline;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ComparisonOpType.COMPARISON_OP_TYPE_EQUALS;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ComparisonOpType.COMPARISON_OP_TYPE_GREATER_THAN;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ComparisonOpType.COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ComparisonOpType.COMPARISON_OP_TYPE_LESS_THAN;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ComparisonOpType.COMPARISON_OP_TYPE_LESS_THAN_OR_EQUAL_TO;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ComparisonOpType.COMPARISON_OP_TYPE_NOT_EQUALS;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ComparisonOpType.COMPARISON_OP_TYPE_UNDEFINED;
import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_AND;
import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_EQUAL;
import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_NOT_EQUAL;
import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_OR;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.LogicalOpType.LOGICAL_OP_TYPE_UNDEFINED;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.wear.protolayout.expression.AppDataKey;
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool;
import androidx.wear.protolayout.expression.pipeline.BoolNodes.FixedBoolNode;
import androidx.wear.protolayout.expression.pipeline.BoolNodes.StateBoolNode;
+import androidx.wear.protolayout.expression.pipeline.FloatNodes.FixedFloatNode;
+import androidx.wear.protolayout.expression.pipeline.Int32Nodes.FixedInt32Node;
import androidx.wear.protolayout.expression.proto.DynamicDataProto.DynamicDataValue;
import androidx.wear.protolayout.expression.proto.DynamicProto;
import androidx.wear.protolayout.expression.proto.DynamicProto.LogicalBoolOp;
import androidx.wear.protolayout.expression.proto.DynamicProto.StateBoolSource;
import androidx.wear.protolayout.expression.proto.FixedProto.FixedBool;
+import androidx.wear.protolayout.expression.proto.FixedProto.FixedFloat;
+import androidx.wear.protolayout.expression.proto.FixedProto.FixedInt32;
import com.google.common.collect.ImmutableMap;
@@ -44,129 +58,302 @@
@RunWith(AndroidJUnit4.class)
public class BoolNodesTest {
- @Test
- public void fixedBoolNodeTest() {
- List<Boolean> results = new ArrayList<>();
+ @Test
+ public void fixedBoolNodeTest() {
+ List<Boolean> results = new ArrayList<>();
- FixedBool protoNode = FixedBool.newBuilder().setValue(false).build();
- FixedBoolNode node = new FixedBoolNode(protoNode, new AddToListCallback<>(results));
+ FixedBool protoNode = FixedBool.newBuilder().setValue(false).build();
+ FixedBoolNode node = new FixedBoolNode(protoNode, new AddToListCallback<>(results));
- node.preInit();
- node.init();
+ node.preInit();
+ node.init();
- assertThat(results).containsExactly(false);
- }
+ assertThat(results).containsExactly(false);
+ }
- @Test
- public void stateBoolNodeTest() {
- List<Boolean> results = new ArrayList<>();
- StateStore oss =
- new StateStore(
- ImmutableMap.of(
- new AppDataKey<DynamicBool>("foo"),
- DynamicDataValue.newBuilder()
- .setBoolVal(FixedBool.newBuilder().setValue(true))
- .build()));
+ @Test
+ public void stateBoolNodeTest() {
+ List<Boolean> results = new ArrayList<>();
+ StateStore oss =
+ new StateStore(
+ ImmutableMap.of(
+ new AppDataKey<DynamicBool>("foo"),
+ DynamicDataValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(true))
+ .build()));
- StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
- StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
+ StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
+ StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
- node.preInit();
- node.init();
+ node.preInit();
+ node.init();
- assertThat(results).containsExactly(true);
- }
+ assertThat(results).containsExactly(true);
+ }
- @Test
- public void stateBoolUpdatesWithStateChanges() {
- List<Boolean> results = new ArrayList<>();
- StateStore oss =
- new StateStore(
- ImmutableMap.of(
- new AppDataKey<DynamicBool>("foo"),
- DynamicDataValue.newBuilder()
- .setBoolVal(FixedBool.newBuilder().setValue(true))
- .build()));
+ @Test
+ public void stateBoolUpdatesWithStateChanges() {
+ List<Boolean> results = new ArrayList<>();
+ StateStore oss =
+ new StateStore(
+ ImmutableMap.of(
+ new AppDataKey<DynamicBool>("foo"),
+ DynamicDataValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(true))
+ .build()));
- StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
- StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
+ StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
+ StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
- node.preInit();
- node.init();
+ node.preInit();
+ node.init();
- results.clear();
+ results.clear();
- oss.setAppStateEntryValuesProto(
- ImmutableMap.of(
- new AppDataKey<DynamicBool>("foo"),
- DynamicDataValue.newBuilder()
- .setBoolVal(FixedBool.newBuilder().setValue(false))
- .build()));
+ oss.setAppStateEntryValuesProto(
+ ImmutableMap.of(
+ new AppDataKey<DynamicBool>("foo"),
+ DynamicDataValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(false))
+ .build()));
- assertThat(results).containsExactly(false);
- }
+ assertThat(results).containsExactly(false);
+ }
- @Test
- public void stateBoolNoUpdatesAfterDestroy() {
- List<Boolean> results = new ArrayList<>();
- AppDataKey<DynamicBool> keyFoo = new AppDataKey<>("foo");
- StateStore oss =
- new StateStore(
- ImmutableMap.of(
- keyFoo,
- DynamicDataValue.newBuilder()
- .setBoolVal(FixedBool.newBuilder().setValue(false))
- .build()));
+ @Test
+ public void stateBoolNoUpdatesAfterDestroy() {
+ List<Boolean> results = new ArrayList<>();
+ AppDataKey<DynamicBool> keyFoo = new AppDataKey<>("foo");
+ StateStore oss =
+ new StateStore(
+ ImmutableMap.of(
+ keyFoo,
+ DynamicDataValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(false))
+ .build()));
- StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
- StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
+ StateBoolSource protoNode = StateBoolSource.newBuilder().setSourceKey("foo").build();
+ StateBoolNode node = new StateBoolNode(oss, protoNode, new AddToListCallback<>(results));
- node.preInit();
- node.init();
- assertThat(results).containsExactly(false);
+ node.preInit();
+ node.init();
+ assertThat(results).containsExactly(false);
- results.clear();
- node.destroy();
- oss.setAppStateEntryValuesProto(
- ImmutableMap.of(
- keyFoo,
- DynamicDataValue.newBuilder()
- .setBoolVal(FixedBool.newBuilder().setValue(true))
- .build()));
- assertThat(results).isEmpty();
- }
+ results.clear();
+ node.destroy();
+ oss.setAppStateEntryValuesProto(
+ ImmutableMap.of(
+ keyFoo,
+ DynamicDataValue.newBuilder()
+ .setBoolVal(FixedBool.newBuilder().setValue(true))
+ .build()));
+ assertThat(results).isEmpty();
+ }
- @Test
- public void logicalBoolOpTest() {
- assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_AND, true, true)).isTrue();
- assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_AND, true, false)).isFalse();
+ @Test
+ public void logicalBoolOpTest_unknownOp_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluateLogicalOperation(LOGICAL_OP_TYPE_UNDEFINED, true, true));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluateLogicalOperation(-1 /* UNRECOGNIZED */, true, true));
+ }
- assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_OR, true, false)).isTrue();
- assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_OR, false, false)).isFalse();
+ @Test
+ public void logicalBoolOpTest() {
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_AND, true, true)).isTrue();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_AND, true, false)).isFalse();
- assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_EQUAL, true, true)).isTrue();
- assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_EQUAL, true, false)).isFalse();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_OR, true, false)).isTrue();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_OR, false, false)).isFalse();
- assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_NOT_EQUAL, true, false)).isTrue();
- assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_NOT_EQUAL, false, false)).isFalse();
- }
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_EQUAL, true, true)).isTrue();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_EQUAL, true, false)).isFalse();
- private static boolean evaluateLogicalOperation(
- DynamicProto.LogicalOpType logicalOpType, boolean lhs, boolean rhs) {
- List<Boolean> results = new ArrayList<>();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_NOT_EQUAL, true, false)).isTrue();
+ assertThat(evaluateLogicalOperation(LOGICAL_OP_TYPE_NOT_EQUAL, false, false)).isFalse();
+ }
- LogicalBoolOp protoNode = LogicalBoolOp.newBuilder().setOperationType(logicalOpType).build();
- BoolNodes.LogicalBoolOp node =
- new BoolNodes.LogicalBoolOp(protoNode, new AddToListCallback<>(results));
+ @Test
+ public void int32CompareOp_unknownOp_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_UNDEFINED, 1, 1));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluateInt32ComparisonOperation(-1 /* UNRECOGNIZED */, 1, 1));
+ }
- FixedBool lhsProtoNode = FixedBool.newBuilder().setValue(lhs).build();
- FixedBoolNode lhsNode = new FixedBoolNode(lhsProtoNode, node.getLhsUpstreamCallback());
- lhsNode.init();
+ @Test
+ public void int32CompareOpTest() {
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_EQUALS, 1, 1)).isTrue();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_EQUALS, 1, 2)).isFalse();
- FixedBool rhsProtoNode = FixedBool.newBuilder().setValue(rhs).build();
- FixedBoolNode rhsNode = new FixedBoolNode(rhsProtoNode, node.getRhsUpstreamCallback());
- rhsNode.init();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_NOT_EQUALS, 1, 1)).isFalse();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_NOT_EQUALS, 1, 2)).isTrue();
- return results.get(0);
- }
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_GREATER_THAN, 1, 2))
+ .isFalse();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_GREATER_THAN, 1, 1))
+ .isFalse();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_GREATER_THAN, 2, 1))
+ .isTrue();
+
+ assertThat(
+ evaluateInt32ComparisonOperation(
+ COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO, 1, 2))
+ .isFalse();
+ assertThat(
+ evaluateInt32ComparisonOperation(
+ COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO, 1, 1))
+ .isTrue();
+ assertThat(
+ evaluateInt32ComparisonOperation(
+ COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO, 2, 1))
+ .isTrue();
+
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN, 2, 1)).isFalse();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN, 1, 1)).isFalse();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN, 1, 2)).isTrue();
+
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN_OR_EQUAL_TO, 2, 1))
+ .isFalse();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN_OR_EQUAL_TO, 1, 1))
+ .isTrue();
+ assertThat(evaluateInt32ComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN_OR_EQUAL_TO, 1, 2))
+ .isTrue();
+ }
+
+ @Test
+ public void floatCompareOp_unknownOp_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_UNDEFINED, 1, 1));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluateFloatComparisonOperation(-1 /* UNRECOGNIZED */, 1, 1));
+ }
+
+ @Test
+ public void floatCompareOpTest() {
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_EQUALS, 1, 1)).isTrue();
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_EQUALS, 1, 2)).isFalse();
+
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_NOT_EQUALS, 1, 1)).isFalse();
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_NOT_EQUALS, 1, 2)).isTrue();
+
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_GREATER_THAN, 1, 2))
+ .isFalse();
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_GREATER_THAN, 1, 1))
+ .isFalse();
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_GREATER_THAN, 2, 1))
+ .isTrue();
+
+ assertThat(
+ evaluateFloatComparisonOperation(
+ COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO, 1, 2))
+ .isFalse();
+ assertThat(
+ evaluateFloatComparisonOperation(
+ COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO, 1, 1))
+ .isTrue();
+ assertThat(
+ evaluateFloatComparisonOperation(
+ COMPARISON_OP_TYPE_GREATER_THAN_OR_EQUAL_TO, 2, 1))
+ .isTrue();
+
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN, 2, 1)).isFalse();
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN, 1, 1)).isFalse();
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN, 1, 2)).isTrue();
+
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN_OR_EQUAL_TO, 2, 1))
+ .isFalse();
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN_OR_EQUAL_TO, 1, 1))
+ .isTrue();
+ assertThat(evaluateFloatComparisonOperation(COMPARISON_OP_TYPE_LESS_THAN_OR_EQUAL_TO, 1, 2))
+ .isTrue();
+ }
+
+ private static boolean evaluateLogicalOperation(
+ DynamicProto.LogicalOpType logicalOpType, boolean lhs, boolean rhs) {
+ return evaluateLogicalOperation(logicalOpType.getNumber(), lhs, rhs);
+ }
+
+ private static boolean evaluateLogicalOperation(int logicalOpType, boolean lhs, boolean rhs) {
+ List<Boolean> results = new ArrayList<>();
+
+ LogicalBoolOp protoNode =
+ LogicalBoolOp.newBuilder().setOperationTypeValue(logicalOpType).build();
+ BoolNodes.LogicalBoolOp node =
+ new BoolNodes.LogicalBoolOp(protoNode, new AddToListCallback<>(results));
+
+ FixedBool lhsProtoNode = FixedBool.newBuilder().setValue(lhs).build();
+ FixedBoolNode lhsNode = new FixedBoolNode(lhsProtoNode, node.getLhsUpstreamCallback());
+
+ FixedBool rhsProtoNode = FixedBool.newBuilder().setValue(rhs).build();
+ FixedBoolNode rhsNode = new FixedBoolNode(rhsProtoNode, node.getRhsUpstreamCallback());
+
+ lhsNode.preInit();
+ rhsNode.preInit();
+
+ lhsNode.init();
+ rhsNode.init();
+
+ return results.get(0);
+ }
+
+ private static boolean evaluateInt32ComparisonOperation(
+ DynamicProto.ComparisonOpType opType, int lhs, int rhs) {
+ return evaluateInt32ComparisonOperation(opType.getNumber(), lhs, rhs);
+ }
+
+ private static boolean evaluateInt32ComparisonOperation(int opType, int lhs, int rhs) {
+ List<Boolean> results = new ArrayList<>();
+
+ DynamicProto.ComparisonInt32Op protoNode =
+ DynamicProto.ComparisonInt32Op.newBuilder().setOperationTypeValue(opType).build();
+ BoolNodes.ComparisonInt32Node node =
+ new BoolNodes.ComparisonInt32Node(protoNode, new AddToListCallback<>(results));
+
+ FixedInt32 lhsProtoNode = FixedInt32.newBuilder().setValue(lhs).build();
+ FixedInt32Node lhsNode = new FixedInt32Node(lhsProtoNode, node.getLhsUpstreamCallback());
+ lhsNode.preInit();
+
+ FixedInt32 rhsProtoNode = FixedInt32.newBuilder().setValue(rhs).build();
+ FixedInt32Node rhsNode = new FixedInt32Node(rhsProtoNode, node.getRhsUpstreamCallback());
+ rhsNode.preInit();
+
+ lhsNode.init();
+ rhsNode.init();
+
+ return results.get(0);
+ }
+
+ private static boolean evaluateFloatComparisonOperation(
+ DynamicProto.ComparisonOpType opType, float lhs, float rhs) {
+ return evaluateFloatComparisonOperation(opType.getNumber(), lhs, rhs);
+ }
+
+ private static boolean evaluateFloatComparisonOperation(int opType, float lhs, float rhs) {
+ List<Boolean> results = new ArrayList<>();
+
+ DynamicProto.ComparisonFloatOp protoNode =
+ DynamicProto.ComparisonFloatOp.newBuilder().setOperationTypeValue(opType).build();
+ BoolNodes.ComparisonFloatNode node =
+ new BoolNodes.ComparisonFloatNode(protoNode, new AddToListCallback<>(results));
+
+ FixedFloat lhsProtoNode = FixedFloat.newBuilder().setValue(lhs).build();
+ FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsUpstreamCallback());
+ lhsNode.preInit();
+
+ FixedFloat rhsProtoNode = FixedFloat.newBuilder().setValue(rhs).build();
+ FixedFloatNode rhsNode = new FixedFloatNode(rhsProtoNode, node.getRhsUpstreamCallback());
+ rhsNode.preInit();
+
+ lhsNode.init();
+ rhsNode.init();
+
+ return results.get(0);
+ }
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
index 55d6ee1..dca1d9e 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
@@ -33,6 +33,8 @@
import androidx.wear.protolayout.expression.PlatformDataKey;
import androidx.wear.protolayout.expression.PlatformHealthSources;
import androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.EvaluationException;
+import androidx.wear.protolayout.expression.proto.DynamicProto;
+import androidx.wear.protolayout.expression.proto.FixedProto;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,6 +58,16 @@
}
@Test
+ public void evaluateBindingRequest_nodeThrows_propagateTheException() {
+ DynamicTypeEvaluator evaluator = createEvaluator();
+ ArrayList<Integer> results = new ArrayList<>();
+ DynamicTypeBindingRequest request = createExpressionWithUnrecognizedEnum(results);
+
+ assertThrows(
+ IllegalArgumentException.class, () -> evaluator.bind(request).startEvaluation());
+ }
+
+ @Test
public void evaluateBindingRequest_insufficientDynamicNodeQuota_throws() {
DynamicTypeEvaluator evaluator =
createEvaluatorWithQuota(
@@ -110,11 +122,10 @@
AddToListCallback<Integer> results = new AddToListCallback<>(new ArrayList<>());
DynamicTypeBindingRequest request =
DynamicTypeBindingRequest.forDynamicInt32(
- PlatformHealthSources.dailySteps(),
- new MainThreadExecutor(), results);
+ PlatformHealthSources.dailySteps(), new MainThreadExecutor(), results);
PlatformDataProvider provider = mock(PlatformDataProvider.class);
- DynamicTypeEvaluator evaluator = createEvaluatorWithProvider(provider,
- PlatformHealthSources.Keys.DAILY_STEPS);
+ DynamicTypeEvaluator evaluator =
+ createEvaluatorWithProvider(provider, PlatformHealthSources.Keys.DAILY_STEPS);
BoundDynamicType boundDynamicType = evaluator.bind(request);
boundDynamicType.startEvaluation();
@@ -133,8 +144,8 @@
/* animationQuota= */ unlimitedQuota(),
/* dynamicTypesQuota= */ new FixedQuotaManagerImpl(1));
ArrayList<Boolean> results = new ArrayList<>();
- BoundDynamicType boundDynamicType = evaluator.bind(
- createSingleNodeDynamicBoolRequest(results));
+ BoundDynamicType boundDynamicType =
+ evaluator.bind(createSingleNodeDynamicBoolRequest(results));
for (int i = 0; i < 10; i++) {
boundDynamicType.close();
@@ -151,12 +162,33 @@
}
@NonNull
+ private static DynamicTypeBindingRequest createExpressionWithUnrecognizedEnum(
+ ArrayList<Integer> results) {
+ return DynamicTypeBindingRequest.forDynamicInt32Internal(
+ DynamicProto.DynamicInt32.newBuilder()
+ .setFloatToInt(
+ DynamicProto.FloatToInt32Op.newBuilder()
+ .setInput(
+ DynamicProto.DynamicFloat.newBuilder()
+ .setFixed(
+ FixedProto.FixedFloat
+ .getDefaultInstance())
+ .build())
+ .setRoundModeValue(-1)
+ .build())
+ .build(),
+ new AddToListCallback<Integer>(results));
+ }
+
+ @NonNull
private static DynamicTypeBindingRequest createSingleNodeDynamicStringFromTimePlatformRequest(
ArrayList<String> results) {
return DynamicTypeBindingRequest.forDynamicString(
- DynamicBuilders.DynamicInstant.platformTimeWithSecondsPrecision().durationUntil(
- DynamicBuilders.DynamicInstant
- .withSecondsPrecision(Instant.now())).getSecondsPart().format(),
+ DynamicBuilders.DynamicInstant.platformTimeWithSecondsPrecision()
+ .durationUntil(
+ DynamicBuilders.DynamicInstant.withSecondsPrecision(Instant.now()))
+ .getSecondsPart()
+ .format(),
ULocale.ENGLISH,
new MainThreadExecutor(),
new AddToListCallback<>(results));
@@ -166,8 +198,8 @@
return createEvaluatorWithQuota(unlimitedQuota(), unlimitedQuota());
}
- private static DynamicTypeEvaluator createEvaluatorWithProvider(PlatformDataProvider provider
- , PlatformDataKey<?> key) {
+ private static DynamicTypeEvaluator createEvaluatorWithProvider(
+ PlatformDataProvider provider, PlatformDataKey<?> key) {
return new DynamicTypeEvaluator(
new DynamicTypeEvaluator.Config.Builder()
.setAnimationQuotaManager(unlimitedQuota())
@@ -216,8 +248,7 @@
}
@Override
- public void setReceiver(
- @NonNull Executor executor, @NonNull Runnable tick) {
+ public void setReceiver(@NonNull Executor executor, @NonNull Runnable tick) {
super.setReceiver(executor, tick);
mRegisteredReceiver = tick;
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/FloatNodeTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/FloatNodeTest.java
index c8aea06..1ca5905 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/FloatNodeTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/FloatNodeTest.java
@@ -20,6 +20,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
@@ -345,30 +346,35 @@
}
@Test
+ public void arithmeticFloat_unknownOperation_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ evaluateArithmeticExpression(
+ /* lhs= */ 1,
+ /* rhs= */ 1,
+ ArithmeticOpType.ARITHMETIC_OP_TYPE_UNDEFINED,
+ new AddToListCallback<>(new ArrayList<>())));
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ evaluateArithmeticExpression(
+ /* lhs= */ 1,
+ /* rhs= */ 1,
+ -1 /* UNRECOGNIZED */,
+ new AddToListCallback<>(new ArrayList<>())));
+ }
+
+ @Test
public void arithmeticFloat_resultIsNaN_invalidate() {
List<Float> results = new ArrayList<>();
List<Boolean> invalidList = new ArrayList<>();
- ArithmeticFloatOp protoNode =
- ArithmeticFloatOp.newBuilder()
- .setOperationType(ArithmeticOpType.ARITHMETIC_OP_TYPE_DIVIDE)
- .build();
-
- ArithmeticFloatNode node =
- new ArithmeticFloatNode(protoNode, new AddToListCallback<>(results, invalidList));
-
- float numerator = 0f;
- FixedFloat lhsProtoNode = FixedFloat.newBuilder().setValue(numerator).build();
- FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsUpstreamCallback());
-
- float denominator = 0f;
- FixedFloat rhsProtoNode = FixedFloat.newBuilder().setValue(denominator).build();
- FixedFloatNode rhsNode = new FixedFloatNode(rhsProtoNode, node.getRhsUpstreamCallback());
- lhsNode.preInit();
- rhsNode.preInit();
-
- lhsNode.init();
- rhsNode.init();
+ evaluateArithmeticExpression(
+ /* lhs= */ 0,
+ /* rhs= */ 0,
+ ArithmeticOpType.ARITHMETIC_OP_TYPE_DIVIDE,
+ new AddToListCallback<>(results, invalidList));
assertThat(results).isEmpty();
assertThat(invalidList).containsExactly(true);
@@ -379,26 +385,11 @@
List<Float> results = new ArrayList<>();
List<Boolean> invalidList = new ArrayList<>();
- ArithmeticFloatOp protoNode =
- ArithmeticFloatOp.newBuilder()
- .setOperationType(ArithmeticOpType.ARITHMETIC_OP_TYPE_DIVIDE)
- .build();
-
- ArithmeticFloatNode node =
- new ArithmeticFloatNode(protoNode, new AddToListCallback<>(results, invalidList));
-
- float numerator = 1f;
- FixedFloat lhsProtoNode = FixedFloat.newBuilder().setValue(numerator).build();
- FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsUpstreamCallback());
-
- float denominator = 0;
- FixedFloat rhsProtoNode = FixedFloat.newBuilder().setValue(denominator).build();
- FixedFloatNode rhsNode = new FixedFloatNode(rhsProtoNode, node.getRhsUpstreamCallback());
- lhsNode.preInit();
- rhsNode.preInit();
-
- lhsNode.init();
- rhsNode.init();
+ evaluateArithmeticExpression(
+ /* lhs= */ 1,
+ /* rhs= */ 0,
+ ArithmeticOpType.ARITHMETIC_OP_TYPE_DIVIDE,
+ new AddToListCallback<>(results, invalidList));
assertThat(results).isEmpty();
assertThat(invalidList).containsExactly(true);
@@ -570,4 +561,31 @@
assertThat(Iterables.getLast(results)).isEqualTo(value3);
assertThat(results).isInOrder();
}
+
+ private static void evaluateArithmeticExpression(
+ float lhs,
+ float rhs,
+ ArithmeticOpType op,
+ DynamicTypeValueReceiverWithPreUpdate<Float> receiver) {
+ evaluateArithmeticExpression(lhs, rhs, op.getNumber(), receiver);
+ }
+
+ private static void evaluateArithmeticExpression(
+ float lhs, float rhs, int op, DynamicTypeValueReceiverWithPreUpdate<Float> receiver) {
+ ArithmeticFloatOp protoNode =
+ ArithmeticFloatOp.newBuilder().setOperationTypeValue(op).build();
+
+ ArithmeticFloatNode node = new ArithmeticFloatNode(protoNode, receiver);
+
+ FixedFloat lhsProtoNode = FixedFloat.newBuilder().setValue(lhs).build();
+ FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsUpstreamCallback());
+
+ FixedFloat rhsProtoNode = FixedFloat.newBuilder().setValue(rhs).build();
+ FixedFloatNode rhsNode = new FixedFloatNode(rhsProtoNode, node.getRhsUpstreamCallback());
+ lhsNode.preInit();
+ rhsNode.preInit();
+
+ lhsNode.init();
+ rhsNode.init();
+ }
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/Int32NodesTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/Int32NodesTest.java
index 1771223..8b7fce5 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/Int32NodesTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/Int32NodesTest.java
@@ -18,9 +18,16 @@
import static androidx.wear.protolayout.expression.PlatformHealthSources.Keys.DAILY_STEPS;
import static androidx.wear.protolayout.expression.PlatformHealthSources.Keys.HEART_RATE_BPM;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ArithmeticOpType.ARITHMETIC_OP_TYPE_DIVIDE;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.ArithmeticOpType.ARITHMETIC_OP_TYPE_UNDEFINED;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.FloatToInt32RoundMode.ROUND_MODE_CEILING;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.FloatToInt32RoundMode.ROUND_MODE_FLOOR;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.FloatToInt32RoundMode.ROUND_MODE_ROUND;
+import static androidx.wear.protolayout.expression.proto.DynamicProto.FloatToInt32RoundMode.ROUND_MODE_UNDEFINED;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
@@ -46,7 +53,6 @@
import androidx.wear.protolayout.expression.proto.DynamicDataProto.DynamicDataValue;
import androidx.wear.protolayout.expression.proto.DynamicProto;
import androidx.wear.protolayout.expression.proto.DynamicProto.AnimatableFixedInt32;
-import androidx.wear.protolayout.expression.proto.DynamicProto.ArithmeticOpType;
import androidx.wear.protolayout.expression.proto.DynamicProto.DurationPartType;
import androidx.wear.protolayout.expression.proto.DynamicProto.GetDurationPartOp;
import androidx.wear.protolayout.expression.proto.DynamicProto.GetZonedDateTimePartOp;
@@ -99,32 +105,37 @@
}
@Test
+ public void testArithmeticOperation_unknownOp_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ evaluateArithmeticExpression(
+ 1,
+ 1,
+ ARITHMETIC_OP_TYPE_UNDEFINED.getNumber(),
+ new AddToListCallback<>(new ArrayList<>())));
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ evaluateArithmeticExpression(
+ /* lhs= */ 1,
+ /* rhs= */ 1,
+ -1 /* UNRECOGNIZED */,
+ new AddToListCallback<>(new ArrayList<>())));
+ }
+
+ @Test
public void testArithmeticOperation_validResult_invalidateNotCalled() {
List<Integer> results = new ArrayList<>();
List<Boolean> invalidList = new ArrayList<>();
- DynamicProto.ArithmeticInt32Op protoNode =
- DynamicProto.ArithmeticInt32Op.newBuilder()
- .setOperationType(ArithmeticOpType.ARITHMETIC_OP_TYPE_DIVIDE)
- .build();
+ evaluateArithmeticExpression(
+ /* lhs= */ 4,
+ /* rhs= */ 3,
+ ARITHMETIC_OP_TYPE_DIVIDE.getNumber(),
+ new AddToListCallback<>(results, invalidList));
- ArithmeticInt32Node node =
- new ArithmeticInt32Node(protoNode, new AddToListCallback<>(results, invalidList));
-
- int numerator = 4;
- FixedInt32 lhsProtoNode = FixedInt32.newBuilder().setValue(numerator).build();
- FixedInt32Node lhsNode = new FixedInt32Node(lhsProtoNode, node.getLhsUpstreamCallback());
-
- int denominator = 2;
- FixedInt32 rhsProtoNode = FixedInt32.newBuilder().setValue(denominator).build();
- FixedInt32Node rhsNode = new FixedInt32Node(rhsProtoNode, node.getRhsUpstreamCallback());
- lhsNode.preInit();
- rhsNode.preInit();
-
- lhsNode.init();
- rhsNode.init();
-
- assertThat(results).containsExactly(2);
+ assertThat(results).containsExactly(1);
assertThat(invalidList).isEmpty();
}
@@ -133,32 +144,32 @@
List<Integer> results = new ArrayList<>();
List<Boolean> invalidList = new ArrayList<>();
- DynamicProto.ArithmeticInt32Op protoNode =
- DynamicProto.ArithmeticInt32Op.newBuilder()
- .setOperationType(ArithmeticOpType.ARITHMETIC_OP_TYPE_DIVIDE)
- .build();
-
- ArithmeticInt32Node node =
- new ArithmeticInt32Node(protoNode, new AddToListCallback<>(results, invalidList));
-
- int numerator = 0;
- FixedInt32 lhsProtoNode = FixedInt32.newBuilder().setValue(numerator).build();
- FixedInt32Node lhsNode = new FixedInt32Node(lhsProtoNode, node.getLhsUpstreamCallback());
-
- int denominator = 0;
- FixedInt32 rhsProtoNode = FixedInt32.newBuilder().setValue(denominator).build();
- FixedInt32Node rhsNode = new FixedInt32Node(rhsProtoNode, node.getRhsUpstreamCallback());
- lhsNode.preInit();
- rhsNode.preInit();
-
- lhsNode.init();
- rhsNode.init();
+ evaluateArithmeticExpression(
+ /* lhs= */ 0,
+ /* rhs= */ 0,
+ ARITHMETIC_OP_TYPE_DIVIDE.getNumber(),
+ new AddToListCallback<>(results, invalidList));
assertThat(results).isEmpty();
assertThat(invalidList).containsExactly(true);
}
@Test
+ public void testGetDurationPartOpNode_unknownPart_throws() {
+ Duration duration = Duration.ofSeconds(123456);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ createGetDurationPartOpNodeAndGetPart(
+ duration, DurationPartType.DURATION_PART_TYPE_UNDEFINED));
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> createGetDurationPartOpNodeAndGetPart(duration, -1 /* UNRECOGNIZED */));
+ }
+
+ @Test
public void testGetDurationPartOpNode_positiveDuration() {
// Equivalent to 1day and 10h:17m:36s
@@ -239,10 +250,14 @@
}
private int createGetDurationPartOpNodeAndGetPart(Duration duration, DurationPartType part) {
+ return createGetDurationPartOpNodeAndGetPart(duration, part.getNumber());
+ }
+
+ private int createGetDurationPartOpNodeAndGetPart(Duration duration, int part) {
List<Integer> results = new ArrayList<>();
GetDurationPartOpNode node =
new GetDurationPartOpNode(
- GetDurationPartOp.newBuilder().setDurationPart(part).build(),
+ GetDurationPartOp.newBuilder().setDurationPartValue(part).build(),
new AddToListCallback<>(results));
node.getIncomingCallback().onData(duration);
return results.get(0);
@@ -632,4 +647,60 @@
zonedDateTime, ZonedDateTimePartType.ZONED_DATE_TIME_PART_SECOND))
.isEqualTo(0);
}
+
+ @Test
+ public void testFloatToInt32Node() {
+ assertThat(evaluateFloatToInt32Expression(12.49f, ROUND_MODE_CEILING)).isEqualTo(13);
+ assertThat(evaluateFloatToInt32Expression(12.99f, ROUND_MODE_FLOOR)).isEqualTo(12);
+ assertThat(evaluateFloatToInt32Expression(12.49f, ROUND_MODE_ROUND)).isEqualTo(12);
+ assertThat(evaluateFloatToInt32Expression(12.50f, ROUND_MODE_ROUND)).isEqualTo(13);
+ }
+
+ @Test
+ public void testFloatToInt32Node__unknownRoundType_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluateFloatToInt32Expression(12.34f, ROUND_MODE_UNDEFINED));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> evaluateFloatToInt32Expression(12.34f, -1 /* UNRECOGNIZED */));
+ }
+
+ private static void evaluateArithmeticExpression(
+ int lhs, int rhs, int opType, DynamicTypeValueReceiverWithPreUpdate<Integer> receiver) {
+ DynamicProto.ArithmeticInt32Op protoNode =
+ DynamicProto.ArithmeticInt32Op.newBuilder().setOperationTypeValue(opType).build();
+
+ ArithmeticInt32Node node = new ArithmeticInt32Node(protoNode, receiver);
+
+ FixedInt32 lhsProtoNode = FixedInt32.newBuilder().setValue(lhs).build();
+ FixedInt32Node lhsNode = new FixedInt32Node(lhsProtoNode, node.getLhsUpstreamCallback());
+
+ FixedInt32 rhsProtoNode = FixedInt32.newBuilder().setValue(rhs).build();
+ FixedInt32Node rhsNode = new FixedInt32Node(rhsProtoNode, node.getRhsUpstreamCallback());
+ lhsNode.preInit();
+ rhsNode.preInit();
+
+ lhsNode.init();
+ rhsNode.init();
+ }
+
+ private static int evaluateFloatToInt32Expression(
+ float value, DynamicProto.FloatToInt32RoundMode roundMode) {
+ return evaluateFloatToInt32Expression(value, roundMode.getNumber());
+ }
+
+ private static int evaluateFloatToInt32Expression(float value, int roundMode) {
+ List<Integer> results = new ArrayList<>();
+ Int32Nodes.FloatToInt32Node node =
+ new Int32Nodes.FloatToInt32Node(
+ DynamicProto.FloatToInt32Op.newBuilder()
+ .setRoundModeValue(roundMode)
+ .build(),
+ new AddToListCallback<>(results));
+ node.getIncomingCallback().onPreUpdate();
+ node.getIncomingCallback().onData(value);
+
+ return results.get(0);
+ }
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
index f68ef36..b839904 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/ParametrizedDynamicTypeEvaluatorTest.java
@@ -40,6 +40,7 @@
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicInt32;
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicInt32.IntFormatter;
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString;
+import androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.EvaluationException;
import androidx.wear.protolayout.expression.proto.DynamicDataProto.DynamicDataValue;
import androidx.wear.protolayout.expression.proto.FixedProto.FixedBool;
import androidx.wear.protolayout.expression.proto.FixedProto.FixedFloat;
@@ -380,14 +381,20 @@
DynamicString bindUnderTest, String expectedValue) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicStringProto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(
- bindUnderTest,
- ULocale.getDefault(),
- new MainThreadExecutor(),
- cb)
- .startEvaluation(),
+ .bind(
+ DynamicTypeBindingRequest.forDynamicString(
+ bindUnderTest,
+ ULocale.getDefault(),
+ new MainThreadExecutor(),
+ cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ },
expectedValue);
}
@@ -395,10 +402,17 @@
DynamicInt32 bindUnderTest, Integer expectedValue) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicInt32Proto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(bindUnderTest, new MainThreadExecutor(), cb)
- .startEvaluation(),
+ .bind(
+ DynamicTypeBindingRequest.forDynamicInt32(
+ bindUnderTest, new MainThreadExecutor(), cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ },
expectedValue);
}
@@ -406,10 +420,17 @@
DynamicColor bindUnderTest, Integer expectedValue) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicColorProto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(bindUnderTest, new MainThreadExecutor(), cb)
- .startEvaluation(),
+ .bind(
+ DynamicTypeBindingRequest.forDynamicColor(
+ bindUnderTest, new MainThreadExecutor(), cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ },
expectedValue);
}
@@ -417,10 +438,17 @@
DynamicInstant bindUnderTest, Instant instant) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicInstantProto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(bindUnderTest, new MainThreadExecutor(), cb)
- .startEvaluation(),
+ .bind(
+ DynamicTypeBindingRequest.forDynamicInstant(
+ bindUnderTest, new MainThreadExecutor(), cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ },
instant);
}
@@ -428,10 +456,17 @@
DynamicDuration bindUnderTest, Duration duration) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicDurationProto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(bindUnderTest, new MainThreadExecutor(), cb)
- .startEvaluation(),
+ .bind(
+ DynamicTypeBindingRequest.forDynamicDuration(
+ bindUnderTest, new MainThreadExecutor(), cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ },
duration);
}
@@ -439,10 +474,17 @@
DynamicFloat bindUnderTest, Float expectedValue) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicFloatProto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(bindUnderTest, new MainThreadExecutor(), cb)
- .startEvaluation(),
+ .bind(
+ DynamicTypeBindingRequest.forDynamicFloat(
+ bindUnderTest, new MainThreadExecutor(), cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ },
expectedValue);
}
@@ -450,10 +492,17 @@
DynamicBool bindUnderTest, Boolean expectedValue) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicBoolProto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(bindUnderTest, new MainThreadExecutor(), cb)
- .startEvaluation(),
+ .bind(
+ DynamicTypeBindingRequest.forDynamicBool(
+ bindUnderTest, new MainThreadExecutor(), cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ },
expectedValue);
}
@@ -461,20 +510,34 @@
DynamicInt32 bindUnderTest) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicInt32Proto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(bindUnderTest, new MainThreadExecutor(), cb)
- .startEvaluation());
+ .bind(
+ DynamicTypeBindingRequest.forDynamicInt32(
+ bindUnderTest, new MainThreadExecutor(), cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ });
}
private static ParametrizedDynamicTypeEvaluatorTest.TestCase<Float> testForInvalidValue(
DynamicFloat bindUnderTest) {
return new ParametrizedDynamicTypeEvaluatorTest.TestCase<>(
bindUnderTest.toDynamicFloatProto().toString(),
- (evaluator, cb) ->
+ (evaluator, cb) -> {
+ try {
evaluator
- .bindInternal(bindUnderTest, new MainThreadExecutor(), cb)
- .startEvaluation());
+ .bind(
+ DynamicTypeBindingRequest.forDynamicFloat(
+ bindUnderTest, new MainThreadExecutor(), cb))
+ .startEvaluation();
+ } catch (EvaluationException e) {
+ throw new RuntimeException(e);
+ }
+ });
}
private static class TestCase<T> {
diff --git a/wear/protolayout/protolayout-material/api/current.txt b/wear/protolayout/protolayout-material/api/current.txt
index d73127d..01b19f2 100644
--- a/wear/protolayout/protolayout-material/api/current.txt
+++ b/wear/protolayout/protolayout-material/api/current.txt
@@ -138,14 +138,12 @@
method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
method public String? getIconContent();
method public String getText();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public boolean hasExcludeFontPadding();
}
public static final class CompactChip.Builder {
ctor public CompactChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
method public androidx.wear.protolayout.material.CompactChip build();
method public androidx.wear.protolayout.material.CompactChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.material.CompactChip.Builder setExcludeFontPadding(boolean);
method public androidx.wear.protolayout.material.CompactChip.Builder setIconContent(String);
}
@@ -175,7 +173,6 @@
method public int getOverflow();
method public androidx.wear.protolayout.TypeBuilders.StringProp getText();
method public int getWeight();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public boolean hasExcludeFontPadding();
method public boolean isItalic();
method public boolean isUnderline();
}
@@ -185,7 +182,6 @@
ctor public Text.Builder(android.content.Context, String);
method public androidx.wear.protolayout.material.Text build();
method public androidx.wear.protolayout.material.Text.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.material.Text.Builder setExcludeFontPadding(boolean);
method public androidx.wear.protolayout.material.Text.Builder setItalic(boolean);
method public androidx.wear.protolayout.material.Text.Builder setMaxLines(@IntRange(from=1) int);
method public androidx.wear.protolayout.material.Text.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
@@ -204,14 +200,12 @@
method public String? getIconContent();
method public String getText();
method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getWidth();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public boolean hasExcludeFontPadding();
}
public static final class TitleChip.Builder {
ctor public TitleChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
method public androidx.wear.protolayout.material.TitleChip build();
method public androidx.wear.protolayout.material.TitleChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.material.TitleChip.Builder setExcludeFontPadding(boolean);
method public androidx.wear.protolayout.material.TitleChip.Builder setHorizontalAlignment(int);
method public androidx.wear.protolayout.material.TitleChip.Builder setIconContent(String);
method public androidx.wear.protolayout.material.TitleChip.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
diff --git a/wear/protolayout/protolayout-material/api/restricted_current.txt b/wear/protolayout/protolayout-material/api/restricted_current.txt
index d73127d..01b19f2 100644
--- a/wear/protolayout/protolayout-material/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-material/api/restricted_current.txt
@@ -138,14 +138,12 @@
method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
method public String? getIconContent();
method public String getText();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public boolean hasExcludeFontPadding();
}
public static final class CompactChip.Builder {
ctor public CompactChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
method public androidx.wear.protolayout.material.CompactChip build();
method public androidx.wear.protolayout.material.CompactChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.material.CompactChip.Builder setExcludeFontPadding(boolean);
method public androidx.wear.protolayout.material.CompactChip.Builder setIconContent(String);
}
@@ -175,7 +173,6 @@
method public int getOverflow();
method public androidx.wear.protolayout.TypeBuilders.StringProp getText();
method public int getWeight();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public boolean hasExcludeFontPadding();
method public boolean isItalic();
method public boolean isUnderline();
}
@@ -185,7 +182,6 @@
ctor public Text.Builder(android.content.Context, String);
method public androidx.wear.protolayout.material.Text build();
method public androidx.wear.protolayout.material.Text.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.material.Text.Builder setExcludeFontPadding(boolean);
method public androidx.wear.protolayout.material.Text.Builder setItalic(boolean);
method public androidx.wear.protolayout.material.Text.Builder setMaxLines(@IntRange(from=1) int);
method public androidx.wear.protolayout.material.Text.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
@@ -204,14 +200,12 @@
method public String? getIconContent();
method public String getText();
method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getWidth();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public boolean hasExcludeFontPadding();
}
public static final class TitleChip.Builder {
ctor public TitleChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
method public androidx.wear.protolayout.material.TitleChip build();
method public androidx.wear.protolayout.material.TitleChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.material.TitleChip.Builder setExcludeFontPadding(boolean);
method public androidx.wear.protolayout.material.TitleChip.Builder setHorizontalAlignment(int);
method public androidx.wear.protolayout.material.TitleChip.Builder setIconContent(String);
method public androidx.wear.protolayout.material.TitleChip.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java
index 04eb408..282fe24 100644
--- a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java
@@ -31,7 +31,6 @@
import androidx.wear.protolayout.LayoutElementBuilders;
import androidx.wear.protolayout.LayoutElementBuilders.Box;
import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
-import androidx.wear.protolayout.LayoutElementBuilders.Row;
import androidx.wear.protolayout.ModifiersBuilders.Background;
import androidx.wear.protolayout.ModifiersBuilders.Clickable;
import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
@@ -54,6 +53,7 @@
* as it should point on the same size independent image.
*/
@NonNull
+ @SuppressWarnings("deprecation")
static Map<String, LayoutElement> generateTestCases(
@NonNull Context context,
@NonNull DeviceParametersBuilders.DeviceParameters deviceParameters,
@@ -210,78 +210,61 @@
testCases.put(
"compactchip_default_len2_golden" + goldenSuffix,
new CompactChip.Builder(context, "Ab", clickable, deviceParameters)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"compactchip_default_len5_golden" + goldenSuffix,
new CompactChip.Builder(context, "Abcde", clickable, deviceParameters)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"compactchip_default_len9_golden" + goldenSuffix,
new CompactChip.Builder(context, "Abcdefghi", clickable, deviceParameters)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"compactchip_default_toolong_golden" + goldenSuffix,
new CompactChip.Builder(
context, "AbcdefghiEXTRAEXTRAEXTRA", clickable, deviceParameters)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"compactchip_custom_default_golden" + goldenSuffix,
new CompactChip.Builder(context, "Action", clickable, deviceParameters)
.setChipColors(new ChipColors(Color.YELLOW, Color.BLACK))
- .setExcludeFontPadding(true)
- .build());
- testCases.put(
- "compactchip_includepadding_default_golden" + goldenSuffix,
- new CompactChip.Builder(context, "Action", clickable, deviceParameters)
- .setExcludeFontPadding(false)
.build());
testCases.put(
"compactchip_icon_default_golden" + goldenSuffix,
new CompactChip.Builder(context, "Action", clickable, deviceParameters)
.setIconContent(ICON_ID_SMALL)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"compactchip_icon_toolong_golden" + goldenSuffix,
new CompactChip.Builder(
context, "AbcdefghiEXTRAEXTRAEXTRA", clickable, deviceParameters)
.setIconContent(ICON_ID_SMALL)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"compactchip_icon_len2_golden" + goldenSuffix,
new CompactChip.Builder(context, "Ab", clickable, deviceParameters)
.setIconContent(ICON_ID_SMALL)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"compactchip_icon_custom_golden" + goldenSuffix,
new CompactChip.Builder(context, "Action", clickable, deviceParameters)
.setIconContent(ICON_ID_SMALL)
- .setExcludeFontPadding(true)
.setChipColors(new ChipColors(Color.YELLOW, Color.BLACK))
.build());
testCases.put(
"titlechip_default_golden" + goldenSuffix,
new TitleChip.Builder(context, largeChipText, clickable, deviceParameters)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"titlechip_default_texttoolong_golden" + goldenSuffix,
new TitleChip.Builder(context, "abcdeabcdeabcdeEXTRA", clickable, deviceParameters)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"titlechip_leftalign_secondary_default_golden" + goldenSuffix,
new TitleChip.Builder(context, largeChipText, clickable, deviceParameters)
.setHorizontalAlignment(HORIZONTAL_ALIGN_START)
.setChipColors(ChipDefaults.TITLE_SECONDARY_COLORS)
- .setExcludeFontPadding(true)
.build());
testCases.put(
"titlechip_centered_custom_150_secondary_default_golden" + goldenSuffix,
@@ -289,23 +272,15 @@
.setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
.setChipColors(new ChipColors(Color.YELLOW, Color.BLUE))
.setWidth(150)
- .setExcludeFontPadding(true)
- .build());
- testCases.put(
- "titlechip_includepadding_default_golden" + goldenSuffix,
- new TitleChip.Builder(context, largeChipText, clickable, deviceParameters)
- .setExcludeFontPadding(false)
.build());
testCases.put(
"titlechip_icon_default_golden" + goldenSuffix,
new TitleChip.Builder(context, largeChipText, clickable, deviceParameters)
- .setExcludeFontPadding(true)
.setIconContent(ICON_ID)
.build());
testCases.put(
"titlechip_icon_default_texttoolong_golden" + goldenSuffix,
new TitleChip.Builder(context, "abcdeabcdeabcdeEXTRA", clickable, deviceParameters)
- .setExcludeFontPadding(true)
.setIconContent(ICON_ID)
.build());
testCases.put(
@@ -313,7 +288,6 @@
new TitleChip.Builder(context, largeChipText, clickable, deviceParameters)
.setChipColors(new ChipColors(Color.YELLOW, Color.BLUE))
.setWidth(150)
- .setExcludeFontPadding(true)
.setIconContent(ICON_ID)
.build());
@@ -348,31 +322,8 @@
.build());
testCases.put(
- "default_text_golden" + goldenSuffix, new Text.Builder(context, "Testing").build());
- testCases.put(
- "excluded_padding_text_golden" + goldenSuffix,
- new Row.Builder()
- .addContent(
- new Box.Builder()
- .setModifiers(buildBackgroundColorModifier(Color.YELLOW))
- .addContent(
- new Text.Builder(context, "Inc padd ")
- .setExcludeFontPadding(false)
- .setTypography(
- Typography.TYPOGRAPHY_CAPTION1)
- .build())
- .build())
- .addContent(
- new Box.Builder()
- .setModifiers(buildBackgroundColorModifier(Color.CYAN))
- .addContent(
- new Text.Builder(context, "Excl padd")
- .setExcludeFontPadding(true)
- .setTypography(
- Typography.TYPOGRAPHY_CAPTION1)
- .build())
- .build())
- .build());
+ "default_text_golden" + goldenSuffix,
+ new Text.Builder(context, "Testing").build());
testCases.put(
"custom_text_golden" + goldenSuffix,
new Text.Builder(context, "Testing text.")
@@ -384,6 +335,47 @@
testCases.put(
"overflow_text_golden" + goldenSuffix,
new Text.Builder(context, "abcdeabcdeabcde").build());
+ testCases.put(
+ "overflow_ellipsize_maxlines_notreached" + goldenSuffix,
+ new Box.Builder()
+ .setWidth(dp(100))
+ .setHeight(dp(42))
+ .setModifiers(buildBackgroundColorModifier(Color.YELLOW))
+ .addContent(
+ new Text.Builder(
+ context,
+ "Very long text that won't fit in its parent box so it"
+ + "needs to be ellipsized correctly before its "
+ + "last line")
+ // Line height = 20sp
+ .setTypography(Typography.TYPOGRAPHY_BODY1)
+ .setOverflow(LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE)
+ .setMultilineAlignment(
+ LayoutElementBuilders.TEXT_ALIGN_START)
+ .setMaxLines(6)
+ .build())
+ .build());
+ testCases.put(
+ "overflow_ellipsize_end_maxlines_notreached" + goldenSuffix,
+ new Box.Builder()
+ .setWidth(dp(100))
+ .setHeight(dp(42))
+ .setModifiers(buildBackgroundColorModifier(Color.YELLOW))
+ .addContent(
+ new Text.Builder(
+ context,
+ "Very long text that won't fit in its parent box so it"
+ + "needs to be ellipsized correctly before its "
+ + "last line")
+ // Line height = 20sp
+ .setTypography(Typography.TYPOGRAPHY_BODY1)
+ .setOverflow(
+ LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END)
+ .setMultilineAlignment(
+ LayoutElementBuilders.TEXT_ALIGN_START)
+ .setMaxLines(6)
+ .build())
+ .build());
return testCases;
}
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/TestCasesGenerator.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/TestCasesGenerator.java
index 7c7dc9d..a515831 100644
--- a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/TestCasesGenerator.java
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/TestCasesGenerator.java
@@ -75,12 +75,9 @@
HashMap<String, LayoutElement> testCases = new HashMap<>();
TitleChip content =
- new TitleChip.Builder(context, "Action", clickable, deviceParameters)
- .setExcludeFontPadding(true)
- .build();
+ new TitleChip.Builder(context, "Action", clickable, deviceParameters).build();
CompactChip.Builder primaryChipBuilder =
- new CompactChip.Builder(context, "Action", clickable, deviceParameters)
- .setExcludeFontPadding(true);
+ new CompactChip.Builder(context, "Action", clickable, deviceParameters);
testCases.put(
"default_empty_primarychiplayout_golden" + goldenSuffix,
@@ -96,7 +93,6 @@
"Too_long_textToo_long_textToo_long_text",
clickable,
deviceParameters)
- .setExcludeFontPadding(true)
.build())
.build());
testCases.put(
@@ -166,8 +162,7 @@
.build());
primaryChipBuilder =
- new CompactChip.Builder(context, "Action", clickable, deviceParameters)
- .setExcludeFontPadding(true);
+ new CompactChip.Builder(context, "Action", clickable, deviceParameters);
testCases.put(
"coloredbox_1_chip_columnslayout_golden" + goldenSuffix,
new PrimaryLayout.Builder(deviceParameters)
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Button.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Button.java
index d35c3db..56ef3bc 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Button.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Button.java
@@ -40,6 +40,7 @@
import androidx.annotation.Dimension;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.protolayout.ColorBuilders.ColorProp;
@@ -51,6 +52,7 @@
import androidx.wear.protolayout.ModifiersBuilders.Clickable;
import androidx.wear.protolayout.TypeBuilders.StringProp;
import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.expression.ProtoLayoutExperimental;
import androidx.wear.protolayout.material.Typography.TypographyName;
import androidx.wear.protolayout.materialcore.Button.Builder.ButtonType;
import androidx.wear.protolayout.proto.LayoutElementProto;
@@ -285,6 +287,7 @@
}
@NonNull
+ @OptIn(markerClass = ProtoLayoutExperimental.class)
private LayoutElement getCorrectContent() {
LayoutElement.Builder content;
switch (mType) {
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
index 67ddcc5..c0854ac 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
@@ -111,7 +111,6 @@
@HorizontalAlignment private int mHorizontalAlign = HORIZONTAL_ALIGN_UNDEFINED;
@TypographyName private int mPrimaryLabelTypography;
private boolean mIsScalable = true;
- private boolean mIsFontPaddingExcluded = false;
private int mMaxLines = 0; // 0 indicates that is not set.
@NonNull private final androidx.wear.protolayout.materialcore.Chip.Builder mCoreBuilder;
@@ -240,23 +239,6 @@
}
/**
- * Sets whether the font padding for the primary label is excluded.
- *
- * <p>It should be used for creating {@code CompactChip} and {@code TitleChip} to make the
- * label vertically aligned. Shouldn't be used if there is anything else in chip besides
- * primary label.
- *
- * @see Text.Builder#setExcludeFontPadding
- */
- @NonNull
- @ProtoLayoutExperimental
- @SuppressWarnings("MissingGetterMatchingBuilder")
- Builder setPrimaryLabelExcludeFontPadding(boolean excluded) {
- this.mIsFontPaddingExcluded = excluded;
- return this;
- }
-
- /**
* Sets the secondary label for the {@link Chip}. Any previously added custom content will
* be overridden. If secondary label is set, primary label must be set too with {@link
* #setPrimaryLabelContent}.
@@ -372,6 +354,7 @@
}
@OptIn(markerClass = ProtoLayoutExperimental.class)
+ @SuppressWarnings("deprecation")
private void setCorrectContent() {
Text mainTextElement =
new Text.Builder(mContext, checkNotNull(mPrimaryLabel))
@@ -381,7 +364,6 @@
.setOverflow(LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END)
.setMultilineAlignment(LayoutElementBuilders.TEXT_ALIGN_START)
.setIsScalable(mIsScalable)
- .setExcludeFontPadding(mIsFontPaddingExcluded)
.build();
mCoreBuilder.setPrimaryLabelContent(mainTextElement);
@@ -543,13 +525,6 @@
return mElement.getMetadataTag();
}
- /** Returns whether the font padding for the primary label is excluded. */
- @ProtoLayoutExperimental
- boolean hasPrimaryLabelExcludeFontPadding() {
- Text primaryLabel = getPrimaryLabelContentObject();
- return primaryLabel != null && primaryLabel.hasExcludeFontPadding();
- }
-
/**
* Returns Chip object from the given LayoutElement (e.g. one retrieved from a container's
* content with {@code container.getContents().get(index)}) if that element can be converted to
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java
index 436aaad..9787fa6 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java
@@ -83,7 +83,6 @@
@NonNull private final Clickable mClickable;
@NonNull private final DeviceParameters mDeviceParameters;
@NonNull private ChipColors mChipColors = COMPACT_PRIMARY_COLORS;
- private boolean mIsFontPaddingExcluded = false;
@Nullable private String mIconResourceId = null;
/**
@@ -119,25 +118,9 @@
}
/**
- * Sets whether the font padding is excluded or not. If not set, default to false, meaning
- * that text will have font padding included.
- *
- * <p>Setting this to {@code true} will perfectly align the text label.
- */
- @NonNull
- @ProtoLayoutExperimental
- @SuppressWarnings("MissingGetterMatchingBuilder")
- public Builder setExcludeFontPadding(boolean excluded) {
- this.mIsFontPaddingExcluded = excluded;
- return this;
- }
-
- /**
* Sets the icon for the {@link CompactChip}. Provided icon will be tinted to the given
* content color from {@link ChipColors}. This icon should be image with chosen alpha
* channel that can be tinted.
- *
- * <p>It is highly recommended to use it with {@link #setExcludeFontPadding} set to true.
*/
@NonNull
public Builder setIconContent(@NonNull String imageResourceId) {
@@ -161,7 +144,6 @@
.setHorizontalPadding(COMPACT_HORIZONTAL_PADDING)
.setPrimaryLabelContent(mText)
.setPrimaryLabelTypography(Typography.TYPOGRAPHY_CAPTION1)
- .setPrimaryLabelExcludeFontPadding(mIsFontPaddingExcluded)
.setIsPrimaryLabelScalable(false);
if (mIconResourceId != null) {
@@ -221,12 +203,6 @@
return coreChip == null ? null : new CompactChip(new Chip(coreChip));
}
- /** Returns whether the font padding for the primary label is excluded. */
- @ProtoLayoutExperimental
- public boolean hasExcludeFontPadding() {
- return mElement.hasPrimaryLabelExcludeFontPadding();
- }
-
@RestrictTo(Scope.LIBRARY_GROUP)
@NonNull
@Override
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Text.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Text.java
index 5f0ef96..dd110c0 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Text.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Text.java
@@ -30,6 +30,7 @@
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.protolayout.ColorBuilders.ColorProp;
@@ -91,6 +92,8 @@
@Nullable private Integer mCustomWeight = null;
@NonNull
+ @OptIn(markerClass = ProtoLayoutExperimental.class)
+ @SuppressWarnings("deprecation")
private final LayoutElementBuilders.Text.Builder mElementBuilder =
new LayoutElementBuilders.Text.Builder()
.setMaxLines(1)
@@ -221,24 +224,6 @@
return this;
}
- /**
- * Sets whether the {@link Text} excludes extra top and bottom padding above the normal
- * ascent and descent. The default is false.
- */
- // TODO(b/252767963): Coordinate the transition of the default from false->true along with
- // other impacted UI Libraries - needs care as will have an impact on layout and needs to be
- // communicated clearly.
- @NonNull
- @ProtoLayoutExperimental
- @SuppressWarnings("MissingGetterMatchingBuilder")
- public Builder setExcludeFontPadding(boolean excludeFontPadding) {
- this.mElementBuilder.setAndroidTextStyle(
- new LayoutElementBuilders.AndroidTextStyle.Builder()
- .setExcludeFontPadding(excludeFontPadding)
- .build());
- return this;
- }
-
/** Constructs and returns {@link Text} with the provided content and look. */
@NonNull
@Override
@@ -321,15 +306,6 @@
}
/**
- * Returns whether the Text has extra top and bottom padding above the normal ascent and descent
- * excluded.
- */
- @ProtoLayoutExperimental
- public boolean hasExcludeFontPadding() {
- return checkNotNull(mText.getAndroidTextStyle()).getExcludeFontPadding();
- }
-
- /**
* Returns Material Text object from the given LayoutElement (e.g. one retrieved from a
* container's content with {@code container.getContents().get(index)}) if that element can be
* converted to Material Text. Otherwise, it will return null.
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/TitleChip.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/TitleChip.java
index e6e355c..25de00b 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/TitleChip.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/TitleChip.java
@@ -93,7 +93,6 @@
// Indicates that the width isn't set, so it will be automatically set by Chip.Builder
// constructor.
@Nullable private ContainerDimension mWidth = null;
- private boolean mIsFontPaddingExcluded = false;
@Nullable private String mIconResourceId = null;
/**
@@ -157,25 +156,9 @@
}
/**
- * Sets whether the font padding is excluded or not. If not set, default to false, meaning
- * that text will have font padding included.
- *
- * <p>Setting this to {@code true} will perfectly align the text label.
- */
- @NonNull
- @ProtoLayoutExperimental
- @SuppressWarnings("MissingGetterMatchingBuilder")
- public Builder setExcludeFontPadding(boolean excluded) {
- this.mIsFontPaddingExcluded = excluded;
- return this;
- }
-
- /**
* Sets the icon for the {@link TitleChip}. Provided icon will be tinted to the given
* content color from {@link ChipColors}. This icon should be image with chosen alpha
* channel that can be tinted.
- *
- * <p>It is highly recommended to use it with {@link #setExcludeFontPadding} set to true.
*/
@NonNull
public Builder setIconContent(@NonNull String imageResourceId) {
@@ -197,7 +180,6 @@
.setHorizontalPadding(TITLE_HORIZONTAL_PADDING)
.setPrimaryLabelContent(mText)
.setPrimaryLabelTypography(Typography.TYPOGRAPHY_TITLE2)
- .setPrimaryLabelExcludeFontPadding(mIsFontPaddingExcluded)
.setIsPrimaryLabelScalable(false);
if (mWidth != null) {
@@ -273,12 +255,6 @@
return coreChip == null ? null : new TitleChip(new Chip(coreChip));
}
- /** Returns whether the font padding for the primary label is excluded. */
- @ProtoLayoutExperimental
- public boolean hasExcludeFontPadding() {
- return mElement.hasPrimaryLabelExcludeFontPadding();
- }
-
@RestrictTo(Scope.LIBRARY_GROUP)
@NonNull
@Override
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TextTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TextTest.java
index ea6f7b9..fbf9866 100644
--- a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TextTest.java
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TextTest.java
@@ -21,7 +21,6 @@
import static androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_MEDIUM;
import static androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_NORMAL;
import static androidx.wear.protolayout.LayoutElementBuilders.TEXT_ALIGN_END;
-import static androidx.wear.protolayout.LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END;
import static androidx.wear.protolayout.material.Typography.TYPOGRAPHY_BODY1;
import static androidx.wear.protolayout.material.Typography.TYPOGRAPHY_CAPTION2;
import static androidx.wear.protolayout.material.Typography.TYPOGRAPHY_TITLE1;
@@ -130,6 +129,7 @@
@Test
@ProtoLayoutExperimental
+ @SuppressWarnings("deprecation")
public void testText() {
String textContent = "Testing text.";
Modifiers modifiers =
@@ -145,10 +145,9 @@
.setUnderline(true)
.setMaxLines(2)
.setModifiers(modifiers)
- .setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END)
+ .setOverflow(LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END)
.setMultilineAlignment(TEXT_ALIGN_END)
.setWeight(FONT_WEIGHT_BOLD)
- .setExcludeFontPadding(true)
.build();
FontStyle expectedFontStyle =
@@ -201,6 +200,7 @@
}
@ProtoLayoutExperimental
+ @SuppressWarnings("deprecation")
private void assertTextIsEqual(
Text actualText,
String expectedTextContent,
@@ -210,12 +210,12 @@
assertThat(actualText.getFontStyle().toProto()).isEqualTo(expectedFontStyle.toProto());
assertThat(actualText.getText().getValue()).isEqualTo(expectedTextContent);
assertThat(actualText.getColor().getArgb()).isEqualTo(expectedColor);
- assertThat(actualText.getOverflow()).isEqualTo(TEXT_OVERFLOW_ELLIPSIZE_END);
+ assertThat(actualText.getOverflow())
+ .isEqualTo(LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END);
assertThat(actualText.getMultilineAlignment()).isEqualTo(TEXT_ALIGN_END);
assertThat(actualText.getMaxLines()).isEqualTo(2);
assertThat(actualText.getLineHeight())
.isEqualTo(getLineHeightForTypography(TYPOGRAPHY_TITLE1).getValue());
- assertThat(actualText.hasExcludeFontPadding()).isTrue();
}
private void assertFontStyle(
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
index 96fc634..cd2caa6 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/layout.proto
@@ -151,6 +151,7 @@
// Truncate the text to fit in the Text element's bounds, but add an ellipsis
// (i.e. ...) to the end of the text if it has been truncated.
+ // @deprecated Use TEXT_OVERFLOW_ELLIPSIZE instead.
TEXT_OVERFLOW_ELLIPSIZE_END = 2;
// Enable marquee animation for texts that don't fit inside the Text element.
@@ -167,6 +168,8 @@
// Note that, when this is used, the parent of the Text element this
// corresponds to shouldn't have its width and height set to wrapped, as it
// can lead to unexpected results.
+ // <p>Note that, on SpanText, this will behave exactly the same way as
+ // TEXT_OVERFLOW_ELLIPSIZE_END.
TEXT_OVERFLOW_ELLIPSIZE = 4;
}
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
index 993dd38..baf27ab 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
@@ -69,6 +69,7 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewOutlineProvider;
import android.view.ViewParent;
+import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.animation.AlphaAnimation;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
@@ -1873,13 +1874,12 @@
// A null TruncateAt disables adding an ellipsis.
return null;
case TEXT_OVERFLOW_ELLIPSIZE_END:
+ case TEXT_OVERFLOW_ELLIPSIZE:
return TruncateAt.END;
case TEXT_OVERFLOW_MARQUEE:
return TruncateAt.MARQUEE;
case TEXT_OVERFLOW_UNDEFINED:
case UNRECOGNIZED:
- // TODO(b/302531877): Implement ellipsize.
- case TEXT_OVERFLOW_ELLIPSIZE:
return TEXT_OVERFLOW_DEFAULT;
}
@@ -2550,7 +2550,14 @@
} else {
textView.setMaxLines(TEXT_MAX_LINES_DEFAULT);
}
- applyTextOverflow(textView, text.getOverflow(), text.getMarqueeParameters());
+
+ TextOverflowProp overflow = text.getOverflow();
+ applyTextOverflow(textView, overflow, text.getMarqueeParameters());
+
+ if (overflow.getValue() == TextOverflow.TEXT_OVERFLOW_ELLIPSIZE
+ && !text.getText().hasDynamicValue()) {
+ adjustMaxLinesForEllipsize(textView);
+ }
// Text auto size is not supported for dynamic text.
boolean isAutoSizeAllowed = !(text.hasText() && text.getText().hasDynamicValue());
@@ -2642,6 +2649,52 @@
}
/**
+ * Sorts out what maxLines should be if the text could possibly be truncated before maxLines is
+ * reached.
+ *
+ * <p>Should be only called for the {@link TextOverflow#TEXT_OVERFLOW_ELLIPSIZE} option which
+ * ellipsizes the text even before the last line, if there's no space for all lines. This is
+ * different than what TEXT_OVERFLOW_ELLIPSIZE_END does, as that option just ellipsizes the last
+ * line of text.
+ */
+ private void adjustMaxLinesForEllipsize(@NonNull TextView textView) {
+ textView
+ .getViewTreeObserver()
+ .addOnPreDrawListener(
+ new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ ViewParent maybeParent = textView.getParent();
+ if (!(maybeParent instanceof View)) {
+ Log.d(
+ TAG,
+ "Couldn't adjust max lines for ellipsizing as"
+ + "there's no View/ViewGroup parent.");
+ return false;
+ }
+
+ textView.getViewTreeObserver().removeOnPreDrawListener(this);
+
+ View parent = (View) maybeParent;
+ int availableHeight = parent.getHeight();
+ int oneLineHeight = textView.getLineHeight();
+ // This is what was set in proto, we shouldn't exceed it.
+ int maxMaxLines = textView.getMaxLines();
+ // Avoid having maxLines as 0 in case the space is really tight.
+ int availableLines = max(availableHeight / oneLineHeight, 1);
+
+ // Update only if changed.
+ if (availableLines < maxMaxLines) {
+ textView.setMaxLines(availableLines);
+ }
+
+ // Cancel the current drawing pass.
+ return false;
+ }
+ });
+ }
+
+ /**
* Sets whether the padding is included or not. If font padding is not included, sets the
* correct padding to the TextView to avoid clipping taller languages.
*/
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
index 3c7e0b8..151dabf 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
@@ -52,6 +52,7 @@
import androidx.wear.protolayout.expression.proto.AnimationParameterProto.RepeatMode;
import androidx.wear.protolayout.expression.proto.AnimationParameterProto.Repeatable;
import androidx.wear.protolayout.expression.proto.DynamicDataProto.DynamicDataValue;
+import androidx.wear.protolayout.expression.proto.DynamicProto;
import androidx.wear.protolayout.expression.proto.DynamicProto.AnimatableDynamicColor;
import androidx.wear.protolayout.expression.proto.DynamicProto.AnimatableDynamicFloat;
import androidx.wear.protolayout.expression.proto.DynamicProto.AnimatableFixedColor;
@@ -855,7 +856,12 @@
.build();
DynamicInt32 dynamicInt32 =
DynamicInt32.newBuilder()
- .setFloatToInt(FloatToInt32Op.newBuilder().setInput(dynamicFloat).build())
+ .setFloatToInt(
+ FloatToInt32Op.newBuilder()
+ .setRoundMode(
+ DynamicProto.FloatToInt32RoundMode.ROUND_MODE_ROUND)
+ .setInput(dynamicFloat)
+ .build())
.build();
DynamicString dynamicString =
DynamicString.newBuilder()
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
index 4fa4807..950e73f 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
@@ -2765,6 +2765,70 @@
}
@Test
+ public void inflate_textView_ellipsize() {
+ String textContents = "Text that is very large so it will go to many lines";
+ Text.Builder text1 =
+ Text.newBuilder()
+ .setLineHeight(sp(16))
+ .setText(string(textContents))
+ .setFontStyle(FontStyle.newBuilder().addSize(sp(16)))
+ .setMaxLines(Int32Prop.newBuilder().setValue(6))
+ .setOverflow(
+ TextOverflowProp.newBuilder().setValue(
+ TextOverflow.TEXT_OVERFLOW_ELLIPSIZE));
+ Layout layout1 =
+ fingerprintedLayout(
+ LayoutElement.newBuilder()
+ .setBox(buildFixedSizeBoxWIthText(text1)).build());
+
+ Text.Builder text2 =
+ Text.newBuilder()
+ .setText(string(textContents))
+ // Diff
+ .setLineHeight(sp(4))
+ .setFontStyle(FontStyle.newBuilder().addSize(sp(4)))
+ .setMaxLines(Int32Prop.newBuilder().setValue(6))
+ .setOverflow(
+ TextOverflowProp.newBuilder().setValue(
+ TextOverflow.TEXT_OVERFLOW_ELLIPSIZE));
+ Layout layout2 =
+ fingerprintedLayout(
+ LayoutElement.newBuilder()
+ .setBox(buildFixedSizeBoxWIthText(text2)).build());
+
+ // Initial layout.
+ Renderer renderer = renderer(layout1);
+ ViewGroup inflatedViewParent = renderer.inflate();
+ TextView textView1 = (TextView) ((ViewGroup) inflatedViewParent
+ .getChildAt(0)).getChildAt(0);
+
+ // Apply the mutation.
+ ViewGroupMutation mutation =
+ renderer.computeMutation(getRenderedMetadata(inflatedViewParent), layout2);
+ assertThat(mutation).isNotNull();
+ assertThat(mutation.isNoOp()).isFalse();
+ boolean mutationResult = renderer.applyMutation(inflatedViewParent, mutation);
+ assertThat(mutationResult).isTrue();
+
+ // This contains layout after the mutation.
+ TextView textView2 = (TextView) ((ViewGroup) inflatedViewParent
+ .getChildAt(0)).getChildAt(0);
+
+ expect.that(textView1.getEllipsize()).isEqualTo(TruncateAt.END);
+ expect.that(textView1.getMaxLines()).isEqualTo(2);
+
+ expect.that(textView2.getEllipsize()).isEqualTo(TruncateAt.END);
+ expect.that(textView2.getMaxLines()).isEqualTo(3);
+ }
+
+ private static Box.Builder buildFixedSizeBoxWIthText(Text.Builder content) {
+ return Box.newBuilder()
+ .setWidth(ContainerDimension.newBuilder().setLinearDimension(dp(100)))
+ .setHeight(ContainerDimension.newBuilder().setLinearDimension(dp(120)))
+ .addContents(LayoutElement.newBuilder().setText(content));
+ }
+
+ @Test
public void inflate_textView_marquee_animationsDisabled() {
String textContents = "Marquee Animation";
LayoutElement root =
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index e3f0976..2a416d7 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -127,9 +127,8 @@
}
public static final class ColorBuilders.ColorStop.Builder {
- ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public ColorBuilders.ColorStop.Builder(androidx.wear.protolayout.ColorBuilders.ColorProp);
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public ColorBuilders.ColorStop.Builder(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.TypeBuilders.FloatProp);
method public androidx.wear.protolayout.ColorBuilders.ColorStop build();
- method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public androidx.wear.protolayout.ColorBuilders.ColorStop.Builder setOffset(androidx.wear.protolayout.TypeBuilders.FloatProp);
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public static final class ColorBuilders.SweepGradient implements androidx.wear.protolayout.ColorBuilders.Brush {
@@ -139,6 +138,7 @@
}
public static final class ColorBuilders.SweepGradient.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) @java.lang.SafeVarargs public ColorBuilders.SweepGradient.Builder(androidx.wear.protolayout.ColorBuilders.ColorProp!...);
ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) @java.lang.SafeVarargs public ColorBuilders.SweepGradient.Builder(androidx.wear.protolayout.ColorBuilders.ColorStop!...);
method public androidx.wear.protolayout.ColorBuilders.SweepGradient build();
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public androidx.wear.protolayout.ColorBuilders.SweepGradient.Builder setEndAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
@@ -359,7 +359,7 @@
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_ALIGN_START = 1; // 0x1
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_ALIGN_UNDEFINED = 0; // 0x0
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public static final int TEXT_OVERFLOW_ELLIPSIZE = 4; // 0x4
- field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2; // 0x2
+ field @Deprecated @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2; // 0x2
field @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final int TEXT_OVERFLOW_MARQUEE = 3; // 0x3
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_OVERFLOW_TRUNCATE = 1; // 0x1
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_OVERFLOW_UNDEFINED = 0; // 0x0
@@ -369,16 +369,6 @@
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
}
- @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class LayoutElementBuilders.AndroidTextStyle {
- method public boolean getExcludeFontPadding();
- }
-
- public static final class LayoutElementBuilders.AndroidTextStyle.Builder {
- ctor public LayoutElementBuilders.AndroidTextStyle.Builder();
- method public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle build();
- method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder setExcludeFontPadding(boolean);
- }
-
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class LayoutElementBuilders.Arc implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
@@ -725,7 +715,6 @@
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class LayoutElementBuilders.SpanText implements androidx.wear.protolayout.LayoutElementBuilders.Span {
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle? getAndroidTextStyle();
method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers? getModifiers();
method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
@@ -734,7 +723,6 @@
public static final class LayoutElementBuilders.SpanText.Builder {
ctor public LayoutElementBuilders.SpanText.Builder();
method public androidx.wear.protolayout.LayoutElementBuilders.SpanText build();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.SpanModifiers);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
@@ -789,7 +777,6 @@
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle? getAndroidTextStyle();
method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint? getLayoutConstraintsForDynamicText();
method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
@@ -804,7 +791,6 @@
public static final class LayoutElementBuilders.Text.Builder {
ctor public LayoutElementBuilders.Text.Builder();
method public androidx.wear.protolayout.LayoutElementBuilders.Text build();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLayoutConstraintsForDynamicText(androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index e3f0976..2a416d7 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -127,9 +127,8 @@
}
public static final class ColorBuilders.ColorStop.Builder {
- ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public ColorBuilders.ColorStop.Builder(androidx.wear.protolayout.ColorBuilders.ColorProp);
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public ColorBuilders.ColorStop.Builder(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.TypeBuilders.FloatProp);
method public androidx.wear.protolayout.ColorBuilders.ColorStop build();
- method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public androidx.wear.protolayout.ColorBuilders.ColorStop.Builder setOffset(androidx.wear.protolayout.TypeBuilders.FloatProp);
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public static final class ColorBuilders.SweepGradient implements androidx.wear.protolayout.ColorBuilders.Brush {
@@ -139,6 +138,7 @@
}
public static final class ColorBuilders.SweepGradient.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) @java.lang.SafeVarargs public ColorBuilders.SweepGradient.Builder(androidx.wear.protolayout.ColorBuilders.ColorProp!...);
ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) @java.lang.SafeVarargs public ColorBuilders.SweepGradient.Builder(androidx.wear.protolayout.ColorBuilders.ColorStop!...);
method public androidx.wear.protolayout.ColorBuilders.SweepGradient build();
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public androidx.wear.protolayout.ColorBuilders.SweepGradient.Builder setEndAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
@@ -359,7 +359,7 @@
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_ALIGN_START = 1; // 0x1
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_ALIGN_UNDEFINED = 0; // 0x0
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public static final int TEXT_OVERFLOW_ELLIPSIZE = 4; // 0x4
- field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2; // 0x2
+ field @Deprecated @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2; // 0x2
field @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final int TEXT_OVERFLOW_MARQUEE = 3; // 0x3
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_OVERFLOW_TRUNCATE = 1; // 0x1
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int TEXT_OVERFLOW_UNDEFINED = 0; // 0x0
@@ -369,16 +369,6 @@
field @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final int VERTICAL_ALIGN_UNDEFINED = 0; // 0x0
}
- @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class LayoutElementBuilders.AndroidTextStyle {
- method public boolean getExcludeFontPadding();
- }
-
- public static final class LayoutElementBuilders.AndroidTextStyle.Builder {
- ctor public LayoutElementBuilders.AndroidTextStyle.Builder();
- method public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle build();
- method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder setExcludeFontPadding(boolean);
- }
-
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class LayoutElementBuilders.Arc implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
@@ -725,7 +715,6 @@
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class LayoutElementBuilders.SpanText implements androidx.wear.protolayout.LayoutElementBuilders.Span {
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle? getAndroidTextStyle();
method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
method public androidx.wear.protolayout.ModifiersBuilders.SpanModifiers? getModifiers();
method public androidx.wear.protolayout.TypeBuilders.StringProp? getText();
@@ -734,7 +723,6 @@
public static final class LayoutElementBuilders.SpanText.Builder {
ctor public LayoutElementBuilders.SpanText.Builder();
method public androidx.wear.protolayout.LayoutElementBuilders.SpanText build();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.SpanModifiers);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder setText(androidx.wear.protolayout.TypeBuilders.StringProp);
@@ -789,7 +777,6 @@
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental public androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle? getAndroidTextStyle();
method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint? getLayoutConstraintsForDynamicText();
method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
@@ -804,7 +791,6 @@
public static final class LayoutElementBuilders.Text.Builder {
ctor public LayoutElementBuilders.Text.Builder();
method public androidx.wear.protolayout.LayoutElementBuilders.Text build();
- method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLayoutConstraintsForDynamicText(androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ColorBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ColorBuilders.java
index f14f152..cfcce8d 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ColorBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ColorBuilders.java
@@ -263,7 +263,7 @@
*/
@RequiresSchemaVersion(major = 1, minor = 300)
@NonNull
- private Builder setColor(@NonNull ColorProp color) {
+ Builder setColor(@NonNull ColorProp color) {
if (color.getDynamicValue() != null) {
throw new IllegalArgumentException(
"ColorStop.Builder.setColor doesn't support dynamic values.");
@@ -284,7 +284,7 @@
*/
@RequiresSchemaVersion(major = 1, minor = 300)
@NonNull
- public Builder setOffset(@NonNull FloatProp offset) {
+ Builder setOffset(@NonNull FloatProp offset) {
if (offset.getDynamicValue() != null) {
throw new IllegalArgumentException(
"ColorStop.Builder.setOffset doesn't support dynamic values.");
@@ -303,18 +303,22 @@
/**
* Creates an instance of {@link Builder}.
*
- * <p>If all {@link ColorStop} in a Gradient have no offset, the colors are evenly
- * distributed in the gradient.
- *
* @param color the color for this stop. Only opaque colors are supported. Any
* transparent colors will have their alpha component set to 0xFF (opaque). Note
* that this parameter only supports static values.
+ * @param offset the relative offset for this color, between 0 and 1. This determines
+ * where the color is positioned relative to a gradient space. Note that this
+ * parameter only supports static values.
*/
@RequiresSchemaVersion(major = 1, minor = 300)
- public Builder(@NonNull ColorProp color) {
+ public Builder(@NonNull ColorProp color, @NonNull FloatProp offset) {
this.setColor(color);
+ this.setOffset(offset);
}
+ /** Creates an instance of {@link Builder}. */
+ Builder() {}
+
/** Builds an instance from accumulated values. */
@NonNull
public ColorStop build() {
@@ -524,16 +528,12 @@
* Creates an instance of {@link Builder}.
*
* @param colorStops The color stops defining how the colors are distributed around the
- * gradient center. The color sequence starts at the start angle and spans 360
- * degrees clockwise, finishing at the same angle.
+ * gradient center.
* <p>A color stop is composed of a color and its offset in the gradient. The offset
* is the relative position of the color, beginning with 0 from the start angle and
* ending with 1.0 at the end angle, spanning clockwise.
- * <p>If offsets are not set, the colors are evenly distributed in the gradient.
* @throws IllegalArgumentException if the number of colors is less than 2 or larger
* than 10.
- * @throws IllegalArgumentException if offsets in {@code colorStops} are partially set.
- * Either all or none of the {@link ColorStop} parameters should have an offset.
*/
@RequiresSchemaVersion(major = 1, minor = 300)
@SafeVarargs
@@ -543,18 +543,37 @@
"Size of colorStops must not be less than 2 or greater than 10. Got "
+ colorStops.length);
}
- boolean offsetsShouldBePresent = colorStops[0].getOffset() != null;
for (ColorStop colorStop : colorStops) {
- boolean stopHasOffset = colorStop.getOffset() != null;
- if (offsetsShouldBePresent != stopHasOffset) {
- throw new IllegalArgumentException(
- "Either all or none of the colorStops should have an offset.");
- }
addColorStop(colorStop);
}
}
/**
+ * Creates an instance of {@link Builder}.
+ *
+ * <p>The colors are evenly distributed in the gradient.
+ *
+ * @param colors The color sequence to be distributed around the gradient center. The
+ * color sequence is distributed between the gradient's start and end angles.
+ *
+ * @throws IllegalArgumentException if the number of colors is less than 2 or larger
+ * than 10.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 300)
+ @SafeVarargs
+ public Builder(@NonNull ColorProp... colors) {
+ if (colors.length < 2 || colors.length > 10) {
+ throw new IllegalStateException(
+ "Size of colors must not be less than 2 or greater than 10. Got "
+ + colors.length);
+ }
+ for (ColorProp colorProp : colors) {
+ ColorStop stop = new ColorStop.Builder().setColor(colorProp).build();
+ addColorStop(stop);
+ }
+ }
+
+ /**
* Builds an instance from accumulated values.
*
* @throws IllegalStateException if size of colorStops is less than 2 or greater than
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
index dd99305..e0aab79 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
@@ -202,7 +202,10 @@
* Truncate the text at the last line defined by {@code setMaxLines} in {@link Text} to fit in
* the {@link Text} element's bounds, but add an ellipsis (i.e. ...) to the end of the text if
* it has been truncated.
+ *
+ * @deprecated Use {@link #TEXT_OVERFLOW_ELLIPSIZE} instead.
*/
+ @Deprecated
@RequiresSchemaVersion(major = 1, minor = 0)
public static final int TEXT_OVERFLOW_ELLIPSIZE_END = 2;
@@ -222,6 +225,9 @@
* parent container. Note that, when this is used, the parent of the {@link Text} element this
* corresponds to shouldn't have its width and height set to wrapped, as it can lead to
* unexpected results.
+ *
+ * <p>Note that, on {@link SpanText}, this will behave exactly the same way as
+ * TEXT_OVERFLOW_ELLIPSIZE_END.
*/
@RequiresSchemaVersion(major = 1, minor = 300)
public static final int TEXT_OVERFLOW_ELLIPSIZE = 4;
@@ -1100,6 +1106,7 @@
*/
@RequiresSchemaVersion(major = 1, minor = 200)
@ProtoLayoutExperimental
+ @RestrictTo(Scope.LIBRARY_GROUP)
public static final class AndroidTextStyle {
private final LayoutElementProto.AndroidTextStyle mImpl;
@Nullable private final Fingerprint mFingerprint;
@@ -1159,7 +1166,11 @@
private final Fingerprint mFingerprint = new Fingerprint(408674745);
/** Creates an instance of {@link Builder}. */
- public Builder() {}
+ public Builder() {
+ // Setting this to true before setter is called, so that default behaviour is to
+ // exclude padding.
+ mImpl.setExcludeFontPadding(true);
+ }
/**
* Sets whether the {@link Text} excludes padding specified by the font, i.e. extra top
@@ -1291,20 +1302,6 @@
}
/**
- * Gets an Android platform specific text style configuration options for styling and
- * compatibility.
- */
- @ProtoLayoutExperimental
- @Nullable
- public AndroidTextStyle getAndroidTextStyle() {
- if (mImpl.hasAndroidTextStyle()) {
- return AndroidTextStyle.fromProto(mImpl.getAndroidTextStyle());
- } else {
- return null;
- }
- }
-
- /**
* Gets the number of times to repeat the Marquee animation. Only applies when overflow is
* TEXT_OVERFLOW_MARQUEE. Set to -1 to repeat indefinitely. Defaults to repeat indefinitely.
*/
@@ -1380,8 +1377,6 @@
+ getOverflow()
+ ", lineHeight="
+ getLineHeight()
- + ", androidTextStyle="
- + getAndroidTextStyle()
+ "}";
}
@@ -1392,7 +1387,11 @@
private final Fingerprint mFingerprint = new Fingerprint(814133697);
/** Creates an instance of {@link Builder}. */
- public Builder() {}
+ public Builder() {
+ mImpl.setAndroidTextStyle(
+ LayoutElementProto.AndroidTextStyle.newBuilder()
+ .setExcludeFontPadding(true));
+ }
/**
* Sets the text to render.
@@ -1535,20 +1534,6 @@
}
/**
- * Sets an Android platform specific text style configuration options for styling and
- * compatibility.
- */
- @RequiresSchemaVersion(major = 1, minor = 200)
- @ProtoLayoutExperimental
- @NonNull
- public Builder setAndroidTextStyle(@NonNull AndroidTextStyle androidTextStyle) {
- mImpl.setAndroidTextStyle(androidTextStyle.toProto());
- mFingerprint.recordPropertyUpdate(
- 8, checkNotNull(androidTextStyle.getFingerprint()).aggregateValueAsInt());
- return this;
- }
-
- /**
* Sets the number of times to repeat the Marquee animation. Only applies when overflow
* is TEXT_OVERFLOW_MARQUEE. Set to -1 to repeat indefinitely. Defaults to repeat
* indefinitely.
@@ -2590,20 +2575,6 @@
}
}
- /**
- * Gets an Android platform specific text style configuration options for styling and
- * compatibility.
- */
- @ProtoLayoutExperimental
- @Nullable
- public AndroidTextStyle getAndroidTextStyle() {
- if (mImpl.hasAndroidTextStyle()) {
- return AndroidTextStyle.fromProto(mImpl.getAndroidTextStyle());
- } else {
- return null;
- }
- }
-
@Override
@RestrictTo(Scope.LIBRARY_GROUP)
@Nullable
@@ -2649,8 +2620,6 @@
+ getFontStyle()
+ ", modifiers="
+ getModifiers()
- + ", androidTextStyle="
- + getAndroidTextStyle()
+ "}";
}
@@ -2661,7 +2630,11 @@
private final Fingerprint mFingerprint = new Fingerprint(266451531);
/** Creates an instance of {@link Builder}. */
- public Builder() {}
+ public Builder() {
+ mImpl.setAndroidTextStyle(
+ LayoutElementProto.AndroidTextStyle.newBuilder()
+ .setExcludeFontPadding(true));
+ }
/**
* Sets the text to render.
@@ -2718,20 +2691,6 @@
return this;
}
- /**
- * Sets an Android platform specific text style configuration options for styling and
- * compatibility.
- */
- @RequiresSchemaVersion(major = 1, minor = 200)
- @ProtoLayoutExperimental
- @NonNull
- public Builder setAndroidTextStyle(@NonNull AndroidTextStyle androidTextStyle) {
- mImpl.setAndroidTextStyle(androidTextStyle.toProto());
- mFingerprint.recordPropertyUpdate(
- 4, checkNotNull(androidTextStyle.getFingerprint()).aggregateValueAsInt());
- return this;
- }
-
/** Builds an instance from accumulated values. */
@Override
@NonNull
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
index a8352c8..e91a92d 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
@@ -271,6 +271,7 @@
}
@Test
+ @SuppressWarnings("deprecation")
public void testTextSetOverflow_ellipsizeEnd() {
LayoutElementBuilders.Text text =
new LayoutElementBuilders.Text.Builder()
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileService.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileService.java
index 73fd138..06f7334 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileService.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileService.java
@@ -272,10 +272,10 @@
* changed by the time the result is received. {@link TileService#onTileAddEvent} and {@link
* TileService#onTileRemoveEvent} should be used instead for live updates.
*
- * <p>This method is a best-effort to match platform behavior, but may not always return all
- * tiles present in the carousel. The possibly omitted tiles being the pre-installed tiles, all
- * tiles if the user has cleared the app data, or the tiles a user hasn't visited in the last 60
- * days, while tiles removed by an app update may be shown as active for 60 days afterwards.
+ * <p>This method may not always return all tiles present in the carousel. The possibly
+ * omitted tiles being the pre-installed tiles, all tiles if the user has cleared the app
+ * data, or the tiles a user hasn't visited in the last 60 days, while tiles removed by an
+ * app update may be shown as active for 60 days afterwards.
*
* @param context The application context.
* @param executor The executor on which methods should be invoked. To dispatch events through
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/constraints/WorkConstraintsTracker.kt b/work/work-runtime/src/main/java/androidx/work/impl/constraints/WorkConstraintsTracker.kt
index 27e6336..7811847 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/constraints/WorkConstraintsTracker.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/constraints/WorkConstraintsTracker.kt
@@ -194,10 +194,17 @@
override fun hasConstraint(workSpec: WorkSpec): Boolean =
workSpec.constraints.requiredNetworkRequest != null
- override fun isCurrentlyConstrained(workSpec: WorkSpec): Boolean =
+ override fun isCurrentlyConstrained(workSpec: WorkSpec): Boolean {
+ // It happens because ConstraintTrackingWorker can still run on API level 28
+ // after OS upgrade, because we're wrapping workers as ConstraintTrackingWorker at
+ // the enqueue time instead of execution time.
+ // However, ConstraintTrackingWorker won't have requiredNetworkRequest set
+ // because they were enqueued on APIs 23..25, in this case we don't throw.
+ if (!hasConstraint(workSpec)) return false
throw IllegalStateException(
"isCurrentlyConstrained() must never be called on" +
"NetworkRequestConstraintController. isCurrentlyConstrained() is called only " +
"on older platforms where NetworkRequest isn't supported"
)
+ }
}
diff --git a/work/work-runtime/src/test/java/androidx/work/NetworkRequestConstraintControllerTest.kt b/work/work-runtime/src/test/java/androidx/work/NetworkRequestConstraintControllerTest.kt
index e91bee2..c27890b 100644
--- a/work/work-runtime/src/test/java/androidx/work/NetworkRequestConstraintControllerTest.kt
+++ b/work/work-runtime/src/test/java/androidx/work/NetworkRequestConstraintControllerTest.kt
@@ -27,7 +27,9 @@
import androidx.work.impl.constraints.ConstraintsState.ConstraintsMet
import androidx.work.impl.constraints.ConstraintsState.ConstraintsNotMet
import androidx.work.impl.constraints.NetworkRequestConstraintController
+import androidx.work.impl.model.WorkSpec
import com.google.common.truth.Truth.assertThat
+import java.util.UUID
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.collectIndexed
import kotlinx.coroutines.flow.first
@@ -115,6 +117,15 @@
)
}
}
+
+ @Test
+ fun testIsCurrentlyConstrained() {
+ val connectivityManager = getApplicationContext<Context>()
+ .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ val controller = NetworkRequestConstraintController(connectivityManager, 0)
+ val workSpec = WorkSpec(id = UUID.randomUUID().toString(), workerClassName = "Foo")
+ assertThat(controller.isCurrentlyConstrained(workSpec)).isFalse()
+ }
}
@RequiresApi(28)