Merge "Update Kotlinpoet to 1.14.2" into androidx-main
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index fb17a4b..aa16fad 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -131,6 +131,11 @@
android:theme="@style/Theme.TextColors"/>
<activity
+ android:name="androidx.appcompat.widget.AppCompatTextViewFontScalingTest$TextViewFontScalingActivity"
+ android:label="@string/app_compat_text_view_activity"
+ android:theme="@style/Theme.TextColors" />
+
+ <activity
android:name="androidx.appcompat.widget.AppCompatTextViewAutoSizeActivity"
android:label="@string/app_compat_text_view_auto_size_activity"
android:theme="@style/Theme.AppCompat.Light"/>
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt
new file mode 100644
index 0000000..a87d598
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt
@@ -0,0 +1,197 @@
+/*
+ * 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.appcompat.widget
+
+import android.app.Activity
+import android.os.Build
+import android.os.Bundle
+import android.util.TypedValue
+import android.widget.TextView
+import androidx.appcompat.test.R
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.testutils.AndroidFontScaleHelper.resetSystemFontScale
+import androidx.testutils.AndroidFontScaleHelper.setSystemFontScale
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test {@link AppCompatTextView} under non-linear font scaling.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class AppCompatTextViewFontScalingTest {
+ @get:Rule
+ val scenarioRule = ActivityScenarioRule(TextViewFontScalingActivity::class.java)
+
+ @After
+ fun teardown() {
+ // Have to manually check the version here because if we try to rely on the assumeTrue() in
+ // resetSystemFontScale(), it is called twice (again in setSystemFontScale()) and the test
+ // fails with a "TestCouldNotBeSkippedException: Test could not be skipped due to other
+ // failures" because it thinks the second assumeTrue() was a separate error.
+ // tl;dr avoids a bug in jUnit when multiple assumeTrue()s happen in @Test and @After
+ if (Build.VERSION.SDK_INT >= 29) {
+ resetSystemFontScale(scenarioRule.scenario)
+ }
+ }
+
+ @Test
+ @Throws(Throwable::class)
+ fun testNonLinearFontScaling_testSetLineHeightSpAndSetTextSizeSp() {
+ setSystemFontScale(2f, scenarioRule.scenario)
+ scenarioRule.scenario.onActivity { activity ->
+ assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+ val textView = AppCompatTextView(activity)
+ val textSizeSp = 20f
+ val lineHeightSp = 40f
+
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSizeSp)
+ textView.setLineHeight(TypedValue.COMPLEX_UNIT_SP, lineHeightSp)
+
+ verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+ }
+ }
+
+ @Test
+ @Throws(Throwable::class)
+ fun testNonLinearFontScaling_overwriteXml_testSetLineHeightSpAndSetTextSizeSp() {
+ setSystemFontScale(2f, scenarioRule.scenario)
+ scenarioRule.scenario.onActivity { activity ->
+ assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+ val textView = findTextView(activity, R.id.textview_lineheight2x)
+ val textSizeSp = 20f
+ val lineHeightSp = 40f
+
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSizeSp)
+ textView.setLineHeight(TypedValue.COMPLEX_UNIT_SP, lineHeightSp)
+
+ verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+ }
+ }
+
+ @Test
+ @Throws(Throwable::class)
+ fun testNonLinearFontScaling_xml_testLineHeightAttrSpAndTextSizeAttrSp() {
+ setSystemFontScale(2f, scenarioRule.scenario)
+ scenarioRule.scenario.onActivity { activity ->
+ assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+ val textView = findTextView(activity, R.id.textview_lineheight2x)
+ val textSizeSp = 20f
+ val lineHeightSp = 40f
+
+ verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+ }
+ }
+
+ @Test
+ @Throws(Throwable::class)
+ fun testNonLinearFontScaling_dimenXml_testLineHeightAttrSpAndTextSizeAttrSp() {
+ setSystemFontScale(2f, scenarioRule.scenario)
+ scenarioRule.scenario.onActivity { activity ->
+ assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+ val textView = findTextView(activity, R.id.textview_lineheight_dimen3x)
+ val textSizeSp = 20f
+ val lineHeightSp = 60f
+
+ verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+ }
+ }
+
+ @Test
+ @Throws(Throwable::class)
+ fun testNonLinearFontScaling_styleXml_testLineHeightAttrSpAndTextSizeAttrSp() {
+ setSystemFontScale(2f, scenarioRule.scenario)
+ scenarioRule.scenario.onActivity { activity ->
+ assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+ val textView = findTextView(activity, R.id.textview_lineheight_style3x)
+ val textSizeSp = 20f
+ val lineHeightSp = 60f
+
+ verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+ }
+ }
+
+ @Test
+ @Throws(Throwable::class)
+ fun testNonLinearFontScaling_dimenXml_testSetLineHeightSpAndTextSizeAttrSp() {
+ setSystemFontScale(2f, scenarioRule.scenario)
+ scenarioRule.scenario.onActivity { activity ->
+ assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+ val textView = findTextView(activity, R.id.textview_lineheight_dimen3x)
+ val textSizeSp = 20f
+ val lineHeightSp = 30f
+
+ textView.setLineHeight(TypedValue.COMPLEX_UNIT_SP, lineHeightSp)
+
+ verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+ }
+ }
+
+ private fun findTextView(activity: Activity, id: Int): AppCompatTextView {
+ return activity.findViewById(id)!!
+ }
+
+ class TextViewFontScalingActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.appcompat_textview_fontscaling_activity)
+ }
+ }
+
+ companion object {
+ /**
+ * Tolerance for comparing expected float lineHeight to the integer one returned by
+ * getLineHeight(). It is pretty lenient to account for integer rounding when text size is
+ * loaded from an attribute. (When loading an SP resource from an attribute for textSize,
+ * it is rounded to the nearest pixel, which can throw off calculations quite a lot. Not
+ * enough to make much of a difference to the user, but enough to need a wide tolerance in
+ * tests. See b/279456702 for more details.)
+ */
+ private const val TOLERANCE = 5f
+
+ private fun verifyLineHeightIsIntendedProportions(
+ lineHeightSp: Float,
+ textSizeSp: Float,
+ activity: Activity,
+ textView: TextView
+ ) {
+ val lineHeightMultiplier = lineHeightSp / textSizeSp
+ // Calculate what line height would be without non-linear font scaling compressing it.
+ // The trick is multiplying afterwards (by the pixel value) instead of before (by the SP
+ // value)
+ val expectedLineHeightPx = lineHeightMultiplier * TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_SP,
+ textSizeSp,
+ activity.resources.displayMetrics
+ )
+ assertThat(textView.lineHeight.toFloat())
+ .isWithin(TOLERANCE)
+ .of(expectedLineHeightPx)
+ }
+ }
+}
diff --git a/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_fontscaling_activity.xml b/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_fontscaling_activity.xml
new file mode 100644
index 0000000..fadabf4
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_fontscaling_activity.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/layout_textviewtest"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Tests line height 2x the text size -->
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/textview_lineheight2x"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text_long"
+ android:textSize="20sp"
+ android:lineHeight="40sp" />
+
+ <!-- Tests line height 3x the text size -->
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/textview_lineheight_dimen3x"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text_long"
+ android:textSize="@dimen/textview_fontScaling_textSize"
+ android:lineHeight="@dimen/textview_fontScaling_lineHeight" />
+
+ <!-- Tests line height 3x the text size -->
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/textview_lineheight_style3x"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text_long"
+ style="@style/TextAppearance.FontScaling" />
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/appcompat/appcompat/src/androidTest/res/values/dimens.xml b/appcompat/appcompat/src/androidTest/res/values/dimens.xml
index d4a8a0e..aed0b5d 100644
--- a/appcompat/appcompat/src/androidTest/res/values/dimens.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/dimens.xml
@@ -27,4 +27,7 @@
<dimen name="textview_firstBaselineToTopHeight">100dp</dimen>
<dimen name="textview_lastBaselineToBottomHeight">30dp</dimen>
<dimen name="textview_lineHeight">60dp</dimen>
+
+ <dimen name="textview_fontScaling_textSize">20sp</dimen>
+ <dimen name="textview_fontScaling_lineHeight">60sp</dimen>
</resources>
\ No newline at end of file
diff --git a/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml b/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
index 151400a..7a07538 100644
--- a/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
@@ -71,6 +71,16 @@
<string name="app_compat_button_auto_size_activity">AppCompat button auto-size</string>
<string name="sample_text1">Sample text 1</string>
<string name="sample_text2">Sample text 2</string>
+ <string name="sample_text_long">This is a really long string which exceeds the width of the
+view. New devices have a much larger screen which actually enables long strings to be displayed
+with no fading. I have made this string longer to fix this case. If you are correcting this
+text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer
+string! I think so, so how about double this string, like copy and paste!
+This is a really long string which exceeds the width of the view.
+New devices have a much larger screen which actually enables long strings to be displayed
+with no fading. I have made this string longer to fix this case. If you are correcting this
+text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer
+string! I think so, so how about double this string, like copy and paste!</string>
<string name="app_compat_image_button_activity">AppCompat image button</string>
<string name="app_compat_image_view_activity">AppCompat image view</string>
<string name="app_compat_button_activity">AppCompat button</string>
diff --git a/appcompat/appcompat/src/androidTest/res/values/styles.xml b/appcompat/appcompat/src/androidTest/res/values/styles.xml
index 0b540bf..a076019 100644
--- a/appcompat/appcompat/src/androidTest/res/values/styles.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/styles.xml
@@ -146,4 +146,9 @@
<item name="android:colorForeground">@color/color_state_list_lilac</item>
</style>
+ <!-- Tests line height 3x the text size -->
+ <style name="TextAppearance.FontScaling">
+ <item name="android:textSize">@dimen/textview_fontScaling_textSize</item>
+ <item name="android:lineHeight">@dimen/textview_fontScaling_lineHeight</item>
+ </style>
</resources>
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
index fda4335..4eb3371 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
@@ -42,6 +42,7 @@
import androidx.annotation.RestrictTo;
import androidx.appcompat.R;
import androidx.core.content.res.ResourcesCompat;
+import androidx.core.util.TypedValueCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.inputmethod.EditorInfoCompat;
import androidx.core.widget.TextViewCompat;
@@ -326,8 +327,20 @@
R.styleable.AppCompatTextView_firstBaselineToTopHeight, -1);
final int lastBaselineToBottomHeight = a.getDimensionPixelSize(
R.styleable.AppCompatTextView_lastBaselineToBottomHeight, -1);
- final int lineHeight = a.getDimensionPixelSize(
- R.styleable.AppCompatTextView_lineHeight, -1);
+ float lineHeight = -1;
+ int lineHeightUnit = -1;
+ if (a.hasValue(R.styleable.AppCompatTextView_lineHeight)) {
+ TypedValue peekValue = a.peekValue(R.styleable.AppCompatTextView_lineHeight);
+ if (peekValue != null && peekValue.type == TypedValue.TYPE_DIMENSION) {
+ lineHeightUnit = TypedValueCompat.getUnitFromComplexDimension(peekValue.data);
+ lineHeight = TypedValue.complexToFloat(peekValue.data);
+ } else {
+ lineHeight = a.getDimensionPixelSize(
+ R.styleable.AppCompatTextView_lineHeight,
+ -1
+ );
+ }
+ }
a.recycle();
if (firstBaselineToTopHeight != -1) {
@@ -337,7 +350,11 @@
TextViewCompat.setLastBaselineToBottomHeight(mView, lastBaselineToBottomHeight);
}
if (lineHeight != -1) {
- TextViewCompat.setLineHeight(mView, lineHeight);
+ if (lineHeightUnit == -1) {
+ TextViewCompat.setLineHeight(mView, (int) lineHeight);
+ } else {
+ TextViewCompat.setLineHeight(mView, lineHeightUnit, lineHeight);
+ }
}
}
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
index 1e92e4a..1f0e15a 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
@@ -37,6 +37,7 @@
import android.widget.TextView;
import androidx.annotation.DrawableRes;
+import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -477,6 +478,15 @@
TextViewCompat.setLineHeight(this, lineHeight);
}
+ @Override
+ public void setLineHeight(int unit, @FloatRange(from = 0) float lineHeight) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ getSuperCaller().setLineHeight(unit, lineHeight);
+ } else {
+ TextViewCompat.setLineHeight(this, unit, lineHeight);
+ }
+ }
+
/**
* See
* {@link TextViewCompat#setCustomSelectionActionModeCallback(TextView, ActionMode.Callback)}
@@ -789,7 +799,9 @@
@RequiresApi(api = 26)
SuperCaller getSuperCaller() {
if (mSuperCaller == null) {
- if (Build.VERSION.SDK_INT >= 28) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ mSuperCaller = new SuperCallerApi34();
+ } else if (Build.VERSION.SDK_INT >= 28) {
mSuperCaller = new SuperCallerApi28();
} else if (Build.VERSION.SDK_INT >= 26) {
mSuperCaller = new SuperCallerApi26();
@@ -817,6 +829,9 @@
// api 28
void setFirstBaselineToTopHeight(@Px int firstBaselineToTopHeight);
void setLastBaselineToBottomHeight(@Px int lastBaselineToBottomHeight);
+
+ // api 34
+ void setLineHeight(int unit, @FloatRange(from = 0) float lineHeight);
}
@RequiresApi(api = 26)
@@ -878,6 +893,9 @@
@Override
public void setLastBaselineToBottomHeight(int lastBaselineToBottomHeight) {}
+
+ @Override
+ public void setLineHeight(int unit, float lineHeight) {}
}
@RequiresApi(api = 28)
@@ -893,4 +911,12 @@
AppCompatTextView.super.setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
}
}
+
+ @RequiresApi(api = 34)
+ class SuperCallerApi34 extends SuperCallerApi28 {
+ @Override
+ public void setLineHeight(int unit, float lineHeight) {
+ AppCompatTextView.super.setLineHeight(unit, lineHeight);
+ }
+ }
}
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 cbfeea9..de56b69 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
@@ -645,6 +645,8 @@
PrefixUtil.removePrefix(grouping2.getEntryGroupings(1).getNamespace()));
}
+ // @exportToFramework:startStrip()
+ // TODO(b/258715421) start exporting this when it is unhidden in framework
@Test
public void testToResultSpecProto_groupBySchema() throws Exception {
SearchSpec searchSpec = new SearchSpec.Builder()
@@ -691,6 +693,7 @@
.isEqualTo(
PrefixUtil.removePrefix(grouping2.getEntryGroupings(1).getSchema()));
}
+ // @exportToFramework:endStrip()
@Test
public void testToResultSpecProto_groupByNamespaceAndPackage() throws Exception {
@@ -727,6 +730,8 @@
assertThat(resultSpecProto.getResultGroupings(3).getEntryGroupingsList()).hasSize(1);
}
+ // @exportToFramework:startStrip()
+ // TODO(b/258715421) start exporting this when it is unhidden in framework
@Test
public void testToResultSpecProto_groupBySchemaAndPackage() throws Exception {
SearchSpec searchSpec = new SearchSpec.Builder()
@@ -964,6 +969,7 @@
assertThat(grouping8.getEntryGroupings(0).getSchema())
.isEqualTo("package1$database/typeB");
}
+ // @exportToFramework:endStrip()
@Test
public void testGetTargetNamespaceFilters_emptySearchingFilter() {
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
index 794ed1f..ed3a8f6 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
@@ -63,7 +63,7 @@
// Beyond Android U features
case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
- // TODO(b/258715421): Update to reflect support in Android U+ once this feature has
+ // TODO(b/258715421) : Update to reflect support in Android U+ once this feature has
// an extservices sdk that includes it.
// fall through
case Features.SCHEMA_SET_DELETION_PROPAGATION:
diff --git a/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt b/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
index f53eb4f..e4d7736 100644
--- a/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
@@ -39,7 +39,7 @@
fun init() {
builder = ConfigBuilder()
builder.configName("placeHolderAndroidTest.xml")
- .isBenchmark(false)
+ .isMicrobenchmark(false)
.applicationId("com.androidx.placeholder.Placeholder")
.isPostsubmit(true)
.minSdk("15")
@@ -59,7 +59,7 @@
@Test
fun testXmlAgainstGoldenDefaultBenchmark() {
- builder.isBenchmark(true)
+ builder.isMicrobenchmark(true)
MatcherAssert.assertThat(
builder.buildXml(),
CoreMatchers.`is`(goldenDefaultConfigBenchmark)
@@ -123,7 +123,7 @@
@Test
fun testJsonAgainstGoldenPresubmitBenchmark() {
- builder.isBenchmark(true)
+ builder.isMicrobenchmark(true)
.isPostsubmit(false)
MatcherAssert.assertThat(
builder.buildJson(),
@@ -241,7 +241,7 @@
@Test
fun testValidTestConfigXml_benchmarkTrue() {
- builder.isBenchmark(true)
+ builder.isMicrobenchmark(true)
validate(builder.buildXml())
}
@@ -279,7 +279,7 @@
@Test
fun testValidTestConfigXml_presubmitBenchmark() {
builder.isPostsubmit(false)
- .isBenchmark(true)
+ .isMicrobenchmark(true)
validate(builder.buildXml())
}
@@ -349,7 +349,6 @@
<option name="config-descriptor:metadata" key="applicationId" value="com.androidx.placeholder.Placeholder" />
<option name="wifi:disable" value="true" />
<option name="instrumentation-arg" key="notAnnotation" value="androidx.test.filters.FlakyTest" />
- <option name="instrumentation-arg" key="androidx.benchmark.output.enable" value="true" />
<option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
<include name="google/unbundled/common/setup" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 2b8e4e7..2c51b04 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -162,6 +162,7 @@
project.validatePublishedMultiplatformHasDefault()
}
}
+ project.disallowAccidentalAndroidDependenciesInKmpProject(kmpExtension)
}
private fun Project.registerProjectOrArtifact() {
@@ -931,13 +932,6 @@
return
}
project.afterEvaluate {
- if (project.hasKotlinNativeTarget().get()) {
- // KMP plugin cannot handle constraints properly for native targets
- // b/274786186, YT: KT-57531
- // It is expected to be fixed in Kotlin 1.9 after which, we should remove this check
- return@afterEvaluate
- }
-
// make sure that the project has a group
val projectGroup = extension.mavenGroup ?: return@afterEvaluate
// make sure that this group is configured to use a single version
@@ -1221,6 +1215,31 @@
}
}
+/**
+ * Verifies we don't accidentially write "implementation" instead of "commonMainImplementation"
+ */
+fun Project.disallowAccidentalAndroidDependenciesInKmpProject(
+ kmpExtension: AndroidXMultiplatformExtension
+) {
+ project.afterEvaluate {
+ if (kmpExtension.supportedPlatforms.isNotEmpty()) {
+ val androidConfiguration = project.configurations.findByName("implementation")
+ if (androidConfiguration != null) {
+ if (
+ androidConfiguration.dependencies.isNotEmpty() ||
+ androidConfiguration.dependencyConstraints.isNotEmpty()
+ ) {
+ throw GradleException(
+ "The 'implementation' Configuration should not be used in a " +
+ "multiplatform project: this Configuration is declared by the " +
+ "Android plugin rather than the kmp plugin. Did you mean " +
+ "'commonMainImplementation'?")
+ }
+ }
+ }
+ }
+}
+
/** Verifies that ProjectParser computes the correct values for this project */
fun Project.validateProjectParser(extension: AndroidXExtension) {
// If configuration fails, we don't want to validate the ProjectParser
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index 86d3217..a3fd34e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -50,16 +50,14 @@
private val kotlinExtension: KotlinMultiplatformExtension by kotlinExtensionDelegate
/**
- * The list of platforms that have been requested in the build configuration.
+ * The list of platforms that have been declared as supported in the build configuration.
*
- * The list of enabled platforms in [targetPlatforms] will vary based on the build environment.
- * For example, a project's build configuration may have requested `mac()` but this is not
- * available when building on Linux.
+ * This may be a superset of the currently enabled platforms in [targetPlatforms].
*/
- val requestedPlatforms: MutableSet<PlatformIdentifier> = mutableSetOf()
+ val supportedPlatforms: MutableSet<PlatformIdentifier> = mutableSetOf()
/**
- * The list of platforms that are enabled.
+ * The list of platforms that are currently enabled.
*
* This will vary across build environments. For example, a project's build configuration may
* have requested `mac()` but this is not available when building on Linux.
@@ -91,14 +89,14 @@
* identifier for that platform.
*/
var defaultPlatform: String? = null
- get() = field ?: requestedPlatforms.singleOrNull()?.id
+ get() = field ?: supportedPlatforms.singleOrNull()?.id
set(value) {
if (value != null) {
- if (requestedPlatforms.none { it.id == value }) {
+ if (supportedPlatforms.none { it.id == value }) {
throw GradleException(
"Platform $value has not been requested as a target. " +
"Available platforms are: " +
- requestedPlatforms.joinToString(", ") { it.id }
+ supportedPlatforms.joinToString(", ") { it.id }
)
}
if (targetPlatforms.none { it == value }) {
@@ -145,7 +143,7 @@
@JvmOverloads
fun jvm(block: Action<KotlinJvmTarget>? = null): KotlinJvmTarget? {
- requestedPlatforms.add(PlatformIdentifier.JVM)
+ supportedPlatforms.add(PlatformIdentifier.JVM)
return if (project.enableJvm()) {
kotlinExtension.jvm {
block?.execute(this)
@@ -163,7 +161,7 @@
@JvmOverloads
fun android(block: Action<KotlinAndroidTarget>? = null): KotlinAndroidTarget? {
- requestedPlatforms.add(PlatformIdentifier.ANDROID)
+ supportedPlatforms.add(PlatformIdentifier.ANDROID)
return if (project.enableJvm()) {
kotlinExtension.androidTarget { block?.execute(this) }
} else {
@@ -173,7 +171,7 @@
@JvmOverloads
fun desktop(block: Action<KotlinJvmTarget>? = null): KotlinJvmTarget? {
- requestedPlatforms.add(PlatformIdentifier.DESKTOP)
+ supportedPlatforms.add(PlatformIdentifier.DESKTOP)
return if (project.enableDesktop()) {
kotlinExtension.jvm("desktop") { block?.execute(this) }
} else {
@@ -189,7 +187,7 @@
@JvmOverloads
fun macosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
- requestedPlatforms.add(PlatformIdentifier.MAC_OSX_64)
+ supportedPlatforms.add(PlatformIdentifier.MAC_OSX_64)
return if (project.enableMac()) {
kotlinExtension.macosX64().also { block?.execute(it) }
} else {
@@ -199,7 +197,7 @@
@JvmOverloads
fun macosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
- requestedPlatforms.add(PlatformIdentifier.MAC_ARM_64)
+ supportedPlatforms.add(PlatformIdentifier.MAC_ARM_64)
return if (project.enableMac()) {
kotlinExtension.macosArm64().also { block?.execute(it) }
} else {
@@ -209,7 +207,7 @@
@JvmOverloads
fun iosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
- requestedPlatforms.add(PlatformIdentifier.IOS_ARM_64)
+ supportedPlatforms.add(PlatformIdentifier.IOS_ARM_64)
return if (project.enableMac()) {
kotlinExtension.iosArm64().also { block?.execute(it) }
} else {
@@ -225,7 +223,7 @@
@JvmOverloads
fun iosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
- requestedPlatforms.add(PlatformIdentifier.IOS_X_64)
+ supportedPlatforms.add(PlatformIdentifier.IOS_X_64)
return if (project.enableMac()) {
kotlinExtension.iosX64().also { block?.execute(it) }
} else {
@@ -235,7 +233,7 @@
@JvmOverloads
fun iosSimulatorArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
- requestedPlatforms.add(PlatformIdentifier.IOS_SIMULATOR_ARM_64)
+ supportedPlatforms.add(PlatformIdentifier.IOS_SIMULATOR_ARM_64)
return if (project.enableMac()) {
kotlinExtension.iosSimulatorArm64().also { block?.execute(it) }
} else {
@@ -252,7 +250,7 @@
@JvmOverloads
fun linuxX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
- requestedPlatforms.add(PlatformIdentifier.LINUX_64)
+ supportedPlatforms.add(PlatformIdentifier.LINUX_64)
return if (project.enableLinux()) {
kotlinExtension.linuxX64().also { block?.execute(it) }
} else {
@@ -262,7 +260,7 @@
@JvmOverloads
fun js(block: Action<KotlinJsTargetDsl>? = null): KotlinJsTargetDsl? {
- requestedPlatforms.add(PlatformIdentifier.JS)
+ supportedPlatforms.add(PlatformIdentifier.JS)
return if (project.enableJs()) {
kotlinExtension.js().also { block?.execute(it) }
} else {
@@ -286,7 +284,7 @@
fun Project.validatePublishedMultiplatformHasDefault() {
val extension = project.extensions.getByType(AndroidXMultiplatformExtension::class.java)
- if (extension.defaultPlatform == null && extension.requestedPlatforms.isNotEmpty()) {
+ if (extension.defaultPlatform == null && extension.supportedPlatforms.isNotEmpty()) {
throw GradleException(
"Project is published and multiple platforms are requested. You " +
"must explicitly specify androidXMultiplatform.defaultPlatform as one of: " +
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index 9265e5c..724504f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -566,10 +566,6 @@
":media2:media2-session:version-compat-tests:service",
":media2:media2-session:version-compat-tests:client-previous",
":media2:media2-session:version-compat-tests:service-previous"
- ), // Link graphics and material to always run @Large in presubmit per b/160624022
- setOf(
- ":compose:ui:ui-graphics",
- ":compose:material:material"
), // Link material and material-ripple
setOf(":compose:material:material-ripple", ":compose:material:material"),
setOf(
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
index a3c6691..657f702 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
@@ -23,7 +23,7 @@
var appApkName: String? = null
var appApkSha256: String? = null
lateinit var applicationId: String
- var isBenchmark: Boolean = false
+ var isMicrobenchmark: Boolean = false
var isPostsubmit: Boolean = true
lateinit var minSdk: String
val tags = mutableListOf<String>()
@@ -40,7 +40,8 @@
fun applicationId(applicationId: String) = apply { this.applicationId = applicationId }
- fun isBenchmark(isBenchmark: Boolean) = apply { this.isBenchmark = isBenchmark }
+ fun isMicrobenchmark(isMicrobenchmark: Boolean) =
+ apply { this.isMicrobenchmark = isMicrobenchmark }
fun isPostsubmit(isPostsubmit: Boolean) = apply { this.isPostsubmit = isPostsubmit }
@@ -59,7 +60,7 @@
fun buildJson(): String {
val gson = GsonBuilder().setPrettyPrinting().create()
val instrumentationArgs =
- if (isBenchmark && !isPostsubmit) {
+ if (isMicrobenchmark && !isPostsubmit) {
listOf(
InstrumentationArg("notAnnotation", "androidx.test.filters.FlakyTest"),
InstrumentationArg("androidx.benchmark.dryRunMode.enable", "true"),
@@ -91,11 +92,11 @@
sb.append(MODULE_METADATA_TAG_OPTION.replace("APPLICATION_ID", applicationId))
.append(WIFI_DISABLE_OPTION)
.append(FLAKY_TEST_OPTION)
- if (isBenchmark) {
+ if (isMicrobenchmark) {
if (isPostsubmit) {
- sb.append(BENCHMARK_POSTSUBMIT_OPTIONS)
+ sb.append(MICROBENCHMARK_POSTSUBMIT_OPTIONS)
} else {
- sb.append(BENCHMARK_PRESUBMIT_OPTION)
+ sb.append(MICROBENCHMARK_PRESUBMIT_OPTION)
}
}
sb.append(SETUP_INCLUDE)
@@ -105,7 +106,7 @@
sb.append(APK_INSTALL_OPTION.replace("APK_NAME", appApkName!!))
sb.append(TARGET_PREPARER_CLOSE)
// Post install commands after SuiteApkInstaller is declared
- if (isBenchmark) {
+ if (isMicrobenchmark) {
sb.append(benchmarkPostInstallCommandOption(applicationId))
}
sb.append(TEST_BLOCK_OPEN)
@@ -312,16 +313,15 @@
"""
.trimIndent()
-private val BENCHMARK_PRESUBMIT_OPTION =
+private val MICROBENCHMARK_PRESUBMIT_OPTION =
"""
<option name="instrumentation-arg" key="androidx.benchmark.dryRunMode.enable" value="true" />
"""
.trimIndent()
-private val BENCHMARK_POSTSUBMIT_OPTIONS =
+private val MICROBENCHMARK_POSTSUBMIT_OPTIONS =
"""
- <option name="instrumentation-arg" key="androidx.benchmark.output.enable" value="true" />
<option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
"""
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
index 87b7114..d428c0f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
@@ -134,10 +134,14 @@
configBuilder.isPostsubmit(!isPresubmit)
// This section adds metadata tags that will help filter runners to specific modules.
if (hasBenchmarkPlugin.get()) {
- configBuilder.isBenchmark(true)
- if (configBuilder.isPostsubmit) {
- configBuilder.tag("microbenchmarks")
- } else {
+ configBuilder.isMicrobenchmark(true)
+
+ // tag microbenchmarks as "microbenchmarks" in either build config, so that benchmark
+ // test configs will always have something to run, regardless of build (though presubmit
+ // builds will still set dry run, and not output metrics)
+ configBuilder.tag("microbenchmarks")
+
+ if (isPresubmit) {
// in presubmit, we treat micro benchmarks as regular correctness tests as
// they run with dryRunMode to check crashes don't happen, without measurement
configBuilder.tag("androidx_unit_tests")
diff --git a/buildSrc/repos.gradle b/buildSrc/repos.gradle
index 4ae93fa..9b5077d 100644
--- a/buildSrc/repos.gradle
+++ b/buildSrc/repos.gradle
@@ -65,13 +65,6 @@
url("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
handler.mavenLocal()
- // TODO(b/280646217): Remove after official release to gmaven.
- handler.maven {
- url("https://storage.googleapis.com/r8-releases/raw")
- content {
- includeModule("com.android.tools", "r8")
- }
- }
}
// Ordering appears to be important: b/229733266
def androidPluginRepoOverride = System.getenv("GRADLE_PLUGIN_REPO")
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
index 273d30d..c8276d6 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
@@ -17,6 +17,7 @@
package androidx.camera.camera2.pipe.integration.adapter
import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
import androidx.camera.camera2.pipe.CameraDevices
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.integration.internal.CameraGraphCreator
@@ -25,6 +26,7 @@
import androidx.camera.core.CameraInfo
import androidx.camera.core.CameraSelector
import androidx.camera.core.concurrent.CameraCoordinator
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode
import androidx.camera.core.impl.CameraInternal
@@ -33,17 +35,15 @@
cameraDevices: CameraDevices,
private val cameraGraphCreator: CameraGraphCreator
) : CameraCoordinator {
- private val cameraInternalMap = mutableMapOf<CameraId, CameraInternalAdapter>()
-
- private var concurrentCameraIdsSet: Set<Set<CameraId>> = mutableSetOf()
- private var concurrentCameraIdMap: MutableMap<String, MutableList<String>> = mutableMapOf()
- private var activeConcurrentCameraInfosList: MutableList<CameraInfo> = mutableListOf()
-
- private var concurrentMode: Int = CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
- private var concurrentModeOn = false
+ @VisibleForTesting val cameraInternalMap = mutableMapOf<CameraId, CameraInternalAdapter>()
+ @VisibleForTesting var concurrentCameraIdsSet = mutableSetOf<Set<CameraId>>()
+ @VisibleForTesting var concurrentCameraIdMap = mutableMapOf<String, MutableList<String>>()
+ @VisibleForTesting var activeConcurrentCameraInfosList = mutableListOf<CameraInfo>()
+ @VisibleForTesting var concurrentMode: Int = CAMERA_OPERATING_MODE_UNSPECIFIED
+ @VisibleForTesting var concurrentModeOn = false
init {
- concurrentCameraIdsSet = cameraDevices.awaitConcurrentCameraIds()!!
+ concurrentCameraIdsSet = cameraDevices.awaitConcurrentCameraIds()!!.toMutableSet()
for (cameraIdSet in concurrentCameraIdsSet) {
val cameraIdsList = cameraIdSet.toList()
if (cameraIdsList.size >= 2) {
@@ -129,4 +129,13 @@
override fun removeListener(listener: CameraCoordinator.ConcurrentCameraModeListener) {
}
+
+ override fun shutdown() {
+ cameraInternalMap.clear()
+ concurrentCameraIdsSet.clear()
+ concurrentCameraIdMap.clear()
+ activeConcurrentCameraInfosList.clear()
+ concurrentMode = CAMERA_OPERATING_MODE_UNSPECIFIED
+ concurrentModeOn = false
+ }
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
index 1222685..12c69f8 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
@@ -25,6 +25,7 @@
import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT
import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
import androidx.camera.core.impl.CameraInfoInternal
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -123,4 +124,22 @@
assertThat(cameraCoordinatorAdapter.cameraOperatingMode)
.isEqualTo(CAMERA_OPERATING_MODE_SINGLE)
}
+
+ @Test
+ fun shutdown() {
+ cameraCoordinatorAdapter.cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
+ cameraCoordinatorAdapter.activeConcurrentCameraInfos = mutableListOf(
+ FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("0")),
+ FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("1")))
+
+ cameraCoordinatorAdapter.shutdown()
+
+ assertThat(cameraCoordinatorAdapter.cameraInternalMap).isEmpty()
+ assertThat(cameraCoordinatorAdapter.activeConcurrentCameraInfos).isEmpty()
+ assertThat(cameraCoordinatorAdapter.concurrentCameraIdMap).isEmpty()
+ assertThat(cameraCoordinatorAdapter.concurrentCameraIdsSet).isEmpty()
+ assertThat(cameraCoordinatorAdapter.cameraOperatingMode).isEqualTo(
+ CAMERA_OPERATING_MODE_UNSPECIFIED)
+ assertThat(cameraCoordinatorAdapter.concurrentModeOn).isFalse()
+ }
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
index 93e24c7..e6680bf 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
@@ -140,6 +140,15 @@
mConcurrentCameraModeListeners.remove(listener);
}
+ @Override
+ public void shutdown() {
+ mConcurrentCameraModeListeners.clear();
+ mConcurrentCameraIdMap.clear();
+ mActiveConcurrentCameraInfos.clear();
+ mConcurrentCameraIds.clear();
+ mCameraOperatingMode = CAMERA_OPERATING_MODE_UNSPECIFIED;
+ }
+
private void retrieveConcurrentCameraIds() {
try {
mConcurrentCameraIds = mCameraManager.getConcurrentCameraIds();
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
index 6b7de32..fa3f220 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
@@ -57,8 +57,6 @@
)
class Camera2CameraCoordinatorTest {
- private val mContext = ApplicationProvider.getApplicationContext<Context>()
-
private lateinit var cameraCoordinator: CameraCoordinator
@Before
@@ -109,26 +107,7 @@
@Test
fun getPairedCameraId() {
- val characteristics0 = ShadowCameraCharacteristics.newCameraCharacteristics()
- (Shadow.extract<Any>(
- ApplicationProvider.getApplicationContext<Context>()
- .getSystemService(Context.CAMERA_SERVICE)
- ) as ShadowCameraManager)
- .addCamera("0", characteristics0)
- val characteristics1 = ShadowCameraCharacteristics.newCameraCharacteristics()
- (Shadow.extract<Any>(
- ApplicationProvider.getApplicationContext<Context>()
- .getSystemService(Context.CAMERA_SERVICE)
- ) as ShadowCameraManager)
- .addCamera("1", characteristics1)
-
- val mCameraManagerCompat =
- CameraManagerCompat.from((ApplicationProvider.getApplicationContext() as Context))
-
- cameraCoordinator.activeConcurrentCameraInfos = listOf(
- Camera2CameraInfoImpl("0", mCameraManagerCompat),
- Camera2CameraInfoImpl("1", mCameraManagerCompat)
- )
+ cameraCoordinator.activeConcurrentCameraInfos = createConcurrentCameraInfos()
assertThat(cameraCoordinator.getPairedConcurrentCameraId("0")).isEqualTo("1")
assertThat(cameraCoordinator.getPairedConcurrentCameraId("1")).isEqualTo("0")
@@ -164,6 +143,41 @@
anyInt(), anyInt())
}
+ @Test
+ fun shutdown() {
+ cameraCoordinator.cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
+ cameraCoordinator.activeConcurrentCameraInfos = createConcurrentCameraInfos()
+
+ cameraCoordinator.shutdown()
+
+ assertThat(cameraCoordinator.concurrentCameraSelectors).isEmpty()
+ assertThat(cameraCoordinator.activeConcurrentCameraInfos).isEmpty()
+ assertThat(cameraCoordinator.cameraOperatingMode).isEqualTo(
+ CAMERA_OPERATING_MODE_UNSPECIFIED)
+ }
+
+ private fun createConcurrentCameraInfos(): List<Camera2CameraInfoImpl> {
+ val characteristics0 = ShadowCameraCharacteristics.newCameraCharacteristics()
+ (Shadow.extract<Any>(
+ ApplicationProvider.getApplicationContext<Context>()
+ .getSystemService(Context.CAMERA_SERVICE)
+ ) as ShadowCameraManager)
+ .addCamera("0", characteristics0)
+ val characteristics1 = ShadowCameraCharacteristics.newCameraCharacteristics()
+ (Shadow.extract<Any>(
+ ApplicationProvider.getApplicationContext<Context>()
+ .getSystemService(Context.CAMERA_SERVICE)
+ ) as ShadowCameraManager)
+ .addCamera("1", characteristics1)
+ val cameraManagerCompat =
+ CameraManagerCompat.from((ApplicationProvider.getApplicationContext() as Context))
+
+ return listOf(
+ Camera2CameraInfoImpl("0", cameraManagerCompat),
+ Camera2CameraInfoImpl("1", cameraManagerCompat)
+ )
+ }
+
private class FakeCameraManagerImpl : CameraManagerCompat.CameraManagerCompatImpl {
private val mCameraManagerImpl = CameraManagerCompat.CameraManagerCompatImpl.from(
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java b/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
index 60d6be3..b98d11e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
@@ -130,6 +130,11 @@
void removeListener(@NonNull ConcurrentCameraModeListener listener);
/**
+ * Clean up all the resources when CameraX shutdown.
+ */
+ void shutdown();
+
+ /**
* Interface for concurrent camera mode update.
*
* <p>Everytime user changes {@link CameraOperatingMode}, the observer will be notified and
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 f585001..7dbc5c3 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
@@ -30,6 +30,7 @@
import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
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.CameraFactory
import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
import androidx.camera.testing.fakes.FakeAppConfig
@@ -63,6 +64,7 @@
private val context = ApplicationProvider.getApplicationContext() as Context
private val lifecycleOwner0 = FakeLifecycleOwner()
private val lifecycleOwner1 = FakeLifecycleOwner()
+ private val cameraCoordinator = FakeCameraCoordinator()
private lateinit var provider: ProcessCameraProvider
@@ -665,6 +667,10 @@
// Should not throw exception
ProcessCameraProvider.configureInstance(FakeAppConfig.create())
+ assertThat(cameraCoordinator.cameraOperatingMode).isEqualTo(
+ CAMERA_OPERATING_MODE_UNSPECIFIED)
+ assertThat(cameraCoordinator.concurrentCameraSelectors).isEmpty()
+ assertThat(cameraCoordinator.activeConcurrentCameraInfos).isEmpty()
}
@Test
@@ -841,7 +847,6 @@
}
private fun createConcurrentCameraAppConfig(): CameraXConfig {
- val cameraCoordinator = FakeCameraCoordinator()
val combination0 = mapOf(
"0" to CameraSelector.Builder().requireLensFacing(LENS_FACING_BACK).build(),
"1" to CameraSelector.Builder().requireLensFacing(LENS_FACING_FRONT).build())
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 d675fb8..f6e56cd 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
@@ -291,6 +291,10 @@
mLifecycleCameraRepository.clear();
});
+ if (mCameraX != null) {
+ mCameraX.getCameraFactory().getCameraCoordinator().shutdown();
+ }
+
ListenableFuture<Void> shutdownFuture = mCameraX != null ? mCameraX.shutdown() :
Futures.immediateFuture(null);
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
index 3281ec1..1dc5b00 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
@@ -125,4 +125,13 @@
public void removeListener(@NonNull ConcurrentCameraModeListener listener) {
mConcurrentCameraModeListeners.remove(listener);
}
+
+ @Override
+ public void shutdown() {
+ mConcurrentCameraIdMap.clear();
+ mConcurrentCameraIds.clear();
+ mConcurrentCameraSelectors.clear();
+ mActiveConcurrentCameraInfos.clear();
+ mConcurrentCameraModeListeners.clear();
+ }
}
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 5c2d5cb..67d58c8e 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -900,6 +900,7 @@
}
@SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
method public androidx.car.app.model.CarIcon? getIcon();
method public String getId();
@@ -912,6 +913,7 @@
public static final class ConversationItem.Builder {
ctor public ConversationItem.Builder();
ctor public ConversationItem.Builder(androidx.car.app.messaging.model.ConversationItem);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder addAction(androidx.car.app.model.Action);
method public androidx.car.app.messaging.model.ConversationItem build();
method public androidx.car.app.messaging.model.ConversationItem.Builder setConversationCallback(androidx.car.app.messaging.model.ConversationCallback);
method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 5c2d5cb..67d58c8e 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -900,6 +900,7 @@
}
@SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
method public androidx.car.app.model.CarIcon? getIcon();
method public String getId();
@@ -912,6 +913,7 @@
public static final class ConversationItem.Builder {
ctor public ConversationItem.Builder();
ctor public ConversationItem.Builder(androidx.car.app.messaging.model.ConversationItem);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder addAction(androidx.car.app.model.Action);
method public androidx.car.app.messaging.model.ConversationItem build();
method public androidx.car.app.messaging.model.ConversationItem.Builder setConversationCallback(androidx.car.app.messaging.model.ConversationCallback);
method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
index d83d5b0..1acc0f4 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
@@ -29,13 +29,16 @@
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.KeepFields;
import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.model.Action;
import androidx.car.app.model.CarIcon;
import androidx.car.app.model.CarText;
import androidx.car.app.model.Item;
+import androidx.car.app.model.constraints.ActionsConstraints;
import androidx.car.app.utils.CollectionUtils;
import androidx.core.app.Person;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -58,6 +61,8 @@
private final List<CarMessage> mMessages;
@NonNull
private final ConversationCallbackDelegate mConversationCallbackDelegate;
+ @NonNull
+ private final List<Action> mActions;
@Override
public int hashCode() {
@@ -67,7 +72,8 @@
mTitle,
mIcon,
mIsGroupConversation,
- mMessages
+ mMessages,
+ mActions
);
}
@@ -89,6 +95,7 @@
.arePersonsEqual(getSelf(), otherConversationItem.getSelf())
&& mIsGroupConversation == otherConversationItem.mIsGroupConversation
&& Objects.equals(mMessages, otherConversationItem.mMessages)
+ && Objects.equals(mActions, otherConversationItem.mActions)
;
}
@@ -101,6 +108,7 @@
this.mMessages = requireNonNull(CollectionUtils.unmodifiableCopy(builder.mMessages));
checkState(!mMessages.isEmpty(), "Message list cannot be empty.");
this.mConversationCallbackDelegate = requireNonNull(builder.mConversationCallbackDelegate);
+ this.mActions = CollectionUtils.unmodifiableCopy(builder.mActions);
}
/** Default constructor for serialization. */
@@ -123,6 +131,7 @@
// Do nothing
}
});
+ mActions = Collections.emptyList();
}
/**
@@ -175,6 +184,16 @@
}
/**
+ * Returns the list of additional actions.
+ *
+ * @see ConversationItem.Builder#addAction(Action)
+ */
+ @NonNull
+ public List<Action> getActions() {
+ return mActions;
+ }
+
+ /**
* Verifies that a given {@link Person} has the required fields to be a message sender. Returns
* the input {@link Person} if valid, or throws an exception if invalid.
*
@@ -202,6 +221,7 @@
List<CarMessage> mMessages;
@Nullable
ConversationCallbackDelegate mConversationCallbackDelegate;
+ final List<Action> mActions;
/**
* Specifies a unique identifier for the conversation
@@ -278,6 +298,23 @@
return this;
}
+ /**
+ * Adds an additional action for the conversation.
+ *
+ * @throws NullPointerException if {@code action} is {@code null}
+ * @throws IllegalArgumentException if {@code action} contains unsupported Action types,
+ * exceeds the maximum number of allowed actions (1) or
+ * does not contain a valid {@link CarIcon}.
+ */
+ @NonNull
+ public Builder addAction(@NonNull Action action) {
+ List<Action> mActionsCopy = new ArrayList<>(mActions);
+ mActionsCopy.add(requireNonNull(action));
+ ActionsConstraints.ACTIONS_CONSTRAINTS_CONVERSATION_ITEM.validateOrThrow(mActionsCopy);
+ mActions.add(action);
+ return this;
+ }
+
/** Returns a new {@link ConversationItem} instance defined by this builder */
@NonNull
public ConversationItem build() {
@@ -286,6 +323,7 @@
/** Returns an empty {@link Builder} instance. */
public Builder() {
+ mActions = new ArrayList<>();
}
/** Returns a builder from the given {@link ConversationItem}. */
@@ -297,6 +335,7 @@
this.mIsGroupConversation = other.isGroupConversation();
this.mConversationCallbackDelegate = other.getConversationCallbackDelegate();
this.mMessages = other.getMessages();
+ this.mActions = new ArrayList<>(other.getActions());
}
}
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index b91efc0f..e61e54b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -145,6 +145,19 @@
.build();
/**
+ * Constraints for additional ConversationItem actions. Only allows custom actions.
+ */
+ @NonNull
+ public static final ActionsConstraints ACTIONS_CONSTRAINTS_CONVERSATION_ITEM =
+ new ActionsConstraints.Builder()
+ .setMaxActions(1)
+ .setMaxCustomTitles(1)
+ .addAllowedActionType(Action.TYPE_CUSTOM)
+ .setRequireActionIcons(true)
+ .setOnClickListenerAllowed(true)
+ .build();
+
+ /**
* Constraints for floating action buttons.
*
* <p>Only buttons with icons and background color are allowed.
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
index ef2956c..e6b6e49 100644
--- a/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
@@ -21,8 +21,11 @@
import static org.junit.Assert.assertThrows;
import androidx.annotation.NonNull;
+import androidx.car.app.TestUtils;
+import androidx.car.app.model.Action;
import androidx.car.app.model.CarIcon;
import androidx.car.app.model.CarText;
+import androidx.test.core.app.ApplicationProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -191,6 +194,77 @@
assertEqual(fullyPopulatedItem, modifiedConversationCallback);
}
+ @Test
+ public void addAction() {
+ CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+ "ic_test_1");
+ Action customAction = new Action.Builder().setIcon(icon).build();
+ ConversationItem item =
+ TestConversationFactory
+ .createFullyPopulatedConversationItemBuilder()
+ .addAction(customAction)
+ .build();
+
+ assertThat(item.getActions()).containsExactly(customAction);
+ }
+
+ @Test
+ public void addAction_appIconInvalid_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> TestConversationFactory
+ .createFullyPopulatedConversationItemBuilder()
+ .addAction(Action.APP_ICON)
+ .build());
+ }
+
+ @Test
+ public void addAction_backInvalid_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> TestConversationFactory
+ .createFullyPopulatedConversationItemBuilder()
+ .addAction(Action.BACK)
+ .build());
+ }
+
+ @Test
+ public void addAction_panInvalid_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> TestConversationFactory
+ .createFullyPopulatedConversationItemBuilder()
+ .addAction(Action.PAN)
+ .build());
+ }
+
+ @Test
+ public void addAction_manyActions_throws() {
+ CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+ "ic_test_1");
+ Action customAction = new Action.Builder().setIcon(icon).build();
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> TestConversationFactory
+ .createFullyPopulatedConversationItemBuilder()
+ .addAction(customAction)
+ .addAction(customAction)
+ .build());
+ }
+
+ @Test
+ public void addAction_invalidActionNullIcon_throws() {
+ Action customAction = TestUtils.createAction("Title", /* icon= */ null);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> TestConversationFactory
+ .createFullyPopulatedConversationItemBuilder()
+ .addAction(customAction)
+ .build());
+ }
+
private void assertEqual(ConversationItem item1, ConversationItem item2) {
assertThat(item1).isEqualTo(item2);
assertThat(item1.hashCode()).isEqualTo(item2.hashCode());
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
index 148eb8b..bfcd637 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
@@ -18,6 +18,7 @@
package androidx.compose.animation.demos.lookahead
+import android.annotation.SuppressLint
import androidx.compose.animation.demos.gesture.pastelColors
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -49,6 +50,7 @@
import androidx.compose.ui.layout.LookaheadScope
import androidx.compose.ui.unit.dp
+@SuppressLint("UnusedBoxWithConstraintsScope")
@Composable
fun LookaheadWithBoxWithConstraints() {
Box(Modifier.fillMaxSize()) {
diff --git a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/BoxWithConstraintsDetector.kt b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/BoxWithConstraintsDetector.kt
new file mode 100644
index 0000000..9667018
--- /dev/null
+++ b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/BoxWithConstraintsDetector.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.foundation.lint
+
+import androidx.compose.lint.isInPackageName
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.android.tools.lint.detector.api.computeKotlinArgumentMapping
+import com.intellij.psi.PsiMethod
+import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.ULambdaExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.tryResolve
+import org.jetbrains.uast.visitor.AbstractUastVisitor
+
+class BoxWithConstraintsDetector : Detector(), SourceCodeScanner {
+ override fun getApplicableMethodNames(): List<String> = listOf(
+ FoundationNames.Layout.BoxWithConstraints.shortName
+ )
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ if (method.isInPackageName(FoundationNames.Layout.PackageName)) {
+ val contentArgument = computeKotlinArgumentMapping(node, method)
+ .orEmpty()
+ .filter { (_, parameter) ->
+ parameter.name == "content"
+ }
+ .keys
+ .filterIsInstance<ULambdaExpression>()
+ .firstOrNull() ?: return
+
+ var foundValidReference = false
+ contentArgument.accept(object : AbstractUastVisitor() {
+ override fun visitSimpleNameReferenceExpression(
+ node: USimpleNameReferenceExpression
+ ): Boolean {
+ val reference = (node.tryResolve() as? PsiMethod)
+ ?: return foundValidReference // No need to continue if we have already found one
+ if (reference.isInPackageName(FoundationNames.Layout.PackageName) &&
+ reference.containingClass?.name == "BoxWithConstraintsScope"
+ ) {
+ foundValidReference = true
+ }
+ return foundValidReference
+ }
+ })
+ if (!foundValidReference) {
+ context.report(
+ UnusedConstraintsParameter,
+ node,
+ context.getLocation(contentArgument),
+ "BoxWithConstraints scope is not used"
+ )
+ }
+ }
+ }
+
+ companion object {
+ val UnusedConstraintsParameter = Issue.create(
+ "UnusedBoxWithConstraintsScope",
+ "BoxWithConstraints content should use the constraints provided " +
+ "as via BoxWithConstraintsScope",
+ "The `content` lambda in BoxWithConstraints has a scope " +
+ "which will include the incoming constraints. If this " +
+ "scope is ignored, then the cost of subcomposition is being wasted and " +
+ "this BoxWithConstraints should be replaced with a Box.",
+ Category.CORRECTNESS, 3, Severity.ERROR,
+ Implementation(
+ BoxWithConstraintsDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+ }
+}
diff --git a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationIssueRegistry.kt b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationIssueRegistry.kt
index 2a936c1..d3de50b 100644
--- a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationIssueRegistry.kt
+++ b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationIssueRegistry.kt
@@ -32,7 +32,8 @@
override val issues get() = listOf(
LazyLayoutStateReadInCompositionDetector.FrequentlyChangedStateReadInComposition,
UnrememberedMutableInteractionSourceDetector.UnrememberedMutableInteractionSource,
- NonLambdaOffsetModifierDetector.UseOfNonLambdaOverload
+ NonLambdaOffsetModifierDetector.UseOfNonLambdaOverload,
+ BoxWithConstraintsDetector.UnusedConstraintsParameter
)
override val vendor = Vendor(
vendorName = "Jetpack Compose",
diff --git a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationNames.kt b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationNames.kt
index b6a07ab..99d313a 100644
--- a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationNames.kt
+++ b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationNames.kt
@@ -41,5 +41,6 @@
val PackageName = Package(FoundationNames.PackageName, "layout")
val Offset = Name(PackageName, "offset")
val AbsoluteOffset = Name(PackageName, "absoluteOffset")
+ val BoxWithConstraints = Name(PackageName, "BoxWithConstraints")
}
}
diff --git a/compose/foundation/foundation-lint/src/test/java/androidx/compose/foundation/lint/BoxWithConstraintsDetectorTest.kt b/compose/foundation/foundation-lint/src/test/java/androidx/compose/foundation/lint/BoxWithConstraintsDetectorTest.kt
new file mode 100644
index 0000000..947f2b6
--- /dev/null
+++ b/compose/foundation/foundation-lint/src/test/java/androidx/compose/foundation/lint/BoxWithConstraintsDetectorTest.kt
@@ -0,0 +1,216 @@
+/*
+ * 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.foundation.lint
+
+import androidx.compose.lint.test.Stubs
+import androidx.compose.lint.test.bytecodeStub
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BoxWithConstraintsDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = BoxWithConstraintsDetector()
+
+ override fun getIssues(): MutableList<Issue> =
+ mutableListOf(BoxWithConstraintsDetector.UnusedConstraintsParameter)
+
+ private val BoxWithConstraintsStub = bytecodeStub(
+ filename = "BoxWithConstraints.kt",
+ filepath = "androidx/compose/foundation/layout",
+ checksum = 0xddc1f733,
+ source =
+ """
+ package androidx.compose.foundation.layout
+
+ import androidx.compose.runtime.Composable
+
+ interface Constraints {
+ val minWidth: Int
+ }
+ interface Dp {}
+ interface BoxWithConstraintsScope {
+ val constraints: Constraints
+ val minWidth: Dp
+ val maxWidth: Dp
+ val minHeight: Dp
+ val maxHeight: Dp
+ }
+
+ @Composable
+ fun BoxWithConstraints(
+ propagateMinConstraints: Boolean = false,
+ content: @Composable BoxWithConstraintsScope.() -> Unit
+ ) {}
+ """.trimIndent(),
+ """
+ META-INF/main.kotlin_module:
+ H4sIAAAAAAAA/2NgYGBmYGBgBGJ2KM3AZcWllJiXUpSfmVKhl5yfW5BfnKqX
+ ll+al5JYkpmfp5eTWJlfWiIk4pRfEZ5ZkuGcn1dcUpSYmVdS7F3CxcvFnJaf
+ L8QWklpc4l2ixKDFAAD5174zYwAAAA==
+ """,
+ """
+ androidx/compose/foundation/layout/BoxWithConstraintsKt.class:
+ H4sIAAAAAAAA/6VTXU8TQRQ9sy394qsU+SqIKCAgwhZiwkOJiRKJjQWNRYzw
+ NGyHsrSdaXZnCbwY/oav/gPfiA+G+OiPMt7ZtlrsA4lusnfunTl77p17z/74
+ +fUbgCdYZ9jgsuwpt3xuO6reUL6wj1Ugy1y7Sto1fqECbT9X5+9dfbKlpK89
+ 7krtv9JxMIb0KT/jhJIV+/XRqXBoN8KQ6cYzzC0eFKtK11xpn57V7eNAOiaF
+ b2+3vLX80j5D41bY5krxn0ouOaoh8m3yd9LV+adhyvluPi+Q2q0LeyuM+VFN
+ 5Blmi8qr2KdCHxlC3+ZSKs2b1e0qvRvUaoSKO0pqIXUCKYbpjqtQDcKTvGYX
+ pPboe9fx4+hjGHFOhFNtEbzhHq8LAjIsLBb/7m6+Y6dkSCp0gT4MYDCFfqQZ
+ xhqeavAK12LHlTfazw4YZm4bAEO2u29zZXHMg5omqdw+wkJ3zabCHsRSsDDG
+ MNRm2BGa08g4JbXqZxGSIzMmTqVWjWPR/rlrvBx55TWGD9eX06nry5SVtppL
+ b7iMW51vNpe+vsxaObaeSBCQvMj6VDqaHc9EM1YuFlqW6/n+OWYl4qFNvIyb
+ BPQrgKTbLq+zKZv/IzgSQZvzxTkJw6dv2uR7FyFgpPvb1Sr1O7qlyoJhsOhK
+ sRvUj4S3Z6RoqlQOr+1zzzVxazNZciuS68Ajf/JtU8AFeeb6Lh0/+6NV+hX/
+ Pv2tuhuw/pLmTnWHN1oJUiUVeI7Ydk0w0eLY7+LHGk06CvNYmDCjp2iJojzF
+ lhnxcqb3CkNfQsAjsjFqfAwjWCZ/tAlBBsMhRRwp3KHzxyE6jpUWPkHrKr1J
+ A6fLA+kkUYySb3JttGoYmIp+/ISeSD67fIXxZkqbbAQsEeYegJFYhjiHiTND
+ x7kQtEjXALaJzlwhe4hIAZMFTJHF3QKmca+AGdw/BPPxALOHSPro8THnIxPa
+ lI95Hw99JHws/AJecgBzcAUAAA==
+ """,
+ """
+ androidx/compose/foundation/layout/BoxWithConstraintsScope.class:
+ H4sIAAAAAAAA/5WSzW7aQBDH/2vA2IYQJ21aQvqdSm0uNUU9tb30Q1WRSCol
+ UhOJkzEOLNi7iF0QvfEUfYAe+hA9VCjHPlTVMSUBQSpRazWz89N/dtYz++v3
+ j58AXuAxw0tfNPuSN0deIOOeVKF3Lgei6WsuhRf5X+RAe2/l6JTr9jsplO77
+ XGh1EshemAVjcDv+0CehaHmfGp0w0FmkGAqtUC/IGSpPD2prVFrIecWwX5P9
+ ltcJdSNByvOFkHqqV96R1EeDKCJVjmodcnHKm7rNcLBeofe9y0x/NMvM/z3n
+ Y8hbbT0L/dFluFXrSh1x4R2G2qezfMo34mGK+sgSk2VgXUIjnkRl2jWfM3yd
+ jEuOUTQcw52MHVrTvZWaecs6L07GFaPMjrddo2SUU2cX39MX30yzlLbSboao
+ STS7QC3XJuos0dyU5pfoxpQWluimaye3qzC8XqdT/xg+/T6og8HikMv/P2I7
+ nrf4ydqDs+KrqdnxfGRWfPUMdlav/axLkr3jgdA8DqtiyBVvROGb+ZticE7k
+ oB+EH3gUMuzOpJ9XhCb1D2kkXybNkIFJrXhIUeKzADEL9gpzrmG5a1h+mVG1
+ R1P7APvk60Q3qGqhjlQVm1W4ZLGVmO0qbuAmCRR2cKsOV+G2QlFhV6GkkFEw
+ FfYU7ijkFWyFuwqOwj2FnMJ9BesPb1xrKxsEAAA=
+ """,
+ """
+ androidx/compose/foundation/layout/Constraints.class:
+ H4sIAAAAAAAA/5WPz07CQBDGv9mWUot/CooiT6AXWonxYjyoiQkJxAQTMeFU
+ aIEVumvYheCNZ/HgQ3gwhKMPZdxyMF7dbH47szO73zdf3x+fAM5xRKhFIp5K
+ Hi+CvkxfpEqCgZyJONJcimASvcqZDm6lUHoacaFVHkTwn6N5ZIpiGNz3npO+
+ zsMiFIaJbnHR4bEeEayT0wah2BxLPeEiaCU6Mp9GlwSWzi2jThnyBBqbqwXP
+ stBE8RnhYrUseazCPOavlp7ZzHc95jJ3UFkt6yykdslnVRZaT+t3e/3mOFXb
+ tf1c9rpOCJv/G8lYAsFNf62Xb+Siw/XoT09trAneg5xN+8kdnySE4/ZMaJ4m
+ j1zx3iS5FkLqjYJyjA/YyBbZhBwcEzGUNzzAoTmvjGDeVNwurAa2GvAMUciw
+ 3cAOdrsghT34XTgKRYWSwv6GOQXnB5/tt/C/AQAA
+ """,
+ """
+ androidx/compose/foundation/layout/Dp.class:
+ H4sIAAAAAAAA/41Oy07DQAwcb6Ep4ZXykMoHIG6krXrjxENIlYqQQIJDT9tm
+ C9sku1V2U4Vbv4sD6pmPQjjlB7Cl8diWZ/z98/kFYIATwrk0SWF1UsVTmy+s
+ U/HMliaRXlsTZ/LDlj6+WwQgQjSXS8kz8xY/TuZq6gM0CO1Ran2mTfygvOQ7
+ eUUQ+bLBBlRDQKCUR5Wuuy6zpEc4Xa9aoeiIUETMZp31qi+6VC/7hIvRv55i
+ I7DSja1etX+/tcb5Qmrj3WXqCeGzLYuputeZIpw9lcbrXL1opyeZujbG+o2a
+ a7IntvAXAkcbbOOYa4/VtzmbYzSGCIZoMWKnhnCIXeyNQQ77OBhDOBw6RL/m
+ nxtjWQEAAA==
+ """
+ )
+
+ @Test
+ fun unreferencedConstraints() {
+ lint().files(
+ kotlin(
+ """
+ package foo
+
+ import androidx.compose.foundation.layout.BoxWithConstraints
+ import androidx.compose.runtime.Composable
+
+ @Composable
+ fun Test() {
+ val foo = 123
+ BoxWithConstraints { /**/ }
+ BoxWithConstraints { foo }
+ BoxWithConstraints(content = { /**/ })
+ BoxWithConstraints(propagateMinConstraints = false, content = { /**/ })
+ }
+ """.trimIndent()
+ ),
+ BoxWithConstraintsStub,
+ Stubs.Composable,
+ )
+ .run()
+ .expect(
+ """
+src/foo/test.kt:9: Error: BoxWithConstraints scope is not used [UnusedBoxWithConstraintsScope]
+ BoxWithConstraints { /**/ }
+ ~~~~~~~~
+src/foo/test.kt:10: Error: BoxWithConstraints scope is not used [UnusedBoxWithConstraintsScope]
+ BoxWithConstraints { foo }
+ ~~~~~~~
+src/foo/test.kt:11: Error: BoxWithConstraints scope is not used [UnusedBoxWithConstraintsScope]
+ BoxWithConstraints(content = { /**/ })
+ ~~~~~~~~
+src/foo/test.kt:12: Error: BoxWithConstraints scope is not used [UnusedBoxWithConstraintsScope]
+ BoxWithConstraints(propagateMinConstraints = false, content = { /**/ })
+ ~~~~~~~~
+4 errors, 0 warnings
+ """
+ )
+ }
+
+ @Test
+ fun referencedConstraints() {
+ lint().files(
+ kotlin(
+ """
+ package foo
+
+ import androidx.compose.foundation.layout.BoxWithConstraints
+ import androidx.compose.runtime.Composable
+
+ @Composable
+ fun Foo(content: @Composable ()->Unit) {}
+ @Composable
+ fun Bar() {}
+
+ @Composable
+ fun Test() {
+ BoxWithConstraints { constraints }
+ BoxWithConstraints { constraints.minWidth }
+ BoxWithConstraints { minWidth }
+ BoxWithConstraints { maxWidth }
+ BoxWithConstraints { minHeight }
+ BoxWithConstraints { maxHeight }
+ BoxWithConstraints(content = { maxWidth })
+ BoxWithConstraints(propagateMinConstraints = false, content = { minWidth })
+ BoxWithConstraints {
+ if (constraints.minWidth > 100) {
+ Foo {}
+ } else {
+ Bar()
+ }
+ }
+ BoxWithConstraints {
+ Foo {
+ constraints
+ }
+ }
+ }
+ """.trimIndent()
+ ),
+ BoxWithConstraintsStub,
+ Stubs.Composable,
+ )
+ .run()
+ .expectClean()
+ }
+}
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyBoxWithConstraintsActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyBoxWithConstraintsActivity.kt
index d4714d9..54c3d1a 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyBoxWithConstraintsActivity.kt
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyBoxWithConstraintsActivity.kt
@@ -16,6 +16,7 @@
package androidx.compose.integration.macrobenchmark.target
+import android.annotation.SuppressLint
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@@ -73,6 +74,7 @@
}
}
+@SuppressLint("UnusedBoxWithConstraintsScope") // Need the nested subcompose layout for testing
@Composable
private fun NonLazyRow(entry: NestedListEntry) {
BoxWithConstraints {
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
index 203ba8b..e66d965 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
@@ -40,6 +40,7 @@
import org.hamcrest.Matcher;
import org.junit.After;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
@@ -132,6 +133,7 @@
verifyBarViewStacking(textView, 0);
}
+ @Ignore // b/292019798
@Test
public void testBehaviorBasedSlidingFromClassAnnotation() {
// Use a layout in which a custom child view has Behavior object configured via
@@ -160,6 +162,7 @@
verifyBarViewStacking(textView, 0);
}
+ @Ignore // b/292021877
@Test
public void testBehaviorBasedSlidingFromRuntimeApiCall() {
// Use a layout in which a TextView child doesn't have any configured Behavior
diff --git a/core/core-performance-play-services/build.gradle b/core/core-performance-play-services/build.gradle
index f6643d6..42cab7be 100644
--- a/core/core-performance-play-services/build.gradle
+++ b/core/core-performance-play-services/build.gradle
@@ -25,7 +25,12 @@
dependencies {
api(libs.kotlinStdlib)
+ implementation(libs.playServicesDevicePerformance)
+
+ // Coroutines
implementation(libs.kotlinCoroutinesCore)
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1")
+
implementation(project(":core:core-performance"))
testImplementation(libs.testCore)
@@ -37,6 +42,9 @@
}
android {
+ defaultConfig {
+ minSdkVersion 24
+ }
namespace "androidx.core.performance.play.services"
}
diff --git a/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt b/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt
index 0682f5a..ea1ea30 100644
--- a/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt
+++ b/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt
@@ -18,6 +18,9 @@
import android.content.Context
import androidx.core.performance.DevicePerformanceRetriever
+import com.google.android.gms.deviceperformance.DevicePerformanceClient
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.tasks.await
/**
* A DevicePerformanceRetriever that uses Google Play Services to retrieve media performance class data.
@@ -31,10 +34,11 @@
);
companion object {
- @Suppress("UNUSED_PARAMETER")
@JvmStatic
fun getPerformanceClass(context: Context): Int {
- return 0
+ val client: DevicePerformanceClient =
+ com.google.android.gms.deviceperformance.DevicePerformance.getClient(context)
+ return runBlocking { client.mediaPerformanceClass().await() }
}
}
}
diff --git a/core/core-performance-testing/build.gradle b/core/core-performance-testing/build.gradle
index 2109a27..39b4dd1 100644
--- a/core/core-performance-testing/build.gradle
+++ b/core/core-performance-testing/build.gradle
@@ -36,6 +36,9 @@
}
android {
+ defaultConfig {
+ minSdkVersion 24
+ }
namespace "androidx.core.performance.testing"
}
diff --git a/core/core-performance/build.gradle b/core/core-performance/build.gradle
index 044a5ad..3dd2a66 100644
--- a/core/core-performance/build.gradle
+++ b/core/core-performance/build.gradle
@@ -47,5 +47,8 @@
}
android {
+ defaultConfig {
+ minSdkVersion 24
+ }
namespace "androidx.core.performance"
}
diff --git a/core/core-performance/samples/build.gradle b/core/core-performance/samples/build.gradle
index cbd177a..0087990 100644
--- a/core/core-performance/samples/build.gradle
+++ b/core/core-performance/samples/build.gradle
@@ -38,5 +38,8 @@
}
android {
+ defaultConfig {
+ minSdkVersion 24
+ }
namespace "androidx.core.performance.samples"
}
diff --git a/core/core/api/1.12.0-beta01.txt b/core/core/api/1.12.0-beta01.txt
index 4bb3fb0..7ff5c2a 100644
--- a/core/core/api/1.12.0-beta01.txt
+++ b/core/core/api/1.12.0-beta01.txt
@@ -2390,6 +2390,7 @@
public class TypedValueCompat {
method public static float deriveDimension(int, float, android.util.DisplayMetrics);
method public static float dpToPx(float, android.util.DisplayMetrics);
+ method public static int getUnitFromComplexDimension(int);
method public static float pxToDp(float, android.util.DisplayMetrics);
method public static float pxToSp(float, android.util.DisplayMetrics);
method public static float spToPx(float, android.util.DisplayMetrics);
@@ -4199,6 +4200,7 @@
method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+ method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
diff --git a/core/core/api/current.ignore b/core/core/api/current.ignore
index f623e8e..a3517f0 100644
--- a/core/core/api/current.ignore
+++ b/core/core/api/current.ignore
@@ -1,51 +1,5 @@
// Baseline format: 1.0
-AddedClass: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder:
- Added class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder
-
-
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_CONTENT_INVALID:
- Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_INVALID
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_ENABLED:
- Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ENABLED
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_ERROR:
- Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ERROR
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_ANCESTORS:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_HYBRID:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_HYBRID
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_SIBLINGS:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_SIBLINGS
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_UNINTERRUPTIBLE:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_UNINTERRUPTIBLE
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#MAX_NUMBER_OF_PREFETCHED_NODES:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.MAX_NUMBER_OF_PREFETCHED_NODES
-AddedField: androidx.core.view.accessibility.AccessibilityWindowInfoCompat#TYPE_MAGNIFICATION_OVERLAY:
- Added field androidx.core.view.accessibility.AccessibilityWindowInfoCompat.TYPE_MAGNIFICATION_OVERLAY
-
-
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getBoundsInWindow(android.graphics.Rect):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getBoundsInWindow(android.graphics.Rect)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getChild(int, int):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getChild(int,int)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getContainerTitle():
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getContainerTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getParent(int):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getParent(int)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setBoundsInWindow(android.graphics.Rect):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setBoundsInWindow(android.graphics.Rect)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setContainerTitle(CharSequence):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setContainerTitle(CharSequence)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setQueryFromAppProcessEnabled(android.view.View, boolean):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setQueryFromAppProcessEnabled(android.view.View,boolean)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat#getColumnTitle():
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.getColumnTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat#getRowTitle():
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.getRowTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat#RangeInfoCompat(int, float, float, float):
- Added constructor androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat(int,float,float,float)
-AddedMethod: androidx.core.view.accessibility.AccessibilityWindowInfoCompat#getRoot(int):
- Added method androidx.core.view.accessibility.AccessibilityWindowInfoCompat.getRoot(int)
+AddedMethod: androidx.core.util.TypedValueCompat#getUnitFromComplexDimension(int):
+ Added method androidx.core.util.TypedValueCompat.getUnitFromComplexDimension(int)
+AddedMethod: androidx.core.widget.TextViewCompat#setLineHeight(android.widget.TextView, int, float):
+ Added method androidx.core.widget.TextViewCompat.setLineHeight(android.widget.TextView,int,float)
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 4bb3fb0..7ff5c2a 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -2390,6 +2390,7 @@
public class TypedValueCompat {
method public static float deriveDimension(int, float, android.util.DisplayMetrics);
method public static float dpToPx(float, android.util.DisplayMetrics);
+ method public static int getUnitFromComplexDimension(int);
method public static float pxToDp(float, android.util.DisplayMetrics);
method public static float pxToSp(float, android.util.DisplayMetrics);
method public static float spToPx(float, android.util.DisplayMetrics);
@@ -4199,6 +4200,7 @@
method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+ method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
diff --git a/core/core/api/restricted_1.12.0-beta01.txt b/core/core/api/restricted_1.12.0-beta01.txt
index 102534f..7be35d5 100644
--- a/core/core/api/restricted_1.12.0-beta01.txt
+++ b/core/core/api/restricted_1.12.0-beta01.txt
@@ -2814,6 +2814,7 @@
public class TypedValueCompat {
method public static float deriveDimension(int, float, android.util.DisplayMetrics);
method public static float dpToPx(float, android.util.DisplayMetrics);
+ method public static int getUnitFromComplexDimension(int);
method public static float pxToDp(float, android.util.DisplayMetrics);
method public static float pxToSp(float, android.util.DisplayMetrics);
method public static float spToPx(float, android.util.DisplayMetrics);
@@ -4700,6 +4701,7 @@
method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+ method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
diff --git a/core/core/api/restricted_current.ignore b/core/core/api/restricted_current.ignore
index f623e8e..a3517f0 100644
--- a/core/core/api/restricted_current.ignore
+++ b/core/core/api/restricted_current.ignore
@@ -1,51 +1,5 @@
// Baseline format: 1.0
-AddedClass: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder:
- Added class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder
-
-
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_CONTENT_INVALID:
- Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_INVALID
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_ENABLED:
- Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ENABLED
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_ERROR:
- Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ERROR
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_ANCESTORS:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_HYBRID:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_HYBRID
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_SIBLINGS:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_SIBLINGS
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_UNINTERRUPTIBLE:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_UNINTERRUPTIBLE
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#MAX_NUMBER_OF_PREFETCHED_NODES:
- Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.MAX_NUMBER_OF_PREFETCHED_NODES
-AddedField: androidx.core.view.accessibility.AccessibilityWindowInfoCompat#TYPE_MAGNIFICATION_OVERLAY:
- Added field androidx.core.view.accessibility.AccessibilityWindowInfoCompat.TYPE_MAGNIFICATION_OVERLAY
-
-
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getBoundsInWindow(android.graphics.Rect):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getBoundsInWindow(android.graphics.Rect)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getChild(int, int):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getChild(int,int)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getContainerTitle():
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getContainerTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getParent(int):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getParent(int)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setBoundsInWindow(android.graphics.Rect):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setBoundsInWindow(android.graphics.Rect)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setContainerTitle(CharSequence):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setContainerTitle(CharSequence)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setQueryFromAppProcessEnabled(android.view.View, boolean):
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setQueryFromAppProcessEnabled(android.view.View,boolean)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat#getColumnTitle():
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.getColumnTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat#getRowTitle():
- Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.getRowTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat#RangeInfoCompat(int, float, float, float):
- Added constructor androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat(int,float,float,float)
-AddedMethod: androidx.core.view.accessibility.AccessibilityWindowInfoCompat#getRoot(int):
- Added method androidx.core.view.accessibility.AccessibilityWindowInfoCompat.getRoot(int)
+AddedMethod: androidx.core.util.TypedValueCompat#getUnitFromComplexDimension(int):
+ Added method androidx.core.util.TypedValueCompat.getUnitFromComplexDimension(int)
+AddedMethod: androidx.core.widget.TextViewCompat#setLineHeight(android.widget.TextView, int, float):
+ Added method androidx.core.widget.TextViewCompat.setLineHeight(android.widget.TextView,int,float)
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 102534f..7be35d5 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -2814,6 +2814,7 @@
public class TypedValueCompat {
method public static float deriveDimension(int, float, android.util.DisplayMetrics);
method public static float dpToPx(float, android.util.DisplayMetrics);
+ method public static int getUnitFromComplexDimension(int);
method public static float pxToDp(float, android.util.DisplayMetrics);
method public static float pxToSp(float, android.util.DisplayMetrics);
method public static float spToPx(float, android.util.DisplayMetrics);
@@ -4700,6 +4701,7 @@
method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+ method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
diff --git a/core/core/build.gradle b/core/core/build.gradle
index 149662a..967e12e 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -57,6 +57,7 @@
androidTestImplementation(project(":internal-testutils-mockito"))
testImplementation(libs.junit)
+ testImplementation(libs.testExtJunit)
testImplementation(libs.testCore)
testImplementation(libs.testRunner)
testImplementation(libs.truth)
diff --git a/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java b/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
index 856acd4..a717d3d 100644
--- a/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
+++ b/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
@@ -17,7 +17,6 @@
package androidx.core.math;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThrows;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -30,78 +29,6 @@
public class MathUtilsTest {
@Test
- public void testAddExact() {
- assertEquals(2, MathUtils.addExact(1, 1));
- assertEquals(2L, MathUtils.addExact(1L, 1L));
- assertEquals(0, MathUtils.addExact(1, -1));
- assertEquals(0L, MathUtils.addExact(1L, -1L));
- assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Integer.MAX_VALUE, 1));
- assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Long.MAX_VALUE, 1L));
- assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Integer.MIN_VALUE, -1));
- assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Long.MIN_VALUE, -1L));
- }
-
- @Test
- public void testSubtractExact() {
- assertEquals(0, MathUtils.subtractExact(1, 1));
- assertEquals(0L, MathUtils.subtractExact(1L, 1L));
- assertEquals(2, MathUtils.subtractExact(1, -1));
- assertEquals(2L, MathUtils.subtractExact(1L, -1L));
- assertThrows(ArithmeticException.class,
- () -> MathUtils.subtractExact(Integer.MAX_VALUE, -1));
- assertThrows(ArithmeticException.class, () -> MathUtils.subtractExact(Long.MAX_VALUE, -1L));
- assertThrows(ArithmeticException.class,
- () -> MathUtils.subtractExact(Integer.MIN_VALUE, 1));
- assertThrows(ArithmeticException.class, () -> MathUtils.subtractExact(Long.MIN_VALUE, 1L));
- }
-
- @Test
- public void testMultiplyExact() {
- assertEquals(4, MathUtils.multiplyExact(2, 2));
- assertEquals(4L, MathUtils.multiplyExact(2L, 2L));
- assertEquals(0, MathUtils.multiplyExact(2, 0));
- assertEquals(0L, MathUtils.multiplyExact(2L, 0L));
- assertEquals(-4, MathUtils.multiplyExact(2, -2));
- assertEquals(-4L, MathUtils.multiplyExact(2L, -2L));
- assertThrows(ArithmeticException.class,
- () -> MathUtils.multiplyExact(Integer.MAX_VALUE, 2));
- assertThrows(ArithmeticException.class, () -> MathUtils.multiplyExact(Long.MAX_VALUE, 2L));
- assertThrows(ArithmeticException.class,
- () -> MathUtils.multiplyExact(Integer.MIN_VALUE, 2));
- assertThrows(ArithmeticException.class, () -> MathUtils.multiplyExact(Long.MIN_VALUE, 2L));
- }
-
- @Test
- public void testIncrementExact() {
- assertEquals(1, MathUtils.incrementExact(0));
- assertEquals(1L, MathUtils.incrementExact(0L));
- assertThrows(ArithmeticException.class, () -> MathUtils.incrementExact(Integer.MAX_VALUE));
- assertThrows(ArithmeticException.class, () -> MathUtils.incrementExact(Long.MAX_VALUE));
- }
-
- @Test
- public void testDecrementExact() {
- assertEquals(-1, MathUtils.decrementExact(0));
- assertEquals(-1L, MathUtils.decrementExact(0L));
- assertThrows(ArithmeticException.class, () -> MathUtils.decrementExact(Integer.MIN_VALUE));
- assertThrows(ArithmeticException.class, () -> MathUtils.decrementExact(Long.MIN_VALUE));
- }
-
- @Test
- public void testNegateExact() {
- assertEquals(Integer.MIN_VALUE + 1, MathUtils.negateExact(Integer.MAX_VALUE));
- assertEquals(Long.MIN_VALUE + 1, MathUtils.negateExact(Long.MAX_VALUE));
- assertThrows(ArithmeticException.class, () -> MathUtils.negateExact(Integer.MIN_VALUE));
- assertThrows(ArithmeticException.class, () -> MathUtils.negateExact(Long.MIN_VALUE));
- }
-
- @Test
- public void testToIntExact() {
- assertEquals(1, MathUtils.toIntExact(1L));
- assertThrows(ArithmeticException.class, () -> MathUtils.toIntExact(Long.MAX_VALUE));
- }
-
- @Test
public void testClamp() {
// Int
assertEquals(0, MathUtils.clamp(-4, 0, 7));
diff --git a/core/core/src/main/java/androidx/core/math/MathUtils.java b/core/core/src/main/java/androidx/core/math/MathUtils.java
index 8a42777..f6129cc 100644
--- a/core/core/src/main/java/androidx/core/math/MathUtils.java
+++ b/core/core/src/main/java/androidx/core/math/MathUtils.java
@@ -24,172 +24,225 @@
private MathUtils() {}
/**
- * See {@link Math#addExact(int, int)}.
+ * Returns the sum of its arguments, throwing an exception if the result overflows an
+ * {@code int}.
+ *
+ * @param x the first value
+ * @param y the second value
+ * @return the result
+ * @throws ArithmeticException if the result overflows an int
*/
public static int addExact(int x, int y) {
- // copied from Math.java
- int r = x + y;
- // HD 2-12 Overflow iff both arguments have the opposite sign of the result
- if (((x ^ r) & (y ^ r)) < 0) {
+ int sum = x + y;
+ // If x and y have the same sign, their sum should have the same sign as well
+ if ((x >= 0 == y >= 0) && (x >= 0 != sum >= 0)) {
throw new ArithmeticException("integer overflow");
+ } else {
+ return sum;
}
- return r;
}
/**
- * See {@link Math#addExact(long, long)}.
+ * Returns the sum of its arguments, throwing an exception if the result overflows a
+ * {@code long}.
+ *
+ * @param x the first value
+ * @param y the second value
+ * @return the result
+ * @throws ArithmeticException if the result overflows a long
*/
public static long addExact(long x, long y) {
- // copied from Math.java
- long r = x + y;
- // HD 2-12 Overflow iff both arguments have the opposite sign of the result
- if (((x ^ r) & (y ^ r)) < 0) {
- throw new ArithmeticException("long overflow");
+ long sum = x + y;
+ // If x and y have the same sign, their sum should have the same sign as well
+ if ((x >= 0 == y >= 0) && (x >= 0 != sum >= 0)) {
+ throw new ArithmeticException("integer overflow");
+ } else {
+ return sum;
}
- return r;
}
-
/**
- * See {@link Math#subtractExact(int, int)}.
+ * Returns the difference of the arguments, throwing an exception if the result overflows an
+ * {@code int}.
+ *
+ * @param x the first value
+ * @param y the second value to subtract from the first
+ * @return the result
+ * @throws ArithmeticException if the result overflows an int
*/
public static int subtractExact(int x, int y) {
- // copied from Math.java
- int r = x - y;
- // HD 2-12 Overflow iff the arguments have different signs and
- // the sign of the result is different than the sign of x
- if (((x ^ y) & (x ^ r)) < 0) {
+ int difference = x - y;
+ // If only one of x or y is negative, the difference should have the same sign as x
+ if ((x < 0 != y < 0) && (x < 0 != difference < 0)) {
throw new ArithmeticException("integer overflow");
}
- return r;
+ return difference;
}
/**
- * See {@link Math#subtractExact(long, long)}.
+ * Returns the difference of the arguments, throwing an exception if the result overflows a
+ * {@code long}.
+ *
+ * @param x the first value
+ * @param y the second value to subtract from the first
+ * @return the result
+ * @throws ArithmeticException if the result overflows a long
*/
public static long subtractExact(long x, long y) {
- // copied from Math.java
- long r = x - y;
- // HD 2-12 Overflow iff the arguments have different signs and
- // the sign of the result is different than the sign of x
- if (((x ^ y) & (x ^ r)) < 0) {
- throw new ArithmeticException("long overflow");
+ long difference = x - y;
+ // If only one of x or y is negative, the difference should have the same sign as x
+ if ((x < 0 != y < 0) && (x < 0 != difference < 0)) {
+ throw new ArithmeticException("integer overflow");
}
- return r;
+ return difference;
}
/**
- * See {@link Math#multiplyExact(int, int)}.
+ * Returns the product of the arguments, throwing an exception if the result overflows an
+ * {@code int}.
+ *
+ * @param x the first value
+ * @param y the second value
+ * @return the result
+ * @throws ArithmeticException if the result overflows an int
*/
public static int multiplyExact(int x, int y) {
- // copied from Math.java
- long r = (long) x * (long) y;
- if ((int) r != r) {
+ int product = x * y;
+ // Dividing back by one of x or y should get the other back unless there was overflow
+ if (x != 0 && y != 0 && (product / x != y || product / y != x)) {
throw new ArithmeticException("integer overflow");
}
- return (int) r;
+ return product;
}
/**
- * See {@link Math#multiplyExact(long, long)}.
+ * Returns the product of the arguments, throwing an exception if the result overflows a
+ * {@code long}.
+ *
+ * @param x the first value
+ * @param y the second value
+ * @return the result
+ * @throws ArithmeticException if the result overflows a long
*/
public static long multiplyExact(long x, long y) {
- // copied from Math.java
- long r = x * y;
- long ax = Math.abs(x);
- long ay = Math.abs(y);
- if (((ax | ay) >>> 31 != 0)) {
- // Some bits greater than 2^31 that might cause overflow
- // Check the result using the divide operator
- // and check for the special case of Long.MIN_VALUE * -1
- if (((y != 0) && (r / y != x)) || (x == Long.MIN_VALUE && y == -1)) {
- throw new ArithmeticException("long overflow");
- }
+ long product = x * y;
+ // Dividing back by one of x or y should get the other back unless there was overflow
+ if (x != 0 && y != 0 && (product / x != y || product / y != x)) {
+ throw new ArithmeticException("integer overflow");
}
- return r;
+ return product;
}
/**
- * See {@link Math#incrementExact(int)}.
+ * Returns the argument incremented by one, throwing an exception if the result overflows an
+ * {@code int}. The overflow only occurs for {@linkplain Integer#MAX_VALUE the maximum value}.
+ *
+ * @param a the value to increment
+ * @return the result
+ * @throws ArithmeticException if the result overflows an int
*/
public static int incrementExact(int a) {
- // copied from Math.java
if (a == Integer.MAX_VALUE) {
throw new ArithmeticException("integer overflow");
+ } else {
+ return a + 1;
}
-
- return a + 1;
}
/**
- * See {@link Math#incrementExact(long)}.
+ * Returns the argument incremented by one, throwing an exception if the result overflows a
+ * {@code long}. The overflow only occurs for {@linkplain Long#MAX_VALUE the maximum value}.
+ *
+ * @param a the value to increment
+ * @return the result
+ * @throws ArithmeticException if the result overflows a long
*/
public static long incrementExact(long a) {
- // copied from Math.java
if (a == Long.MAX_VALUE) {
- throw new ArithmeticException("long overflow");
+ throw new ArithmeticException("integer overflow");
+ } else {
+ return a + 1;
}
-
- return a + 1L;
}
/**
- * See {@link Math#decrementExact(int)}.
+ * Returns the argument decremented by one, throwing an exception if the result overflows an
+ * {@code int}. The overflow only occurs for {@linkplain Integer#MIN_VALUE the minimum value}.
+ *
+ * @param a the value to decrement
+ * @return the result
+ * @throws ArithmeticException if the result overflows an int
*/
public static int decrementExact(int a) {
- // copied from Math.java
if (a == Integer.MIN_VALUE) {
throw new ArithmeticException("integer overflow");
+ } else {
+ return a - 1;
}
-
- return a - 1;
}
/**
- * See {@link Math#decrementExact(long)}.
+ * Returns the argument decremented by one, throwing an exception if the result overflows a
+ * {@code long}. The overflow only occurs for {@linkplain Long#MIN_VALUE the minimum value}.
+ *
+ * @param a the value to decrement
+ * @return the result
+ * @throws ArithmeticException if the result overflows a long
*/
public static long decrementExact(long a) {
- // copied from Math.java
if (a == Long.MIN_VALUE) {
- throw new ArithmeticException("long overflow");
+ throw new ArithmeticException("integer overflow");
+ } else {
+ return a - 1;
}
-
- return a - 1L;
}
/**
- * See {@link Math#negateExact(int)}.
+ * Returns the negation of the argument, throwing an exception if the result overflows an
+ * {@code int}. The overflow only occurs for {@linkplain Integer#MIN_VALUE the minimum value}.
+ *
+ * @param a the value to negate
+ * @return the result
+ * @throws ArithmeticException if the result overflows an int
*/
public static int negateExact(int a) {
- // copied from Math.java
if (a == Integer.MIN_VALUE) {
throw new ArithmeticException("integer overflow");
+ } else {
+ return -a;
}
-
- return -a;
}
/**
- * See {@link Math#negateExact(long)}.
+ * Returns the negation of the argument, throwing an exception if the result overflows a
+ * {@code long}. The overflow only occurs for {@linkplain Long#MIN_VALUE the minimum value}.
+ *
+ * @param a the value to negate
+ * @return the result
+ * @throws ArithmeticException if the result overflows a long
*/
public static long negateExact(long a) {
- // copied from Math.java
if (a == Long.MIN_VALUE) {
- throw new ArithmeticException("long overflow");
+ throw new ArithmeticException("integer overflow");
+ } else {
+ return -a;
}
-
- return -a;
}
/**
- * See {@link Math#toIntExact(long)}.
+ * Returns the value of the {@code long} argument, throwing an exception if the value
+ * overflows an {@code int}.
+ *
+ * @param value the long value
+ * @return the argument as an int
+ * @throws ArithmeticException if the {@code argument} overflows an int
*/
public static int toIntExact(long value) {
- // copied from Math.java
- if ((int) value != value) {
+ if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
throw new ArithmeticException("integer overflow");
+ } else {
+ return (int) value;
}
- return (int) value;
}
/**
diff --git a/core/core/src/main/java/androidx/core/util/TypedValueCompat.java b/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
index 49f3bab..55f102d 100644
--- a/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
+++ b/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
@@ -44,6 +44,17 @@
private TypedValueCompat() {}
/**
+ * Return the complex unit type for the given complex dimension. For example, a dimen type
+ * with value 12sp will return {@link TypedValue#COMPLEX_UNIT_SP}.
+ *
+ * @param complexDimension the dimension, typically {@link TypedValue#data}
+ * @return The complex unit type
+ */
+ public static int getUnitFromComplexDimension(int complexDimension) {
+ return TypedValue.COMPLEX_UNIT_MASK & (complexDimension >> TypedValue.COMPLEX_UNIT_SHIFT);
+ }
+
+ /**
* Converts a pixel value to the given dimension, e.g. PX to DP.
*
* <p>This is the inverse of {@link TypedValue#applyDimension(int, float, DisplayMetrics)}
diff --git a/core/core/src/main/java/androidx/core/widget/TextViewCompat.java b/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
index 915d0af2..0e58694 100644
--- a/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
+++ b/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
@@ -54,6 +54,7 @@
import androidx.annotation.DoNotInline;
import androidx.annotation.DrawableRes;
+import androidx.annotation.FloatRange;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
@@ -806,6 +807,7 @@
* Sets an explicit line height for this TextView. This is equivalent to the vertical distance
* between subsequent baselines in the TextView.
*
+ * @param textView the TextView to modify
* @param lineHeight the line height in pixels
*
* @see TextView#setLineSpacing(float, float)
@@ -827,6 +829,38 @@
}
/**
+ * Sets an explicit line height to a given unit and value for the TextView. This is equivalent
+ * to the vertical distance between subsequent baselines in the TextView. See {@link
+ * TypedValue} for the possible dimension units.
+ *
+ * @param textView the TextView to modify
+ * @param unit The desired dimension unit. SP units are strongly recommended so that line height
+ * stays proportional to the text size when fonts are scaled up for accessibility.
+ * @param lineHeight The desired line height in the given units.
+ *
+ * @see TextView#setLineSpacing(float, float)
+ * @see TextView#getLineSpacingExtra()
+ *
+ * @attr ref android.R.styleable#TextView_lineHeight
+ */
+ public static void setLineHeight(
+ @NonNull TextView textView,
+ int unit,
+ @FloatRange(from = 0) float lineHeight
+ ) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ Api34Impl.setLineHeight(textView, unit, lineHeight);
+ } else {
+ float lineHeightPx = TypedValue.applyDimension(
+ unit,
+ lineHeight,
+ textView.getResources().getDisplayMetrics()
+ );
+ setLineHeight(textView, Math.round(lineHeightPx));
+ }
+ }
+
+ /**
* Gets the parameters for text layout precomputation, for use with
* {@link PrecomputedTextCompat}.
*
@@ -1283,4 +1317,20 @@
return DecimalFormatSymbols.getInstance(locale);
}
}
+
+ @RequiresApi(34)
+ static class Api34Impl {
+ private Api34Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ public static void setLineHeight(
+ @NonNull TextView textView,
+ int unit,
+ @FloatRange(from = 0) float lineHeight
+ ) {
+ textView.setLineHeight(unit, lineHeight);
+ }
+ }
}
diff --git a/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt b/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt
new file mode 100644
index 0000000..3074b56
--- /dev/null
+++ b/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt
@@ -0,0 +1,354 @@
+/*
+ * 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.core.math
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MathUtilsTest {
+
+ @Test
+ fun testAddExact() {
+ // zero + zero
+ Assert.assertEquals(0, MathUtils.addExact(0, 0).toLong())
+ Assert.assertEquals(0L, MathUtils.addExact(0L, 0L))
+ // positive + positive
+ Assert.assertEquals(2, MathUtils.addExact(1, 1).toLong())
+ Assert.assertEquals(2L, MathUtils.addExact(1L, 1L))
+ // negative + negative
+ Assert.assertEquals(-2, MathUtils.addExact(-1, -1).toLong())
+ Assert.assertEquals(-2L, MathUtils.addExact(-1L, -1L))
+ // positive + negative
+ Assert.assertEquals(0, MathUtils.addExact(1, -1).toLong())
+ Assert.assertEquals(0L, MathUtils.addExact(1L, -1L))
+ Assert.assertEquals(-1, MathUtils.addExact(1, -2).toLong())
+ Assert.assertEquals(-1L, MathUtils.addExact(1L, -2L))
+ Assert.assertEquals(1, MathUtils.addExact(2, -1).toLong())
+ Assert.assertEquals(1L, MathUtils.addExact(2L, -1L))
+ // negative + positive
+ Assert.assertEquals(0, MathUtils.addExact(-1, 1).toLong())
+ Assert.assertEquals(0L, MathUtils.addExact(-1L, 1L))
+ Assert.assertEquals(1, MathUtils.addExact(-1, 2).toLong())
+ Assert.assertEquals(1L, MathUtils.addExact(-1L, 2L))
+ Assert.assertEquals(-1, MathUtils.addExact(-2, 1).toLong())
+ Assert.assertEquals(-1L, MathUtils.addExact(-2L, 1L))
+ // zero + positive, positive + zero
+ Assert.assertEquals(1, MathUtils.addExact(0, 1).toLong())
+ Assert.assertEquals(1L, MathUtils.addExact(0L, 1L))
+ Assert.assertEquals(1, MathUtils.addExact(1, 0).toLong())
+ Assert.assertEquals(1L, MathUtils.addExact(1L, 0L))
+ // zero + negative, negative + zero
+ Assert.assertEquals(-1, MathUtils.addExact(0, -1).toLong())
+ Assert.assertEquals(-1L, MathUtils.addExact(0L, -1L))
+ Assert.assertEquals(-1, MathUtils.addExact(-1, 0).toLong())
+ Assert.assertEquals(-1L, MathUtils.addExact(-1L, 0L))
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.addExact(
+ Int.MAX_VALUE,
+ 1
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.addExact(
+ Long.MAX_VALUE,
+ 1L
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.addExact(
+ Int.MIN_VALUE,
+ -1
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.addExact(
+ Long.MIN_VALUE,
+ -1L
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.addExact(
+ Integer.MIN_VALUE,
+ Integer.MIN_VALUE
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.addExact(
+ Long.MIN_VALUE,
+ Long.MIN_VALUE
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.addExact(
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.addExact(
+ Long.MAX_VALUE,
+ Long.MAX_VALUE
+ )
+ }
+ }
+
+ @Test
+ fun testSubtractExact() {
+ // zero - zero
+ Assert.assertEquals(0, MathUtils.subtractExact(0, 0).toLong())
+ Assert.assertEquals(0L, MathUtils.subtractExact(0L, 0L))
+ // positive - positive
+ Assert.assertEquals(0, MathUtils.subtractExact(1, 1).toLong())
+ Assert.assertEquals(0L, MathUtils.subtractExact(1L, 1L))
+ Assert.assertEquals(1, MathUtils.subtractExact(2, 1).toLong())
+ Assert.assertEquals(1L, MathUtils.subtractExact(2L, 1L))
+ Assert.assertEquals(-1, MathUtils.subtractExact(1, 2).toLong())
+ Assert.assertEquals(-1L, MathUtils.subtractExact(1L, 2L))
+ // negative - negative
+ Assert.assertEquals(0, MathUtils.subtractExact(-1, -1).toLong())
+ Assert.assertEquals(0L, MathUtils.subtractExact(-1L, -1L))
+ Assert.assertEquals(-1, MathUtils.subtractExact(-2, -1).toLong())
+ Assert.assertEquals(-1L, MathUtils.subtractExact(-2L, -1L))
+ Assert.assertEquals(1, MathUtils.subtractExact(-1, -2).toLong())
+ Assert.assertEquals(1L, MathUtils.subtractExact(-1L, -2L))
+ // positive - negative, negative - positive
+ Assert.assertEquals(2, MathUtils.subtractExact(1, -1).toLong())
+ Assert.assertEquals(2L, MathUtils.subtractExact(1L, -1L))
+ Assert.assertEquals(-2, MathUtils.subtractExact(-1, 1).toLong())
+ Assert.assertEquals(-2L, MathUtils.subtractExact(-1L, 1L))
+ // zero - positive, positive - zero
+ Assert.assertEquals(-1, MathUtils.subtractExact(0, 1).toLong())
+ Assert.assertEquals(-1L, MathUtils.subtractExact(0L, 1L))
+ Assert.assertEquals(1, MathUtils.subtractExact(1, 0).toLong())
+ Assert.assertEquals(1L, MathUtils.subtractExact(1L, 0L))
+ // zero - negative, negative - zero
+ Assert.assertEquals(1, MathUtils.subtractExact(0, -1).toLong())
+ Assert.assertEquals(1L, MathUtils.subtractExact(0L, -1L))
+ Assert.assertEquals(-1, MathUtils.subtractExact(-1, 0).toLong())
+ Assert.assertEquals(-1L, MathUtils.subtractExact(-1L, 0))
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.subtractExact(
+ Int.MAX_VALUE,
+ -1
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.subtractExact(
+ Long.MAX_VALUE,
+ -1L
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.subtractExact(
+ Int.MIN_VALUE,
+ 1
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.subtractExact(
+ Long.MIN_VALUE,
+ 1L
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.subtractExact(
+ 0,
+ Int.MIN_VALUE
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.subtractExact(
+ 0,
+ Long.MIN_VALUE
+ )
+ }
+ }
+
+ @Test
+ fun testMultiplyExact() {
+ Assert.assertEquals(0, MathUtils.multiplyExact(0, 0).toLong())
+ Assert.assertEquals(4, MathUtils.multiplyExact(2, 2).toLong())
+ Assert.assertEquals(0L, MathUtils.multiplyExact(0L, 0L))
+ Assert.assertEquals(4L, MathUtils.multiplyExact(2L, 2L))
+ Assert.assertEquals(0, MathUtils.multiplyExact(2, 0).toLong())
+ Assert.assertEquals(0L, MathUtils.multiplyExact(2L, 0L))
+ Assert.assertEquals(-4, MathUtils.multiplyExact(2, -2).toLong())
+ Assert.assertEquals(-4L, MathUtils.multiplyExact(2L, -2L))
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Int.MAX_VALUE,
+ 2
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Long.MAX_VALUE,
+ 2L
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Int.MIN_VALUE,
+ 2
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Long.MIN_VALUE,
+ 2L
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Int.MAX_VALUE / 2 + 1,
+ 2
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Long.MAX_VALUE / 2L + 1L,
+ 2L
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Int.MIN_VALUE / 2 - 1,
+ 2
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Long.MIN_VALUE / 2L - 1L,
+ 2L
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Int.MIN_VALUE,
+ -1
+ )
+ }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) {
+ MathUtils.multiplyExact(
+ Long.MIN_VALUE,
+ -1L
+ )
+ }
+ }
+
+ @Test
+ fun testIncrementExact() {
+ Assert.assertEquals(1, MathUtils.incrementExact(0).toLong())
+ Assert.assertEquals(1L, MathUtils.incrementExact(0L))
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) { MathUtils.incrementExact(Int.MAX_VALUE) }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) { MathUtils.incrementExact(Long.MAX_VALUE) }
+ }
+
+ @Test
+ fun testDecrementExact() {
+ Assert.assertEquals(-1, MathUtils.decrementExact(0).toLong())
+ Assert.assertEquals(-1L, MathUtils.decrementExact(0L))
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) { MathUtils.decrementExact(Int.MIN_VALUE) }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) { MathUtils.decrementExact(Long.MIN_VALUE) }
+ }
+
+ @Test
+ fun testNegateExact() {
+ Assert.assertEquals(
+ (Int.MIN_VALUE + 1).toLong(),
+ MathUtils.negateExact(Int.MAX_VALUE).toLong()
+ )
+ Assert.assertEquals(Long.MIN_VALUE + 1, MathUtils.negateExact(Long.MAX_VALUE))
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) { MathUtils.negateExact(Int.MIN_VALUE) }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) { MathUtils.negateExact(Long.MIN_VALUE) }
+ }
+
+ @Test
+ fun testToIntExact() {
+ Assert.assertEquals(1, MathUtils.toIntExact(1L).toLong())
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) { MathUtils.toIntExact(Long.MAX_VALUE) }
+ Assert.assertThrows(
+ ArithmeticException::class.java
+ ) { MathUtils.toIntExact(Long.MIN_VALUE) }
+ }
+}
diff --git a/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml b/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
index 73badf7..d7b0e91 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
+++ b/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
@@ -21,9 +21,5 @@
android:name="androidx.credentials.playservices.TestCredentialsActivity"
android:exported="false"
/>
- <activity
- android:name="androidx.credentials.playservices.TestCredentialsFragmentActivity"
- android:exported="false"
- />
</application>
</manifest>
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt
deleted file mode 100644
index f7ad106..0000000
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt
+++ /dev/null
@@ -1,28 +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.credentials.playservices
-
-import android.os.Build
-import androidx.annotation.RequiresApi
-import androidx.fragment.app.FragmentActivity
-
-/**
- * This is a test activity used by the Robolectric Activity Scenario tests. It acts as a calling
- * activity in our test cases. This activity uses fragments.
- */
-@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
-class TestCredentialsFragmentActivity : FragmentActivity()
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
index acbfe57..e4cddda 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
@@ -20,14 +20,12 @@
import static org.junit.Assert.assertThrows;
-import android.app.Activity;
-import android.os.Build;
-
import androidx.credentials.GetCredentialRequest;
import androidx.credentials.GetPasswordOption;
import androidx.credentials.playservices.TestCredentialsActivity;
import androidx.credentials.playservices.controllers.BeginSignIn.CredentialProviderBeginSignInController;
import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.google.android.gms.auth.api.identity.BeginSignInRequest;
@@ -35,56 +33,19 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
import java.util.HashSet;
import java.util.List;
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
@SuppressWarnings("deprecation")
public class CredentialProviderBeginSignInControllerJavaTest {
-
- private final boolean mUseFragmentActivity;
-
- @Parameterized.Parameters
- public static Object[] data() {
- return new Object[] {true, false};
- }
-
- public CredentialProviderBeginSignInControllerJavaTest(final boolean useFragmentActivity)
- throws Throwable {
- mUseFragmentActivity = useFragmentActivity;
- }
-
- interface TestActivityListener {
- void onActivity(Activity a);
- }
-
- private void launchTestActivity(TestActivityListener listener) {
- if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
- ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
- activityScenario =
- ActivityScenario.launch(
- androidx.credentials.playservices
- .TestCredentialsFragmentActivity.class);
- activityScenario.onActivity(
- activity -> {
- listener.onActivity((Activity) activity);
- });
- } else {
- ActivityScenario<TestCredentialsActivity> activityScenario =
- ActivityScenario.launch(TestCredentialsActivity.class);
- activityScenario.onActivity(
- activity -> {
- listener.onActivity((Activity) activity);
- });
- }
- }
-
@Test
public void convertRequestToPlayServices_setPasswordOptionRequestAndFalseAutoSelect_success() {
- launchTestActivity(
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(
activity -> {
BeginSignInRequest actualResponse =
CredentialProviderBeginSignInController.getInstance(activity)
@@ -99,7 +60,9 @@
@Test
public void convertRequestToPlayServices_setPasswordOptionRequestAndTrueAutoSelect_success() {
- launchTestActivity(
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(
activity -> {
BeginSignInRequest actualResponse =
CredentialProviderBeginSignInController.getInstance(activity)
@@ -116,7 +79,9 @@
@Test
public void convertRequestToPlayServices_nullRequest_throws() {
- launchTestActivity(
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(
activity -> {
assertThrows(
"null get credential request must throw exception",
@@ -129,7 +94,9 @@
@Test
public void convertResponseToCredentialManager_nullRequest_throws() {
- launchTestActivity(
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(
activity -> {
assertThrows(
"null sign in credential response must throw exception",
@@ -142,6 +109,9 @@
@Test
public void convertRequestToPlayServices_setGoogleIdOptionRequestAndTrueAutoSelect_success() {
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+
GetGoogleIdOption option =
new GetGoogleIdOption.Builder()
.setServerClientId("server_client_id")
@@ -152,7 +122,7 @@
.setAutoSelectEnabled(true)
.build();
- launchTestActivity(
+ activityScenario.onActivity(
activity -> {
BeginSignInRequest actualRequest =
CredentialProviderBeginSignInController.getInstance(activity)
@@ -181,7 +151,9 @@
@Test
public void duplicateGetInstance_shouldBeEqual() {
- launchTestActivity(
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(
activity -> {
CredentialProviderBeginSignInController firstInstance =
CredentialProviderBeginSignInController.getInstance(activity);
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
index 5e05605..d245c4b 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
@@ -16,55 +16,32 @@
package androidx.credentials.playservices.beginsignin
-import android.app.Activity
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetPasswordOption
import androidx.credentials.playservices.TestCredentialsActivity
-import androidx.credentials.playservices.TestCredentialsFragmentActivity
import androidx.credentials.playservices.controllers.BeginSignIn.CredentialProviderBeginSignInController.Companion.getInstance
import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@Suppress("deprecation")
@RequiresApi(api = Build.VERSION_CODES.O)
-class CredentialProviderBeginSignInControllerTest(val useFragmentActivity: Boolean) {
-
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "{0}")
- fun initParameters() = listOf(true, false)
- }
-
- private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
- if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
- var activityScenario =
- ActivityScenario.launch(
- androidx.credentials.playservices
- .TestCredentialsFragmentActivity::class.java)
- activityScenario.onActivity { activity: Activity ->
- callback.invoke(activity)
- }
- } else {
- var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
- activityScenario.onActivity { activity: Activity ->
- callback.invoke(activity)
- }
- }
- }
-
+class CredentialProviderBeginSignInControllerTest {
@Test
fun convertRequestToPlayServices_setPasswordOptionRequestAndFalseAutoSelect_success() {
- launchTestActivity { activity: Activity ->
- val actualResponse = getInstance(activity)
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
+ val actualResponse = getInstance(activity!!)
.convertRequestToPlayServices(
GetCredentialRequest(
listOf(
@@ -81,8 +58,11 @@
@Test
fun convertRequestToPlayServices_setPasswordOptionRequestAndTrueAutoSelect_success() {
- launchTestActivity { activity: Activity ->
- val actualResponse = getInstance(activity)
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
+ val actualResponse = getInstance(activity!!)
.convertRequestToPlayServices(
GetCredentialRequest(
listOf(
@@ -99,6 +79,10 @@
@Test
fun convertRequestToPlayServices_setGoogleIdOptionRequest_success() {
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+
val option = GetGoogleIdOption.Builder()
.setServerClientId("server_client_id")
.setNonce("nonce")
@@ -108,8 +92,8 @@
.setAutoSelectEnabled(true)
.build()
- launchTestActivity { activity: Activity ->
- val actualRequest = getInstance(activity)
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
+ val actualRequest = getInstance(activity!!)
.convertRequestToPlayServices(
GetCredentialRequest(
listOf(
@@ -136,9 +120,12 @@
@Test
fun duplicateGetInstance_shouldBeEqual() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
- val firstInstance = getInstance(activity)
+ val firstInstance = getInstance(activity!!)
val secondInstance = getInstance(activity)
assertThat(firstInstance).isEqualTo(secondInstance)
}
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
index f0d5a85..00b86ba 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
@@ -20,8 +20,6 @@
import static org.junit.Assert.assertThrows;
-import android.app.Activity;
-import android.os.Build;
import android.os.Bundle;
import androidx.credentials.CreateCredentialResponse;
@@ -31,6 +29,7 @@
import androidx.credentials.playservices.TestUtils;
import androidx.credentials.playservices.controllers.CreatePassword.CredentialProviderCreatePasswordController;
import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.google.android.gms.auth.api.identity.SignInPassword;
@@ -39,53 +38,17 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class CredentialProviderCreatePasswordControllerJavaTest {
- private final boolean mUseFragmentActivity;
-
- @Parameterized.Parameters
- public static Object[] data() {
- return new Object[] {true, false};
- }
-
- public CredentialProviderCreatePasswordControllerJavaTest(final boolean useFragmentActivity)
- throws Throwable {
- mUseFragmentActivity = useFragmentActivity;
- }
-
- interface TestActivityListener {
- void onActivity(Activity a);
- }
-
- private void launchTestActivity(TestActivityListener listener) {
- if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
- ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
- activityScenario =
- ActivityScenario.launch(
- androidx.credentials.playservices
- .TestCredentialsFragmentActivity.class);
- activityScenario.onActivity(
- activity -> {
- listener.onActivity((Activity) activity);
- });
- } else {
- ActivityScenario<TestCredentialsActivity> activityScenario =
- ActivityScenario.launch(TestCredentialsActivity.class);
- activityScenario.onActivity(
- activity -> {
- listener.onActivity((Activity) activity);
- });
- }
- }
-
@Test
public void convertResponseToCredentialManager_unitInput_success() {
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
String expectedResponseType = new CreatePasswordResponse().getType();
- launchTestActivity(
+ activityScenario.onActivity(
activity -> {
CreateCredentialResponse actualResponse =
CredentialProviderCreatePasswordController.getInstance(activity)
@@ -99,9 +62,11 @@
@Test
public void convertRequestToPlayServices_createPasswordRequest_success() {
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
String expectedId = "LM";
String expectedPassword = "SodaButton";
- launchTestActivity(
+ activityScenario.onActivity(
activity -> {
SignInPassword actualRequest =
CredentialProviderCreatePasswordController.getInstance(activity)
@@ -118,7 +83,7 @@
public void convertRequestToPlayServices_nullRequest_throws() {
ActivityScenario<TestCredentialsActivity> activityScenario =
ActivityScenario.launch(TestCredentialsActivity.class);
- launchTestActivity(
+ activityScenario.onActivity(
activity -> {
assertThrows(
"null create password request must throw exception",
@@ -132,7 +97,9 @@
@Test
public void convertResponseToCredentialManager_nullRequest_throws() {
- launchTestActivity(
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(
activity -> {
assertThrows(
"null unit response must throw exception",
@@ -145,7 +112,9 @@
@Test
public void duplicateGetInstance_shouldBeEqual() {
- launchTestActivity(
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(
activity -> {
CredentialProviderCreatePasswordController firstInstance =
CredentialProviderCreatePasswordController.getInstance(activity);
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
index cc2e365..0710e37 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
@@ -16,56 +16,31 @@
package androidx.credentials.playservices.createpassword
-import android.app.Activity
-import android.os.Build
import android.os.Bundle
-import androidx.annotation.DoNotInline
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CreatePasswordResponse
import androidx.credentials.playservices.TestCredentialsActivity
import androidx.credentials.playservices.TestUtils.Companion.equals
import androidx.credentials.playservices.controllers.CreatePassword.CredentialProviderCreatePasswordController.Companion.getInstance
import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
-class CredentialProviderCreatePasswordControllerTest(val useFragmentActivity: Boolean) {
-
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "{0}")
- fun initParameters() = listOf(true, false)
- }
-
- @DoNotInline
- private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
- if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
- var activityScenario =
- ActivityScenario.launch(
- androidx.credentials.playservices
- .TestCredentialsFragmentActivity::class.java)
- activityScenario.onActivity { activity: Activity ->
- callback.invoke(activity)
- }
- } else {
- var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
- activityScenario.onActivity { activity: Activity ->
- callback.invoke(activity)
- }
- }
- }
-
+class CredentialProviderCreatePasswordControllerTest {
@Test
fun convertResponseToCredentialManager_unitInput_success() {
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
val expectedResponseType = CreatePasswordResponse().type
- launchTestActivity { activity: Activity ->
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
- val actualResponse = getInstance(activity)
+ val actualResponse = getInstance(activity!!)
.convertResponseToCredentialManager(Unit)
assertThat(actualResponse.type)
@@ -76,11 +51,14 @@
@Test
fun convertRequestToPlayServices_createPasswordRequest_success() {
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
val expectedId = "LM"
val expectedPassword = "SodaButton"
- launchTestActivity { activity: Activity ->
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
- val actualRequest = getInstance(activity)
+ val actualRequest = getInstance(activity!!)
.convertRequestToPlayServices(CreatePasswordRequest(
expectedId, expectedPassword)).signInPassword
@@ -92,9 +70,12 @@
@Test
fun duplicateGetInstance_shouldBeEqual() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
- val firstInstance = getInstance(activity)
+ val firstInstance = getInstance(activity!!)
val secondInstance = getInstance(activity)
assertThat(firstInstance).isEqualTo(secondInstance)
}
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
index 48d07d7..a814d5f 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
@@ -16,6 +16,7 @@
package androidx.credentials.playservices.createpublickeycredential;
+
import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.ALL_REQUIRED_AND_OPTIONAL_SIGNATURE;
import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.ALL_REQUIRED_FIELDS_SIGNATURE;
import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT;
@@ -33,14 +34,12 @@
import static org.junit.Assert.assertThrows;
-import android.app.Activity;
-import android.os.Build;
-
import androidx.credentials.CreatePublicKeyCredentialRequest;
import androidx.credentials.playservices.TestCredentialsActivity;
import androidx.credentials.playservices.TestUtils;
import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.CredentialProviderCreatePublicKeyCredentialController;
import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions;
@@ -49,212 +48,156 @@
import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class CredentialProviderCreatePublicKeyCredentialControllerJavaTest {
-
- private final boolean mUseFragmentActivity;
-
- @Parameterized.Parameters
- public static Object[] data() {
- return new Object[] {true, false};
- }
-
- public CredentialProviderCreatePublicKeyCredentialControllerJavaTest(
- final boolean useFragmentActivity) throws Throwable {
- mUseFragmentActivity = useFragmentActivity;
- }
-
- interface TestActivityListener {
- void onActivity(Activity a);
- }
-
- private void launchTestActivity(TestActivityListener listener) {
- if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
- ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
- activityScenario =
- ActivityScenario.launch(
- androidx.credentials.playservices
- .TestCredentialsFragmentActivity.class);
- activityScenario.onActivity(
- activity -> {
- listener.onActivity((Activity) activity);
- });
- } else {
- ActivityScenario<TestCredentialsActivity> activityScenario =
- ActivityScenario.launch(TestCredentialsActivity.class);
- activityScenario.onActivity(
- activity -> {
- listener.onActivity((Activity) activity);
- });
- }
- }
-
- private PublicKeyCredentialCreationOptions convertRequestToPlayServices(
- Activity activity, String type) {
- CreatePublicKeyCredentialRequest pubKeyRequest = new CreatePublicKeyCredentialRequest(type);
- return CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
- .convertRequestToPlayServices(pubKeyRequest);
- }
-
@Test
public void convertRequestToPlayServices_correctRequiredOnlyRequest_success() {
- launchTestActivity(
- activity -> {
- try {
- JSONObject expectedJson =
- new JSONObject(MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(activity -> {
+ try {
+ JSONObject expectedJson = new JSONObject(
+ MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
- PublicKeyCredentialCreationOptions actualResponse =
- convertRequestToPlayServices(
- activity, MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
- JSONObject actualJson =
- createJsonObjectFromPublicKeyCredentialCreationOptions(
- actualResponse);
- JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_FIELDS_SIGNATURE);
+ PublicKeyCredentialCreationOptions actualResponse =
+ CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+ .convertRequestToPlayServices(
+ new CreatePublicKeyCredentialRequest(
+ MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT));
+ JSONObject actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
+ actualResponse);
+ JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_FIELDS_SIGNATURE);
- assertThat(
- TestUtils.Companion.isSubsetJson(
- expectedJson, actualJson, requiredKeys))
- .isTrue();
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- });
+ assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+ requiredKeys)).isTrue();
+ // TODO("Add remaining tests in detail after discussing ideal form")
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ });
}
@Test
public void convertRequestToPlayServices_correctRequiredAndOptionalRequest_success() {
- launchTestActivity(
- activity -> {
- try {
- JSONObject expectedJson =
- new JSONObject(
- MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(activity -> {
+ try {
+ JSONObject expectedJson = new JSONObject(
+ MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
- PublicKeyCredentialCreationOptions actualResponse =
- convertRequestToPlayServices(
- activity,
- MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
- JSONObject actualJson =
- createJsonObjectFromPublicKeyCredentialCreationOptions(
- actualResponse);
- JSONObject requiredKeys =
- new JSONObject(ALL_REQUIRED_AND_OPTIONAL_SIGNATURE);
+ PublicKeyCredentialCreationOptions actualResponse =
+ CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+ .convertRequestToPlayServices(new CreatePublicKeyCredentialRequest(
+ MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT));
+ JSONObject actualJson =
+ createJsonObjectFromPublicKeyCredentialCreationOptions(
+ actualResponse);
+ JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_AND_OPTIONAL_SIGNATURE);
- assertThat(
- TestUtils.Companion.isSubsetJson(
- expectedJson, actualJson, requiredKeys))
- .isTrue();
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- });
+ assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+ requiredKeys)).isTrue();
+ // TODO("Add remaining tests in detail after discussing ideal form")
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ });
}
-
@Test
public void convertRequestToPlayServices_missingRequired_throws() {
- launchTestActivity(
- activity -> {
- try {
- PublicKeyCredentialCreationOptions actualResponse =
- convertRequestToPlayServices(
- activity, MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
-
- CreatePublicKeyCredentialRequest pubKeyRequest =
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(activity -> {
+ try {
+ CredentialProviderCreatePublicKeyCredentialController
+ .getInstance(activity)
+ .convertRequestToPlayServices(
new CreatePublicKeyCredentialRequest(
- MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
- CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
- .convertRequestToPlayServices(
- new CreatePublicKeyCredentialRequest(
- MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD));
+ MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD));
- // Should not reach here.
- assertWithMessage("Exception should be thrown").that(true).isFalse();
- } catch (Exception e) {
- assertThat(e.getMessage().contains("No value for id")).isTrue();
- assertThat(e.getClass().getName().contains("JSONException")).isTrue();
- }
- });
+ // Should not reach here.
+ assertWithMessage("Exception should be thrown").that(true).isFalse();
+ } catch (Exception e) {
+ assertThat(e.getMessage().contains("No value for id")).isTrue();
+ assertThat(e.getClass().getName().contains("JSONException")).isTrue();
+ }
+ });
}
@Test
public void convertRequestToPlayServices_emptyRequired_throws() {
- launchTestActivity(
- activity -> {
- assertThrows(
- "Expected bad required json to throw",
- JSONException.class,
- () ->
- convertRequestToPlayServices(
- activity, MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY));
- });
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(activity -> {
+
+ assertThrows("Expected bad required json to throw",
+ JSONException.class,
+ () -> CredentialProviderCreatePublicKeyCredentialController
+ .getInstance(activity).convertRequestToPlayServices(
+ new CreatePublicKeyCredentialRequest(
+ MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)));
+ });
}
@Test
public void convertRequestToPlayServices_missingOptionalRequired_throws() {
- launchTestActivity(
- activity -> {
- assertThrows(
- "Expected bad required json to throw",
- JSONException.class,
- () ->
- convertRequestToPlayServices(
- activity, OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD));
- });
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(activity -> {
+
+ assertThrows("Expected bad required json to throw",
+ JSONException.class,
+ () -> CredentialProviderCreatePublicKeyCredentialController
+ .getInstance(activity)
+ .convertRequestToPlayServices(
+ new CreatePublicKeyCredentialRequest(
+ OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)));
+ });
}
@Test
public void convertRequestToPlayServices_emptyOptionalRequired_throws() {
- launchTestActivity(
- activity -> {
- assertThrows(
- "Expected bad required json to throw",
- JSONException.class,
- () ->
- convertRequestToPlayServices(
- activity, OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD));
- });
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(activity -> {
+
+ assertThrows("Expected bad required json to throw",
+ JSONException.class,
+ () -> CredentialProviderCreatePublicKeyCredentialController
+ .getInstance(activity)
+ .convertRequestToPlayServices(
+ new CreatePublicKeyCredentialRequest(
+ OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)));
+ });
}
@Test
public void convertRequestToPlayServices_missingOptionalNotRequired_success() {
- launchTestActivity(
- activity -> {
- try {
- JSONObject expectedJson =
- new JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
+ ActivityScenario<TestCredentialsActivity> activityScenario =
+ ActivityScenario.launch(TestCredentialsActivity.class);
+ activityScenario.onActivity(activity -> {
+ try {
+ JSONObject expectedJson = new JSONObject(
+ OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
- PublicKeyCredentialCreationOptions actualResponse =
- convertRequestToPlayServices(
- activity, OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
- JSONObject actualJson =
- createJsonObjectFromPublicKeyCredentialCreationOptions(
- actualResponse);
- JSONObject requiredKeys =
- new JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE);
+ PublicKeyCredentialCreationOptions actualResponse =
+ CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+ .convertRequestToPlayServices(
+ new CreatePublicKeyCredentialRequest(
+ OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD));
+ JSONObject actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
+ actualResponse);
+ JSONObject requiredKeys = new
+ JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE);
- assertThat(
- TestUtils.Companion.isSubsetJson(
- expectedJson, actualJson, requiredKeys))
- .isTrue();
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- });
- }
-
- @Test
- public void getInstanceRepeatedTest() {
- launchTestActivity(
- a -> {
- CredentialProviderCreatePublicKeyCredentialController firstInstance =
- CredentialProviderCreatePublicKeyCredentialController.getInstance(a);
- CredentialProviderCreatePublicKeyCredentialController secondInstance =
- CredentialProviderCreatePublicKeyCredentialController.getInstance(a);
- assertThat(firstInstance).isEqualTo(secondInstance);
- });
+ assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+ requiredKeys)).isTrue();
+ // TODO("Add remaining tests in detail after discussing ideal form")
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ });
}
}
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
index 0e8a50e..3a9ae60 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
@@ -16,9 +16,6 @@
package androidx.credentials.playservices.createpublickeycredential
-import android.app.Activity
-import android.os.Build
-import androidx.annotation.DoNotInline
import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.playservices.TestCredentialsActivity
import androidx.credentials.playservices.TestUtils.Companion.isSubsetJson
@@ -35,6 +32,7 @@
import androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.Companion.OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD
import androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.Companion.createJsonObjectFromPublicKeyCredentialCreationOptions
import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.json.JSONException
@@ -43,51 +41,29 @@
import org.junit.Test
import org.junit.function.ThrowingRunnable
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
-class CredentialProviderCreatePublicKeyCredentialControllerTest(val useFragmentActivity: Boolean) {
-
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "{0}")
- fun initParameters() = listOf(true, false)
- }
-
- @DoNotInline
- private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
- if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
- var activityScenario =
- ActivityScenario.launch(
- androidx.credentials.playservices
- .TestCredentialsFragmentActivity::class.java)
- activityScenario.onActivity { activity: Activity ->
- callback.invoke(activity)
- }
- } else {
- var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
- activityScenario.onActivity { activity: Activity ->
- callback.invoke(activity)
- }
- }
- }
-
+class CredentialProviderCreatePublicKeyCredentialControllerTest {
@Test
fun convertRequestToPlayServices_correctRequiredOnlyRequest_success() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
try {
val expectedJson = JSONObject(MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT)
- val actualResponse = getInstance(activity).convertRequestToPlayServices(
- CreatePublicKeyCredentialRequest(
- MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT))
+ val actualResponse = getInstance(activity!!).convertRequestToPlayServices(
+ CreatePublicKeyCredentialRequest(
+ MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT))
val actualJson =
createJsonObjectFromPublicKeyCredentialCreationOptions(actualResponse)
val requiredKeys =
JSONObject(ALL_REQUIRED_FIELDS_SIGNATURE)
assertThat(isSubsetJson(expectedJson, actualJson, requiredKeys)).isTrue()
+ // TODO("Add remaining tests in detail after discussing ideal form")
} catch (e: JSONException) {
throw RuntimeException(e)
}
@@ -96,20 +72,24 @@
@Test
fun convertRequestToPlayServices_correctRequiredAndOptionalRequest_success() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
try {
val expectedJson = JSONObject(
MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT)
- val actualResponse = getInstance(activity)
- .convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
- MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT))
+ val actualResponse = getInstance(activity!!)
+ .convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
+ MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT))
val actualJson =
createJsonObjectFromPublicKeyCredentialCreationOptions(actualResponse)
val requiredKeys =
JSONObject(ALL_REQUIRED_AND_OPTIONAL_SIGNATURE)
assertThat(isSubsetJson(expectedJson, actualJson, requiredKeys)).isTrue()
+ // TODO("Add remaining tests in detail after discussing ideal form")
} catch (e: JSONException) {
throw java.lang.RuntimeException(e)
}
@@ -118,88 +98,94 @@
@Test
fun convertRequestToPlayServices_missingRequired_throws() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
Assert.assertThrows("Expected bad required json to throw",
JSONException::class.java,
ThrowingRunnable {
getInstance(
- activity
+ activity!!
).convertRequestToPlayServices(
- CreatePublicKeyCredentialRequest(
- MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD
- )) })
+ CreatePublicKeyCredentialRequest(
+ MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD
+ )) })
}
}
@Test
fun convertRequestToPlayServices_emptyRequired_throws() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
Assert.assertThrows("Expected bad required json to throw",
JSONException::class.java,
- ThrowingRunnable { getInstance(activity
- ).convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
- MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)) })
+ ThrowingRunnable { getInstance(activity!!
+ ).convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
+ MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)) })
}
}
@Test
fun convertRequestToPlayServices_missingOptionalRequired_throws() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
Assert.assertThrows("Expected bad required json to throw",
JSONException::class.java,
ThrowingRunnable {
getInstance(
- activity
+ activity!!
).convertRequestToPlayServices(
- CreatePublicKeyCredentialRequest(
- OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)) })
+ CreatePublicKeyCredentialRequest(
+ OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)) })
}
}
@Test
fun convertRequestToPlayServices_emptyOptionalRequired_throws() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
Assert.assertThrows("Expected bad required json to throw",
JSONException::class.java,
- ThrowingRunnable { getInstance(activity).convertRequestToPlayServices(
- CreatePublicKeyCredentialRequest(
- OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)) })
+ ThrowingRunnable { getInstance(activity!!).convertRequestToPlayServices(
+ CreatePublicKeyCredentialRequest(
+ OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)) })
}
}
@Test
fun convertRequestToPlayServices_missingOptionalNotRequired_success() {
- launchTestActivity { activity: Activity ->
+ val activityScenario = ActivityScenario.launch(
+ TestCredentialsActivity::class.java
+ )
+ activityScenario.onActivity { activity: TestCredentialsActivity? ->
try {
val expectedJson = JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD)
val actualResponse =
- getInstance(activity)
+ getInstance(activity!!)
.convertRequestToPlayServices(
CreatePublicKeyCredentialRequest(
OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD))
val actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
- actualResponse)
+ actualResponse)
val requiredKeys =
JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE)
assertThat(isSubsetJson(expectedJson, actualJson, requiredKeys)).isTrue()
+ // TODO("Add remaining tests in detail after discussing ideal form")
} catch (e: JSONException) {
throw java.lang.RuntimeException(e)
}
}
}
-
- @Test
- fun getInstanceRepeatedTest() {
- launchTestActivity { activity: Activity ->
-
- val firstInstance = getInstance(activity)
- val secondInstance = getInstance(activity)
- assertThat(firstInstance).isEqualTo(secondInstance)
- }
- }
}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt
deleted file mode 100644
index 2d7f1e9..0000000
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt
+++ /dev/null
@@ -1,93 +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.credentials.playservices
-
-import android.content.Intent
-import android.os.Bundle
-import android.os.ResultReceiver
-import androidx.annotation.RestrictTo
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController
-import androidx.fragment.app.Fragment
-
-/** A fragment used if we are passed a fragment activity. */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@Suppress("Deprecation")
-open class CredentialProviderFragment : Fragment() {
-
- private var resultReceiver: ResultReceiver? = null
- private var mWaitingForActivityResult = false
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- resultReceiver = getResultReceiver()
- if (resultReceiver == null) {
- return
- }
-
- restoreState(savedInstanceState)
- if (mWaitingForActivityResult) {
- return // Past call still active
- }
- }
-
- private fun getResultReceiver(): ResultReceiver? {
- if (getArguments() == null) {
- return null
- }
-
- return getArguments()!!.getParcelable(CredentialProviderBaseController.RESULT_RECEIVER_TAG)
- as? ResultReceiver
- }
-
- private fun restoreState(savedInstanceState: Bundle?) {
- if (savedInstanceState != null) {
- mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
- }
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
- super.onSaveInstanceState(outState)
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- val bundle = Bundle()
- bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
- bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
- bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
- resultReceiver?.send(resultCode, bundle)
- mWaitingForActivityResult = false
- }
-
- companion object {
- private const val TAG = "CredentialProviderFragment"
- private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
-
- fun createFrom(resultReceiver: ResultReceiver): CredentialProviderFragment {
- val f = CredentialProviderFragment()
-
- // Supply index input as an argument.
- val args = Bundle()
- args.putParcelable(CredentialProviderBaseController.RESULT_RECEIVER_TAG, resultReceiver)
- f.setArguments(args)
-
- return f
- }
- }
-}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt
deleted file mode 100644
index 112599d..0000000
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt
+++ /dev/null
@@ -1,266 +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.credentials.playservices
-
-import android.app.Activity
-import android.app.PendingIntent
-import android.content.IntentSender
-import android.os.Bundle
-import android.os.ResultReceiver
-import android.util.Log
-import androidx.annotation.RestrictTo
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_INTERRUPTED
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_UNKNOWN
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_INTERRUPTED
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_NO_CREDENTIALS
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_UNKNOWN
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
-import com.google.android.gms.auth.api.identity.BeginSignInRequest
-import com.google.android.gms.auth.api.identity.CredentialSavingClient
-import com.google.android.gms.auth.api.identity.SavePasswordRequest
-import com.google.android.gms.auth.api.identity.SignInClient
-import com.google.android.gms.common.api.ApiException
-import com.google.android.gms.fido.fido2.Fido2ApiClient
-import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
-
-/** A util class for interacting with GmsCore. */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@Suppress("Deprecation", "ForbiddenSuperClass")
-open class GmsCoreUtils {
-
- class GmsCoreUtilsResult(var waitingForActivityResult: Boolean, var hasFinished: Boolean)
-
- internal companion object {
- private const val TAG = "GmsCoreUtils"
-
- const val DEFAULT_REQUEST_CODE = 1
-
- class FragmentCreationException() : Exception("Failed to create exception")
-
- fun handleCreatePublicKeyCredential(
- apiClient: Fido2ApiClient,
- resultReceiver: ResultReceiver,
- fidoRegistrationRequest: PublicKeyCredentialCreationOptions?,
- requestCode: Int,
- activity: Activity
- ): GmsCoreUtilsResult {
- var waitingForActivityResult = false
- var hasActivityFinished = false
- var fragment = setupFragmentActivity(activity, resultReceiver)
-
- fidoRegistrationRequest?.let {
- apiClient
- .getRegisterPendingIntent(fidoRegistrationRequest)
- .addOnSuccessListener { result: PendingIntent ->
- try {
- startIntentSender(
- activity,
- result.intentSender,
- requestCode,
- fragment,
- )
- } catch (e: IntentSender.SendIntentException) {
- setupFailure(
- resultReceiver,
- CREATE_UNKNOWN,
- "During public key credential, found IntentSender " +
- "failure on public key creation: ${e.message}"
- )
- }
- }
- .addOnFailureListener { e: Exception ->
- var errName: String = CREATE_UNKNOWN
- if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
- errName = CREATE_INTERRUPTED
- }
- setupFailure(
- resultReceiver,
- errName,
- "During create public key credential, fido registration " + "failure: ${e.message}"
- )
- }
- }
- ?: run {
- Log.w(
- TAG,
- "During create public key credential, request is null, so nothing to " +
- "launch for public key credentials"
- )
- hasActivityFinished = true
- }
- return GmsCoreUtilsResult(waitingForActivityResult, hasActivityFinished)
- }
-
- fun handleBeginSignIn(
- apiClient: SignInClient,
- resultReceiver: ResultReceiver,
- params: BeginSignInRequest?,
- requestCode: Int,
- activity: Activity
- ): GmsCoreUtilsResult {
- var waitingForActivityResult = false
- var hasFinished = false
- var fragment = setupFragmentActivity(activity, resultReceiver)
-
- params?.let {
- apiClient
- .beginSignIn(params)
- .addOnSuccessListener {
- try {
- waitingForActivityResult = true
- startIntentSender(
- activity,
- it.pendingIntent.intentSender,
- requestCode,
- fragment,
- )
- } catch (e: IntentSender.SendIntentException) {
- setupFailure(
- resultReceiver,
- GET_UNKNOWN,
- "During begin sign in, one tap ui intent sender " + "failure: ${e.message}"
- )
- }
- }
- .addOnFailureListener { e: Exception ->
- var errName: String = GET_NO_CREDENTIALS
- if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
- errName = GET_INTERRUPTED
- }
- setupFailure(
- resultReceiver,
- errName,
- "During begin sign in, failure response from one tap: ${e.message}"
- )
- }
- }
- ?: run {
- Log.i(
- TAG,
- "During begin sign in, params is null, nothing to launch for " + "begin sign in"
- )
- hasFinished = true
- }
- return GmsCoreUtilsResult(waitingForActivityResult, hasFinished)
- }
-
- fun handleCreatePassword(
- apiClient: CredentialSavingClient,
- resultReceiver: ResultReceiver,
- params: SavePasswordRequest?,
- requestCode: Int,
- activity: Activity
- ): GmsCoreUtilsResult {
- var waitingForActivityResult = false
- var hasFinished = false
- var fragment = setupFragmentActivity(activity, resultReceiver)
-
- params?.let {
- apiClient
- .savePassword(params)
- .addOnSuccessListener {
- try {
- waitingForActivityResult = true
- startIntentSender(
- activity,
- it.pendingIntent.intentSender,
- requestCode,
- fragment,
- )
- } catch (e: IntentSender.SendIntentException) {
- setupFailure(
- resultReceiver,
- CREATE_UNKNOWN,
- "During save password, found UI intent sender " + "failure: ${e.message}"
- )
- }
- }
- .addOnFailureListener { e: Exception ->
- var errName: String = CREATE_UNKNOWN
- if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
- errName = CREATE_INTERRUPTED
- }
- setupFailure(
- resultReceiver,
- errName,
- "During save password, found " + "password failure response from one tap ${e.message}"
- )
- }
- }
- ?: run {
- Log.i(
- TAG,
- "During save password, params is null, nothing to launch for create" + " password"
- )
- hasFinished = true
- }
- return GmsCoreUtilsResult(waitingForActivityResult, hasFinished)
- }
-
- private fun setupFragmentActivity(
- activity: Activity,
- resultReceiver: ResultReceiver
- ): Fragment? {
- if (activity is FragmentActivity) {
- val fragment = CredentialProviderFragment.createFrom(resultReceiver)
- val manager = activity.getSupportFragmentManager()
- manager.beginTransaction().add(fragment, "credman").commit()
-
- if (!fragment.isAdded()) {
- throw FragmentCreationException()
- }
-
- return fragment
- }
-
- return null
- }
-
- private fun startIntentSender(
- activity: Activity,
- intentSender: IntentSender,
- requestCode: Int,
- fragment: Fragment?,
- ) {
- if (fragment != null && fragment.isAdded() && activity is FragmentActivity) {
- activity.startIntentSenderFromFragment(
- fragment,
- intentSender,
- requestCode,
- null,
- 0,
- 0,
- 0,
- null,
- )
- return
- }
-
- activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0, null)
- }
-
- private fun setupFailure(resultReceiver: ResultReceiver, errName: String, errMsg: String) {
- val bundle = Bundle()
- bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, true)
- bundle.putString(CredentialProviderBaseController.EXCEPTION_TYPE_TAG, errName)
- bundle.putString(CredentialProviderBaseController.EXCEPTION_MESSAGE_TAG, errMsg)
- resultReceiver.send(Integer.MAX_VALUE, bundle)
- }
- }
-}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
index 8586305..bde5471 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
@@ -17,150 +17,230 @@
package androidx.credentials.playservices
import android.app.Activity
+import android.app.PendingIntent
import android.content.Intent
+import android.content.IntentSender
import android.os.Bundle
import android.os.ResultReceiver
import android.util.Log
import androidx.annotation.RestrictTo
import androidx.credentials.playservices.controllers.CredentialProviderBaseController
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_INTERRUPTED
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_UNKNOWN
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_INTERRUPTED
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_NO_CREDENTIALS
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_UNKNOWN
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.Identity
import com.google.android.gms.auth.api.identity.SavePasswordRequest
+import com.google.android.gms.common.api.ApiException
import com.google.android.gms.fido.Fido
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
-/** An activity used to ensure all required API versions work as intended. */
+/**
+ * An activity used to ensure all required API versions work as intended.
+ */
@RestrictTo(RestrictTo.Scope.LIBRARY)
@Suppress("Deprecation", "ForbiddenSuperClass")
open class HiddenActivity : Activity() {
- private var resultReceiver: ResultReceiver? = null
- private var mWaitingForActivityResult = false
+ private var resultReceiver: ResultReceiver? = null
+ private var mWaitingForActivityResult = false
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- overridePendingTransition(0, 0)
- val type: String? = intent.getStringExtra(CredentialProviderBaseController.TYPE_TAG)
- resultReceiver = intent.getParcelableExtra(CredentialProviderBaseController.RESULT_RECEIVER_TAG)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ overridePendingTransition(0, 0)
+ val type: String? = intent.getStringExtra(CredentialProviderBaseController.TYPE_TAG)
+ resultReceiver = intent.getParcelableExtra(
+ CredentialProviderBaseController.RESULT_RECEIVER_TAG)
- if (resultReceiver == null) {
- finish()
+ if (resultReceiver == null) {
+ finish()
+ }
+
+ restoreState(savedInstanceState)
+ if (mWaitingForActivityResult) {
+ return; // Past call still active
+ }
+
+ when (type) {
+ CredentialProviderBaseController.BEGIN_SIGN_IN_TAG -> {
+ handleBeginSignIn()
+ }
+ CredentialProviderBaseController.CREATE_PASSWORD_TAG -> {
+ handleCreatePassword()
+ }
+ CredentialProviderBaseController.CREATE_PUBLIC_KEY_CREDENTIAL_TAG -> {
+ handleCreatePublicKeyCredential()
+ } else -> {
+ Log.w(TAG, "Activity handed an unsupported type")
+ finish()
+ }
+ }
}
- restoreState(savedInstanceState)
- if (mWaitingForActivityResult) {
- return
- // Past call still active
+ private fun restoreState(savedInstanceState: Bundle?) {
+ if (savedInstanceState != null) {
+ mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
+ }
}
- when (type) {
- CredentialProviderBaseController.BEGIN_SIGN_IN_TAG -> {
- handleBeginSignIn(intent, resultReceiver)
- }
- CredentialProviderBaseController.CREATE_PASSWORD_TAG -> {
- handleCreatePassword(intent, resultReceiver)
- }
- CredentialProviderBaseController.CREATE_PUBLIC_KEY_CREDENTIAL_TAG -> {
- handleCreatePublicKeyCredential(intent, resultReceiver)
- }
- else -> {
- Log.w(TAG, "Activity handed an unsupported type")
+ private fun handleCreatePublicKeyCredential() {
+ val fidoRegistrationRequest: PublicKeyCredentialCreationOptions? = intent
+ .getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
+ val requestCode: Int = intent.getIntExtra(
+ CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+ DEFAULT_VALUE)
+ fidoRegistrationRequest?.let {
+ Fido.getFido2ApiClient(this)
+ .getRegisterPendingIntent(fidoRegistrationRequest)
+ .addOnSuccessListener { result: PendingIntent ->
+ try {
+ mWaitingForActivityResult = true
+ startIntentSenderForResult(
+ result.intentSender,
+ requestCode,
+ null, /* fillInIntent= */
+ 0, /* flagsMask= */
+ 0, /* flagsValue= */
+ 0, /* extraFlags= */
+ null /* options= */
+ )
+ } catch (e: IntentSender.SendIntentException) {
+ setupFailure(resultReceiver!!,
+ CREATE_UNKNOWN,
+ "During public key credential, found IntentSender " +
+ "failure on public key creation: ${e.message}")
+ }
+ }
+ .addOnFailureListener { e: Exception ->
+ var errName: String = CREATE_UNKNOWN
+ if (e is ApiException && e.statusCode in
+ CredentialProviderBaseController.retryables) {
+ errName = CREATE_INTERRUPTED
+ }
+ setupFailure(resultReceiver!!, errName,
+ "During create public key credential, fido registration " +
+ "failure: ${e.message}")
+ }
+ } ?: run {
+ Log.w(TAG, "During create public key credential, request is null, so nothing to " +
+ "launch for public key credentials")
+ finish()
+ }
+ }
+
+ private fun setupFailure(resultReceiver: ResultReceiver, errName: String, errMsg: String) {
+ val bundle = Bundle()
+ bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, true)
+ bundle.putString(CredentialProviderBaseController.EXCEPTION_TYPE_TAG, errName)
+ bundle.putString(CredentialProviderBaseController.EXCEPTION_MESSAGE_TAG, errMsg)
+ resultReceiver.send(Integer.MAX_VALUE, bundle)
finish()
- }
}
- }
- private fun restoreState(savedInstanceState: Bundle?) {
- if (savedInstanceState != null) {
- mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
+ override fun onSaveInstanceState(outState: Bundle) {
+ outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
+ super.onSaveInstanceState(outState)
}
- }
- private fun handleBeginSignIn(intent: Intent, resultReceiver: ResultReceiver?) {
- val params: BeginSignInRequest? =
- intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
- val requestCode: Int =
- intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
+ private fun handleBeginSignIn() {
+ val params: BeginSignInRequest? = intent.getParcelableExtra(
+ CredentialProviderBaseController.REQUEST_TAG)
+ val requestCode: Int = intent.getIntExtra(
+ CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+ DEFAULT_VALUE)
+ params?.let {
+ Identity.getSignInClient(this).beginSignIn(params).addOnSuccessListener {
+ try {
+ mWaitingForActivityResult = true
+ startIntentSenderForResult(
+ it.pendingIntent.intentSender,
+ requestCode,
+ null,
+ 0,
+ 0,
+ 0,
+ null
+ )
+ } catch (e: IntentSender.SendIntentException) {
+ setupFailure(resultReceiver!!,
+ GET_UNKNOWN,
+ "During begin sign in, one tap ui intent sender " +
+ "failure: ${e.message}")
+ }
+ }.addOnFailureListener { e: Exception ->
+ var errName: String = GET_NO_CREDENTIALS
+ if (e is ApiException && e.statusCode in
+ CredentialProviderBaseController.retryables) {
+ errName = GET_INTERRUPTED
+ }
+ setupFailure(resultReceiver!!, errName,
+ "During begin sign in, failure response from one tap: ${e.message}")
+ }
+ } ?: run {
+ Log.i(TAG, "During begin sign in, params is null, nothing to launch for " +
+ "begin sign in")
+ finish()
+ }
+ }
- if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
- val result =
- GmsCoreUtils.handleBeginSignIn(
- Identity.getSignInClient(this),
- resultReceiver,
- params!!,
- requestCode,
- this
- )
- mWaitingForActivityResult = result.waitingForActivityResult
- if (result.hasFinished) {
+ private fun handleCreatePassword() {
+ val params: SavePasswordRequest? = intent.getParcelableExtra(
+ CredentialProviderBaseController.REQUEST_TAG)
+ val requestCode: Int = intent.getIntExtra(
+ CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+ DEFAULT_VALUE)
+ params?.let {
+ Identity.getCredentialSavingClient(this).savePassword(params)
+ .addOnSuccessListener {
+ try {
+ mWaitingForActivityResult = true
+ startIntentSenderForResult(
+ it.pendingIntent.intentSender,
+ requestCode,
+ null,
+ 0,
+ 0,
+ 0,
+ null
+ )
+ } catch (e: IntentSender.SendIntentException) {
+ setupFailure(resultReceiver!!,
+ CREATE_UNKNOWN,
+ "During save password, found UI intent sender " +
+ "failure: ${e.message}")
+ }
+ }.addOnFailureListener { e: Exception ->
+ var errName: String = CREATE_UNKNOWN
+ if (e is ApiException && e.statusCode in
+ CredentialProviderBaseController.retryables) {
+ errName = CREATE_INTERRUPTED
+ }
+ setupFailure(resultReceiver!!, errName, "During save password, found " +
+ "password failure response from one tap ${e.message}")
+ }
+ } ?: run {
+ Log.i(TAG, "During save password, params is null, nothing to launch for create" +
+ " password")
+ finish()
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ val bundle = Bundle()
+ bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
+ bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
+ bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
+ resultReceiver?.send(resultCode, bundle)
+ mWaitingForActivityResult = false
finish()
- }
}
- }
- private fun handleCreatePassword(intent: Intent, resultReceiver: ResultReceiver?) {
- val params: SavePasswordRequest? =
- intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
- val requestCode: Int =
- intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
-
- if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
- val result =
- GmsCoreUtils.handleCreatePassword(
- Identity.getCredentialSavingClient(this),
- resultReceiver,
- params!!,
- requestCode,
- this
- )
- mWaitingForActivityResult = result.waitingForActivityResult
- if (result.hasFinished) {
- finish()
- }
+ companion object {
+ private const val DEFAULT_VALUE: Int = 1
+ private const val TAG = "HiddenActivity"
+ private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
}
- }
-
- private fun handleCreatePublicKeyCredential(intent: Intent, resultReceiver: ResultReceiver?) {
- val fidoRegistrationRequest: PublicKeyCredentialCreationOptions? =
- intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
- val requestCode: Int =
- intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
-
- if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
- val result =
- GmsCoreUtils.handleCreatePublicKeyCredential(
- Fido.getFido2ApiClient(this),
- resultReceiver,
- fidoRegistrationRequest!!,
- requestCode,
- this
- )
- mWaitingForActivityResult = result.waitingForActivityResult
- if (result.hasFinished) {
- finish()
- }
- }
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
- super.onSaveInstanceState(outState)
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- val bundle = Bundle()
- bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
- bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
- bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
- resultReceiver?.send(resultCode, bundle)
- mWaitingForActivityResult = false
- finish()
- }
-
- companion object {
- private const val DEFAULT_VALUE: Int = 1
- private const val TAG = "HiddenActivity"
- private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
- }
}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
index 18f8a16..b0d9453 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
@@ -36,13 +36,11 @@
import androidx.credentials.exceptions.GetCredentialInterruptedException
import androidx.credentials.exceptions.GetCredentialUnknownException
import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
import androidx.credentials.playservices.HiddenActivity
import androidx.credentials.playservices.controllers.BeginSignIn.BeginSignInControllerUtility.Companion.constructBeginSignInRequest
import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.PublicKeyCredentialControllerUtility
import androidx.credentials.playservices.controllers.CredentialProviderBaseController
import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.Identity
import com.google.android.gms.auth.api.identity.SignInCredential
@@ -119,20 +117,6 @@
}
val convertedRequest: BeginSignInRequest = this.convertRequestToPlayServices(request)
-
- // If we were passed a fragment activity use that instead of a hidden one.
- if (context is FragmentActivity) {
- try {
- GmsCoreUtils.handleBeginSignIn(
- Identity.getSignInClient(context),
- resultReceiver, convertedRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
- context)
- return
- } catch (e: Exception) {
- Log.e(TAG, "Failed to use fragment flow", e)
- }
- }
-
val hiddenIntent = Intent(context, HiddenActivity::class.java)
hiddenIntent.putExtra(REQUEST_TAG, convertedRequest)
generateHiddenActivityIntent(resultReceiver, hiddenIntent, BEGIN_SIGN_IN_TAG)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
index ee4c3bb..360b0c6 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
@@ -32,12 +32,9 @@
import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.CreateCredentialUnknownException
import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
import androidx.credentials.playservices.HiddenActivity
import androidx.credentials.playservices.controllers.CredentialProviderBaseController
import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
-import com.google.android.gms.auth.api.identity.Identity
import com.google.android.gms.auth.api.identity.SavePasswordRequest
import com.google.android.gms.auth.api.identity.SignInPassword
import java.util.concurrent.Executor
@@ -103,20 +100,6 @@
}
val convertedRequest: SavePasswordRequest = this.convertRequestToPlayServices(request)
-
- // If we were passed a fragment activity use that instead of a hidden one.
- if (context is FragmentActivity) {
- try {
- GmsCoreUtils.handleCreatePassword(
- Identity.getCredentialSavingClient(context),
- resultReceiver, convertedRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
- context)
- return
- } catch (e: Exception) {
- Log.e(TAG, "Failed to use fragment flow", e)
- }
- }
-
val hiddenIntent = Intent(context, HiddenActivity::class.java)
hiddenIntent.putExtra(REQUEST_TAG, convertedRequest)
generateHiddenActivityIntent(resultReceiver, hiddenIntent, CREATE_PASSWORD_TAG)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
index 17a5b34..1302a81 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
@@ -35,11 +35,9 @@
import androidx.credentials.exceptions.domerrors.UnknownError
import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException
import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
import androidx.credentials.playservices.HiddenActivity
import androidx.credentials.playservices.controllers.CredentialProviderBaseController
import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
import com.google.android.gms.fido.Fido
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
@@ -121,19 +119,6 @@
if (CredentialProviderPlayServicesImpl.cancellationReviewer(cancellationSignal)) {
return
}
-
- // If we were passed a fragment activity use that instead of a hidden one.
- if (context is FragmentActivity) {
- try {
- GmsCoreUtils.handleCreatePublicKeyCredential(Fido.getFido2ApiClient(context),
- resultReceiver, fidoRegistrationRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
- context)
- return
- } catch (e: Exception) {
- Log.e(TAG, "Failed to use fragment flow", e)
- }
- }
-
val hiddenIntent = Intent(context, HiddenActivity::class.java)
hiddenIntent.putExtra(REQUEST_TAG, fidoRegistrationRequest)
generateHiddenActivityIntent(resultReceiver, hiddenIntent,
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
index a2c44d0..e8d55f0 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
@@ -23,16 +23,22 @@
import android.os.CancellationSignal;
import android.os.OutcomeReceiver;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.List;
-//@RunWith(AndroidJUnit4.class)
-//@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SmallTest
@SdkSuppress(minSdkVersion = 34)
public class CredentialProviderServiceJavaTest {
- //@Test
+ @Test
public void test_createRequest() {
CredentialProviderServiceTestImpl service = new CredentialProviderServiceTestImpl();
service.setTestMode(true);
@@ -60,54 +66,59 @@
assertThat(service.getLastCreateRequest()).isNotNull();
}
- //@Test
+ @Test
public void test_getRequest() {
CredentialProviderServiceTestImpl service = new CredentialProviderServiceTestImpl();
service.setTestMode(true);
- BeginGetCredentialRequest request =
- new BeginGetCredentialRequest(new ArrayList<BeginGetCredentialOption>());
- OutcomeReceiver<
- androidx.credentials.provider.BeginGetCredentialResponse,
- androidx.credentials.exceptions.GetCredentialException>
- outcome =
- new OutcomeReceiver<
- androidx.credentials.provider.BeginGetCredentialResponse,
- androidx.credentials.exceptions.GetCredentialException>() {
- public void onResult(
- androidx.credentials.provider.BeginGetCredentialResponse
- response) {}
+ android.service.credentials.BeginGetCredentialOption option =
+ new android.service.credentials.BeginGetCredentialOption(
+ "id", "type", new Bundle());
+ List<android.service.credentials.BeginGetCredentialOption> options = new ArrayList<>();
+ options.add(option);
- public void onError(
- androidx.credentials.exceptions.GetCredentialException error) {}
+ android.service.credentials.BeginGetCredentialRequest request =
+ new android.service.credentials.BeginGetCredentialRequest.Builder()
+ .setBeginGetCredentialOptions(options).build();
+ OutcomeReceiver<
+ android.service.credentials.BeginGetCredentialResponse,
+ android.credentials.GetCredentialException>
+ outcome = new OutcomeReceiver<
+ android.service.credentials.BeginGetCredentialResponse,
+ android.credentials.GetCredentialException>() {
+ public void onResult(
+ android.service.credentials.BeginGetCredentialResponse response) {}
+
+ public void onError(android.credentials.GetCredentialException error) {}
};
// Call the service.
assertThat(service.getLastGetRequest()).isNull();
- service.onBeginGetCredentialRequest(request, new CancellationSignal(), outcome);
+ service.onBeginGetCredential(request, new CancellationSignal(), outcome);
assertThat(service.getLastGetRequest()).isNotNull();
}
- //@Test
+ @Test
public void test_clearRequest() {
CredentialProviderServiceTestImpl service = new CredentialProviderServiceTestImpl();
service.setTestMode(true);
- ProviderClearCredentialStateRequest request =
- new ProviderClearCredentialStateRequest(
- new CallingAppInfo("name", new SigningInfo()));
- OutcomeReceiver<Void, androidx.credentials.exceptions.ClearCredentialException> outcome =
+ android.service.credentials.ClearCredentialStateRequest request =
+ new android.service.credentials.ClearCredentialStateRequest(
+ new android.service.credentials.CallingAppInfo(
+ "name", new SigningInfo()), new Bundle());
+ OutcomeReceiver<Void, android.credentials.ClearCredentialStateException> outcome =
new OutcomeReceiver<
- Void, androidx.credentials.exceptions.ClearCredentialException>() {
+ Void, android.credentials.ClearCredentialStateException>() {
public void onResult(Void response) {}
public void onError(
- androidx.credentials.exceptions.ClearCredentialException error) {}
+ android.credentials.ClearCredentialStateException error) {}
};
// Call the service.
assertThat(service.getLastClearRequest()).isNull();
- service.onClearCredentialStateRequest(request, new CancellationSignal(), outcome);
+ service.onClearCredentialState(request, new CancellationSignal(), outcome);
assertThat(service.getLastClearRequest()).isNotNull();
}
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
index 700c7bd..df32b01 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
@@ -21,17 +21,21 @@
import android.os.CancellationSignal
import android.os.OutcomeReceiver
import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
@SdkSuppress(minSdkVersion = 34)
-// @RunWith(AndroidJUnit4::class)
-// @SmallTest
+@RunWith(AndroidJUnit4::class)
+@SmallTest
class CredentialProviderServiceTest {
private val LOG_TAG = "CredentialProviderServiceTest"
- // @Test
+ @Test
fun test_createRequest() {
var service = CredentialProviderServiceTestImpl()
service.isTestMode = true
@@ -54,49 +58,53 @@
assertThat(service.lastCreateRequest).isNotNull()
}
- // @Test
+ @Test
fun test_getRequest() {
var service = CredentialProviderServiceTestImpl()
service.isTestMode = true
- var request = BeginGetCredentialRequest(listOf<BeginGetCredentialOption>())
- val outcome = OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,
- androidx.credentials.exceptions.GetCredentialException> {
- fun onResult(response: androidx.credentials.provider.BeginGetCredentialResponse) {
+ var option = android.service.credentials.BeginGetCredentialOption("id", "type", Bundle())
+ var request = android.service.credentials.BeginGetCredentialRequest.Builder()
+ .setBeginGetCredentialOptions(listOf(option)).build()
+ val outcome = OutcomeReceiver<
+ android.service.credentials.BeginGetCredentialResponse,
+ android.credentials.GetCredentialException> {
+ fun onResult(response: android.service.credentials.BeginGetCredentialResponse) {
Log.i(LOG_TAG, "get request: " + response.toString())
}
- fun onError(error: androidx.credentials.exceptions.GetCredentialException) {
+ fun onError(error: android.credentials.GetCredentialException) {
Log.e(LOG_TAG, "get request error", error)
}
}
// Call the service.
assertThat(service.lastGetRequest).isNull()
- service.onBeginGetCredentialRequest(request, CancellationSignal(), outcome)
+ service.onBeginGetCredential(request, CancellationSignal(), outcome)
assertThat(service.lastGetRequest).isNotNull()
}
- // @Test
+ @Test
fun test_clearRequest() {
var service = CredentialProviderServiceTestImpl()
service.isTestMode = true
- var request = ProviderClearCredentialStateRequest(CallingAppInfo("name", SigningInfo()))
- val outcome = OutcomeReceiver<Void?,
- androidx.credentials.exceptions.ClearCredentialException> {
- fun onResult(response: Void?) {
+ var request = android.service.credentials.ClearCredentialStateRequest(
+ android.service.credentials.CallingAppInfo("name", SigningInfo()), Bundle())
+ val outcome = OutcomeReceiver<Void,
+ android.credentials.ClearCredentialStateException> {
+ fun onResult(response: Void) {
Log.i(LOG_TAG, "clear request: " + response.toString())
}
- fun onError(error: androidx.credentials.exceptions.ClearCredentialException) {
+ fun onError(error: android.credentials.ClearCredentialStateException) {
Log.e(LOG_TAG, "clear request error", error)
}
}
// Call the service.
assertThat(service.lastClearRequest).isNull()
- service.onClearCredentialStateRequest(request, CancellationSignal(), outcome)
+ service.onClearCredentialState(request, CancellationSignal(), outcome)
assertThat(service.lastClearRequest).isNotNull()
}
}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
index c61120c..2843f46 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
@@ -241,14 +241,6 @@
val builder = android.credentials.GetCredentialRequest.Builder(
GetCredentialRequest.toRequestDataBundle(request))
request.credentialOptions.forEach {
- if (request.preferImmediatelyAvailableCredentials &&
- it is GetPublicKeyCredentialOption) {
- it.requestData.putBoolean(
- "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS",
- true,
- )
- }
-
builder.addCredentialOption(
android.credentials.CredentialOption.Builder(
it.type, it.requestData, it.candidateQueryData
diff --git a/development/referenceDocs/switcher.py b/development/referenceDocs/switcher.py
index 63195ae..562d433 100755
--- a/development/referenceDocs/switcher.py
+++ b/development/referenceDocs/switcher.py
@@ -81,14 +81,14 @@
stub = doc.replace(java_source_abs_path, kotlin_source_abs_path)
# Always add the switcher for java files, switch to the package summary if
# the page itself doesn't exist in kotlin
- slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_java_switcher2.md\" %}",doc)
+ slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_java_switcher2.md\" %}",doc)
else:
file_path = doc[len(kotlin_ref_root)+1:]
stub = doc.replace(kotlin_source_abs_path, java_source_abs_path)
if (both):
- slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
+ slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
else:
- slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
+ slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
os.system(slug1)
if both or java:
diff --git a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
index 35c63ca..ca2a19f 100644
--- a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
+++ b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
@@ -579,97 +579,36 @@
}
@Test
- @LargeTest
- @Ignore("b/290382533")
+ @SmallTest
public void testDoNotFailOnCorruptedImage() throws Throwable {
- // ExifInterface shouldn't raise any exceptions except an IOException when unable to open
- // a file, even with a corrupted image. Generates randomly corrupted image stream for
- // testing. Uses Epoch date count as random seed so that we can reproduce a broken test.
- long seed = System.currentTimeMillis() / (86400 * 1000);
- Log.d(TAG, "testDoNotFailOnCorruptedImage random seed: " + seed);
- Random random = new Random(seed);
+ Random random = new Random(/* seed= */ 0);
byte[] bytes = new byte[8096];
+ random.nextBytes(bytes);
+ // Overwrite the start of the random bytes with some JPEG-like data, so it starts like a
+ // plausible image with EXIF data.
ByteBuffer buffer = ByteBuffer.wrap(bytes);
- for (int i = 0; i < TEST_NUMBER_OF_CORRUPTED_IMAGE_STREAMS; i++) {
- buffer.clear();
- random.nextBytes(bytes);
- if (!randomlyCorrupted(random)) {
- buffer.put(ExifInterface.JPEG_SIGNATURE);
- }
- if (!randomlyCorrupted(random)) {
- buffer.put(ExifInterface.MARKER_APP1);
- }
- buffer.putShort((short) (random.nextInt(100) + 300));
- if (!randomlyCorrupted(random)) {
- buffer.put(ExifInterface.IDENTIFIER_EXIF_APP1);
- }
- if (!randomlyCorrupted(random)) {
- buffer.putShort(ExifInterface.BYTE_ALIGN_MM);
- }
- if (!randomlyCorrupted(random)) {
- buffer.put((byte) 0);
- buffer.put(ExifInterface.START_CODE);
- }
- buffer.putInt(8);
+ buffer.put(ExifInterface.JPEG_SIGNATURE);
+ buffer.put(ExifInterface.MARKER_APP1);
+ buffer.putShort((short) 350);
+ buffer.put(ExifInterface.IDENTIFIER_EXIF_APP1);
+ buffer.putShort(ExifInterface.BYTE_ALIGN_MM);
+ buffer.put((byte) 0);
+ buffer.put(ExifInterface.START_CODE);
+ buffer.putInt(8);
+ // Number of primary tag directories
+ buffer.putShort((short) 1);
+ // Corruption starts here
- // Primary Tags
- int numberOfDirectory = random.nextInt(8) + 1;
- if (!randomlyCorrupted(random)) {
- buffer.putShort((short) numberOfDirectory);
- }
- for (int j = 0; j < numberOfDirectory; j++) {
- generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_PRIMARY, random);
- }
- if (!randomlyCorrupted(random)) {
- buffer.putInt(buffer.position() - 8);
- }
-
- // Thumbnail Tags
- numberOfDirectory = random.nextInt(8) + 1;
- if (!randomlyCorrupted(random)) {
- buffer.putShort((short) numberOfDirectory);
- }
- for (int j = 0; j < numberOfDirectory; j++) {
- generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_THUMBNAIL, random);
- }
- if (!randomlyCorrupted(random)) {
- buffer.putInt(buffer.position() - 8);
- }
-
- // Preview Tags
- numberOfDirectory = random.nextInt(8) + 1;
- if (!randomlyCorrupted(random)) {
- buffer.putShort((short) numberOfDirectory);
- }
- for (int j = 0; j < numberOfDirectory; j++) {
- generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_PREVIEW, random);
- }
- if (!randomlyCorrupted(random)) {
- buffer.putInt(buffer.position() - 8);
- }
-
- if (!randomlyCorrupted(random)) {
- buffer.put(ExifInterface.MARKER);
- }
- if (!randomlyCorrupted(random)) {
- buffer.put(ExifInterface.MARKER_EOI);
- }
-
- try {
- new ExifInterface(new ByteArrayInputStream(bytes));
- // Always success
- } catch (IOException e) {
- fail("Should not reach here!");
- }
- }
+ ExifInterface exifInterface = new ExifInterface(new ByteArrayInputStream(bytes));
+ exifInterface.getAttribute(ExifInterface.TAG_ARTIST);
+ // Test will fail if the ExifInterface constructor or getter throw an exception.
}
@Test
@SmallTest
- @Ignore("b/290382533")
public void testSetGpsInfo() throws IOException {
final String provider = "ExifInterfaceTest";
- final long timestamp = System.currentTimeMillis();
+ final long timestamp = 1689328448000L; // 2023-07-14T09:54:32.000Z
final float speedInMeterPerSec = 36.627533f;
Location location = new Location(provider);
location.setLatitude(TEST_LATITUDE_VALID_VALUES[TEST_LATITUDE_VALID_VALUES.length - 1]);
@@ -761,7 +700,6 @@
*/
@Test
@SmallTest
- @Ignore("b/290382533")
public void testGetSetDateTime() throws IOException {
final long expectedGetDatetimeValue =
1454027547000L /* TAG_DATETIME value ("2016:01:29 18:32:27") converted to msec */
@@ -788,12 +726,12 @@
exif.getAttribute(ExifInterface.TAG_OFFSET_TIME_DIGITIZED));
// Test setting datetime values
- final long currentTimeStamp = System.currentTimeMillis();
+ final long newTimestamp = 1689328448000L; // 2023-07-14T09:54:32.000Z
final long expectedDatetimeOffsetLongValue = 32400000L;
- exif.setDateTime(currentTimeStamp);
+ exif.setDateTime(newTimestamp);
exif.saveAttributes();
exif = new ExifInterface(imageFile.getAbsolutePath());
- assertEquals(currentTimeStamp - expectedDatetimeOffsetLongValue, (long) exif.getDateTime());
+ assertEquals(newTimestamp - expectedDatetimeOffsetLongValue, (long) exif.getDateTime());
// Test that setting null throws NPE
try {
@@ -1433,31 +1371,6 @@
assertEquals(expectedValue.thumbnailHeight, thumbnailBitmap.getHeight());
}
- private void generateRandomExifTag(ByteBuffer buffer, int ifdType, Random random) {
- ExifInterface.ExifTag[] tagGroup = ExifInterface.EXIF_TAGS[ifdType];
- ExifInterface.ExifTag tag = tagGroup[random.nextInt(tagGroup.length)];
- if (!randomlyCorrupted(random)) {
- buffer.putShort((short) tag.number);
- }
- int dataFormat = random.nextInt(ExifInterface.IFD_FORMAT_NAMES.length);
- if (!randomlyCorrupted(random)) {
- buffer.putShort((short) dataFormat);
- }
- buffer.putInt(1);
- int dataLength = ExifInterface.IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
- if (dataLength > 4) {
- buffer.putShort((short) random.nextInt(8096 - dataLength));
- buffer.position(buffer.position() + 2);
- } else {
- buffer.position(buffer.position() + 4);
- }
- }
-
- private boolean randomlyCorrupted(Random random) {
- // Corrupts somewhere in a possibility of 1/500.
- return random.nextInt(500) == 0;
- }
-
private void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index 4b59ee3..a8e7d1c 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -117,7 +117,6 @@
LayoutGeneratorTask.registerLayoutGenerator(
project,
- android,
/* containerLayoutDirectory= */ file("src/main/layoutTemplates"),
/* childLayoutDirectory= */ file("src/main/res/layout")
)
diff --git a/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt b/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
index 75f6129..a54cc8a 100644
--- a/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
+++ b/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
@@ -20,7 +20,7 @@
import androidx.glance.appwidget.layoutgenerator.LayoutGenerator
import androidx.glance.appwidget.layoutgenerator.cleanResources
import androidx.glance.appwidget.layoutgenerator.generateRegistry
-import com.android.build.gradle.LibraryExtension
+import com.android.build.api.variant.AndroidComponentsExtension
import java.io.File
import org.gradle.api.DefaultTask
import org.gradle.api.Project
@@ -57,6 +57,10 @@
@TaskAction
fun execute() {
+
+ outputSourceDir.asFile.get().mkdirs()
+ outputResourcesDir.asFile.get().mkdirs()
+
val generatedLayouts = LayoutGenerator().generateAllFiles(
checkNotNull(containerLayoutDirectory.get().asFile.listFiles()).asList(),
checkNotNull(childLayoutDirectory.get().asFile.listFiles()).asList(),
@@ -70,54 +74,51 @@
outputSourceDir = outputSourceDir.get().asFile
)
cleanResources(
- outputResourcesDir.get().asFile,
- generatedLayouts.extractGeneratedFiles()
+ outputResourcesDir.get().asFile, generatedLayouts.extractGeneratedFiles()
)
}
private fun GeneratedFiles.extractGeneratedFiles(): Set<File> =
generatedContainers.values.flatMap { container ->
container.map { it.generatedFile }
- }.toSet() +
- generatedBoxChildren.values.flatMap { child ->
+ }.toSet() + generatedBoxChildren.values.flatMap { child ->
child.map { it.generatedFile }
- }.toSet() +
- generatedRowColumnChildren.values.flatMap { child ->
+ }.toSet() + generatedRowColumnChildren.values.flatMap { child ->
child.map { it.generatedFile }
- }.toSet() +
- extraFiles
+ }.toSet() + extraFiles
companion object {
/**
- * Registers [LayoutGeneratorTask] in [project] for all variants in [libraryExtension].
+ * Registers [LayoutGeneratorTask] in [project] for all variants.
*/
@JvmStatic
fun registerLayoutGenerator(
project: Project,
- libraryExtension: LibraryExtension,
containerLayoutDirectory: File,
childLayoutDirectory: File,
) {
- libraryExtension.libraryVariants.all { variant ->
- val variantName = variant.name
- val outputDirectory = project.buildDir.resolve("generatedLayouts/$variantName")
- val outputResourcesDir = outputDirectory.resolve("res/layouts")
- val outputSourceDir = outputDirectory.resolve("kotlin")
- val taskName =
- "generateLayouts" + variantName.replaceFirstChar { it.uppercaseChar() }
- outputResourcesDir.mkdirs()
- outputSourceDir.mkdirs()
- val task = project.tasks.register(taskName, LayoutGeneratorTask::class.java) {
- it.containerLayoutDirectory.set(containerLayoutDirectory)
- it.childLayoutDirectory.set(childLayoutDirectory)
- it.outputResourcesDir.set(outputResourcesDir)
- it.outputSourceDir.set(outputSourceDir)
- }
- variant.registerGeneratedResFolders(
- project.files(outputResourcesDir).builtBy(task)
- )
- variant.registerJavaGeneratingTask(task, outputSourceDir)
+
+ val outputDirectory = "generatedLayouts"
+ val buildDirectory = project.layout.buildDirectory
+
+ val taskName = "generateLayouts"
+
+ val task = project.tasks.register(taskName, LayoutGeneratorTask::class.java) {
+ it.containerLayoutDirectory.set(containerLayoutDirectory)
+ it.childLayoutDirectory.set(childLayoutDirectory)
+ it.outputSourceDir.set(buildDirectory.dir("$outputDirectory/kotlin"))
+ it.outputResourcesDir.set(buildDirectory.dir("$outputDirectory/res/layouts"))
}
+
+ project.extensions.getByType(AndroidComponentsExtension::class.java)
+ .onVariants { variant ->
+ variant.sources.java?.addGeneratedSourceDirectory(
+ task, LayoutGeneratorTask::outputSourceDir
+ )
+ variant.sources.res?.addGeneratedSourceDirectory(
+ task, LayoutGeneratorTask::outputResourcesDir
+ )
+ }
}
}
}
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
index 0ec5659..f9a902b 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
@@ -19,6 +19,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
import androidx.glance.ExperimentalGlanceApi
import androidx.glance.GlanceModifier
import androidx.glance.GlanceNode
@@ -229,12 +230,8 @@
internal class EmittableCheckBox(
var colors: CheckBoxColors
-) : Emittable {
+) : EmittableCheckable() {
override var modifier: GlanceModifier = GlanceModifier
- var checked: Boolean = false
- var text: String = ""
- var style: TextStyle? = null
- var maxLines: Int = Int.MAX_VALUE
override fun copy(): Emittable = EmittableCheckBox(colors = colors).also {
it.modifier = modifier
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
index 63e99e3..aca8172 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
@@ -19,6 +19,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
import androidx.glance.ExperimentalGlanceApi
import androidx.glance.GlanceModifier
import androidx.glance.GlanceNode
@@ -39,13 +40,9 @@
internal class EmittableRadioButton(
var colors: RadioButtonColors
-) : Emittable {
+) : EmittableCheckable() {
override var modifier: GlanceModifier = GlanceModifier
- var checked: Boolean = false
var enabled: Boolean = true
- var text: String = ""
- var style: TextStyle? = null
- var maxLines: Int = Int.MAX_VALUE
override fun copy(): Emittable = EmittableRadioButton(colors = colors).also {
it.modifier = modifier
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
index 2eb8a9f..dab1746 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
@@ -18,6 +18,7 @@
import androidx.compose.runtime.Composable
import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
import androidx.glance.ExperimentalGlanceApi
import androidx.glance.GlanceModifier
import androidx.glance.GlanceNode
@@ -226,12 +227,8 @@
internal class EmittableSwitch(
var colors: SwitchColors
-) : Emittable {
+) : EmittableCheckable() {
override var modifier: GlanceModifier = GlanceModifier
- var checked: Boolean = false
- var text: String = ""
- var style: TextStyle? = null
- var maxLines: Int = Int.MAX_VALUE
override fun copy(): Emittable = EmittableSwitch(colors = colors).also {
it.modifier = modifier
diff --git a/glance/glance/src/main/java/androidx/glance/Emittables.kt b/glance/glance/src/main/java/androidx/glance/Emittables.kt
index 52a538a..5b03dbf 100644
--- a/glance/glance/src/main/java/androidx/glance/Emittables.kt
+++ b/glance/glance/src/main/java/androidx/glance/Emittables.kt
@@ -18,6 +18,7 @@
import androidx.annotation.RestrictTo
import androidx.glance.layout.Alignment
+import androidx.glance.text.TextStyle
/** @suppress */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -43,3 +44,17 @@
abstract class EmittableLazyItemWithChildren : EmittableWithChildren() {
var alignment: Alignment = Alignment.CenterStart
}
+
+/** @suppress */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+abstract class EmittableWithText : Emittable {
+ var text: String = ""
+ var style: TextStyle? = null
+ var maxLines: Int = Int.MAX_VALUE
+}
+
+/** @suppress */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+abstract class EmittableCheckable : EmittableWithText() {
+ var checked: Boolean = false
+}
diff --git a/glance/glance/src/main/java/androidx/glance/text/Text.kt b/glance/glance/src/main/java/androidx/glance/text/Text.kt
index ace0cfb..2f9974a 100644
--- a/glance/glance/src/main/java/androidx/glance/text/Text.kt
+++ b/glance/glance/src/main/java/androidx/glance/text/Text.kt
@@ -1,4 +1,3 @@
-
/*
* Copyright 2021 The Android Open Source Project
*
@@ -21,6 +20,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.glance.Emittable
+import androidx.glance.EmittableWithText
import androidx.glance.GlanceModifier
import androidx.glance.GlanceNode
import androidx.glance.text.TextDefaults.defaultTextStyle
@@ -60,11 +60,8 @@
/** @suppress */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class EmittableText : Emittable {
+class EmittableText : EmittableWithText() {
override var modifier: GlanceModifier = GlanceModifier
- var text: String = ""
- var style: TextStyle? = null
- var maxLines: Int = Int.MAX_VALUE
override fun copy(): Emittable = EmittableText().also {
it.modifier = modifier
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d8009d4..a4025c9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -104,7 +104,7 @@
checkerframework = { module = "org.checkerframework:checker-qual", version = "2.5.3" }
checkmark = { module = "net.saff.checkmark:checkmark", version = "0.1.6" }
constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.0.1"}
-dackka = { module = "com.google.devsite:dackka", version = "1.3.4" }
+dackka = { module = "com.google.devsite:dackka", version = "1.3.5" }
dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" }
daggerCompiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
dexmakerMockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker" }
diff --git a/health/connect/connect-client/api/current.txt b/health/connect/connect-client/api/current.txt
index 9332a76..e82f8fb 100644
--- a/health/connect/connect-client/api/current.txt
+++ b/health/connect/connect-client/api/current.txt
@@ -109,10 +109,10 @@
package androidx.health.connect.client.contracts {
- public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute.Data> {
+ public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute> {
ctor public ExerciseRouteRequestContract();
method public android.content.Intent createIntent(android.content.Context context, String input);
- method public androidx.health.connect.client.records.ExerciseRoute.Data? parseResult(int resultCode, android.content.Intent? intent);
+ method public androidx.health.connect.client.records.ExerciseRoute? parseResult(int resultCode, android.content.Intent? intent);
}
public final class HealthPermissionsRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends java.lang.String>,java.util.Set<? extends java.lang.String>> {
@@ -442,15 +442,8 @@
property public final java.time.Instant startTime;
}
- public abstract class ExerciseRoute {
- }
-
- public static final class ExerciseRoute.ConsentRequired extends androidx.health.connect.client.records.ExerciseRoute {
- ctor public ExerciseRoute.ConsentRequired();
- }
-
- public static final class ExerciseRoute.Data extends androidx.health.connect.client.records.ExerciseRoute {
- ctor public ExerciseRoute.Data(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
+ public final class ExerciseRoute {
+ ctor public ExerciseRoute(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
method public java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> getRoute();
property public final java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route;
}
@@ -475,8 +468,21 @@
public static final class ExerciseRoute.Location.Companion {
}
- public static final class ExerciseRoute.NoData extends androidx.health.connect.client.records.ExerciseRoute {
- ctor public ExerciseRoute.NoData();
+ public abstract class ExerciseRouteResult {
+ }
+
+ public static final class ExerciseRouteResult.ConsentRequired extends androidx.health.connect.client.records.ExerciseRouteResult {
+ ctor public ExerciseRouteResult.ConsentRequired();
+ }
+
+ public static final class ExerciseRouteResult.Data extends androidx.health.connect.client.records.ExerciseRouteResult {
+ ctor public ExerciseRouteResult.Data(androidx.health.connect.client.records.ExerciseRoute exerciseRoute);
+ method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+ property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+ }
+
+ public static final class ExerciseRouteResult.NoData extends androidx.health.connect.client.records.ExerciseRouteResult {
+ ctor public ExerciseRouteResult.NoData();
}
public final class ExerciseSegment {
@@ -570,10 +576,10 @@
ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata);
ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments);
ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps);
- ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute.Data? exerciseRouteData);
+ ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute? exerciseRoute);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
- method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+ method public androidx.health.connect.client.records.ExerciseRouteResult getExerciseRouteResult();
method public int getExerciseType();
method public java.util.List<androidx.health.connect.client.records.ExerciseLap> getLaps();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
@@ -584,7 +590,7 @@
method public String? getTitle();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
- property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+ property public final androidx.health.connect.client.records.ExerciseRouteResult exerciseRouteResult;
property public final int exerciseType;
property public final java.util.List<androidx.health.connect.client.records.ExerciseLap> laps;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
diff --git a/health/connect/connect-client/api/restricted_current.txt b/health/connect/connect-client/api/restricted_current.txt
index 41f97b3..582c44b 100644
--- a/health/connect/connect-client/api/restricted_current.txt
+++ b/health/connect/connect-client/api/restricted_current.txt
@@ -109,10 +109,10 @@
package androidx.health.connect.client.contracts {
- public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute.Data> {
+ public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute> {
ctor public ExerciseRouteRequestContract();
method public android.content.Intent createIntent(android.content.Context context, String input);
- method public androidx.health.connect.client.records.ExerciseRoute.Data? parseResult(int resultCode, android.content.Intent? intent);
+ method public androidx.health.connect.client.records.ExerciseRoute? parseResult(int resultCode, android.content.Intent? intent);
}
public final class HealthPermissionsRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends java.lang.String>,java.util.Set<? extends java.lang.String>> {
@@ -442,15 +442,8 @@
property public final java.time.Instant startTime;
}
- public abstract class ExerciseRoute {
- }
-
- public static final class ExerciseRoute.ConsentRequired extends androidx.health.connect.client.records.ExerciseRoute {
- ctor public ExerciseRoute.ConsentRequired();
- }
-
- public static final class ExerciseRoute.Data extends androidx.health.connect.client.records.ExerciseRoute {
- ctor public ExerciseRoute.Data(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
+ public final class ExerciseRoute {
+ ctor public ExerciseRoute(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
method public java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> getRoute();
property public final java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route;
}
@@ -475,8 +468,21 @@
public static final class ExerciseRoute.Location.Companion {
}
- public static final class ExerciseRoute.NoData extends androidx.health.connect.client.records.ExerciseRoute {
- ctor public ExerciseRoute.NoData();
+ public abstract class ExerciseRouteResult {
+ }
+
+ public static final class ExerciseRouteResult.ConsentRequired extends androidx.health.connect.client.records.ExerciseRouteResult {
+ ctor public ExerciseRouteResult.ConsentRequired();
+ }
+
+ public static final class ExerciseRouteResult.Data extends androidx.health.connect.client.records.ExerciseRouteResult {
+ ctor public ExerciseRouteResult.Data(androidx.health.connect.client.records.ExerciseRoute exerciseRoute);
+ method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+ property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+ }
+
+ public static final class ExerciseRouteResult.NoData extends androidx.health.connect.client.records.ExerciseRouteResult {
+ ctor public ExerciseRouteResult.NoData();
}
public final class ExerciseSegment {
@@ -570,10 +576,10 @@
ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata);
ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments);
ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps);
- ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute.Data? exerciseRouteData);
+ ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute? exerciseRoute);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
- method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+ method public androidx.health.connect.client.records.ExerciseRouteResult getExerciseRouteResult();
method public int getExerciseType();
method public java.util.List<androidx.health.connect.client.records.ExerciseLap> getLaps();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
@@ -584,7 +590,7 @@
method public String? getTitle();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
- property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+ property public final androidx.health.connect.client.records.ExerciseRouteResult exerciseRouteResult;
property public final int exerciseType;
property public final java.util.List<androidx.health.connect.client.records.ExerciseLap> laps;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
diff --git a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
index 4361146..f22f57d 100644
--- a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
+++ b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
@@ -113,9 +113,9 @@
val latitudeDeltaPerSecond = (endLatitude - startLatitude) / sessionDuration.seconds
val longitudeDeltaPerSecond = (endLongitude - startLongitude) / sessionDuration.seconds
- val exerciseRouteData =
+ val exerciseRoute =
if (grantedPermissions.contains(PERMISSION_WRITE_EXERCISE_ROUTE)) {
- ExerciseRoute.Data(
+ ExerciseRoute(
List(sessionDuration.seconds.toInt()) { timeSeconds ->
ExerciseRoute.Location(
time = sessionStartTime.plusSeconds(timeSeconds.toLong()),
@@ -140,7 +140,7 @@
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
title = "Morning Run",
notes = "A nice run in a park",
- exerciseRouteData = exerciseRouteData
+ exerciseRoute = exerciseRoute
)
healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
diff --git a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
index 8f5cca7..578fd0e 100644
--- a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
+++ b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
@@ -23,6 +23,7 @@
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.contracts.ExerciseRouteRequestContract
import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.SleepSessionRecord
@@ -86,14 +87,14 @@
suspend fun ReadExerciseRoute(
activityResultCaller: ActivityResultCaller,
healthConnectClient: HealthConnectClient,
- displayExerciseRoute: (ExerciseRoute.Data) -> Unit,
+ displayExerciseRoute: (ExerciseRoute) -> Unit,
recordId: String
) {
// See https://developer.android.com/training/basics/intents/result#launch for appropriately
// handling ActivityResultContract.
val requestExerciseRoute =
activityResultCaller.registerForActivityResult(ExerciseRouteRequestContract()) {
- exerciseRoute: ExerciseRoute.Data? ->
+ exerciseRoute: ExerciseRoute? ->
if (exerciseRoute != null) {
displayExerciseRoute(exerciseRoute)
} else {
@@ -105,10 +106,10 @@
val exerciseSessionRecord =
healthConnectClient.readRecord(ExerciseSessionRecord::class, recordId).record
- when (val exerciseRoute = exerciseSessionRecord.exerciseRoute) {
- is ExerciseRoute.Data -> displayExerciseRoute(exerciseRoute)
- is ExerciseRoute.ConsentRequired -> requestExerciseRoute.launch(recordId)
- is ExerciseRoute.NoData -> Unit // No exercise route to show
+ when (val exerciseRouteResult = exerciseSessionRecord.exerciseRouteResult) {
+ is ExerciseRouteResult.Data -> displayExerciseRoute(exerciseRouteResult.exerciseRoute)
+ is ExerciseRouteResult.ConsentRequired -> requestExerciseRoute.launch(recordId)
+ is ExerciseRouteResult.NoData -> Unit // No exercise route to show
else -> Unit
}
}
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
index 23160ad..ea4548d 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
@@ -80,7 +80,7 @@
val intent = Intent()
intent.putExtra(HealthConnectManager.EXTRA_EXERCISE_ROUTE, PlatformExerciseRoute(listOf()))
val result = requestRouteContract.parseResult(0, intent)
- assertThat(result).isEqualTo(ExerciseRoute.Data(listOf()))
+ assertThat(result).isEqualTo(ExerciseRoute(listOf()))
}
@Test
@@ -103,7 +103,7 @@
val result = requestRouteContract.parseResult(0, intent)
assertThat(result)
.isEqualTo(
- ExerciseRoute.Data(
+ ExerciseRoute(
listOf(
ExerciseRoute.Location(
time = Instant.ofEpochMilli(1234L),
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
index d0cf773..953b19b 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
@@ -35,6 +35,7 @@
import androidx.health.connect.client.records.ElevationGainedRecord
import androidx.health.connect.client.records.ExerciseLap
import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
import androidx.health.connect.client.records.ExerciseSegment
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.FloorsClimbedRecord
@@ -408,7 +409,7 @@
)
),
exerciseRoute =
- ExerciseRoute.Data(
+ ExerciseRoute(
listOf(
ExerciseRoute.Location(
START_TIME,
@@ -1261,17 +1262,19 @@
10
)
)
- assertThat(exerciseRoute as ExerciseRoute.Data)
+ assertThat(exerciseRouteResult as ExerciseRouteResult.Data)
.isEqualTo(
- ExerciseRoute.Data(
- listOf(
- ExerciseRoute.Location(
- time = START_TIME,
- latitude = 23.4,
- longitude = -23.4,
- altitude = Length.meters(10.0),
- horizontalAccuracy = Length.meters(2.0),
- verticalAccuracy = Length.meters(3.0)
+ ExerciseRouteResult.Data(
+ ExerciseRoute(
+ listOf(
+ ExerciseRoute.Location(
+ time = START_TIME,
+ latitude = 23.4,
+ longitude = -23.4,
+ altitude = Length.meters(10.0),
+ horizontalAccuracy = Length.meters(2.0),
+ verticalAccuracy = Length.meters(3.0)
+ )
)
)
)
@@ -1283,7 +1286,7 @@
as ExerciseSessionRecord
assertSdkRecord(sdkExerciseSession) {
- assertThat(exerciseRoute).isEqualTo(ExerciseRoute.NoData())
+ assertThat(exerciseRouteResult).isEqualTo(ExerciseRouteResult.NoData())
}
}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
index 6e8e74b..0e1e78f 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
@@ -32,9 +32,9 @@
*
* @sample androidx.health.connect.client.samples.ReadExerciseRoute
*/
-class ExerciseRouteRequestContract : ActivityResultContract<String, ExerciseRoute.Data?>() {
+class ExerciseRouteRequestContract : ActivityResultContract<String, ExerciseRoute?>() {
- private val delegate: ActivityResultContract<String, ExerciseRoute.Data?> =
+ private val delegate: ActivityResultContract<String, ExerciseRoute?> =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
RequestExerciseRouteUpsideDownCake()
} else {
@@ -42,7 +42,7 @@
}
/**
- * Creates an intent to request an [ExerciseRoute.Data]. It receives the exercise session id as
+ * Creates an intent to request an [ExerciseRoute]. It receives the exercise session id as
* [input].
*
* @param context the context
@@ -56,13 +56,13 @@
}
/**
- * Converts the activity result into [ExerciseRoute.Data], to return as output.
+ * Converts the activity result into [ExerciseRoute], to return as output.
*
* @return null if the user didn't grant access to the exercise route or if there's no exercise
* route for the session id passed on [createIntent].
* @see ActivityResultContract.parseResult
*/
- override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+ override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
return delegate.parseResult(resultCode, intent)
}
}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
index dc60de8..a7f7929 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
@@ -35,6 +35,7 @@
import androidx.health.connect.client.records.DistanceRecord
import androidx.health.connect.client.records.ElevationGainedRecord
import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.FloorsClimbedRecord
import androidx.health.connect.client.records.HeartRateRecord
@@ -421,13 +422,13 @@
metadata = metadata,
segments = subTypeDataListsMap["segments"]?.toSegmentList() ?: emptyList(),
laps = subTypeDataListsMap["laps"]?.toLapList() ?: emptyList(),
- exerciseRoute =
+ exerciseRouteResult =
subTypeDataListsMap["route"]?.let {
- ExerciseRoute.Data(route = it.toLocationList())
+ ExerciseRouteResult.Data(ExerciseRoute(route = it.toLocationList()))
}
?: if (valuesMap["hasRoute"]?.booleanVal == true)
- ExerciseRoute.ConsentRequired()
- else ExerciseRoute.NoData(),
+ ExerciseRouteResult.ConsentRequired()
+ else ExerciseRouteResult.NoData(),
)
}
"Distance" ->
@@ -587,8 +588,8 @@
fun toExerciseRouteData(
protoWrapper: androidx.health.platform.client.exerciseroute.ExerciseRoute
-): ExerciseRoute.Data {
- return ExerciseRoute.Data(
+): ExerciseRoute {
+ return ExerciseRoute(
protoWrapper.proto.valuesList.map { value ->
ExerciseRoute.Location(
time = Instant.ofEpochMilli(value.startTimeMillis),
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
index f2fa0f4..168a416 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
@@ -119,17 +119,17 @@
startTime = Instant.ofEpochMilli(it.startTimeMillis),
endTime = Instant.ofEpochMilli(it.endTimeMillis),
stage = STAGE_TYPE_STRING_TO_INT_MAP[it.valuesMap["stage"]?.enumVal]
- ?: SleepSessionRecord.STAGE_TYPE_UNKNOWN
+ ?: SleepSessionRecord.STAGE_TYPE_UNKNOWN
)
}
}
+
internal fun DataProto.DataPoint.SubTypeDataList.toSegmentList(): List<ExerciseSegment> {
return valuesList.map {
ExerciseSegment(
startTime = Instant.ofEpochMilli(it.startTimeMillis),
endTime = Instant.ofEpochMilli(it.endTimeMillis),
- segmentType = (it.valuesMap["type"]?.longVal
- ?: EXERCISE_SEGMENT_TYPE_UNKNOWN).toInt(),
+ segmentType = (it.valuesMap["type"]?.longVal ?: EXERCISE_SEGMENT_TYPE_UNKNOWN).toInt(),
repetitions = it.valuesMap["reps"]?.longVal?.toInt() ?: 0
)
}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
index e9e29d8..34127fc 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
@@ -34,7 +34,7 @@
import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
import androidx.health.connect.client.records.DistanceRecord
import androidx.health.connect.client.records.ElevationGainedRecord
-import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.FloorsClimbedRecord
import androidx.health.connect.client.records.HeartRateRecord
@@ -289,7 +289,7 @@
is ExerciseSessionRecord ->
intervalProto()
.setDataType(protoDataType("ActivitySession"))
- .putValues("hasRoute", boolVal(exerciseRoute !is ExerciseRoute.NoData))
+ .putValues("hasRoute", boolVal(exerciseRouteResult !is ExerciseRouteResult.NoData))
.apply {
val exerciseType =
enumValFromInt(
@@ -316,11 +316,13 @@
.build()
)
}
- if (exerciseRoute is ExerciseRoute.Data) {
+ if (exerciseRouteResult is ExerciseRouteResult.Data) {
putSubTypeDataLists(
"route",
DataProto.DataPoint.SubTypeDataList.newBuilder()
- .addAllValues(exerciseRoute.route.map { it.toProto() })
+ .addAllValues(
+ exerciseRouteResult.exerciseRoute.route.map { it.toProto() }
+ )
.build()
)
}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
index 8c1cbe2..3e5fa01 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
@@ -100,8 +100,10 @@
enumValFromInt(stage, SleepSessionRecord.STAGE_TYPE_INT_TO_STRING_MAP)?.let {
putValues("stage", it)
}
- }.build()
+ }
+ .build()
}
+
internal fun ExerciseSegment.toProto(): DataProto.SubTypeDataValue {
return DataProto.SubTypeDataValue.newBuilder()
.setStartTimeMillis(startTime.toEpochMilli())
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
index 4da86be..ad43fd6 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
@@ -36,6 +36,7 @@
import androidx.health.connect.client.records.ElevationGainedRecord
import androidx.health.connect.client.records.ExerciseLap
import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
import androidx.health.connect.client.records.ExerciseSegment
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.FloorsClimbedRecord
@@ -293,8 +294,9 @@
laps = laps.map { it.toSdkExerciseLap() }.sortedBy { it.startTime },
segments = segments.map { it.toSdkExerciseSegment() }.sortedBy { it.startTime },
metadata = metadata.toSdkMetadata(),
- exerciseRoute = route?.toSdkExerciseRouteData()
- ?: if (hasRoute()) ExerciseRoute.ConsentRequired() else ExerciseRoute.NoData(),
+ exerciseRouteResult = route?.let { ExerciseRouteResult.Data(it.toSdkExerciseRoute()) }
+ ?: if (hasRoute()) ExerciseRouteResult.ConsentRequired()
+ else ExerciseRouteResult.NoData(),
)
private fun PlatformFloorsClimbedRecord.toSdkFloorsClimbedRecord() =
@@ -708,8 +710,8 @@
title?.let { setTitle(it) }
setLaps(laps.map { it.toPlatformExerciseLap() })
setSegments(segments.map { it.toPlatformExerciseSegment() })
- if (exerciseRoute is ExerciseRoute.Data) {
- setRoute(exerciseRoute.toPlatformExerciseRoute())
+ if (exerciseRouteResult is ExerciseRouteResult.Data) {
+ setRoute(exerciseRouteResult.exerciseRoute.toPlatformExerciseRoute())
}
}
.build()
@@ -719,7 +721,7 @@
.apply { length?.let { setLength(it.toPlatformLength()) } }
.build()
-private fun ExerciseRoute.Data.toPlatformExerciseRoute() =
+private fun ExerciseRoute.toPlatformExerciseRoute() =
PlatformExerciseRoute(
route.map { location ->
PlatformExerciseRouteLocationBuilder(
@@ -1033,8 +1035,8 @@
private fun PlatformSleepSessionStage.toSdkSleepSessionStage() =
SleepSessionRecord.Stage(startTime, endTime, type.toSdkSleepStageType())
-internal fun PlatformExerciseRoute.toSdkExerciseRouteData() =
- ExerciseRoute.Data(
+internal fun PlatformExerciseRoute.toSdkExerciseRoute() =
+ ExerciseRoute(
routeLocations.map { value ->
ExerciseRoute.Location(
time = value.time,
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
index 1fdba9c..b96640c 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
@@ -32,8 +32,7 @@
* @see androidx.activity.ComponentActivity.registerForActivityResult
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
-internal class RequestExerciseRouteInternal :
- ActivityResultContract<String, ExerciseRoute.Data?>() {
+internal class RequestExerciseRouteInternal : ActivityResultContract<String, ExerciseRoute?>() {
override fun createIntent(context: Context, input: String): Intent {
require(input.isNotEmpty()) { "Session identifier can't be empty" }
return Intent(HealthDataServiceConstants.ACTION_REQUEST_ROUTE).apply {
@@ -42,7 +41,7 @@
}
@Suppress("DEPRECATION") // getParcelableExtra
- override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+ override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
val route =
intent?.getParcelableExtra<androidx.health.platform.client.exerciseroute.ExerciseRoute>(
HealthDataServiceConstants.EXTRA_EXERCISE_ROUTE
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
index f1a435a..c5ec226 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
@@ -24,7 +24,7 @@
import androidx.annotation.RestrictTo
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.impl.platform.records.PlatformExerciseRoute
-import androidx.health.connect.client.impl.platform.records.toSdkExerciseRouteData
+import androidx.health.connect.client.impl.platform.records.toSdkExerciseRoute
import androidx.health.connect.client.records.ExerciseRoute
import androidx.health.platform.client.impl.logger.Logger
@@ -36,7 +36,7 @@
@RequiresApi(34)
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal class RequestExerciseRouteUpsideDownCake :
- ActivityResultContract<String, ExerciseRoute.Data?>() {
+ ActivityResultContract<String, ExerciseRoute?>() {
override fun createIntent(context: Context, input: String): Intent {
require(input.isNotEmpty()) { "Session identifier can't be empty" }
return Intent(HealthConnectManager.ACTION_REQUEST_EXERCISE_ROUTE).apply {
@@ -44,7 +44,7 @@
}
}
- override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+ override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
val route =
intent?.getParcelableExtra(
HealthConnectManager.EXTRA_EXERCISE_ROUTE,
@@ -55,6 +55,6 @@
return null
}
Logger.debug(HealthConnectClient.HEALTH_CONNECT_CLIENT_TAG, "Returned a route.")
- return route.toSdkExerciseRouteData()
+ return route.toSdkExerciseRoute()
}
}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
index 6f36983..679eddb 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
@@ -19,63 +19,37 @@
import androidx.health.connect.client.units.Length
import java.time.Instant
-/** Captures a route associated with an exercise session a user does. */
-abstract class ExerciseRoute internal constructor() {
-
- /**
- * Class containing data of an exercise route.
- *
- * Contains a sequence of location points, with timestamps, which do not have to be in order.
- *
- * Location points contain a timestamp, longitude, latitude, and optionally altitude, horizontal
- * and vertical accuracy.
- */
- class Data constructor(val route: List<Location>) : ExerciseRoute() {
- init {
- val sortedRoute: List<Location> = route.sortedBy { it.time }
- for (i in 0 until sortedRoute.lastIndex) {
- require(sortedRoute[i].time.isBefore(sortedRoute[i + 1].time))
- }
- }
-
- internal fun isWithin(startTime: Instant, endTime: Instant): Boolean {
- val minTime = route.minBy { it.time }.time
- val maxTime = route.maxBy { it.time }.time
- return !minTime.isBefore(startTime) && maxTime.isBefore(endTime)
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is Data) return false
-
- return route == other.route
- }
-
- override fun hashCode(): Int {
- return route.hashCode()
+/**
+ * Captures a route associated with an exercise session a user does.
+ *
+ * Contains a sequence of location points, with timestamps, which do not have to be in order.
+ *
+ * Location points contain a timestamp, longitude, latitude, and optionally altitude, horizontal and
+ * vertical accuracy.
+ */
+class ExerciseRoute constructor(val route: List<Location>) {
+ init {
+ val sortedRoute: List<Location> = route.sortedBy { it.time }
+ for (i in 0 until sortedRoute.lastIndex) {
+ require(sortedRoute[i].time.isBefore(sortedRoute[i + 1].time))
}
}
- /** Class indicating that a permission hasn't been granted and a value couldn't be returned. */
- class ConsentRequired : ExerciseRoute() {
- override fun equals(other: Any?): Boolean {
- return other is ConsentRequired
- }
-
- override fun hashCode(): Int {
- return 0
- }
+ internal fun isWithin(startTime: Instant, endTime: Instant): Boolean {
+ val minTime = route.minBy { it.time }.time
+ val maxTime = route.maxBy { it.time }.time
+ return !minTime.isBefore(startTime) && maxTime.isBefore(endTime)
}
- /** Class indicating that there's no data to request permissions for. */
- class NoData : ExerciseRoute() {
- override fun equals(other: Any?): Boolean {
- return other is NoData
- }
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is ExerciseRoute) return false
- override fun hashCode(): Int {
- return 0
- }
+ return route == other.route
+ }
+
+ override fun hashCode(): Int {
+ return route.hashCode()
}
/**
@@ -88,7 +62,7 @@
* @param horizontalAccuracy in [Length] unit. Optional field. Valid range: non-negative
* numbers.
* @param verticalAccuracy in [Length] unit. Optional field. Valid range: non-negative numbers.
- * @see ExerciseRoute
+ * @see ExerciseRouteResult
*/
class Location(
val time: Instant,
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
new file mode 100644
index 0000000..3978f5c
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.health.connect.client.records
+
+/** Result of the route associated with an exercise session a user does. */
+abstract class ExerciseRouteResult internal constructor() {
+
+ /** Class containing data for an [ExerciseRoute]. */
+ class Data(val exerciseRoute: ExerciseRoute) : ExerciseRouteResult() {
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is Data) {
+ return false
+ }
+ return exerciseRoute == other.exerciseRoute
+ }
+
+ override fun hashCode(): Int {
+ return 0
+ }
+ }
+
+ /** Class indicating that a permission hasn't been granted and a value couldn't be returned. */
+ class ConsentRequired : ExerciseRouteResult() {
+ override fun equals(other: Any?): Boolean {
+ return other is ConsentRequired
+ }
+
+ override fun hashCode(): Int {
+ return 0
+ }
+ }
+
+ /** Class indicating that there's no data to request permissions for. */
+ class NoData : ExerciseRouteResult() {
+ override fun equals(other: Any?): Boolean {
+ return other is NoData
+ }
+
+ override fun hashCode(): Int {
+ return 0
+ }
+ }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
index 1a3747c..8a33653 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
@@ -59,8 +59,8 @@
*/
val laps: List<ExerciseLap> = emptyList(),
- /** [ExerciseRoute] [ExerciseRoute] of the session. */
- val exerciseRoute: ExerciseRoute = ExerciseRoute.NoData(),
+ /** [ExerciseRouteResult] [ExerciseRouteResult] of the session. */
+ val exerciseRouteResult: ExerciseRouteResult = ExerciseRouteResult.NoData(),
) : IntervalRecord {
@JvmOverloads
@@ -78,7 +78,7 @@
metadata: Metadata = Metadata.EMPTY,
segments: List<ExerciseSegment> = emptyList(),
laps: List<ExerciseLap> = emptyList(),
- exerciseRouteData: ExerciseRoute.Data? = null,
+ exerciseRoute: ExerciseRoute? = null,
) : this(
startTime,
startZoneOffset,
@@ -90,7 +90,7 @@
metadata,
segments,
laps,
- exerciseRouteData ?: ExerciseRoute.NoData()
+ exerciseRoute?.let { ExerciseRouteResult.Data(it) } ?: ExerciseRouteResult.NoData()
)
init {
@@ -130,8 +130,8 @@
"laps can not be out of parent time range."
}
}
- if (exerciseRoute is ExerciseRoute.Data) {
- require(exerciseRoute.isWithin(startTime, endTime)) {
+ if (exerciseRouteResult is ExerciseRouteResult.Data) {
+ require(exerciseRouteResult.exerciseRoute.isWithin(startTime, endTime)) {
"route can not be out of parent time range."
}
}
@@ -151,7 +151,7 @@
if (metadata != other.metadata) return false
if (segments != other.segments) return false
if (laps != other.laps) return false
- if (exerciseRoute != other.exerciseRoute) return false
+ if (exerciseRouteResult != other.exerciseRouteResult) return false
return true
}
@@ -164,7 +164,7 @@
result = 31 * result + endTime.hashCode()
result = 31 * result + (endZoneOffset?.hashCode() ?: 0)
result = 31 * result + metadata.hashCode()
- result = 31 * result + exerciseRoute.hashCode()
+ result = 31 * result + exerciseRouteResult.hashCode()
return result
}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
index ac86a98..2bd6f9b 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
@@ -643,7 +643,7 @@
)
),
exerciseRoute =
- ExerciseRoute.Data(
+ ExerciseRoute(
route =
listOf(
ExerciseRoute.Location(
@@ -676,7 +676,7 @@
startZoneOffset = null,
endTime = END_TIME,
endZoneOffset = null,
- exerciseRouteData = null,
+ exerciseRoute = null,
)
checkProtoAndRecordTypeNameMatch(data)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
index ffa47f9..b650485 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
@@ -73,7 +73,7 @@
)
)
val result = requestRouteContract.parseResult(0, intent)
- assertThat(result).isEqualTo(ExerciseRoute.Data(listOf()))
+ assertThat(result).isEqualTo(ExerciseRoute(listOf()))
}
@Test
@@ -114,7 +114,7 @@
val result = requestRouteContract.parseResult(0, intent)
assertThat(result)
.isEqualTo(
- ExerciseRoute.Data(
+ ExerciseRoute(
listOf(
ExerciseRoute.Location(
time = Instant.ofEpochMilli(1234L),
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
index 46afdf5..35275bf 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
@@ -74,7 +74,7 @@
@Test
fun emptyRoute() {
- assertThat(ExerciseRoute.Data(listOf())).isEqualTo(ExerciseRoute.Data(listOf()))
+ assertThat(ExerciseRoute(listOf())).isEqualTo(ExerciseRoute(listOf()))
}
@Test
@@ -94,8 +94,8 @@
latitude = 34.8,
longitude = -34.8,
)
- assertThat(ExerciseRoute.Data(listOf(location1, location2)))
- .isEqualTo(ExerciseRoute.Data(listOf(location1, location2)))
+ assertThat(ExerciseRoute(listOf(location1, location2)))
+ .isEqualTo(ExerciseRoute(listOf(location1, location2)))
}
@Test
@@ -112,8 +112,6 @@
latitude = 34.8,
longitude = -34.8,
)
- assertFailsWith<IllegalArgumentException> {
- ExerciseRoute.Data(listOf(location1, location2))
- }
+ assertFailsWith<IllegalArgumentException> { ExerciseRoute(listOf(location1, location2)) }
}
}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
index 6adb8bb..73af184 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
@@ -62,7 +62,7 @@
)
),
exerciseRoute =
- ExerciseRoute.Data(
+ ExerciseRoute(
route =
listOf(
ExerciseRoute.Location(
@@ -103,7 +103,7 @@
)
),
exerciseRoute =
- ExerciseRoute.Data(
+ ExerciseRoute(
route =
listOf(
ExerciseRoute.Location(
@@ -131,7 +131,7 @@
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS,
title = "title",
notes = "notes",
- exerciseRouteData = null,
+ exerciseRoute = null,
)
}
}
@@ -181,7 +181,7 @@
segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
)
),
- exerciseRouteData = null,
+ exerciseRoute = null,
)
}
@@ -200,7 +200,7 @@
segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
)
),
- exerciseRouteData = null,
+ exerciseRoute = null,
)
}
}
@@ -221,7 +221,7 @@
endTime = Instant.ofEpochMilli(1235L),
)
),
- exerciseRouteData = null,
+ exerciseRoute = null,
)
}
@@ -239,7 +239,7 @@
endTime = Instant.ofEpochMilli(1236L),
)
),
- exerciseRouteData = null,
+ exerciseRoute = null,
)
}
}
@@ -254,7 +254,7 @@
endZoneOffset = null,
exerciseType = EXERCISE_TYPE_BIKING,
exerciseRoute =
- ExerciseRoute.Data(
+ ExerciseRoute(
route =
listOf(
ExerciseRoute.Location(
@@ -275,7 +275,7 @@
endZoneOffset = null,
exerciseType = EXERCISE_TYPE_BIKING,
exerciseRoute =
- ExerciseRoute.Data(
+ ExerciseRoute(
route =
listOf(
ExerciseRoute.Location(
@@ -311,7 +311,7 @@
segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
),
),
- exerciseRouteData = null,
+ exerciseRoute = null,
)
}
}
@@ -336,7 +336,7 @@
endTime = Instant.ofEpochMilli(1236L),
),
),
- exerciseRouteData = null,
+ exerciseRoute = null,
)
}
}
@@ -358,7 +358,7 @@
segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK
),
),
- exerciseRouteData = null,
+ exerciseRoute = null,
)
}
}
@@ -373,7 +373,7 @@
endZoneOffset = null,
exerciseType = EXERCISE_TYPE_BIKING,
exerciseRoute =
- ExerciseRoute.Data(
+ ExerciseRoute(
route =
listOf(
ExerciseRoute.Location(
@@ -384,18 +384,20 @@
)
),
)
- .exerciseRoute
+ .exerciseRouteResult
)
.isEqualTo(
- ExerciseRoute.Data(
- route =
- listOf(
- ExerciseRoute.Location(
- time = Instant.ofEpochMilli(1235L),
- latitude = 34.5,
- longitude = -34.5
+ ExerciseRouteResult.Data(
+ ExerciseRoute(
+ route =
+ listOf(
+ ExerciseRoute.Location(
+ time = Instant.ofEpochMilli(1235L),
+ latitude = 34.5,
+ longitude = -34.5
+ )
)
- )
+ )
)
)
assertThat(
@@ -405,10 +407,10 @@
endTime = Instant.ofEpochMilli(1236L),
endZoneOffset = null,
exerciseType = EXERCISE_TYPE_BIKING,
- exerciseRouteData = null
+ exerciseRoute = null
)
- .exerciseRoute
+ .exerciseRouteResult
)
- .isEqualTo(ExerciseRoute.NoData())
+ .isEqualTo(ExerciseRouteResult.NoData())
}
}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
index fa9ea16..b61a875 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
@@ -37,13 +37,15 @@
public companion object {
/**
- * Batching mode for receiving [DataType.HEART_RATE_BPM] updates with fast frequency.
+ * Deliver smaller and more frequent batches of [DataType.HEART_RATE_BPM] when the device is
+ * not interactive (e.g. screen is off).
*
- * Note: This mode will cause significantly increased power consumption compared to the
- * default batching mode, while still being more power efficient than streaming when in
- * non-interactive state. The exact power/performance tradeoff of this mode is device
- * implementation dependent and batched updates may be aligned with other wake ups but
- * target five second updates.
+ * This setting significantly increases power consumption, and is intended to be used by
+ * apps which need to send data to a separate device (e.g. a connected phone or TV) for
+ * real-time visualisation. It has no effect if the device is interactive.
+ *
+ * The exact power/performance tradeoff of this mode is device implementation dependent and
+ * batched updates may be aligned with other wake ups but target five second updates.
*/
@JvmField public val HEART_RATE_5_SECONDS: BatchingMode = BatchingMode(1)
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
index 4a43798..2d2bf7e 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
@@ -31,8 +31,8 @@
EXERCISE_CAPABILITIES.getExerciseTypeCapabilities(
ExerciseType.WALKING
).supportedDataTypes
- ).isEqualTo(
- ImmutableSet.of(DataType.STEPS)
+ ).containsExactly(
+ DataType.STEPS
)
}
@@ -93,6 +93,9 @@
assertThat(capabilities.autoPauseAndResumeEnabledExercises).containsExactlyElementsIn(
EXERCISE_CAPABILITIES.autoPauseAndResumeEnabledExercises
)
+ assertThat(capabilities.supportedBatchingModeOverrides).containsExactlyElementsIn(
+ EXERCISE_CAPABILITIES.supportedBatchingModeOverrides
+ )
}
@Test
@@ -106,6 +109,9 @@
assertThat(emptyCapabilities.autoPauseAndResumeEnabledExercises).containsExactlyElementsIn(
roundTripEmptyCapabilities.autoPauseAndResumeEnabledExercises
)
+ assertThat(emptyCapabilities.supportedBatchingModeOverrides).containsExactlyElementsIn(
+ roundTripEmptyCapabilities.supportedBatchingModeOverrides
+ )
}
companion object {
@@ -153,6 +159,7 @@
)
private val EXERCISE_CAPABILITIES: ExerciseCapabilities =
- ExerciseCapabilities(EXERCISE_TYPE_TO_EXERCISE_CAPABILITIES_MAPPING)
+ ExerciseCapabilities(EXERCISE_TYPE_TO_EXERCISE_CAPABILITIES_MAPPING,
+ ImmutableSet.of(BatchingMode.HEART_RATE_5_SECONDS))
}
}
diff --git a/hilt/hilt-compiler/build.gradle b/hilt/hilt-compiler/build.gradle
index 2418bf7..aec41ba 100644
--- a/hilt/hilt-compiler/build.gradle
+++ b/hilt/hilt-compiler/build.gradle
@@ -39,7 +39,7 @@
testImplementation(project(":hilt:hilt-common"))
testImplementation(project(":annotation:annotation"))
testImplementation(libs.junit)
- testImplementation(project(":kruth:kruth"))
+ testImplementation(libs.truth)
testImplementation(project(":room:room-compiler-processing-testing"))
testImplementation(libs.hiltCore)
testImplementationAarAsJar(project(":hilt:hilt-work"))
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
index 6d6a8e6..520e21d7 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
@@ -20,6 +20,8 @@
import java.io.File
import java.util.Locale
import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.provider.Provider
internal fun Variant.taskName(baseName: String) =
"$baseName${name.replaceFirstChar(Char::titlecase)}"
@@ -27,9 +29,8 @@
internal fun Project.taskWorkingDir(
variant: Variant,
baseName: String
-): File {
- val inspectionDir = File(project.buildDir, "androidx_inspection")
- return File(File(inspectionDir, baseName), variant.name)
+): Provider<Directory> {
+ return layout.buildDirectory.dir("androidx_inspection/$baseName/${variant.name}")
}
// Functions below will be removed once registerGenerateProguardDetectionFileTask is migrated
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
index 7d95e6b..e8edc46 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
@@ -128,7 +128,8 @@
): TaskProvider<Copy> {
return tasks.register(variant.taskName("unpackInspectorAAR"), Copy::class.java) {
it.from(zipTree(variant.artifacts.get(SingleArtifact.AAR)))
- it.destinationDir = taskWorkingDir(variant, "unpackedInspectorAAR")
+ // Remove .get().asFile once https://github.com/gradle/gradle/issues/25824 is fixed
+ it.destinationDir = taskWorkingDir(variant, "unpackedInspectorAAR").get().asFile
}
}
@@ -139,15 +140,14 @@
jar: TaskProvider<out Jar>
): TaskProvider<Zip> {
val name = jarName ?: "${project.name}.jar"
- val out = File(taskWorkingDir(variant, "dexedInspector"), name)
+ val output = taskWorkingDir(variant, "dexedInspector").map { it.file(name) }
val dex = tasks.register(variant.taskName("dexInspector"), DexInspectorTask::class.java) {
it.minSdkVersion = extension.defaultConfig.minSdk!!
it.setD8(extension.sdkDirectory, extension.buildToolsVersion)
it.setAndroidJar(extension.sdkDirectory, extension.compileSdkVersion!!)
it.jars.from(jar.get().archiveFile)
- it.outputFile.set(out)
- @Suppress("UnstableApiUsage")
+ it.outputFile.set(output)
it.compileClasspath.from(
variant.compileConfiguration.incoming.artifactView {
it.attributes {
@@ -163,11 +163,10 @@
return tasks.register(variant.taskName("assembleInspectorJar"), Zip::class.java) {
it.from(zipTree(jar.map { it.archiveFile }))
- it.from(zipTree(out))
+ it.from(dex.map { zipTree(it.outputFile) })
it.exclude("**/*.class")
it.archiveFileName.set(name)
it.destinationDirectory.set(taskWorkingDir(variant, "assembleInspectorJar"))
- it.dependsOn(dex)
it.includeEmptyDirs = false
}
}
diff --git a/libraryversions.toml b/libraryversions.toml
index 0e4ded9..19c0a44 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -59,7 +59,7 @@
EXIFINTERFACE = "1.4.0-alpha01"
FRAGMENT = "1.7.0-alpha02"
FUTURES = "1.2.0-alpha02"
-GLANCE = "1.0.0-rc01"
+GLANCE = "1.1.0-alpha01"
GLANCE_PREVIEW = "1.0.0-alpha06"
GLANCE_TEMPLATE = "1.0.0-alpha06"
GLANCE_WEAR_TILES = "1.0.0-alpha06"
diff --git a/privacysandbox/ads/ads-adservices-java/api/current.txt b/privacysandbox/ads/ads-adservices-java/api/current.txt
index 26eea8b..18004f9 100644
--- a/privacysandbox/ads/ads-adservices-java/api/current.txt
+++ b/privacysandbox/ads/ads-adservices-java/api/current.txt
@@ -64,6 +64,7 @@
method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
diff --git a/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt b/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
index 26eea8b..18004f9 100644
--- a/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
@@ -64,6 +64,7 @@
method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
index 6a657ad4..7bc4ea9 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
@@ -26,6 +26,7 @@
import androidx.privacysandbox.ads.adservices.java.endtoend.TestUtil;
import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures;
import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest;
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest;
import androidx.privacysandbox.ads.adservices.measurement.WebSourceParams;
import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest;
import androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams;
@@ -107,6 +108,19 @@
}
@Test
+ public void testRegisterAppSources_NoServerSetup_NoErrors() throws Exception {
+ // Skip the test if SDK extension 5 is not present.
+ Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+
+ SourceRegistrationRequest request =
+ new SourceRegistrationRequest.Builder(
+ Collections.singletonList(SOURCE_REGISTRATION_URI))
+ .build();
+ assertThat(mMeasurementManager.registerSourceAsync(request).get())
+ .isNotNull();
+ }
+
+ @Test
public void testRegisterTrigger_NoServerSetup_NoErrors() throws Exception {
// Skip the test if SDK extension 5 is not present.
Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
index 6e1752b..0bec6eb 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
@@ -24,8 +24,10 @@
import android.os.ext.SdkExtensions
import android.view.InputEvent
import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion.from
import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest
import androidx.privacysandbox.ads.adservices.measurement.WebSourceParams
import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest
import androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams
@@ -36,13 +38,23 @@
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import java.time.Instant
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.Executor
import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+import kotlin.test.fail
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
import org.junit.Assume
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.atMost
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
@@ -278,6 +290,112 @@
assertThat(result.get() == state)
}
+ @ExperimentalFeatures.RegisterSourceOptIn
+ @Test
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+ fun testRegisterSourceAsync_allSuccess() {
+ val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+ Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+ val inputEvent = mock(InputEvent::class.java)
+ val measurementManager = mockMeasurementManager(mContext)
+ val managerCompat = from(mContext)
+ val successCallback = { args: InvocationOnMock ->
+ assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
+ val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+ receiver.onResult(Object())
+ null
+ }
+ doAnswer(successCallback).`when`(measurementManager)
+ .registerSource(any(), any(), any(), any())
+
+ // Actually invoke the compat code.
+ val request = SourceRegistrationRequest.Builder(listOf(uri1, uri2))
+ .setInputEvent(inputEvent)
+ .build()
+ managerCompat!!.registerSourceAsync(request).get()
+
+ // Verify that the compat code was invoked correctly.
+ verify(measurementManager).registerSource(
+ eq(uri1),
+ eq(inputEvent),
+ any(Executor::class.java),
+ any())
+ verify(measurementManager).registerSource(
+ eq(uri2),
+ eq(inputEvent),
+ any(Executor::class.java),
+ any())
+ }
+
+ @ExperimentalFeatures.RegisterSourceOptIn
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+ @Test
+ fun testRegisterSource_15thOf20Fails_atLeast15thExecutes() {
+ val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+ Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+ val measurementManager = mockMeasurementManager(mContext)
+ val mockInputEvent = mock(InputEvent::class.java)
+ val managerCompat = from(mContext)
+ val successCallback = { args: InvocationOnMock ->
+ val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+ receiver.onResult(Object())
+ null
+ }
+
+ val errorMessage = "some error occurred"
+ val errorCallback = { args: InvocationOnMock ->
+ val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+ receiver.onError(IllegalArgumentException(errorMessage))
+ null
+ }
+
+ val uris = (0..20).map { i ->
+ val uri = Uri.parse("www.uri$i.com")
+ if (i == 15) {
+ doAnswer(errorCallback).`when`(measurementManager)
+ .registerSource(eq(uri), any(), any(), any())
+ } else {
+ doAnswer(successCallback).`when`(measurementManager)
+ .registerSource(eq(uri), any(), any(), any())
+ }
+ uri
+ }.toList()
+
+ val request = SourceRegistrationRequest(uris, mockInputEvent)
+
+ // Actually invoke the compat code.
+ runBlocking {
+ try {
+ withContext(Dispatchers.Main) {
+ managerCompat!!.registerSourceAsync(request).get()
+ }
+ fail("Expected failure.")
+ } catch (e: ExecutionException) {
+ assertTrue(e.cause!! is IllegalArgumentException)
+ assertThat(e.cause!!.message).isEqualTo(errorMessage)
+ }
+ }
+
+ // Verify that the compat code was invoked correctly.
+ // registerSource gets called 1-20 times. We cannot predict the exact number because
+ // uri15 would crash asynchronously. Other uris may succeed and those threads on default
+ // dispatcher won't crash.
+ verify(measurementManager, atLeastOnce()).registerSource(
+ any(),
+ eq(mockInputEvent),
+ any(),
+ any()
+ )
+ verify(measurementManager, atMost(20)).registerSource(
+ any(),
+ eq(mockInputEvent),
+ any(),
+ any()
+ )
+ }
+
@SdkSuppress(minSdkVersion = 30)
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
companion object {
diff --git a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
index b04eb21..e6c5487 100644
--- a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
@@ -22,10 +22,12 @@
import android.view.InputEvent
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresPermission
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
import androidx.privacysandbox.ads.adservices.java.internal.asListenableFuture
import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest
import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager
import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion.obtain
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest
import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest
import androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest
import com.google.common.util.concurrent.ListenableFuture
@@ -75,6 +77,19 @@
abstract fun registerTriggerAsync(trigger: Uri): ListenableFuture<Unit>
/**
+ * Register attribution sources(click or view). This API will not process any redirects, all
+ * registration URLs should be supplied with the request.
+ *
+ * @param request source registration request
+ */
+ @ExperimentalFeatures.RegisterSourceOptIn
+ @SuppressWarnings("MissingNullability")
+ @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+ abstract fun registerSourceAsync(
+ request: SourceRegistrationRequest
+ ): ListenableFuture<Unit>
+
+ /**
* Register an attribution source(click or view) from web context. This API will not process any
* redirects, all registration URLs should be supplied with the request. At least one of
* appDestination or webDestination parameters are required to be provided.
@@ -135,6 +150,17 @@
}
@DoNotInline
+ @ExperimentalFeatures.RegisterSourceOptIn
+ @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+ override fun registerSourceAsync(
+ request: SourceRegistrationRequest
+ ): ListenableFuture<Unit> {
+ return CoroutineScope(Dispatchers.Default).async {
+ mMeasurementManager.registerSource(request)
+ }.asListenableFuture()
+ }
+
+ @DoNotInline
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
override fun registerTriggerAsync(trigger: Uri): ListenableFuture<Unit> {
return CoroutineScope(Dispatchers.Default).async {
diff --git a/privacysandbox/ads/ads-adservices/api/current.txt b/privacysandbox/ads/ads-adservices/api/current.txt
index 839dec6..6a4905b 100644
--- a/privacysandbox/ads/ads-adservices/api/current.txt
+++ b/privacysandbox/ads/ads-adservices/api/current.txt
@@ -126,6 +126,12 @@
property public final String identifier;
}
+ public sealed interface ExperimentalFeatures {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+ }
+
}
package androidx.privacysandbox.ads.adservices.customaudience {
@@ -249,6 +255,7 @@
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -261,6 +268,20 @@
method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
}
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+ ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+ method public android.view.InputEvent? getInputEvent();
+ method public java.util.List<android.net.Uri> getRegistrationUris();
+ property public final android.view.InputEvent? inputEvent;
+ property public final java.util.List<android.net.Uri> registrationUris;
+ }
+
+ public static final class SourceRegistrationRequest.Builder {
+ ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ }
+
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
method public boolean getDebugKeyAllowed();
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_current.txt b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
index 839dec6..6a4905b 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
@@ -126,6 +126,12 @@
property public final String identifier;
}
+ public sealed interface ExperimentalFeatures {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+ }
+
}
package androidx.privacysandbox.ads.adservices.customaudience {
@@ -249,6 +255,7 @@
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -261,6 +268,20 @@
method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
}
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+ ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+ method public android.view.InputEvent? getInputEvent();
+ method public java.util.List<android.net.Uri> getRegistrationUris();
+ property public final android.view.InputEvent? inputEvent;
+ property public final java.util.List<android.net.Uri> registrationUris;
+ }
+
+ public static final class SourceRegistrationRequest.Builder {
+ ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ }
+
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
method public boolean getDebugKeyAllowed();
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
index 191e2d8..1e0a826 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
@@ -23,13 +23,16 @@
import android.os.ext.SdkExtensions
import android.view.InputEvent
import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion.obtain
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
+import androidx.testutils.fail
import com.google.common.truth.Truth.assertThat
import java.time.Instant
+import kotlin.IllegalArgumentException
import kotlinx.coroutines.runBlocking
import org.junit.Assume
import org.junit.Before
@@ -37,9 +40,12 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.invocation.InvocationOnMock
@@ -211,6 +217,105 @@
assertThat(!actualRequest.sourceParams[0].isDebugKeyAllowed)
}
+ @ExperimentalFeatures.RegisterSourceOptIn
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+ @Test
+ fun testRegisterSource_allSuccess() {
+ val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+ Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+ val measurementManager = mockMeasurementManager(mContext)
+ val mockInputEvent = mock(InputEvent::class.java)
+ val managerCompat = obtain(mContext)
+ val successCallback = { args: InvocationOnMock ->
+ val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+ receiver.onResult(Object())
+ null
+ }
+ doAnswer(successCallback).`when`(measurementManager)
+ .registerSource(any(), any(), any(), any())
+
+ val request = SourceRegistrationRequest(listOf(uri1, uri2), mockInputEvent)
+
+ // Actually invoke the compat code.
+ runBlocking {
+ managerCompat!!.registerSource(request)
+ }
+
+ // Verify that the compat code was invoked correctly.
+ verify(measurementManager, times(2)).registerSource(
+ any(),
+ eq(mockInputEvent),
+ any(),
+ any()
+ )
+ }
+
+ @ExperimentalFeatures.RegisterSourceOptIn
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+ @Test
+ fun testRegisterSource_15thOf20Fails_remaining5DoNotExecute() {
+ val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+ Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+ val measurementManager = mockMeasurementManager(mContext)
+ val mockInputEvent = mock(InputEvent::class.java)
+ val managerCompat = obtain(mContext)
+ val successCallback = { args: InvocationOnMock ->
+ val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+ receiver.onResult(Object())
+ null
+ }
+
+ val errorMessage = "some error occurred"
+ val errorCallback = { args: InvocationOnMock ->
+ val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+ receiver.onError(IllegalArgumentException(errorMessage))
+ null
+ }
+ val uris = (0..20).map { i ->
+ val uri = Uri.parse("www.uri$i.com")
+ if (i == 15) {
+ doAnswer(errorCallback).`when`(measurementManager)
+ .registerSource(eq(uri), any(), any(), any())
+ } else {
+ doAnswer(successCallback).`when`(measurementManager)
+ .registerSource(eq(uri), any(), any(), any())
+ }
+ uri
+ }.toList()
+
+ val request = SourceRegistrationRequest(uris, mockInputEvent)
+
+ // Actually invoke the compat code.
+ runBlocking {
+ try {
+ managerCompat!!.registerSource(request)
+ fail("Expected failure.")
+ } catch (e: IllegalArgumentException) {
+ assertThat(e.message).isEqualTo(errorMessage)
+ }
+ }
+
+ // Verify that the compat code was invoked correctly.
+ (0..15).forEach { i ->
+ verify(measurementManager).registerSource(
+ eq(Uri.parse("www.uri$i.com")),
+ eq(mockInputEvent),
+ any(),
+ any()
+ )
+ }
+ (16..20).forEach { i ->
+ verify(measurementManager, never()).registerSource(
+ eq(Uri.parse("www.uri$i.com")),
+ eq(mockInputEvent),
+ any(),
+ any()
+ )
+ }
+ }
+
@Test
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
fun testRegisterWebTrigger() {
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt
new file mode 100644
index 0000000..cff885a
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.privacysandbox.ads.adservices.measurement
+
+import android.net.Uri
+import android.view.InputEvent
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@ExperimentalFeatures.RegisterSourceOptIn
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 33)
+class SourceRegistrationRequestTest {
+ @Test
+ fun testToString() {
+ val result = "AppSourcesRegistrationRequest { RegistrationUris=" +
+ "[[www.abc.com, www.xyz.com]], InputEvent=null }"
+
+ val uri1 = Uri.parse("www.abc.com")
+ val uri2 = Uri.parse("www.xyz.com")
+ val params = listOf(uri1, uri2)
+ val request = SourceRegistrationRequest.Builder(params).build()
+ Truth.assertThat(request.toString()).isEqualTo(result)
+ }
+
+ @Test
+ fun testEquals() {
+ val uri1 = Uri.parse("www.abc.com")
+ val uri2 = Uri.parse("www.xyz.com")
+ val params = listOf(uri1, uri2)
+ val inputEvent = mock(InputEvent::class.java)
+ val request1 = SourceRegistrationRequest.Builder(params)
+ .setInputEvent(inputEvent)
+ .build()
+ val request2 = SourceRegistrationRequest(params, inputEvent)
+ val request3 = SourceRegistrationRequest.Builder(params).build()
+
+ Truth.assertThat(request1 == request2).isTrue()
+ Truth.assertThat(request1 != request3).isTrue()
+ }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
index e76c23e..70cd0c4 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
@@ -18,6 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
import com.google.common.truth.Truth
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,4 +42,30 @@
// Verify equality.
Truth.assertThat(request == request2).isTrue()
}
+
+ @Test
+ fun testToString_emptySdkName() {
+ val result = "GetTopicsRequest: adsSdkName=, shouldRecordObservation=true"
+ val request = GetTopicsRequest("", true)
+ Truth.assertThat(request.toString()).isEqualTo(result)
+
+ // Verify Builder.
+ val request2 = GetTopicsRequest.Builder()
+ .setShouldRecordObservation(true)
+ .build()
+ Truth.assertThat(request.toString()).isEqualTo(result)
+
+ // Verify equality.
+ Truth.assertThat(request == request2).isTrue()
+ }
+
+ @Test
+ fun testBuilder_setEmptyAdsSdkName_throwsError() {
+ assertThrows(IllegalStateException::class.java) {
+ GetTopicsRequest.Builder()
+ .setAdsSdkName("")
+ .setShouldRecordObservation(true)
+ .build()
+ }
+ }
}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
new file mode 100644
index 0000000..e1935b3
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.privacysandbox.ads.adservices.common
+
+/**
+ * Contains AdServices experimental feature opt-in anntations.
+ */
+sealed interface ExperimentalFeatures {
+ /**
+ * Clients should use it when they want to use [MeasurementManager#registerSource] API.
+ */
+ @RequiresOptIn("This API is experimental.", RequiresOptIn.Level.WARNING)
+ annotation class RegisterSourceOptIn
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
index e316ab0..de62bd2 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
@@ -27,7 +27,10 @@
import androidx.annotation.RequiresExtension
import androidx.annotation.RequiresPermission
import androidx.core.os.asOutcomeReceiver
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
/**
@@ -82,6 +85,16 @@
abstract suspend fun registerWebTrigger(request: WebTriggerRegistrationRequest)
/**
+ * Register an attribution source(click or view) context. This API will not process any
+ * redirects, all registration URLs should be supplied with the request.
+ *
+ * @param request source registration request
+ */
+ @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+ @ExperimentalFeatures.RegisterSourceOptIn
+ abstract suspend fun registerSource(request: SourceRegistrationRequest)
+
+ /**
* Get Measurement API status.
*
* The call returns an integer value (see [MEASUREMENT_API_STATE_DISABLED] and
@@ -160,6 +173,26 @@
}
}
+ @DoNotInline
+ @ExperimentalFeatures.RegisterSourceOptIn
+ @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+ override suspend fun registerSource(
+ request: SourceRegistrationRequest
+ ): Unit = coroutineScope {
+ request.registrationUris.forEach { uri ->
+ launch {
+ suspendCancellableCoroutine<Any> { continuation ->
+ mMeasurementManager.registerSource(
+ uri,
+ request.inputEvent,
+ Runnable::run,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ }
+ }
+
private fun convertWebSourceRequest(
request: WebSourceRegistrationRequest
): android.adservices.measurement.WebSourceRegistrationRequest {
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt
new file mode 100644
index 0000000..7bda8bf
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt
@@ -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.privacysandbox.ads.adservices.measurement
+
+import android.net.Uri
+import android.view.InputEvent
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+
+/**
+ * Class to hold input to measurement source registration calls from web context.
+ *
+ * @param registrationUris [List] of Registration [Uri]s to fetch sources.
+ * @param inputEvent User Interaction [InputEvent] used by the AttributionReporting API to
+ * distinguish clicks from views.
+ */
+@ExperimentalFeatures.RegisterSourceOptIn
+class SourceRegistrationRequest constructor(
+ val registrationUris: List<Uri>,
+ val inputEvent: InputEvent? = null
+ ) {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is SourceRegistrationRequest) return false
+ return this.registrationUris == other.registrationUris &&
+ this.inputEvent == other.inputEvent
+ }
+
+ override fun hashCode(): Int {
+ var hash = registrationUris.hashCode()
+ if (inputEvent != null) {
+ hash = 31 * hash + inputEvent.hashCode()
+ }
+ return hash
+ }
+
+ override fun toString(): String {
+ val vals = "RegistrationUris=[$registrationUris], InputEvent=$inputEvent"
+ return "AppSourcesRegistrationRequest { $vals }"
+ }
+
+ /**
+ * Builder for [SourceRegistrationRequest].
+ *
+ * @param registrationUris source registration request [Uri]
+ */
+ class Builder(
+ private val registrationUris: List<Uri>
+ ) {
+ private var inputEvent: InputEvent? = null
+
+ /**
+ * Setter for input event.
+ *
+ * @param inputEvent User Interaction InputEvent used by the AttributionReporting API to
+ * distinguish clicks from views.
+ * @return builder
+ */
+ fun setInputEvent(inputEvent: InputEvent): Builder = apply {
+ this.inputEvent = inputEvent
+ }
+
+ /** Pre-validates parameters and builds [SourceRegistrationRequest]. */
+ fun build(): SourceRegistrationRequest {
+ return SourceRegistrationRequest(
+ registrationUris,
+ inputEvent
+ )
+ }
+ }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
index d8f4a81..6dede0c 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
@@ -64,7 +64,10 @@
*
* @param adsSdkName the Ads Sdk Name.
*/
- fun setAdsSdkName(adsSdkName: String): Builder = apply { this.adsSdkName = adsSdkName }
+ fun setAdsSdkName(adsSdkName: String): Builder = apply {
+ check(adsSdkName.isNotEmpty()) { "adsSdkName must be set" }
+ this.adsSdkName = adsSdkName
+ }
/**
* Set the Record Observation.
@@ -80,7 +83,6 @@
/** Builds a [GetTopicsRequest] instance. */
fun build(): GetTopicsRequest {
- check(adsSdkName.isNotEmpty()) { "adsSdkName must be set" }
return GetTopicsRequest(adsSdkName, shouldRecordObservation)
}
}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
index a7ef64d..5753602 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
@@ -20,6 +20,7 @@
import androidx.collection.SparseArrayCompat
import androidx.lifecycle.LiveData
import androidx.room.Dao
+import androidx.room.Delete
import androidx.room.Insert
import androidx.room.MapInfo
import androidx.room.Query
@@ -32,6 +33,9 @@
import androidx.room.integration.kotlintestapp.vo.Artist
import androidx.room.integration.kotlintestapp.vo.Image
import androidx.room.integration.kotlintestapp.vo.ImageFormat
+import androidx.room.integration.kotlintestapp.vo.Playlist
+import androidx.room.integration.kotlintestapp.vo.PlaylistSongXRef
+import androidx.room.integration.kotlintestapp.vo.PlaylistWithSongs
import androidx.room.integration.kotlintestapp.vo.ReleasedAlbum
import androidx.room.integration.kotlintestapp.vo.Song
import androidx.sqlite.db.SupportSQLiteQuery
@@ -41,6 +45,7 @@
import io.reactivex.Flowable
import java.nio.ByteBuffer
import java.util.Date
+import kotlinx.coroutines.flow.Flow
@JvmDefaultWithCompatibility
@Dao
@@ -53,6 +58,16 @@
@Insert
fun addAlbums(vararg albums: Album)
+
+ @Insert
+ fun addPlaylists(vararg playlists: Playlist)
+
+ @Insert
+ fun addPlaylistSongRelations(vararg relations: PlaylistSongXRef)
+
+ @Delete
+ fun removePlaylistSongRelations(vararg relations: PlaylistSongXRef)
+
@Insert
fun addImages(vararg images: Image)
@@ -355,4 +370,8 @@
@MapInfo(keyColumn = "mImageYear", valueColumn = "mTitle")
@RewriteQueriesToDropUnusedColumns
fun getNestedMapWithMapInfoKeyAndValue(): Map<Long, Map<Artist, Map<Album, List<String>>>>
+
+ @Transaction
+ @Query("SELECT * FROM Playlist WHERE mPlaylistId = :id")
+ fun getPlaylistsWithSongsFlow(id: Int): Flow<PlaylistWithSongs>
}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
index 4a23ca5..02d20ce 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
@@ -18,6 +18,10 @@
import androidx.kruth.assertThat
import androidx.room.integration.kotlintestapp.vo.Book
+import androidx.room.integration.kotlintestapp.vo.Playlist
+import androidx.room.integration.kotlintestapp.vo.PlaylistSongXRef
+import androidx.room.integration.kotlintestapp.vo.PlaylistWithSongs
+import androidx.room.integration.kotlintestapp.vo.Song
import androidx.room.withTransaction
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -30,7 +34,6 @@
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.buffer
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.produceIn
import kotlinx.coroutines.flow.take
@@ -318,4 +321,73 @@
job.cancelAndJoin()
}
+
+ @Test
+ fun playlistSongs_async_update(): Unit = runBlocking {
+ val musicDao = database.musicDao()
+ val song1 = Song(
+ 1,
+ "I Know Places",
+ "Taylor Swift",
+ "1989",
+ 195,
+ 2014
+ );
+ val song2 = Song(
+ 2,
+ "Blank Space",
+ "Taylor Swift",
+ "1989",
+ 241,
+ 2014
+ )
+ musicDao.addSongs(song1, song2)
+ musicDao.addPlaylists(
+ Playlist(1),
+ Playlist(2)
+ )
+ musicDao.addPlaylistSongRelations(PlaylistSongXRef(1, 1))
+
+ val latches = Array(3) { CountDownLatch(1) }
+ val results = mutableListOf<PlaylistWithSongs>()
+ var collectCall = 0
+ val job = async(Dispatchers.IO) {
+ musicDao.getPlaylistsWithSongsFlow(1).collect {
+ if (collectCall >= latches.size) {
+ fail("Should have only collected 3 results.")
+ }
+ results.add(it)
+ latches[collectCall].countDown()
+ collectCall++
+ }
+ }
+
+ latches[0].await()
+ assertThat(results.size).isEqualTo(1)
+ results[0].let { playlist ->
+ assertThat(playlist.songs.size).isEqualTo(1)
+ assertThat(playlist.songs[0]).isEqualTo(song1)
+ }
+
+ musicDao.addPlaylistSongRelations(PlaylistSongXRef(1, 2))
+
+ latches[1].await()
+ assertThat(results.size).isEqualTo(2)
+ results[1].let { playlist ->
+ assertThat(playlist.songs.size).isEqualTo(2)
+ assertThat(playlist.songs[0]).isEqualTo(song1)
+ assertThat(playlist.songs[1]).isEqualTo(song2)
+ }
+
+ musicDao.removePlaylistSongRelations(PlaylistSongXRef(1, 2))
+
+ latches[2].await()
+ assertThat(results.size).isEqualTo(3)
+ results[2].let { playlist ->
+ assertThat(playlist.songs.size).isEqualTo(1)
+ assertThat(playlist.songs[0]).isEqualTo(song1)
+ }
+
+ job.cancelAndJoin()
+ }
}
diff --git a/room/room-compiler-processing-testing/build.gradle b/room/room-compiler-processing-testing/build.gradle
index e789fcd..5b2da1b 100644
--- a/room/room-compiler-processing-testing/build.gradle
+++ b/room/room-compiler-processing-testing/build.gradle
@@ -30,7 +30,6 @@
implementation(libs.kotlinStdlibJdk8) // KSP defines older version as dependency, force update.
implementation(libs.ksp)
implementation(libs.googleCompileTesting)
- implementation(project(":kruth:kruth"))
// specify these to match the kotlin compiler version in AndroidX rather than what KSP or KCT
// uses
implementation(libs.kotlinCompilerEmbeddable)
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
index e38371a..6642422 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
@@ -16,16 +16,18 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.FailureMetadata
-import androidx.kruth.StringSubject
-import androidx.kruth.Subject
-import androidx.kruth.assertAbout
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.ExperimentalProcessingApi
import androidx.room.compiler.processing.SyntheticJavacProcessor
import androidx.room.compiler.processing.SyntheticProcessor
import androidx.room.compiler.processing.util.compiler.TestCompilationResult
import androidx.room.compiler.processing.util.runner.CompilationTestRunner
+import com.google.common.truth.Fact.fact
+import com.google.common.truth.Fact.simpleFact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.StringSubject
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
import com.google.testing.compile.Compilation
import java.util.regex.Pattern
import javax.tools.Diagnostic
@@ -124,9 +126,8 @@
class CompilationResultSubject internal constructor(
failureMetadata: FailureMetadata,
val compilationResult: CompilationResult,
-) : Subject<CompilationResult>(
- actual = compilationResult,
- metadata = failureMetadata,
+) : Subject<CompilationResultSubject, CompilationResult>(
+ failureMetadata, compilationResult
) {
/**
* set to true if any assertion on the subject requires it to fail (e.g. looking for errors)
@@ -149,7 +150,9 @@
fun hasRawOutputContaining(expected: String) = apply {
val found = compilationResult.rawOutput().contains(expected)
if (!found) {
- failWithActual("Did not find $expected in the output.")
+ failWithActual(
+ simpleFact("Did not find $expected in the output.")
+ )
}
}
@@ -171,7 +174,9 @@
private fun hasDiagnosticCount(kind: Diagnostic.Kind, expected: Int) = apply {
val actual = compilationResult.diagnosticsOfKind(kind).size
if (actual != expected) {
- failWithActual("expected $expected $kind messages, found $actual")
+ failWithActual(
+ simpleFact("expected $expected $kind messages, found $actual")
+ )
}
}
/**
@@ -326,7 +331,9 @@
fun hasError() = apply {
shouldSucceed = false
if (compilationResult.diagnosticsOfKind(Diagnostic.Kind.ERROR).isEmpty()) {
- failWithActual("expected at least one failure message")
+ failWithActual(
+ simpleFact("expected at least one failure message")
+ )
}
}
@@ -337,9 +344,12 @@
*/
fun generatedSourceFileWithPath(relativePath: String): StringSubject {
val match = findGeneratedSource(relativePath)
- ?: failWithActual("Didn't generate file with path: $relativePath")
-
- return assertThat(match.contents)
+ if (match == null) {
+ failWithActual(
+ simpleFact("Didn't generate file with path: $relativePath")
+ )
+ }
+ return Truth.assertThat(match!!.contents)
}
private fun findGeneratedSource(relativePath: String) = compilationResult.generatedSources
@@ -358,14 +368,20 @@
fun generatedSource(source: Source) = apply {
val match = compilationResult.generatedSources.firstOrNull {
it.relativePath == source.relativePath
- } ?: failWithActual("Didn't generate $source")
+ }
+ if (match == null) {
+ failWithActual(
+ simpleFact("Didn't generate $source")
+ )
+ return@apply
+ }
val mismatch = source.findMismatch(match)
if (mismatch != null) {
failWithActual(
- "Generated code does not match expected",
- "mismatch: $mismatch",
- "expected: ${source.contents}",
- "actual: ${match.contents}",
+ simpleFact("Generated code does not match expected"),
+ fact("mismatch", mismatch),
+ fact("expected", source.contents),
+ fact("actual", match.contents),
)
}
}
@@ -377,8 +393,10 @@
internal fun assertCompilationResult() {
if (compilationResult.successfulCompilation != shouldSucceed) {
failWithActual(
- "expected compilation result to be: $shouldSucceed but was " +
- "${compilationResult.successfulCompilation}"
+ simpleFact(
+ "expected compilation result to be: $shouldSucceed but was " +
+ "${compilationResult.successfulCompilation}"
+ )
)
}
}
@@ -389,7 +407,9 @@
*/
internal fun assertAllExpectedRoundsAreCompleted() {
if (compilationResult.processor.expectsAnotherRound()) {
- failWithActual("Test runner requested another round but that didn't happen")
+ failWithActual(
+ simpleFact("Test runner requested another round but that didn't happen")
+ )
}
}
@@ -427,7 +447,7 @@
}
}
if (matches.isEmpty()) {
- failWithActual(buildErrorMessage())
+ failWithActual(simpleFact(buildErrorMessage()))
}
return DiagnosticMessagesSubject.assertThat(matches)
}
@@ -449,7 +469,7 @@
}
}
if (matches.isEmpty()) {
- failWithActual(buildErrorMessage())
+ failWithActual(simpleFact(buildErrorMessage()))
}
return DiagnosticMessagesSubject.assertThat(matches)
}
@@ -470,10 +490,18 @@
}
companion object {
+ private val FACTORY =
+ Factory<CompilationResultSubject, CompilationResult> { metadata, actual ->
+ CompilationResultSubject(metadata, actual)
+ }
+
fun assertThat(
compilationResult: CompilationResult
- ): CompilationResultSubject =
- assertAbout(::CompilationResultSubject).that(compilationResult)
+ ): CompilationResultSubject {
+ return Truth.assertAbout(FACTORY).that(
+ compilationResult
+ )
+ }
}
}
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
index de1f737..213eea0 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
@@ -16,9 +16,11 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.FailureMetadata
-import androidx.kruth.Subject
-import androidx.kruth.assertAbout
+import com.google.common.truth.Fact.simpleFact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
/**
* Truth subject for diagnostic messages
@@ -26,8 +28,8 @@
class DiagnosticMessagesSubject internal constructor(
failureMetadata: FailureMetadata,
private val diagnosticMessages: List<DiagnosticMessage>,
-) : Subject<List<DiagnosticMessage>>(
- diagnosticMessages, failureMetadata
+) : Subject<DiagnosticMessagesSubject, List<DiagnosticMessage>>(
+ failureMetadata, diagnosticMessages
) {
private val lineContents by lazy {
@@ -50,10 +52,15 @@
* Note that if there are multiple messages, any match will be sufficient.
*/
fun onLine(lineNumber: Int) = apply {
- if (locations.none { it.line == lineNumber }) {
+ if (locations.none {
+ it.line == lineNumber
+ }
+ ) {
failWithActual(
- "expected line $lineNumber but it was " +
- locations.joinToString(",")
+ simpleFact(
+ "expected line $lineNumber but it was " +
+ locations.joinToString(",")
+ )
)
}
}
@@ -64,7 +71,7 @@
fun hasCount(expected: Int) = apply {
if (diagnosticMessages.size != expected) {
failWithActual(
- "expected $expected messages, found ${diagnosticMessages.size}"
+ simpleFact("expected $expected messages, found ${diagnosticMessages.size}")
)
}
}
@@ -75,7 +82,7 @@
fun onLineContaining(content: String) = apply {
if (lineContents.isEmpty()) {
failWithActual(
- "Cannot validate line content due to missing location information"
+ simpleFact("Cannot validate line content due to missing location information")
)
}
if (lineContents.none {
@@ -83,8 +90,10 @@
}
) {
failWithActual(
- "expected line content with $content but was " +
- lineContents.joinToString("\n")
+ simpleFact(
+ "expected line content with $content but was " +
+ lineContents.joinToString("\n")
+ )
)
}
}
@@ -96,18 +105,28 @@
fun onSource(source: Source) = apply {
if (locations.none { it.source == source }) {
failWithActual(
- """
+ simpleFact(
+ """
Expected diagnostic to be on $source but found it on
${locations.joinToString(",")}
- """.trimIndent()
+ """.trimIndent()
+ )
)
}
}
companion object {
+ private val FACTORY =
+ Factory<DiagnosticMessagesSubject, List<DiagnosticMessage>> { metadata, actual ->
+ DiagnosticMessagesSubject(metadata, actual)
+ }
+
fun assertThat(
diagnosticMessages: List<DiagnosticMessage>
- ): DiagnosticMessagesSubject =
- assertAbout(::DiagnosticMessagesSubject).that(diagnosticMessages)
+ ): DiagnosticMessagesSubject {
+ return Truth.assertAbout(FACTORY).that(
+ diagnosticMessages
+ )
+ }
}
}
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
index bcef38b..a0145e7 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.ExperimentalProcessingApi
import androidx.room.compiler.processing.XProcessingEnvConfig
import androidx.room.compiler.processing.XProcessingEnvironmentTestConfigProvider
@@ -32,6 +30,8 @@
import androidx.room.compiler.processing.util.runner.KspCompilationTestRunner
import androidx.room.compiler.processing.util.runner.TestCompilationParameters
import com.google.common.io.Files
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import java.io.File
import java.util.jar.JarEntry
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
index 959e75e..4e230a0 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
@@ -16,10 +16,10 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.ExperimentalProcessingApi
import androidx.room.compiler.processing.XProcessingEnv
import androidx.room.compiler.processing.XRoundEnv
+import com.google.common.truth.Truth
import kotlin.reflect.KClass
/**
@@ -100,7 +100,7 @@
}
private fun assertNotDisposed() {
- assertWithMessage("Cannot use a test invocation after it is disposed.")
+ Truth.assertWithMessage("Cannot use a test invocation after it is disposed.")
.that(disposed)
.isFalse()
}
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
index 1c5c650..66b21fa5 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
@@ -16,10 +16,10 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.compiler.DiagnosticsMessageCollector
import androidx.room.compiler.processing.util.compiler.steps.RawDiagnosticMessage
import androidx.room.compiler.processing.util.compiler.steps.RawDiagnosticMessage.Location
+import com.google.common.truth.Truth.assertThat
import javax.tools.Diagnostic
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.junit.Test
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
index d211727..7829bd4 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
@@ -16,8 +16,8 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.ExperimentalProcessingApi
+import com.google.common.truth.Truth.assertThat
import javax.tools.Diagnostic
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
index 7d71f16..ba43888 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
@@ -16,11 +16,10 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.ExperimentalProcessingApi
import androidx.room.compiler.processing.XElement
import androidx.room.compiler.processing.compat.XConverters.toXProcessing
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
@@ -339,8 +338,7 @@
}
}
- // Kruth doesn't support exceptions yet
- Truth.assertThat(result.exceptionOrNull())
+ assertThat(result.exceptionOrNull())
.hasCauseThat()
.hasMessageThat()
.contains(
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
index 773c6cd..40bdb48 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
@@ -16,7 +16,7 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class LoadFromDefaultEnvironmentConfigurationProviderTest {
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
index f05bf87..7df4df1 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
@@ -16,7 +16,7 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.TypeSpec
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt
index 203f195..98fd644 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt
@@ -16,8 +16,8 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.compiler.SourceSet
+import com.google.common.truth.Truth.assertThat
import org.jetbrains.kotlin.konan.file.File
import org.junit.Rule
import org.junit.Test
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
index cde233d..7f731db 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
@@ -16,8 +16,8 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.CompilationTestCapabilities.Config
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class TestConfigTest {
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
index 10cf032..32fc356 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing.util
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.ExperimentalProcessingApi
import androidx.room.compiler.processing.SyntheticJavacProcessor
import androidx.room.compiler.processing.SyntheticKspProcessor
@@ -29,7 +28,7 @@
import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
import androidx.room.compiler.processing.util.compiler.compile
import androidx.room.compiler.processing.util.compiler.steps.KaptCompilationStep
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -144,8 +143,7 @@
"c" to "d"
)
val handler: (XTestInvocation) -> Unit = {
- // Kruth MapSubject doesn't support containsAtLeastEntriesIn yet
- Truth.assertThat(it.processingEnv.options).containsAtLeastEntriesIn(testOptions)
+ assertThat(it.processingEnv.options).containsAtLeastEntriesIn(testOptions)
}
runJavaProcessorTest(
sources = emptyList(),
@@ -425,7 +423,7 @@
"org.jetbrains.kotlin.kapt3",
listOf("-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true")
).let { options ->
- assertThat(options).containsExactly("correctErrorTypes" to "true")
+ assertThat(options).containsExactly("correctErrorTypes", "true")
}
// zero args
@@ -441,7 +439,7 @@
"org.jetbrains.kotlin.kapt3",
listOf("-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true", "-verbose")
).let { options ->
- assertThat(options).containsExactly("correctErrorTypes" to "true")
+ assertThat(options).containsExactly("correctErrorTypes", "true")
}
// illegal format (missing "=")
@@ -476,8 +474,8 @@
)
).let { options ->
assertThat(options).containsExactly(
- "correctErrorTypes" to "true",
- "sources" to "build/kapt/sources"
+ "correctErrorTypes", "true",
+ "sources", "build/kapt/sources"
)
}
}
diff --git a/room/room-compiler-processing/build.gradle b/room/room-compiler-processing/build.gradle
index 0e71dd2..edd335d 100644
--- a/room/room-compiler-processing/build.gradle
+++ b/room/room-compiler-processing/build.gradle
@@ -84,9 +84,9 @@
testImplementation(libs.jsr250)
testImplementation(libs.ksp)
testImplementation(libs.kotlinMetadataJvm)
+ testImplementation(libs.testParameterInjector)
testImplementation(project(":room:room-compiler-processing-testing"))
testImplementation(project(":internal-testutils-common"))
- testImplementation(project(":kruth:kruth"))
}
tasks.withType(KotlinCompile).configureEach {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
index 13ace16..2012535 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
@@ -51,26 +51,27 @@
}
override val annotationValues: List<XAnnotationValue> by lazy {
- // In KSP the annotation members may be represented by constructor parameters in kotlin
- // source or by abstract methods in java source so we check both.
- val declarationConstructors = typeElement.let {
- // We access constructor using declaration since for compatibility with KAPT,
- // XTypeElement.getConstructors() will return an empty list for annotation classes.
- check(it is KspTypeElement)
- it.declaration.getConstructors().map {
- KspConstructorElement(
- env = env,
- declaration = it
- )
+ // Whether the annotation value is being treated as property or abstract method depends on
+ // the actual usage of the annotation. If the annotation is being used on Java source, then
+ // the annotation value will have a corresponding method element, otherwise, it will become
+ // a kotlin property.
+ val typesByName =
+ buildMap {
+ typeElement.getDeclaredMethods()
+ .filter {
+ if ((typeElement as KspTypeElement).declaration
+ .getConstructors()
+ .single().parameters
+ .isNotEmpty()) {
+ it.isKotlinPropertyMethod()
+ } else {
+ it.isAbstract()
+ }
+ }.forEach {
+ put(it.name, it.returnType)
+ put(it.jvmName, it.returnType)
+ }
}
- }
- val typesByName = if (declarationConstructors.single().parameters.isNotEmpty()) {
- declarationConstructors.single().parameters.associate { it.name to it.type }
- } else {
- typeElement.getDeclaredMethods()
- .filter { it.isAbstract() }
- .associate { it.name to it.returnType }
- }
// KSAnnotated.arguments isn't guaranteed to have the same ordering as declared in the
// annotation declaration, so we order it manually using a map from name to index.
val indexByName = typesByName.keys.mapIndexed { index, name -> name to index }.toMap()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/codegen/XTypeNameTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/codegen/XTypeNameTest.kt
index e93dfd6..7ba9a46 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/codegen/XTypeNameTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/codegen/XTypeNameTest.kt
@@ -16,8 +16,8 @@
package androidx.room.compiler.codegen
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.XNullability
+import com.google.common.truth.Truth.assertThat
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.SHORT
import com.squareup.kotlinpoet.javapoet.JClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
index fd831e9..52f12fa 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
@@ -16,12 +16,12 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.getField
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class FallbackLocationInformationTest {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt
index 7a380e5..66f907f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt
@@ -16,12 +16,12 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.CompilationTestCapabilities
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.runKaptTest
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class InternalModifierTest {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
index 623aa71..ea65ac3 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
@@ -16,13 +16,13 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.testcode.KotlinTestClass
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.asJTypeName
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.getParameter
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class KotlinMetadataTest {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
index 88e08ca..d486317 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.ksp.KspTypeElement
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
@@ -24,6 +23,7 @@
import androidx.room.compiler.processing.util.getAllFieldNames
import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.google.devtools.ksp.symbol.Origin
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MemoizedSequenceTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MemoizedSequenceTest.kt
index 704fd1d..2dc5ae9 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MemoizedSequenceTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MemoizedSequenceTest.kt
@@ -16,8 +16,8 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.MemoizedSequence
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class MemoizedSequenceTest {
@@ -74,7 +74,7 @@
val result = kotlin.runCatching {
memoized.iterator().next()
}
- assertThat(result.exceptionOrNull()).isInstanceOf<NoSuchElementException>()
+ assertThat(result.exceptionOrNull()).isInstanceOf(NoSuchElementException::class.java)
}
@Test
@@ -88,7 +88,7 @@
collected.add(iterator.next())
}
}
- assertThat(result.exceptionOrNull()).isInstanceOf<NoSuchElementException>()
+ assertThat(result.exceptionOrNull()).isInstanceOf(NoSuchElementException::class.java)
assertThat(collected).containsExactly(1, 2, 3)
}
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
index eee3dd6..95fc55e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.javac.JavacMethodElement
import androidx.room.compiler.processing.javac.JavacTypeElement
import androidx.room.compiler.processing.util.Source
@@ -27,6 +26,7 @@
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.testutils.generateAllEnumerations
import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterSpec
import java.io.File
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
index 0d4d22f..177ca7e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
@@ -16,13 +16,13 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.ksp.KSClassDeclarationAsOriginatingElement
import androidx.room.compiler.processing.ksp.KSFileAsOriginatingElement
import androidx.room.compiler.processing.ksp.KspTypeElement
import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.TypeSpec
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.TypeElement
@@ -57,7 +57,7 @@
assertThat(originatingElement).isNotNull()
if (it.isKsp) {
- assertThat(originatingElement).isInstanceOf<KSFileAsOriginatingElement>()
+ assertThat(originatingElement).isInstanceOf(KSFileAsOriginatingElement::class.java)
val originatingFile = (originatingElement as KSFileAsOriginatingElement).ksFile
assertThat(originatingFile)
@@ -65,7 +65,7 @@
(element as KspTypeElement).declaration.containingFile
)
} else {
- assertThat(originatingElement).isInstanceOf<TypeElement>()
+ assertThat(originatingElement).isInstanceOf(TypeElement::class.java)
assertThat((originatingElement as TypeElement).qualifiedName.toString())
.isEqualTo("foo.bar.Baz")
}
@@ -83,7 +83,7 @@
if (it.isKsp) {
assertThat(originatingElement)
- .isInstanceOf<KSClassDeclarationAsOriginatingElement>()
+ .isInstanceOf(KSClassDeclarationAsOriginatingElement::class.java)
val ksClassDeclaration =
(originatingElement as KSClassDeclarationAsOriginatingElement)
@@ -93,7 +93,7 @@
(element as KspTypeElement).declaration
)
} else {
- assertThat(originatingElement).isInstanceOf<TypeElement>()
+ assertThat(originatingElement).isInstanceOf(TypeElement::class.java)
assertThat((originatingElement as TypeElement).qualifiedName.toString())
.isEqualTo("com.google.devtools.ksp.processing.SymbolProcessor")
}
@@ -129,7 +129,7 @@
if (invocation.isKsp) {
assertThat(originatingElement)
- .isInstanceOf<KSFileAsOriginatingElement>()
+ .isInstanceOf(KSFileAsOriginatingElement::class.java)
val originatingFile = (originatingElement as KSFileAsOriginatingElement).ksFile
assertThat(originatingFile)
@@ -138,7 +138,7 @@
.field.declaration.containingFile
)
} else {
- assertThat(originatingElement).isInstanceOf<ExecutableElement>()
+ assertThat(originatingElement).isInstanceOf(ExecutableElement::class.java)
}
}
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TopLevelMembersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TopLevelMembersTest.kt
index 754142b..7e364c44 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TopLevelMembersTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TopLevelMembersTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.ksp.KspExecutableElement
import androidx.room.compiler.processing.ksp.KspFieldElement
import androidx.room.compiler.processing.util.Source
@@ -24,6 +23,7 @@
import androidx.room.compiler.processing.util.kspProcessingEnv
import androidx.room.compiler.processing.util.kspResolver
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertWithMessage
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
index 9639527..517cbd6 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
import androidx.room.compiler.processing.util.CONTINUATION_JCLASS_NAME
@@ -27,6 +26,7 @@
import androidx.room.compiler.processing.util.getField
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.kotlinpoet.LONG
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.javapoet.JParameterizedTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAssignmentTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAssignmentTest.kt
index 1fc6e3a..38444c6 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAssignmentTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAssignmentTest.kt
@@ -16,11 +16,11 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.getField
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
index 79977bf..4c27a38 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
@@ -16,13 +16,13 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.getField
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.getParameter
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index 102650d..7a761a0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
@@ -39,7 +37,8 @@
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
import androidx.room.compiler.processing.util.typeName
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.squareup.javapoet.ClassName
import org.junit.Test
import org.junit.runner.RunWith
@@ -172,8 +171,7 @@
}
annotation.getAsAnnotationBoxArray<OtherAnnotation>("otherAnnotationArray")
.let { boxArray ->
- // Kruth doesn't support arrays yet
- Truth.assertThat(boxArray).hasLength(2)
+ assertThat(boxArray).hasLength(2)
assertThat(boxArray[0].value.value).isEqualTo("other list 1")
assertThat(boxArray[1].value.value).isEqualTo("other list 2")
}
@@ -272,8 +270,7 @@
}
annotation.getAsAnnotationBoxArray<OtherAnnotation>("otherAnnotationArray")
.let { boxArray ->
- // Kruth doesn't support arrays yet
- Truth.assertThat(boxArray).hasLength(2)
+ assertThat(boxArray).hasLength(2)
assertThat(boxArray[0].value.value).isEqualTo("other list 1")
assertThat(boxArray[1].value.value).isEqualTo("other list 2")
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
index b25e8f1..8698722 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
import androidx.room.compiler.processing.compat.XConverters.toJavac
@@ -44,6 +42,8 @@
import androidx.room.compiler.processing.util.getParameter
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.squareup.kotlinpoet.javapoet.JAnnotationSpec
import com.squareup.kotlinpoet.javapoet.JClassName
import org.junit.Test
@@ -153,6 +153,40 @@
}
@Test
+ fun testJvmNameAnnotationValue() {
+ val kotlinSrc = Source.kotlin(
+ "MyAnnotation.kt",
+ """
+ @Target(AnnotationTarget.CLASS)
+ annotation class MyAnnotation(
+ @get:JvmName("stringParameter")
+ val stringParam: String,
+ val intParam: Int,
+ @get:JvmName("longParameter")
+ val longParam: Long
+ )
+ """.trimIndent()
+ )
+ val javaSrc = Source.java(
+ "Foo",
+ """
+ @MyAnnotation(stringParameter = "1", intParam = 2, longParameter = 3)
+ public class Foo {}
+ """.trimIndent()
+ )
+ runTest(sources = listOf(javaSrc, kotlinSrc)) { invocation ->
+ val typeElement = invocation.processingEnv.requireTypeElement("Foo")
+ val annotation =
+ typeElement.getAllAnnotations().single { it.qualifiedName == "MyAnnotation" }
+ assertThat(
+ annotation.annotationValues.map { it.value }
+ ).containsExactly(
+ "1", 2, 3.toLong()
+ ).inOrder()
+ }
+ }
+
+ @Test
fun readsAnnotationsDeclaredInSources() {
val source = Source.kotlin(
"MyClass.kt",
@@ -1389,7 +1423,7 @@
.that(type.getAllAnnotationTypeElements())
.isEmpty()
} else {
- assertWithMessage("$desc type: $type")
+ assertWithMessage("%s type: %s", desc, type.toString())
.that(type.getAllAnnotationTypeElements())
.containsExactly(a, b)
assertWithMessage("$desc type-argument: ${type.typeArguments[0]}")
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
index 866159c..f8b8666 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.JArrayTypeName
import androidx.room.compiler.processing.compat.XConverters.toJavac
import androidx.room.compiler.processing.util.Source
@@ -25,6 +24,7 @@
import androidx.room.compiler.processing.util.asKClassName
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.kotlinpoet.ARRAY
import com.squareup.kotlinpoet.BOOLEAN
import com.squareup.kotlinpoet.BOOLEAN_ARRAY
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
index 11397a3..96a2bc9 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.codegen.JArrayTypeName
import androidx.room.compiler.processing.ksp.KspProcessingEnv
import androidx.room.compiler.processing.ksp.createTypeReference
@@ -28,6 +26,8 @@
import androidx.room.compiler.processing.util.kspResolver
import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.javapoet.JTypeName
import com.squareup.kotlinpoet.javapoet.KTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
index 1763a8b..1e47fd0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.Subject
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
import androidx.room.compiler.processing.javac.JavacTypeElement
@@ -37,6 +35,7 @@
import androidx.room.compiler.processing.util.kspResolver
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
+import com.google.common.truth.Truth.assertThat
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
@@ -929,12 +928,13 @@
if (inv.isKsp) {
getTopLevelFunctionOrPropertyElements(inv, "foo.bar").forEach {
elem ->
- assertThat(elem.enclosingElement).isFileContainer(precompiled)
+ assertThat(elem.enclosingElement).isInstanceOf(
+ getFileContainerClass(precompiled))
}
} else {
inv.processingEnv.getTypeElementsFromPackage("foo.bar").forEach {
typeElement ->
- assertThat(typeElement).isInstanceOf<JavacTypeElement>()
+ assertThat(typeElement).isInstanceOf(JavacTypeElement::class.java)
assertThat(typeElement.enclosingElement).isNull()
typeElement.getEnclosedElements().forEach { elem ->
@@ -1008,10 +1008,12 @@
) { invocation, precompiled ->
if (invocation.isKsp) {
getTopLevelFunctionOrPropertyElements(invocation, "foo.bar").forEach { elem ->
- assertThat(elem.closestMemberContainer).isFileContainer(precompiled)
+ assertThat(elem.closestMemberContainer).isInstanceOf(
+ getFileContainerClass(precompiled))
if (elem is XExecutableElement) {
elem.parameters.forEach { p ->
- assertThat(p.closestMemberContainer).isFileContainer(precompiled)
+ assertThat(p.closestMemberContainer).isInstanceOf(
+ getFileContainerClass(precompiled))
}
}
}
@@ -1129,13 +1131,12 @@
}
.filterNotNull()
- private fun Subject<XElement>.isFileContainer(precompiled: Boolean) {
+ private fun getFileContainerClass(precompiled: Boolean) =
if (precompiled) {
- isInstanceOf<KspSyntheticFileMemberContainer>()
+ KspSyntheticFileMemberContainer::class.java
} else {
- isInstanceOf<KspFileMemberContainer>()
+ KspFileMemberContainer::class.java
}
- }
private fun runProcessorTestHelper(
sources: List<Source>,
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index f30d2d6..df1e04b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
import androidx.room.compiler.processing.util.CONTINUATION_JCLASS_NAME
@@ -30,6 +28,10 @@
import androidx.room.compiler.processing.util.getParameter
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.typeName
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
@@ -46,9 +48,8 @@
import java.io.IOException
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-@RunWith(JUnit4::class)
+@RunWith(TestParameterInjector::class)
class XExecutableElementTest {
@Test
fun basic() {
@@ -1347,4 +1348,85 @@
}
}
}
+
+ @Test
+ fun parameterNames(
+ @TestParameter isJava: Boolean,
+ @TestParameter isPrecompiled: Boolean,
+ @TestParameter hasParametersFlag: Boolean,
+ @TestParameter hasDebugFlag: Boolean
+ ) {
+ val javaSource = Source.java(
+ "foo.bar.Baz",
+ """
+ package foo.bar;
+ public class Baz {
+ private Baz(String param1) {}
+ }
+ """.trimIndent())
+ val kotlinSource = Source.kotlin(
+ "foo.bar.Baz.kt",
+ """
+ package foo.bar
+ class Baz private constructor(param1: String)
+ """.trimIndent())
+
+ val sources: List<Source> =
+ if (isPrecompiled) {
+ emptyList()
+ } else {
+ if (isJava) {
+ listOf(javaSource)
+ } else {
+ listOf(kotlinSource)
+ }
+ }
+ val javacArgs = buildList {
+ if (hasParametersFlag) {
+ // This is used to generate `MethodParameters` in class files
+ add("-parameters")
+ }
+ if (hasDebugFlag) {
+ // This is used to generate `LocalVariableTable` in class files
+ add("-g:vars")
+ }
+ }
+ val classes: List<File> =
+ if (isPrecompiled) {
+ if (isJava) {
+ compileFiles(listOf(javaSource), javacArguments = javacArgs)
+ } else {
+ compileFiles(listOf(kotlinSource), javacArguments = javacArgs)
+ }
+ } else {
+ emptyList()
+ }
+ runProcessorTest(sources = sources, classpath = classes) {
+ val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+ assertThat(element.getConstructors().single().parameters.single().name)
+ .isEqualTo(
+ if (isJava) {
+ if (isPrecompiled) {
+ if (hasParametersFlag) {
+ "param1"
+ } else {
+ if (it.isKsp) {
+ "p0"
+ } else { // Javac/KAPT
+ if (hasDebugFlag) {
+ "param1"
+ } else {
+ "arg0"
+ }
+ }
+ }
+ } else { // Java sources
+ "param1"
+ }
+ } else { // Kotlin sources or classes
+ "param1"
+ }
+ )
+ }
+ }
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
index 95adf50..4c88042 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
@@ -16,14 +16,13 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.CONTINUATION_JCLASS_NAME
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.UNIT_JCLASS_NAME
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.typeName
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
@@ -100,7 +99,7 @@
vararg subjects: XTypeElement,
callback: (XMethodType) -> Unit
) {
- Truth.assertThat(subjects).isNotEmpty() // Kruth doesn't support arrays yet
+ assertThat(subjects).isNotEmpty()
subjects.forEach {
callback(myInterface.getMethodByJvmName(methodName).asMemberOf(it.type))
callback(it.getMethodByJvmName(methodName).asMemberOf(it.type))
@@ -441,7 +440,7 @@
vararg subjects: XTypeElement,
callback: (XMethodType) -> Unit
) {
- Truth.assertThat(subjects).isNotEmpty() // Kruth doesn't support arrays yet
+ assertThat(subjects).isNotEmpty()
subjects.forEach {
callback(myInterface.getMethodByJvmName(methodName).asMemberOf(it.type))
callback(it.getMethodByJvmName(methodName).asMemberOf(it.type))
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XFilerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XFilerTest.kt
index 8029095..209d88e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XFilerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XFilerTest.kt
@@ -16,8 +16,8 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import kotlin.io.path.Path
import org.junit.Assert.fail
import org.junit.Test
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
index b2556ad..7574266 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
@@ -16,9 +16,9 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import javax.tools.Diagnostic
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
index 165deab..313e735c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.XNullability.NONNULL
import androidx.room.compiler.processing.XNullability.NULLABLE
import androidx.room.compiler.processing.XNullability.UNKNOWN
@@ -26,6 +25,7 @@
import androidx.room.compiler.processing.util.getParameter
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
+import com.google.common.truth.Truth.assertThat
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.UNIT
import com.squareup.kotlinpoet.javapoet.JTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvConfigTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvConfigTest.kt
index 65972b9..037b239 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvConfigTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvConfigTest.kt
@@ -16,8 +16,8 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class XProcessingEnvConfigTest {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index d2452bd..33ff8fc 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -16,12 +16,12 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.TypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
index d956499..5e4141e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.javac.JavacBasicAnnotationProcessor
import androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor
import androidx.room.compiler.processing.ksp.KspElement
@@ -31,6 +30,7 @@
import androidx.room.compiler.processing.util.compiler.compile
import androidx.room.compiler.processing.util.runProcessorTest
import com.google.common.truth.Truth.assertAbout
+import com.google.common.truth.Truth.assertThat
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRawTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRawTypeTest.kt
index f507ee8..6d6ca5b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRawTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRawTypeTest.kt
@@ -16,9 +16,9 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeVariableName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
index ac2658a..eb733a0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.processing.testcode.OtherAnnotation
@@ -25,6 +23,8 @@
import androidx.room.compiler.processing.util.getDeclaredMethodByJvmName
import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.UNIT
import com.squareup.kotlinpoet.javapoet.JTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index b69db6e..e6d31ce 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
@@ -32,6 +30,8 @@
import androidx.room.compiler.processing.util.getField
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.javapoet.JClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
index d14fea3..48a219f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
@@ -16,9 +16,9 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
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 1a68ad3..719fb84 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
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XTypeName.Companion.ANY_OBJECT
import androidx.room.compiler.codegen.XTypeName.Companion.UNAVAILABLE_KTYPE_NAME
@@ -38,6 +36,8 @@
import androidx.room.compiler.processing.util.kspResolver
import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.google.devtools.ksp.getClassDeclarationByName
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
index d229610..a287249 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing.compat
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.compat.XConverters.getProcessingEnv
import androidx.room.compiler.processing.compat.XConverters.toJavac
@@ -34,6 +33,7 @@
import androidx.room.compiler.processing.util.runProcessorTest
import com.google.auto.common.MoreElements
import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth.assertThat
import com.google.devtools.ksp.getDeclaredFunctions
import com.google.devtools.ksp.processing.impl.KSNameImpl
import com.squareup.javapoet.ClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
index 86d3822..15156c4 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
@@ -16,9 +16,9 @@
package androidx.room.compiler.processing.javac.kotlin
-import androidx.kruth.assertThat
import com.google.auto.common.MoreElements
import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import com.google.testing.compile.JavaFileObjects
import com.google.testing.compile.JavaSourcesSubjectFactory
import com.squareup.javapoet.ArrayTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
index eb676b6..3db463b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing.javac.kotlin
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.XProcessingEnvConfig
import androidx.room.compiler.processing.javac.JavacProcessingEnv
@@ -27,6 +25,8 @@
import androidx.room.compiler.processing.util.runJavaProcessorTest
import androidx.room.compiler.processing.util.runKaptTest
import androidx.room.compiler.processing.util.sanitizeAsJavaParameterName
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import javax.annotation.processing.ProcessingEnvironment
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.TypeElement
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
index a6e1551..8f9a50a 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.className
@@ -24,6 +23,7 @@
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
index 05c5504..61eee03 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
@@ -16,13 +16,13 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.className
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.kspResolver
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.google.devtools.ksp.getDeclaredProperties
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.squareup.javapoet.ClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
index 3053d5e..595d75c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.XMethodType
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.util.Source
@@ -24,6 +23,7 @@
import androidx.room.compiler.processing.util.getDeclaredMethodByJvmName
import androidx.room.compiler.processing.util.runKaptTest
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
index 7e2b275..688c746 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.XFieldElement
import androidx.room.compiler.processing.ksp.KspFieldElementTest.TestModifier.FINAL
import androidx.room.compiler.processing.ksp.KspFieldElementTest.TestModifier.PRIVATE
@@ -30,6 +28,8 @@
import androidx.room.compiler.processing.util.getField
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.typeName
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeVariableName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
index 73af6bc..62c1489 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.XAnnotation
import androidx.room.compiler.processing.XAnnotationValue
import androidx.room.compiler.processing.XElement
@@ -24,6 +23,7 @@
import androidx.room.compiler.processing.addOriginatingElement
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.symbol.KSClassDeclaration
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
index a0781bf..2e74202 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.XElement
import androidx.room.compiler.processing.XTypeElement
import androidx.room.compiler.processing.isConstructor
@@ -27,6 +26,7 @@
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspProcessingEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspProcessingEnvTest.kt
index eefdb4b..417c63b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspProcessingEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspProcessingEnvTest.kt
@@ -16,10 +16,10 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.util.kspResolver
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.TypeName
import org.junit.Test
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt
index c4e1e87..be5dea0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt
@@ -16,8 +16,8 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.TypeName
import kotlin.reflect.KClass
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
index a97fc52..68f7b5f5 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
@@ -16,8 +16,6 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.XExecutableElement
import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.util.CompilationTestCapabilities
@@ -26,6 +24,8 @@
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.runKaptTest
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.squareup.javapoet.TypeName
import org.junit.Test
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
index 1808bb1..9c69e84 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
@@ -16,7 +16,6 @@
package androidx.room.compiler.processing.ksp
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
@@ -35,6 +34,7 @@
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import com.squareup.kotlinpoet.TypeVariableName
import com.squareup.kotlinpoet.UNIT
import com.squareup.kotlinpoet.javapoet.JClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticFileMemberContainerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticFileMemberContainerTest.kt
index 134aed5..86ea00f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticFileMemberContainerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticFileMemberContainerTest.kt
@@ -16,14 +16,14 @@
package androidx.room.compiler.processing.ksp.synthetic
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.ksp.KspFieldElement
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.getField
import androidx.room.compiler.processing.util.kspResolver
import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import org.junit.Test
diff --git a/room/room-compiler/build.gradle b/room/room-compiler/build.gradle
index 6d06fe8..6c70d6b 100644
--- a/room/room-compiler/build.gradle
+++ b/room/room-compiler/build.gradle
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
import androidx.build.BuildOnServerKt
import androidx.build.LibraryType
import androidx.build.SdkHelperKt
@@ -29,12 +28,6 @@
id("com.github.johnrengelman.shadow")
}
-def antlrOut = "$buildDir/generated/antlr/grammar-gen/"
-sourceSets {
- main.java.srcDirs += "src/main/grammar-gen"
- main.java.srcDirs += antlrOut
-}
-
configurations {
/**
* shadowed is used for dependencies which we jarjar into the library jar instead of adding it
@@ -103,7 +96,6 @@
implementation(libs.apacheCommonsCodec)
implementation(libs.intellijAnnotations)
testImplementation(libs.truth)
- testImplementation(project(":kruth:kruth"))
testImplementation(libs.testParameterInjector)
testImplementation(libs.autoValue) // to access the processor in tests
testImplementation(libs.autoServiceAnnotations)
@@ -121,23 +113,27 @@
testImplementation(project(":internal-testutils-common"))
}
-def generateAntlrTask = task("generateAntlrGrammar", type: GenerateAntlrGrammar) {
- sqliteFile = file("$projectDir/SQLite.g4")
- antlrClasspath = configurations.compileClasspath
- outputDirectory = file(antlrOut)
+def generateAntlrTask = tasks.register("generateAntlrGrammar", GenerateAntlrGrammar) { task ->
+ task.getSqliteFile().set(layout.projectDirectory.file("SQLite.g4"))
+ task.getAntlrClasspath().from(configurations.compileClasspath)
+ task.getOutputDirectory().set(layout.buildDirectory.dir("generated/antlr/grammar-gen/"))
+}
+
+sourceSets {
+ main.java.srcDirs += generateAntlrTask.map { it.outputDirectory }
}
@CacheableTask
abstract class GenerateAntlrGrammar extends DefaultTask {
@PathSensitive(PathSensitivity.NONE)
@InputFile
- File sqliteFile
+ abstract RegularFileProperty getSqliteFile()
@Classpath
- FileCollection antlrClasspath
+ abstract ConfigurableFileCollection getAntlrClasspath()
@OutputDirectory
- File outputDirectory
+ abstract DirectoryProperty getOutputDirectory()
@Inject
abstract ExecOperations getExecOperations()
@@ -152,10 +148,10 @@
void generateAntlrGrammar() {
execOperations.javaexec {
mainClass.set("org.antlr.v4.Tool")
- classpath = antlrClasspath
- args "SQLite.g4",
+ classpath = getAntlrClasspath()
+ args getSqliteFile().asFile.get().absolutePath,
"-visitor",
- "-o", new File(outputDirectory, "androidx/room/parser").path,
+ "-o", new File(getOutputDirectory().asFile.get(), "androidx/room/parser").path,
"-package", "androidx.room.parser"
}
}
@@ -165,23 +161,23 @@
* Room compiler jarjars some dependencies. This task validates the published artifacts of room
* compiler to ensure dependencies are properly jarjarred.
*/
-class CheckArtifactTask extends DefaultTask {
+abstract class CheckArtifactTask extends DefaultTask {
@InputFiles
- FileCollection artifactInputs = project.objects.fileCollection()
+ abstract ConfigurableFileCollection getArtifactInputs()
@InputFile
- File pomFile
+ abstract RegularFileProperty getPomFile()
@OutputFile
- File result = new File(project.buildDir, "checkArtifactOutput.txt")
+ abstract RegularFileProperty getResult()
/**
* Checks the publish task's artifacts to make sure the classes.jar does include jarjarred
* antlr classes.
*/
- def validatePublishTaskOutputs() {
- if (artifactInputs.files.isEmpty()) {
+ void validatePublishTaskOutputs() {
+ if (getArtifactInputs().files.isEmpty()) {
throw new GradleException("Couldn't find the classes.jar for the room-compiler " +
"artifact. Ensure that publish is setup properly.")
}
- artifactInputs.forEach {
+ getArtifactInputs().forEach {
validateJarContents(it)
}
}
@@ -190,7 +186,7 @@
* Traverses the given jar file, looks for the classes that should be jarjarred and validates
* their location.
*/
- def validateJarContents(File jarFile) {
+ static void validateJarContents(File jarFile) {
Boolean found = false
ZipFile zip = new ZipFile(jarFile)
try {
@@ -223,11 +219,12 @@
* Checks the generated pom file to ensure it does not depend on any jarjarred dependencies
* but still depends on others.
*/
- def validatePomTaskOutputs() {
- if (!pomFile.canRead()) {
+ void validatePomTaskOutputs() {
+ File pom = getPomFile().asFile.get()
+ if (!pom.canRead()) {
throw new GradleException("Cannot find the pom file for room-compiler")
}
- def pomContents = pomFile.newReader().text
+ def pomContents = pom.newReader().text
if (pomContents.contains("antlr")) {
throw new GradleException("Room-compiler pom file should not depend on antlr.\n" +
"Pom Contents:\n $pomContents")
@@ -239,24 +236,29 @@
}
@TaskAction
- def validate() {
- result.write("fail\n")
+ void validate() {
+ getResult().asFile.get().write("fail\n")
validatePublishTaskOutputs()
validatePomTaskOutputs()
// have a no-op output to make gradle happy w/ input/output checking.
- result.write("ok\n")
+ getResult().asFile.get().write("ok\n")
}
}
-def checkArtifactContentsTask = tasks.register("checkArtifactTask", CheckArtifactTask) {
- def pomTask = (GenerateMavenPom) project.tasks.named("generatePomFileForMavenPublication").get()
- it.pomFile = pomTask.destination
+def checkArtifactContentsTask = tasks.register("checkArtifact", CheckArtifactTask) { task ->
+ task.getResult().set(layout.buildDirectory.file("checkArtifactOutput.txt"))
+ def pomTask = (TaskProvider<GenerateMavenPom>) project.tasks.named("generatePomFileForMavenPublication")
+ task.getPomFile().set(
+ project.objects.fileProperty().fileProvider(
+ pomTask.map { it.destination }
+ )
+ )
}
afterEvaluate {
def publishTaskProvider = project.tasks.named("publishMavenPublicationToMavenRepository")
- checkArtifactContentsTask.configure {
- it.artifactInputs.from {
+ checkArtifactContentsTask.configure { checkArtifactTask ->
+ checkArtifactTask.getArtifactInputs().from {
publishTaskProvider.map {
((PublishToMavenRepository) it).getPublication().artifacts.matching {
it.classifier == null
@@ -271,11 +273,6 @@
// make sure we validate published artifacts on the build server.
BuildOnServerKt.addToBuildOnServer(project, checkArtifactContentsTask)
-def tasksThatDependOnAntlr = ["compileKotlin", "sourceJar", "kotlinSourcesJar", "generateKmpDocs"]
-tasks.matching { tasksThatDependOnAntlr.contains(it.name) }.configureEach {
- it.dependsOn(generateAntlrTask)
-}
-
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
freeCompilerArgs += [
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
index 1c19e7f..5f216dc 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
@@ -16,7 +16,6 @@
package androidx.room.ext
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.processing.XMethodElement
@@ -24,6 +23,7 @@
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
index 0db9225..346c9fe 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
@@ -16,11 +16,11 @@
package androidx.room.parser
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class SQLTypeAffinityTest {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/parser/SqlParserTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/parser/SqlParserTest.kt
index b3e20de..9d07223 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/parser/SqlParserTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/parser/SqlParserTest.kt
@@ -15,7 +15,7 @@
*/
package androidx.room.parser
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.CoreMatchers.not
import org.hamcrest.MatcherAssert.assertThat
@@ -277,31 +277,31 @@
@Test
fun hasTopStarProjection() {
SqlParser.parse("SELECT * FROM Foo").let {
- assertThat(it.hasTopStarProjection).isTrue()
+ Truth.assertThat(it.hasTopStarProjection).isTrue()
}
SqlParser.parse("SELECT Foo.* FROM Foo").let {
- assertThat(it.hasTopStarProjection).isTrue()
+ Truth.assertThat(it.hasTopStarProjection).isTrue()
}
SqlParser.parse("SELECT 0 as new, Foo.* FROM Foo").let {
- assertThat(it.hasTopStarProjection).isTrue()
+ Truth.assertThat(it.hasTopStarProjection).isTrue()
}
SqlParser.parse("SELECT f.* FROM Foo f").let {
- assertThat(it.hasTopStarProjection).isTrue()
+ Truth.assertThat(it.hasTopStarProjection).isTrue()
}
SqlParser.parse("SELECT id FROM Foo").let {
- assertThat(it.hasTopStarProjection).isFalse()
+ Truth.assertThat(it.hasTopStarProjection).isFalse()
}
SqlParser.parse("SELECT id FROM (SELECT * FROM Foo)").let {
- assertThat(it.hasTopStarProjection).isFalse()
+ Truth.assertThat(it.hasTopStarProjection).isFalse()
}
SqlParser.parse("SELECT COUNT(*) FROM Foo").let {
- assertThat(it.hasTopStarProjection).isFalse()
+ Truth.assertThat(it.hasTopStarProjection).isFalse()
}
SqlParser.parse("SELECT (SELECT * FROM Foo LIMIT 1) == 1 as uno FROM Foo").let {
- assertThat(it.hasTopStarProjection).isFalse()
+ Truth.assertThat(it.hasTopStarProjection).isFalse()
}
SqlParser.parse("INSERT INTO Foo VALUES (:param)").let {
- assertThat(it.hasTopStarProjection).isNull()
+ Truth.assertThat(it.hasTopStarProjection).isNull()
}
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
index b3a46be..a4da24b 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
@@ -16,7 +16,6 @@
package androidx.room.processor
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.VisibilityModifier
import androidx.room.compiler.codegen.XAnnotationSpec
@@ -39,6 +38,7 @@
import androidx.room.processor.ProcessorErrors.TYPE_CONVERTER_UNBOUND_GENERIC
import androidx.room.testing.context
import androidx.room.vo.CustomTypeConverter
+import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.TypeVariableName
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
index dd5133d..076ccf0 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
@@ -17,7 +17,6 @@
package androidx.room.processor
import COMMON
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.isTypeElement
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
@@ -31,6 +30,7 @@
import androidx.room.vo.Dao
import androidx.room.vo.ReadQueryMethod
import androidx.room.vo.Warning
+import com.google.common.truth.Truth
import createVerifierFromEntitiesAndViews
import java.io.File
import org.hamcrest.CoreMatchers.`is`
@@ -277,7 +277,7 @@
val dbType = invocation.context.processingEnv.requireType(ROOM_DB)
val daoProcessor =
DaoProcessor(invocation.context, dao, dbType, null)
- assertThat(daoProcessor.context.logger.suppressedWarnings)
+ Truth.assertThat(daoProcessor.context.logger.suppressedWarnings)
.containsExactly(Warning.CURSOR_MISMATCH)
}
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseProcessorTest.kt
index c8ce1d7..a4c6126 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseProcessorTest.kt
@@ -17,7 +17,6 @@
package androidx.room.processor
import COMMON
-import androidx.kruth.assertThat
import androidx.room.DatabaseProcessingStep
import androidx.room.RoomProcessor
import androidx.room.compiler.codegen.CodeLanguage
@@ -42,6 +41,7 @@
import androidx.room.vo.ReadQueryMethod
import androidx.room.vo.Warning
import com.google.auto.service.processor.AutoServiceProcessor
+import com.google.common.truth.Truth.assertThat
import java.io.File
import java.io.FileOutputStream
import java.net.URL
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseViewProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseViewProcessorTest.kt
index a3f9328..93870ff 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseViewProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseViewProcessorTest.kt
@@ -16,7 +16,6 @@
package androidx.room.processor
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.runProcessorTest
@@ -25,6 +24,7 @@
import androidx.room.testing.context
import androidx.room.verifier.ColumnInfo
import androidx.room.vo.DatabaseView
+import com.google.common.truth.Truth.assertThat
import createVerifierFromEntitiesAndViews
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
index 6781bb3..c71cc23 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
@@ -16,7 +16,6 @@
package androidx.room.processor
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.XTypeName
import androidx.room.parser.SQLTypeAffinity
@@ -24,6 +23,7 @@
import androidx.room.vo.Field
import androidx.room.vo.FieldGetter
import androidx.room.vo.FieldSetter
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
index 45f42288..d00d449 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
@@ -17,7 +17,6 @@
package androidx.room.processor
import COMMON
-import androidx.kruth.assertThat
import androidx.room.Dao
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.XClassName
@@ -39,6 +38,7 @@
import androidx.room.solver.shortcut.result.InsertOrUpsertMethodAdapter
import androidx.room.testing.context
import androidx.room.vo.InsertOrUpsertShortcutMethod
+import com.google.common.truth.Truth.assertThat
import kotlin.reflect.KClass
import org.junit.Test
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertionMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertionMethodProcessorTest.kt
index 3550ce5..55f7026 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertionMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertionMethodProcessorTest.kt
@@ -16,7 +16,6 @@
package androidx.room.processor
-import androidx.kruth.assertThat
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.compiler.processing.XMethodElement
@@ -26,6 +25,7 @@
import androidx.room.processor.ProcessorErrors.INSERT_MULTI_PARAM_SINGLE_RETURN_MISMATCH
import androidx.room.processor.ProcessorErrors.INSERT_SINGLE_PARAM_MULTI_RETURN_MISMATCH
import androidx.room.vo.InsertionMethod
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
index a4c0fd9..723aea9 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
@@ -17,7 +17,6 @@
package androidx.room.processor
import COMMON
-import androidx.kruth.assertThat
import androidx.room.Embedded
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.processing.XFieldElement
@@ -42,6 +41,7 @@
import androidx.room.vo.FieldSetter
import androidx.room.vo.Pojo
import androidx.room.vo.RelationCollector
+import com.google.common.truth.Truth
import java.io.File
import org.hamcrest.CoreMatchers.instanceOf
import org.hamcrest.CoreMatchers.`is`
@@ -2092,7 +2092,7 @@
it.name
}
val stringType = invocation.context.COMMON_TYPES.STRING
- assertThat(
+ Truth.assertThat(
fields["isbn"]?.getter
).isEqualTo(
FieldGetter(
@@ -2102,7 +2102,7 @@
callType = CallType.SYNTHETIC_METHOD
)
)
- assertThat(
+ Truth.assertThat(
fields["isbn"]?.setter
).isEqualTo(
FieldSetter(
@@ -2113,7 +2113,7 @@
)
)
- assertThat(
+ Truth.assertThat(
fields["isbn2"]?.getter
).isEqualTo(
FieldGetter(
@@ -2123,7 +2123,7 @@
callType = CallType.SYNTHETIC_METHOD
)
)
- assertThat(
+ Truth.assertThat(
fields["isbn2"]?.setter
).isEqualTo(
FieldSetter(
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index 13d0293..2fde4d7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -17,7 +17,6 @@
package androidx.room.processor
import COMMON
-import androidx.kruth.assertThat
import androidx.room.Dao
import androidx.room.Query
import androidx.room.compiler.codegen.CodeLanguage
@@ -58,6 +57,7 @@
import androidx.room.vo.ReadQueryMethod
import androidx.room.vo.Warning
import androidx.room.vo.WriteQueryMethod
+import com.google.common.truth.Truth.assertThat
import createVerifierFromEntitiesAndViews
import mockElementAndType
import org.hamcrest.CoreMatchers.hasItem
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
index a5a8112..e7d8207 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
@@ -17,7 +17,6 @@
package androidx.room.processor
import COMMON
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_LONG
@@ -35,6 +34,7 @@
import androidx.room.vo.Index
import androidx.room.vo.Pojo
import androidx.room.vo.columnNames
+import com.google.common.truth.Truth.assertThat
import org.hamcrest.CoreMatchers.hasItems
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
index 997f6d3..cfd3ada 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
@@ -16,7 +16,6 @@
package androidx.room.solver
-import androidx.kruth.assertThat
import androidx.room.RoomKspProcessor
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.processing.XType
@@ -34,6 +33,7 @@
import androidx.room.testing.context
import androidx.room.vo.BuiltInConverterFlags
import androidx.room.writer.DaoWriter
+import com.google.common.truth.Truth.assertThat
import javax.tools.Diagnostic
import org.junit.Rule
import org.junit.Test
@@ -800,7 +800,7 @@
)
) { invocation ->
val store = invocation.createStore(*selectedConverters)
- assertThat(store).isInstanceOf<NullAwareTypeConverterStore>()
+ assertThat(store).isInstanceOf(NullAwareTypeConverterStore::class.java)
val myClassTypeElement = invocation.processingEnv.requireTypeElement(
"MyClass"
)
@@ -860,7 +860,7 @@
)
) { invocation ->
val store = invocation.createStore(*selectedConverters)
- assertThat(store).isInstanceOf<NullAwareTypeConverterStore>()
+ assertThat(store).isInstanceOf(NullAwareTypeConverterStore::class.java)
val myClassTypeElement = invocation.processingEnv.requireTypeElement(
"MyClass"
)
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index 53c6258..f5c3ac3 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -17,7 +17,6 @@
package androidx.room.solver
import COMMON
-import androidx.kruth.assertThat
import androidx.paging.DataSource
import androidx.paging.PagingSource
import androidx.room.Dao
@@ -77,6 +76,7 @@
import androidx.room.testing.context
import androidx.room.vo.BuiltInConverterFlags
import androidx.room.vo.ReadQueryMethod
+import com.google.common.truth.Truth.assertThat
import org.hamcrest.CoreMatchers.instanceOf
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.CoreMatchers.notNullValue
@@ -351,7 +351,7 @@
)
assertThat(adapter).isNotNull()
- assertThat(adapter).isInstanceOf<UuidColumnTypeAdapter>()
+ assertThat(adapter).isInstanceOf(UuidColumnTypeAdapter::class.java)
}
}
@@ -1556,7 +1556,7 @@
isMultipleParameter = true
)
assertThat(adapter).isNotNull()
- assertThat(adapter).isInstanceOf<CollectionQueryParameterAdapter>()
+ assertThat(adapter).isInstanceOf(CollectionQueryParameterAdapter::class.java)
}
}
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterCostTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterCostTest.kt
index 7029a46..0443054 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterCostTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterCostTest.kt
@@ -16,8 +16,8 @@
package androidx.room.solver
-import androidx.kruth.assertThat
import androidx.room.solver.types.TypeConverter.Cost
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
class TypeConverterCostTest {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
index d2fc717..df7480e 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
@@ -16,7 +16,6 @@
package androidx.room.solver
-import androidx.kruth.assertThat
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.runProcessorTest
@@ -26,6 +25,7 @@
import androidx.room.solver.types.TypeConverter
import androidx.room.testing.context
import androidx.room.vo.BuiltInConverterFlags
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
index 650b393..e334883 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
@@ -16,7 +16,6 @@
package androidx.room.solver.query
-import androidx.kruth.assertThat
import androidx.room.Dao
import androidx.room.Query
import androidx.room.compiler.codegen.CodeLanguage
@@ -28,6 +27,7 @@
import androidx.room.processor.QueryMethodProcessor
import androidx.room.testing.context
import androidx.room.writer.QueryWriter
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
index b9251a5..e886664 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
@@ -16,10 +16,10 @@
package androidx.room.testing
-import androidx.kruth.assertThat
import androidx.room.compiler.processing.util.CompilationTestCapabilities
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
index 5c766ef..bec007f 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
@@ -16,7 +16,6 @@
package androidx.room.util
-import androidx.kruth.assertThat
import androidx.room.migration.bundle.DatabaseBundle
import androidx.room.migration.bundle.EntityBundle
import androidx.room.migration.bundle.FieldBundle
@@ -27,6 +26,7 @@
import androidx.room.migration.bundle.TABLE_NAME_PLACEHOLDER
import androidx.room.processor.ProcessorErrors
import androidx.room.vo.AutoMigration
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.fail
import org.junit.Test
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
index 7c76004..4a2e2f7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
@@ -16,7 +16,7 @@
package androidx.room.util
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.fail
import org.junit.Test
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
index 3ee50134..6d6fcc7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
@@ -17,7 +17,6 @@
package androidx.room.writer
import COMMON
-import androidx.kruth.StringSubject
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.processing.XTypeElement
import androidx.room.compiler.processing.util.Source
@@ -25,6 +24,7 @@
import androidx.room.ext.RoomTypeNames.ROOM_DB
import androidx.room.processor.DaoProcessor
import androidx.room.testing.context
+import com.google.common.truth.StringSubject
import createVerifierFromEntitiesAndViews
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.junit.Test
diff --git a/settings.gradle b/settings.gradle
index 6d5d849..ae52660 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -973,6 +973,7 @@
includeProject(":slice:slice-test", [BuildType.MAIN])
includeProject(":slice:slice-view", [BuildType.MAIN])
includeProject(":slidingpanelayout:slidingpanelayout", [BuildType.MAIN, BuildType.FLAN])
+includeProject(":slidingpanelayout:slidingpanelayout-testapp", [BuildType.MAIN])
includeProject(":sqlite:integration-tests:inspection-room-testapp", [BuildType.MAIN])
includeProject(":sqlite:integration-tests:inspection-sqldelight-testapp", [BuildType.MAIN])
includeProject(":sqlite:sqlite", [BuildType.MAIN, BuildType.COMPOSE])
diff --git a/slidingpanelayout/slidingpanelayout-testapp/build.gradle b/slidingpanelayout/slidingpanelayout-testapp/build.gradle
new file mode 100644
index 0000000..7d5ebec
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 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.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.application")
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ api(libs.kotlinStdlib)
+ // Add dependencies here
+ implementation(project(":slidingpanelayout:slidingpanelayout"))
+ implementation("androidx.recyclerview:recyclerview:1.2.1")
+}
+
+android {
+ namespace "androidx.slidingpanelayout.demo"
+}
+
+androidx {
+ name = "androidx.slidingpanelayout:slidingpanelayout-demo"
+ type = LibraryType.SAMPLES
+ inceptionYear = "2023"
+ description = "SlidingPaneLayout Demos"
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e026b28
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+ <application
+ android:allowBackup="true"
+ android:label="SlidingPaneLayout Demo"
+ android:supportsRtl="true">
+
+ <activity android:name="androidx.slidingpanelayout.SlidingPaneLayoutDemos"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="androidx.slidingpanelayout.SlidingPaneLayoutSample"
+ android:exported="true" />
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt
new file mode 100644
index 0000000..5415371
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.slidingpanelayout
+
+import android.app.Activity
+
+class DemoItem<T : Activity>(
+ val clazz: Class<T>,
+ val title: String,
+ val description: String
+)
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt
new file mode 100644
index 0000000..93fac85
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.slidingpanelayout
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class ItemAdapter(private val items: List<DemoItem<*>>) : RecyclerView.Adapter<ItemViewHolder>() {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ val rootView = inflater.inflate(R.layout.item_viewholder, parent, false)
+ return ItemViewHolder(rootView)
+ }
+
+ override fun getItemCount(): Int {
+ return items.size
+ }
+
+ override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
+ holder.bind(items[position])
+ }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt
new file mode 100644
index 0000000..2c48ee1
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.slidingpanelayout
+
+import android.content.Intent
+import android.view.View
+import android.widget.Button
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class ItemViewHolder(rootView: View) : RecyclerView.ViewHolder(rootView) {
+
+ private val titleTextView = rootView.findViewById<TextView>(R.id.title_textview)
+ private val descriptionTextView = rootView.findViewById<TextView>(R.id.description_textview)
+ private val launchButton = rootView.findViewById<Button>(R.id.button_launch)
+
+ fun bind(item: DemoItem<*>) {
+ titleTextView.text = item.title
+ descriptionTextView.text = item.description
+ launchButton.setOnClickListener { view ->
+ val context = view.context
+ val intent = Intent(context, item.clazz)
+ context.startActivity(intent)
+ }
+ }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt
new file mode 100644
index 0000000..b6d4a1e1
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.slidingpanelayout
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class SlidingPaneLayoutDemos : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_slidingpanelayout_demos)
+ val recyclerView = findViewById<RecyclerView>(R.id.demo_recyclerview)
+
+ recyclerView.adapter = ItemAdapter(
+ listOf(
+ DemoItem(
+ SlidingPaneLayoutSample::class.java,
+ "SlidingPaneLayoutSample",
+ "Basic SlidingPaneLayoutSample"
+ )
+ )
+ )
+ }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt
new file mode 100644
index 0000000..59463f5
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.slidingpanelayout
+
+import android.app.Activity
+import android.os.Bundle
+
+class SlidingPaneLayoutSample : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(androidx.slidingpanelayout.demo.R.layout.activity_slidingpanelayout_sample)
+ }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml
new file mode 100644
index 0000000..58bbd7b
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+ -->
+
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:id="@+id/demo_recyclerview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
+
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml
new file mode 100644
index 0000000..436cc28
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+ -->
+
+<androidx.slidingpanelayout.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <View
+ android:background="@android:color/holo_blue_light"
+ android:layout_width="200dp"
+ android:layout_height="match_parent"/>
+
+
+ <View
+ android:background="@android:color/holo_green_dark"
+ android:layout_width="200dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"/>
+
+</androidx.slidingpanelayout.widget.SlidingPaneLayout>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml
new file mode 100644
index 0000000..631c063
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/title_textview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/description_textview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/button_launch"
+ android:text="@string/launch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cbe5256
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<resources>
+ <string name="launch">Launch</string>
+</resources>
\ No newline at end of file
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
index a8c0775..93bcbf8 100644
--- a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
@@ -24,7 +24,6 @@
import com.android.build.api.variant.SourceDirectories
import com.android.build.api.variant.Variant
import com.android.utils.usLocaleCapitalize
-import java.io.File
import org.gradle.api.Action
import org.gradle.api.DomainObjectCollection
import org.gradle.api.Project
@@ -36,6 +35,7 @@
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.TaskOutputFilePropertyBuilder
import org.gradle.api.tasks.TaskProvider
// Gradle task group used to identify Stable AIDL tasks.
@@ -68,7 +68,6 @@
}
}
-@Suppress("UnstableApiUsage") // SourceDirectories.Flat
fun registerCompileAidlApi(
project: Project,
variant: Variant,
@@ -277,18 +276,16 @@
/**
* Tells Gradle to skip running this task, even if this task declares no output files.
*/
-private fun Task.cacheEvenIfNoOutputs() {
- this.outputs.file(this.getPlaceholderOutput())
-}
+private fun Task.cacheEvenIfNoOutputs(): TaskOutputFilePropertyBuilder =
+ outputs.file(getPlaceholderOutput())
/**
* Returns an unused output path that we can pass to Gradle to prevent Gradle from thinking that we
* forgot to declare outputs of this task, and instead to skip this task if its inputs are
* unchanged.
*/
-private fun Task.getPlaceholderOutput(): File {
- return File(this.project.buildDir, "placeholderOutput/" + this.name.replace(":", "-"))
-}
+private fun Task.getPlaceholderOutput(): Provider<RegularFile> =
+ project.layout.buildDirectory.file("placeholderOutput/${name.replace(':', '-')}")
private fun computeTaskName(prefix: String, variant: Variant, suffix: String) =
"$prefix${variant.name.usLocaleCapitalize()}$suffix"
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
index 6b7931d..ad68c56 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
@@ -224,6 +224,11 @@
/**
* Sets the current tool type to use for motion events.
* @see MotionEvent#getToolType(int)
+ * @see MotionEvent#TOOL_TYPE_FINGER
+ * @see MotionEvent#TOOL_TYPE_STYLUS
+ * @see MotionEvent#TOOL_TYPE_MOUSE
+ * @see MotionEvent#TOOL_TYPE_ERASER
+ * @see MotionEvent#TOOL_TYPE_UNKNOWN
*/
public @NonNull Configurator setToolType(final int toolType) {
mToolType = toolType;
diff --git a/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt b/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
index 236b362..844f968 100644
--- a/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
+++ b/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
@@ -33,6 +33,7 @@
*
* See [ParameterizedHelperTest] for more examples.
*/
+// TODO(kuanyingchou): Remove and replace with TestParameterInjector"
fun generateAllEnumerations(vararg args: List<Any>): List<Array<Any>> =
generateAllEnumerationsIteratively(args.toList()).map { it.toTypedArray() }
diff --git a/transition/transition/api/current.txt b/transition/transition/api/current.txt
index 8b6b763..b6bf3ae 100644
--- a/transition/transition/api/current.txt
+++ b/transition/transition/api/current.txt
@@ -209,10 +209,10 @@
method public static void beginDelayedTransition(android.view.ViewGroup);
method public static void beginDelayedTransition(android.view.ViewGroup, androidx.transition.Transition?);
method public static androidx.transition.TransitionSeekController? controlDelayedTransition(android.view.ViewGroup, androidx.transition.Transition);
+ method public static androidx.transition.TransitionSeekController? createSeekController(androidx.transition.Scene, androidx.transition.Transition);
method public static void endTransitions(android.view.ViewGroup?);
method public static void go(androidx.transition.Scene);
method public static void go(androidx.transition.Scene, androidx.transition.Transition?);
- method public static androidx.transition.TransitionSeekController? seekTo(androidx.transition.Scene, androidx.transition.Transition);
method public void setTransition(androidx.transition.Scene, androidx.transition.Scene, androidx.transition.Transition?);
method public void setTransition(androidx.transition.Scene, androidx.transition.Transition?);
method public void transitionTo(androidx.transition.Scene);
diff --git a/transition/transition/api/restricted_current.txt b/transition/transition/api/restricted_current.txt
index bd19de1..3f44ab2 100644
--- a/transition/transition/api/restricted_current.txt
+++ b/transition/transition/api/restricted_current.txt
@@ -242,10 +242,10 @@
method public static void beginDelayedTransition(android.view.ViewGroup);
method public static void beginDelayedTransition(android.view.ViewGroup, androidx.transition.Transition?);
method public static androidx.transition.TransitionSeekController? controlDelayedTransition(android.view.ViewGroup, androidx.transition.Transition);
+ method public static androidx.transition.TransitionSeekController? createSeekController(androidx.transition.Scene, androidx.transition.Transition);
method public static void endTransitions(android.view.ViewGroup?);
method public static void go(androidx.transition.Scene);
method public static void go(androidx.transition.Scene, androidx.transition.Transition?);
- method public static androidx.transition.TransitionSeekController? seekTo(androidx.transition.Scene, androidx.transition.Transition);
method public void setTransition(androidx.transition.Scene, androidx.transition.Scene, androidx.transition.Transition?);
method public void setTransition(androidx.transition.Scene, androidx.transition.Transition?);
method public void transitionTo(androidx.transition.Scene);
diff --git a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
index 852db4e..fd55d1a 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
+++ b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
@@ -1060,7 +1060,7 @@
}
rule.runOnUiThread {
- val controller = TransitionManager.seekTo(scene2, Fade())
+ val controller = TransitionManager.createSeekController(scene2, Fade())
assertThat(controller).isNotNull()
seekController = controller!!
}
@@ -1096,7 +1096,7 @@
}
rule.runOnUiThread {
- TransitionManager.seekTo(scene2, NoSeekingTransition())
+ TransitionManager.createSeekController(scene2, NoSeekingTransition())
}
}
@@ -1111,7 +1111,7 @@
rule.runOnUiThread {
TransitionManager.go(scene2, Fade())
- assertThat(TransitionManager.seekTo(scene1, Fade())).isNull()
+ assertThat(TransitionManager.createSeekController(scene1, Fade())).isNull()
}
}
}
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionManager.java b/transition/transition/src/main/java/androidx/transition/TransitionManager.java
index 2fdb6d5..261738f4 100644
--- a/transition/transition/src/main/java/androidx/transition/TransitionManager.java
+++ b/transition/transition/src/main/java/androidx/transition/TransitionManager.java
@@ -365,7 +365,7 @@
* {@link Transition#isSeekingSupported()}.
*/
@Nullable
- public static TransitionSeekController seekTo(
+ public static TransitionSeekController createSeekController(
@NonNull Scene scene,
@NonNull Transition transition
) {
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java b/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
index f9b00c5..9e4f228 100644
--- a/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
+++ b/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
@@ -44,7 +44,7 @@
long getCurrentPlayTimeMillis();
/**
- * @return The fraction, between 0 and 1, of the progress of the transition.
+ * @return The fraction, between 0 and 1, inclusive, of the progress of the transition.
* @see #getCurrentPlayTimeMillis()
*/
@FloatRange(from = 0.0, to = 1.0)
@@ -132,7 +132,7 @@
void addOnProgressChangedListener(@NonNull Consumer<TransitionSeekController> consumer);
/**
- * Remove a listener previously added in {@link #addOnProgressChangedListener(Consumer)}\
+ * Remove a listener previously added in {@link #addOnProgressChangedListener(Consumer)}
* @param consumer The listener to be removed.
*/
void removeOnProgressChangedListener(@NonNull Consumer<TransitionSeekController> consumer);
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
index c0832bb..2546e1c 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
@@ -645,7 +645,7 @@
@get:ExperimentalTvMaterial3Api
val LocalAbsoluteTonalElevation = compositionLocalOf { 0.dp }
-private val AcceptableKeys = hashSetOf(
+private val AcceptableKeys = intArrayOf(
NativeKeyEvent.KEYCODE_DPAD_CENTER,
NativeKeyEvent.KEYCODE_ENTER,
NativeKeyEvent.KEYCODE_NUMPAD_ENTER
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
index e389f5c..9284438 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
@@ -34,6 +34,8 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
/**
@@ -80,14 +82,16 @@
.clickable(
>
enabled = enabled,
- role = Role.Button,
+ role = null, // provide the role via Modifier.semantics
interactionSource = interactionSource,
indication = rememberRipple(),
)
.then(
// Make sure modifier ordering is clip > clickable > padding > size,
// so that the ripple applies to the entire button shape and size.
+ // Then, apply semantics to apply the default semantic role (can be overridden)
modifier
+ .semantics { role = Role.Button }
)
.size(buttonSize)
.clip(shape) // Clip for the painted background area after size has been applied.
diff --git a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
index 84db9176..ae4e264 100644
--- a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
+++ b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
@@ -767,11 +767,14 @@
setContentWithTheme {
background = MaterialTheme.colors.surface
buttonColor = MaterialTheme.colors.primary
- content(
- Modifier
- .testTag(TEST_TAG)
- .padding(padding)
- .background(background))
+ Box(Modifier.background(background)) {
+ content(
+ Modifier
+ .testTag(TEST_TAG)
+ .padding(padding)
+ .background(background)
+ )
+ }
}
onNodeWithTag(TEST_TAG)
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
index 812ac3d..4a208e3 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
@@ -15,28 +15,22 @@
*/
package androidx.wear.compose.material
-import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.defaultMinSize
-import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
/**
@@ -219,36 +213,24 @@
role: Role = ToggleButtonDefaults.DefaultRole,
content: @Composable BoxScope.() -> Unit,
) {
- Box(
- contentAlignment = Alignment.Center,
- modifier = modifier
- .defaultMinSize(
- minWidth = ToggleButtonDefaults.DefaultToggleButtonSize,
- minHeight = ToggleButtonDefaults.DefaultToggleButtonSize
- )
- .clip(shape)
- .toggleable(
- value = checked,
- >
- enabled = enabled,
- role = role,
- interactionSource = interactionSource,
- indication = rememberRipple()
- )
- .background(
- color = colors.backgroundColor(enabled = enabled, checked = checked).value,
- shape = shape
- )
- ) {
- val contentColor = colors.contentColor(enabled = enabled, checked = checked).value
- CompositionLocalProvider(
- LocalContentColor provides contentColor,
- LocalContentAlpha provides contentColor.alpha,
- LocalTextStyle provides MaterialTheme.typography.button,
- ) {
- content()
- }
- }
+ androidx.wear.compose.materialcore.ToggleButton(
+ checked = checked,
+ >
+ modifier = modifier.semantics { this.role = role },
+ enabled = enabled,
+ backgroundColor = { isEnabled, isChecked ->
+ colors.backgroundColor(enabled = isEnabled, checked = isChecked)
+ },
+ border = { _, _ -> null },
+ toggleButtonSize = ToggleButtonDefaults.DefaultToggleButtonSize,
+ interactionSource = interactionSource,
+ shape = shape,
+ content = provideScopeContent(
+ colors.contentColor(enabled = enabled, checked = checked),
+ MaterialTheme.typography.button,
+ content
+ )
+ )
}
/**
* Represents the background and content colors used in a toggle button in different states.
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
index 76cddf8..d2c396f 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
@@ -29,6 +29,7 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.Button
+import androidx.wear.compose.material3.ButtonColors
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.ChildButton
import androidx.wear.compose.material3.FilledTonalButton
@@ -310,13 +311,17 @@
@Composable
private fun AvatarButton(enabled: Boolean) =
MultilineButton(
- enabled = enabled, icon = { AvatarIcon() }, label = { Text("Primary text") }
+ enabled = enabled,
+ colors = ButtonDefaults.filledTonalButtonColors(),
+ icon = { AvatarIcon() },
+ label = { Text("Primary text") }
)
@Composable
private fun Avatar3SlotButton(enabled: Boolean) =
Multiline3SlotButton(
enabled = enabled,
+ colors = ButtonDefaults.filledTonalButtonColors(),
icon = { AvatarIcon() },
label = { Text("Primary text") },
secondaryLabel = { Text("Secondary label") }
@@ -325,6 +330,7 @@
@Composable
private fun MultilineButton(
enabled: Boolean,
+ colors: ButtonColors = ButtonDefaults.filledButtonColors(),
icon: (@Composable BoxScope.() -> Unit)? = null,
label: @Composable RowScope.() -> Unit = {
Text(
@@ -338,13 +344,15 @@
/* Do something */ },
icon = icon,
label = label,
- enabled = enabled
+ enabled = enabled,
+ colors = colors,
)
}
@Composable
private fun Multiline3SlotButton(
enabled: Boolean,
+ colors: ButtonColors = ButtonDefaults.filledButtonColors(),
icon: (@Composable BoxScope.() -> Unit)? = null,
label: @Composable RowScope.() -> Unit = {
Text(
@@ -366,7 +374,8 @@
icon = icon,
label = label,
secondaryLabel = secondaryLabel,
- enabled = enabled
+ enabled = enabled,
+ colors = colors,
)
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
index eb4b7c0..a5da87e 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
@@ -386,7 +386,7 @@
status = Status.Disabled,
colors = { ButtonDefaults.filledButtonColors() },
expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
- alpha = DisabledBorderAndContainerAlpha
+ alpha = DisabledContainerAlpha
) },
expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
alpha = ContentAlpha.disabled
@@ -412,7 +412,7 @@
status = Status.Disabled,
colors = { ButtonDefaults.filledTonalButtonColors() },
expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
- alpha = DisabledBorderAndContainerAlpha
+ alpha = DisabledContainerAlpha
) },
expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
alpha = ContentAlpha.disabled
@@ -461,9 +461,7 @@
rule.verifyButtonColors(
status = Status.Disabled,
colors = { ButtonDefaults.childButtonColors() },
- expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
- alpha = DisabledBorderAndContainerAlpha
- ) },
+ expectedContainerColor = { Color.Transparent },
expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
alpha = ContentAlpha.disabled
) },
@@ -563,7 +561,9 @@
fun gives_disabled_outlined_button_correct_border_colors() {
val status = Status.Disabled
rule.verifyButtonBorderColor(
- expectedBorderColor = { MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f) },
+ expectedBorderColor = {
+ MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
+ },
content = { modifier: Modifier ->
OutlinedButton(
>
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
index e1bcf92..07d2ceb 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
@@ -70,7 +70,7 @@
}
@Test
- fun filled_icon_button_disabled() = verifyScreenshot("icon_button_disabled") {
+ fun filled_icon_button_disabled() = verifyScreenshot {
sampleFilledIconButton(enabled = false, isCompact = false)
}
@@ -80,7 +80,7 @@
}
@Test
- fun filled_tonal_icon_button_disabled() = verifyScreenshot("icon_button_disabled") {
+ fun filled_tonal_icon_button_disabled() = verifyScreenshot {
sampleFilledTonalIconButton(enabled = false, isCompact = false)
}
@@ -110,7 +110,7 @@
}
@Test
- fun filled_compact_icon_button_disabled() = verifyScreenshot("compact_icon_button_disabled") {
+ fun filled_compact_icon_button_disabled() = verifyScreenshot {
sampleFilledIconButton(enabled = false, isCompact = true)
}
@@ -121,7 +121,7 @@
@Test
fun filled_tonal_compact_icon_button_disabled() =
- verifyScreenshot("compact_icon_button_disabled") {
+ verifyScreenshot {
sampleFilledTonalIconButton(enabled = false, isCompact = true)
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
index d12eb43..f6f9e3b 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
@@ -31,6 +31,8 @@
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertHasClickAction
@@ -191,6 +193,27 @@
}
@Test
+ fun allows_custom_role() {
+ val overrideRole = Role.Checkbox
+
+ rule.setContentWithTheme {
+ IconButton(
+ >
+ modifier = Modifier.testTag(TEST_TAG).semantics { role = overrideRole }
+ ) {
+ TestImage()
+ }
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assert(
+ SemanticsMatcher.expectValue(
+ SemanticsProperties.Role,
+ overrideRole
+ )
+ )
+ }
+
+ @Test
fun gives_default_button_correct_tap_size() {
rule.verifyTapSize(DefaultButtonSize) { modifier ->
IconButton(
@@ -317,7 +340,7 @@
status = Status.Disabled,
colors = { IconButtonDefaults.filledIconButtonColors() },
expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
- alpha = DisabledBorderAndContainerAlpha
+ alpha = DisabledContainerAlpha
) },
expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
alpha = ContentAlpha.disabled
@@ -343,7 +366,7 @@
status = Status.Disabled,
colors = { IconButtonDefaults.filledTonalIconButtonColors() },
expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
- alpha = DisabledBorderAndContainerAlpha
+ alpha = DisabledContainerAlpha
) },
expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
alpha = ContentAlpha.disabled
@@ -397,7 +420,7 @@
val status = Status.Disabled
rule.verifyButtonBorderColor(
expectedBorderColor = {
- MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAndContainerAlpha)
+ MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
},
content = { modifier: Modifier ->
OutlinedIconButton(
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
index 967714f3..11b0b55 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
@@ -66,7 +66,7 @@
@Test
fun filled_text_button_disabled() =
- verifyScreenshot("text_button_disabled") {
+ verifyScreenshot {
sampleFilledTextButton(enabled = false, isCompact = false)
}
@@ -77,7 +77,7 @@
@Test
fun filled_tonal_text_button_disabled() =
- verifyScreenshot("text_button_disabled") {
+ verifyScreenshot {
sampleFilledTonalTextButton(enabled = false, isCompact = false)
}
@@ -108,7 +108,7 @@
@Test
fun filled_compact_text_button_disabled() =
- verifyScreenshot("compact_text_button_disabled") {
+ verifyScreenshot {
sampleFilledTextButton(enabled = false, isCompact = true)
}
@@ -119,7 +119,7 @@
@Test
fun filled_tonal_compact_text_button_disabled() =
- verifyScreenshot("compact_text_button_disabled") {
+ verifyScreenshot {
sampleFilledTonalTextButton(enabled = false, isCompact = true)
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
index a37ef7b..f6a0b7b 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
@@ -31,6 +31,8 @@
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertHasClickAction
@@ -222,6 +224,27 @@
}
@Test
+ fun allows_custom_role() {
+ val overrideRole = Role.Checkbox
+
+ rule.setContentWithTheme {
+ TextButton(
+ >
+ modifier = Modifier.testTag(TEST_TAG).semantics { role = overrideRole }
+ ) {
+ Text("Test")
+ }
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assert(
+ SemanticsMatcher.expectValue(
+ SemanticsProperties.Role,
+ overrideRole
+ )
+ )
+ }
+
+ @Test
fun sets_correct_font() {
var actualTextStyle = TextStyle.Default
var expectedTextStyle = TextStyle.Default
@@ -365,7 +388,7 @@
status = Status.Disabled,
colors = { TextButtonDefaults.filledTextButtonColors() },
expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
- alpha = DisabledBorderAndContainerAlpha
+ alpha = DisabledContainerAlpha
) },
expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
alpha = ContentAlpha.disabled
@@ -391,7 +414,7 @@
status = Status.Disabled,
colors = { TextButtonDefaults.filledTonalTextButtonColors() },
expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
- alpha = DisabledBorderAndContainerAlpha
+ alpha = DisabledContainerAlpha
) },
expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
alpha = ContentAlpha.disabled
@@ -416,9 +439,7 @@
rule.verifyTextButtonColors(
status = Status.Disabled,
colors = { TextButtonDefaults.outlinedTextButtonColors() },
- expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
- alpha = DisabledBorderAndContainerAlpha
- ) },
+ expectedContainerColor = { Color.Transparent },
expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
alpha = ContentAlpha.disabled
) }
@@ -449,7 +470,7 @@
val status = Status.Disabled
rule.verifyButtonBorderColor(
expectedBorderColor = {
- MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAndContainerAlpha)
+ MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
},
content = { modifier: Modifier ->
TextButton(
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
index 6c8de9eb59..e608707 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
@@ -715,7 +715,7 @@
containerColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = MaterialTheme.colorScheme.onSurface,
secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
- iconColor: Color = MaterialTheme.colorScheme.onSurface
+ iconColor: Color = MaterialTheme.colorScheme.primary,
): ButtonColors {
return buttonColors(
containerColor = containerColor,
@@ -742,13 +742,14 @@
fun outlinedButtonColors(
contentColor: Color = MaterialTheme.colorScheme.onSurface,
secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
- iconColor: Color = MaterialTheme.colorScheme.onSurface
+ iconColor: Color = MaterialTheme.colorScheme.primary,
): ButtonColors {
return buttonColors(
containerColor = Color.Transparent,
contentColor = contentColor,
secondaryContentColor = secondaryContentColor,
- iconColor = iconColor
+ iconColor = iconColor,
+ disabledContainerColor = Color.Transparent,
)
}
@@ -769,13 +770,14 @@
fun childButtonColors(
contentColor: Color = MaterialTheme.colorScheme.onSurface,
secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
- iconColor: Color = MaterialTheme.colorScheme.onSurface
+ iconColor: Color = MaterialTheme.colorScheme.primary
): ButtonColors {
return buttonColors(
containerColor = Color.Transparent,
contentColor = contentColor,
secondaryContentColor = secondaryContentColor,
- iconColor = iconColor
+ iconColor = iconColor,
+ disabledContainerColor = Color.Transparent,
)
}
@@ -851,7 +853,7 @@
enabled: Boolean,
borderColor: Color = MaterialTheme.colorScheme.outline,
disabledBorderColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledBorderAlpha
),
borderWidth: Dp = 1.dp
): BorderStroke {
@@ -901,7 +903,7 @@
secondaryContentColor: Color = contentColor,
iconColor: Color = contentColor,
disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledContainerAlpha
),
disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(),
disabledSecondaryContentColor: Color =
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
index 8471524..b978a2b 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
@@ -33,18 +33,18 @@
* body text.
*
*/
-public val LocalContentAlpha: ProvidableCompositionLocal<Float> = compositionLocalOf { 1f }
+val LocalContentAlpha: ProvidableCompositionLocal<Float> = compositionLocalOf { 1f }
/**
* Default alpha levels used by Material components.
*
* See [LocalContentAlpha].
*/
-public object ContentAlpha {
+object ContentAlpha {
/**
* A high level of content alpha, used to represent high emphasis text.
*/
- public val high: Float
+ val high: Float
@Composable
get() = contentAlpha(
highContrastAlpha = HighContrastContentAlpha.high,
@@ -55,7 +55,7 @@
* A medium level of content alpha, used to represent medium emphasis text such as
* placeholder text.
*/
- public val medium: Float
+ val medium: Float
@Composable
get() = contentAlpha(
highContrastAlpha = HighContrastContentAlpha.medium,
@@ -66,7 +66,7 @@
* A low level of content alpha used to represent disabled components, such as text in a
* disabled Button.
*/
- public val disabled: Float
+ val disabled: Float
@Composable
get() = contentAlpha(
highContrastAlpha = HighContrastContentAlpha.disabled,
@@ -123,4 +123,5 @@
const val disabled: Float = 0.38f
}
-internal const val DisabledBorderAndContainerAlpha = 0.12f
+internal const val DisabledContainerAlpha = 0.12f
+internal const val DisabledBorderAlpha = 0.20f
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
index 042f777..ff669b8 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
@@ -358,7 +358,10 @@
): IconButtonColors {
return iconButtonColors(
containerColor = containerColor,
- contentColor = contentColor
+ contentColor = contentColor,
+ disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+ disabledAlpha = DisabledContainerAlpha
+ ),
)
}
@@ -378,7 +381,10 @@
): IconButtonColors {
return iconButtonColors(
containerColor = containerColor,
- contentColor = contentColor
+ contentColor = contentColor,
+ disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+ disabledAlpha = DisabledContainerAlpha
+ ),
)
}
@@ -396,7 +402,7 @@
): IconButtonColors {
return iconButtonColors(
containerColor = Color.Transparent,
- contentColor = contentColor
+ contentColor = contentColor,
)
}
@@ -415,9 +421,7 @@
fun iconButtonColors(
containerColor: Color = Color.Transparent,
contentColor: Color = MaterialTheme.colorScheme.onBackground,
- disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
- ),
+ disabledContainerColor: Color = Color.Transparent,
disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor()
): IconButtonColors = IconButtonColors(
containerColor = containerColor,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt
index adf549b..128b9a5 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt
@@ -491,11 +491,11 @@
uncheckedBoxColor = uncheckedBoxColor,
uncheckedCheckmarkColor = uncheckedCheckmarkColor,
disabledCheckedBoxColor = checkedBoxColor.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledContainerAlpha
),
disabledCheckedCheckmarkColor = checkedCheckmarkColor.toDisabledColor(),
disabledUncheckedBoxColor = uncheckedBoxColor.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledContainerAlpha
),
disabledUncheckedCheckmarkColor = uncheckedCheckmarkColor.toDisabledColor()
)
@@ -539,18 +539,18 @@
disabledCheckedThumbColor = checkedThumbColor.toDisabledColor(),
disabledCheckedThumbIconColor = checkedThumbIconColor.toDisabledColor(),
disabledCheckedTrackColor = checkedTrackColor.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledContainerAlpha
),
disabledCheckedTrackBorderColor = checkedTrackStrokeColor.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledBorderAlpha
),
disabledUncheckedThumbColor = uncheckedThumbColor.toDisabledColor(),
disabledUncheckedThumbIconColor = uncheckedThumbIconColor.toDisabledColor(),
disabledUncheckedTrackColor = uncheckedTrackColor.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledContainerAlpha
),
disabledUncheckedTrackBorderColor = uncheckedTrackStrokeColor.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledBorderAlpha
)
)
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
index d6a155f..9563288 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
@@ -328,13 +328,13 @@
unselectedBarColor: Color = MaterialTheme.colorScheme.background.copy(alpha = 0.3f),
barSeparatorColor: Color = MaterialTheme.colorScheme.primaryDim,
disabledContainerColor: Color = containerColor.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledContainerAlpha
),
disabledButtonIconColor: Color = buttonIconColor.toDisabledColor(),
disabledSelectedBarColor: Color = selectedBarColor.toDisabledColor(),
disabledUnselectedBarColor: Color = unselectedBarColor.toDisabledColor(),
disabledBarSeparatorColor: Color = barSeparatorColor.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
+ disabledAlpha = DisabledContainerAlpha
)
): InlineSliderColors = InlineSliderColors(
containerColor = containerColor,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
index f3d2722..e0349a1 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
@@ -135,7 +135,7 @@
* @param content The text to be drawn inside the toggle button.
*/
@Composable
-public fun TextToggleButton(
+fun TextToggleButton(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
@@ -194,7 +194,10 @@
): TextButtonColors {
return textButtonColors(
containerColor = containerColor,
- contentColor = contentColor
+ contentColor = contentColor,
+ disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+ disabledAlpha = DisabledContainerAlpha
+ ),
)
}
@@ -217,7 +220,10 @@
): TextButtonColors {
return textButtonColors(
containerColor = containerColor,
- contentColor = contentColor
+ contentColor = contentColor,
+ disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+ disabledAlpha = DisabledContainerAlpha
+ ),
)
}
@@ -256,9 +262,7 @@
fun textButtonColors(
containerColor: Color = Color.Transparent,
contentColor: Color = MaterialTheme.colorScheme.onBackground,
- disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
- disabledAlpha = DisabledBorderAndContainerAlpha
- ),
+ disabledContainerColor: Color = Color.Transparent,
disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor()
): TextButtonColors = TextButtonColors(
containerColor = containerColor,
@@ -291,7 +295,7 @@
* unchecked and not enabled
*/
@Composable
- public fun textToggleButtonColors(
+ fun textToggleButtonColors(
checkedContainerColor: Color = MaterialTheme.colorScheme.primary,
checkedContentColor: Color = MaterialTheme.colorScheme.onPrimary,
uncheckedContainerColor: Color = MaterialTheme.colorScheme.surface,