Merge "Only invalidate reads of derivedStateOf() if the result changes." into androidx-main
diff --git a/appcompat/appcompat-resources/lint-baseline.xml b/appcompat/appcompat-resources/lint-baseline.xml
index 4982402..4826d47 100644
--- a/appcompat/appcompat-resources/lint-baseline.xml
+++ b/appcompat/appcompat-resources/lint-baseline.xml
@@ -112,94 +112,6 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 18; however, the containing class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat.AnimationDrawableTransition is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" anim.setAutoCancel(true);"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"
- line="401"
- column="22"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" state.mChangingConfigurations |= a.getChangingConfigurations();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"
- line="452"
- column="48"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" dr = Drawable.createFromXmlInner(resources, parser, attrs, theme);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"
- line="541"
- column="31"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" dr = Drawable.createFromXmlInner(resources, parser, attrs, theme);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"
- line="591"
- column="31"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.appcompat.widget.ResourceManagerInternal.DrawableDelegate is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" drawable.inflate(context.getResources(), parser, attrs, theme);"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java"
- line="565"
- column="34"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 15; however, the containing class androidx.appcompat.widget.ResourcesWrapper is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mResources.getValueForDensity(id, density, outValue, resolveRefs);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appcompat/widget/ResourcesWrapper.java"
- line="241"
- column="20"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.StateListDrawable is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" state.mChangingConfigurations |= a.getChangingConfigurations();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java"
- line="159"
- column="48"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.appcompat.graphics.drawable.StateListDrawable is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" dr = Drawable.createFromXmlInner(r, parser, attrs, theme);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java"
- line="218"
- column="35"/>
- </issue>
-
- <issue
id="KotlinPropertyAccess"
message="The getter return type (`Theme`) and setter parameter type (`int`) getter and setter methods for property `theme` should have exactly the same type to allow be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
errorLine1=" public Resources.Theme getTheme() {"
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java
index f57aace..67c4235 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java
@@ -39,6 +39,7 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.resources.Compatibility;
import androidx.appcompat.resources.R;
import androidx.appcompat.widget.ResourceManagerInternal;
import androidx.collection.LongSparseArray;
@@ -398,7 +399,7 @@
final ObjectAnimator anim =
ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame);
if (SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- anim.setAutoCancel(true);
+ Compatibility.Api18Impl.setAutoCancel(anim, true);
}
anim.setDuration(interp.getTotalDuration());
anim.setInterpolator(interp);
@@ -449,7 +450,7 @@
final AnimatedStateListState state = mState;
// Account for any configuration changes.
if (SDK_INT >= LOLLIPOP) {
- state.mChangingConfigurations |= a.getChangingConfigurations();
+ state.mChangingConfigurations |= Compatibility.Api21Impl.getChangingConfigurations(a);
}
// Extract the theme attributes, if any.
state.setVariablePadding(
@@ -538,7 +539,7 @@
dr = AnimatedVectorDrawableCompat.createFromXmlInner(context, resources, parser,
attrs, theme);
} else if (SDK_INT >= LOLLIPOP) {
- dr = Drawable.createFromXmlInner(resources, parser, attrs, theme);
+ dr = Compatibility.Api21Impl.createFromXmlInner(resources, parser, attrs, theme);
} else {
dr = Drawable.createFromXmlInner(resources, parser, attrs);
}
@@ -588,7 +589,7 @@
if (parser.getName().equals("vector")) {
dr = VectorDrawableCompat.createFromXmlInner(resources, parser, attrs, theme);
} else if (SDK_INT >= LOLLIPOP) {
- dr = Drawable.createFromXmlInner(resources, parser, attrs, theme);
+ dr = Compatibility.Api21Impl.createFromXmlInner(resources, parser, attrs, theme);
} else {
dr = Drawable.createFromXmlInner(resources, parser, attrs);
}
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java
index e501460..ceabac7 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/graphics/drawable/StateListDrawable.java
@@ -35,6 +35,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
+import androidx.appcompat.resources.Compatibility;
import androidx.appcompat.resources.R;
import androidx.appcompat.widget.ResourceManagerInternal;
@@ -156,7 +157,7 @@
final StateListState state = mStateListState;
// Account for any configuration changes.
if (SDK_INT >= LOLLIPOP) {
- state.mChangingConfigurations |= a.getChangingConfigurations();
+ state.mChangingConfigurations |= Compatibility.Api21Impl.getChangingConfigurations(a);
}
state.mVariablePadding = a.getBoolean(
R.styleable.StateListDrawable_android_variablePadding, state.mVariablePadding);
@@ -215,7 +216,7 @@
+ "child tag defining a drawable");
}
if (SDK_INT >= LOLLIPOP) {
- dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
+ dr = Compatibility.Api21Impl.createFromXmlInner(r, parser, attrs, theme);
} else {
dr = Drawable.createFromXmlInner(r, parser, attrs);
}
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java
new file mode 100644
index 0000000..189b793
--- /dev/null
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.resources;
+
+import android.animation.ObjectAnimator;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Unified ApiXXImpls for appcompat-resources.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public final class Compatibility {
+ private Compatibility() {
+ // This class is not instantiable.
+ }
+
+ @RequiresApi(21)
+ public static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ public static void inflate(@NonNull Drawable drawable, @NonNull Resources r,
+ @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
+ @Nullable Resources.Theme theme) throws IOException, XmlPullParserException {
+ drawable.inflate(r, parser, attrs, theme);
+ }
+
+ @DoNotInline
+ public static int getChangingConfigurations(@NonNull TypedArray typedArray) {
+ return typedArray.getChangingConfigurations();
+ }
+
+ @NonNull
+ @DoNotInline
+ public static Drawable createFromXmlInner(@NonNull Resources r,
+ @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
+ @Nullable Resources.Theme theme) throws IOException, XmlPullParserException {
+ return Drawable.createFromXmlInner(r, parser, attrs, theme);
+ }
+ }
+
+ @RequiresApi(18)
+ public static class Api18Impl {
+ private Api18Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ public static void setAutoCancel(@NonNull ObjectAnimator objectAnimator, boolean cancel) {
+ objectAnimator.setAutoCancel(cancel);
+ }
+ }
+
+ @RequiresApi(15)
+ public static class Api15Impl {
+ private Api15Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ public static void getValueForDensity(@NonNull Resources resources, int id, int density,
+ @NonNull TypedValue outValue, boolean resolveRefs) {
+ resources.getValueForDensity(id, density, outValue, resolveRefs);
+ }
+ }
+}
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java
index dfb0754..a269912 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java
@@ -38,6 +38,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat;
+import androidx.appcompat.resources.Compatibility;
import androidx.appcompat.resources.R;
import androidx.collection.LongSparseArray;
import androidx.collection.LruCache;
@@ -562,7 +563,8 @@
.asSubclass(Drawable.class);
Drawable drawable = drawableClass.getDeclaredConstructor().newInstance();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- drawable.inflate(context.getResources(), parser, attrs, theme);
+ Compatibility.Api21Impl.inflate(drawable, context.getResources(), parser,
+ attrs, theme);
} else {
drawable.inflate(context.getResources(), parser, attrs);
}
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourcesWrapper.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourcesWrapper.java
index d81cfe7..b854a9e 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourcesWrapper.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/ResourcesWrapper.java
@@ -29,6 +29,7 @@
import android.util.TypedValue;
import androidx.annotation.RequiresApi;
+import androidx.appcompat.resources.Compatibility;
import androidx.core.content.res.ResourcesCompat;
import org.xmlpull.v1.XmlPullParserException;
@@ -238,7 +239,7 @@
@Override
public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
- mResources.getValueForDensity(id, density, outValue, resolveRefs);
+ Compatibility.Api15Impl.getValueForDensity(mResources, id, density, outValue, resolveRefs);
}
@Override
diff --git a/appcompat/appcompat/api/current.txt b/appcompat/appcompat/api/current.txt
index e1847bb..d206436 100644
--- a/appcompat/appcompat/api/current.txt
+++ b/appcompat/appcompat/api/current.txt
@@ -317,6 +317,7 @@
public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
ctor public AppCompatDialogFragment();
+ ctor public AppCompatDialogFragment(@LayoutRes int);
}
public class AppCompatViewInflater {
diff --git a/appcompat/appcompat/api/public_plus_experimental_current.txt b/appcompat/appcompat/api/public_plus_experimental_current.txt
index cc92740d..6cb8e4a 100644
--- a/appcompat/appcompat/api/public_plus_experimental_current.txt
+++ b/appcompat/appcompat/api/public_plus_experimental_current.txt
@@ -317,6 +317,7 @@
public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
ctor public AppCompatDialogFragment();
+ ctor public AppCompatDialogFragment(@LayoutRes int);
}
public class AppCompatViewInflater {
diff --git a/appcompat/appcompat/api/restricted_current.txt b/appcompat/appcompat/api/restricted_current.txt
index 7fa4fdb..fdc45fa 100644
--- a/appcompat/appcompat/api/restricted_current.txt
+++ b/appcompat/appcompat/api/restricted_current.txt
@@ -341,6 +341,7 @@
public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
ctor public AppCompatDialogFragment();
+ ctor public AppCompatDialogFragment(@LayoutRes int);
}
public class AppCompatViewInflater {
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index 7a8a2fb..590def4 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -239,6 +239,16 @@
android:configChanges="uiMode"/>
<activity
+ android:name="androidx.appcompat.app.NightModeUiModeConfigChangesActivityB"
+ android:theme="@style/Theme.AppCompat.DayNight"
+ android:configChanges="uiMode"/>
+
+ <activity
+ android:name="androidx.appcompat.app.NightModeUiModeConfigChangesActivityC"
+ android:theme="@style/Theme.AppCompat.DayNight"
+ android:configChanges="uiMode"/>
+
+ <activity
android:name="androidx.appcompat.app.NightModeRotateDoesNotRecreateActivity"
android:theme="@style/Theme.AppCompat.DayNight"
android:configChanges="orientation|screenSize"/>
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogFragmentTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogFragmentTest.java
new file mode 100644
index 0000000..cf7049e
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogFragmentTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 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.app;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Dialog;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.test.R;
+import androidx.fragment.app.DialogFragment;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatDialogFragmentTest {
+ @SuppressWarnings("deprecation")
+ @Rule
+ public final androidx.test.rule.ActivityTestRule<WindowDecorAppCompatActivity> mTestRule =
+ new androidx.test.rule.ActivityTestRule<>(WindowDecorAppCompatActivity.class);
+
+ private DialogFragment mFragment;
+
+ @Test
+ public void testDialogFragmentShows() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> mFragment = new TestDialogFragment()
+ );
+ mFragment.show(mTestRule.getActivity().getSupportFragmentManager(), null);
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertNotNull("Dialog was null", mFragment.getDialog());
+ assertTrue("Dialog was not being shown", mFragment.getDialog().isShowing());
+
+ // And make sure we dismiss the dialog
+ mFragment.dismissAllowingStateLoss();
+ }
+
+ @Test
+ public void testDialogFragmentWithLayout() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> mFragment = new AppCompatDialogFragment(R.layout.dialog_layout)
+ );
+ mFragment.show(mTestRule.getActivity().getSupportFragmentManager(), null);
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertNotNull("Dialog is not null", mFragment.getDialog());
+ assertTrue("Dialog is showing", mFragment.getDialog().isShowing());
+ assertNotNull("Dialog is using custom layout",
+ mFragment.getDialog().findViewById(R.id.dialog_content));
+
+ // And make sure we dismiss the dialog
+ mFragment.dismissAllowingStateLoss();
+ }
+
+ public static class TestDialogFragment extends AppCompatDialogFragment {
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(requireContext())
+ .setTitle("Test")
+ .setMessage("Message")
+ .setPositiveButton("Button", null)
+ .create();
+ }
+ }
+}
+
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/DialogTestCase.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/DialogTestCase.java
deleted file mode 100644
index 378cdf5..0000000
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/DialogTestCase.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2015 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.app;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Dialog;
-import android.os.Bundle;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class DialogTestCase {
- @Rule
- public final ActivityTestRule<WindowDecorAppCompatActivity> mActivityTestRule =
- new ActivityTestRule<>(WindowDecorAppCompatActivity.class);
-
- private TestDialogFragment mFragment;
-
- @Test
- public void testDialogFragmentShows() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
- InstrumentationRegistry.getInstrumentation().runOnMainSync(
- new Runnable() {
- @Override
- public void run() {
- mFragment = new TestDialogFragment();
- }
- }
- );
- mFragment.show(mActivityTestRule.getActivity().getSupportFragmentManager(), null);
-
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
- assertNotNull("Dialog was null", mFragment.getDialog());
- assertTrue("Dialog was not being shown", mFragment.getDialog().isShowing());
-
- // And make sure we dismiss the dialog
- mFragment.dismissAllowingStateLoss();
- }
-
- public static class TestDialogFragment extends AppCompatDialogFragment {
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return new AlertDialog.Builder(getContext())
- .setTitle("Test")
- .setMessage("Message")
- .setPositiveButton("Button", null)
- .create();
- }
- }
-}
-
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeStackedHandlingTestCase.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeStackedHandlingTestCase.kt
index 62f9285..d078551 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeStackedHandlingTestCase.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeStackedHandlingTestCase.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:Suppress("DEPRECATION")
+@file:Suppress("deprecation")
package androidx.appcompat.app
@@ -23,14 +23,19 @@
import android.app.Instrumentation.ActivityMonitor
import android.content.Intent
import android.content.res.Configuration
+import android.os.Handler
+import android.os.Looper
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.appcompat.testutils.NightModeUtils
+import androidx.lifecycle.Lifecycle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
+import androidx.testutils.LifecycleOwnerUtils.waitUntilState
import junit.framework.Assert.assertNotNull
+import junit.framework.Assert.assertNotSame
import org.junit.Test
import org.junit.runner.RunWith
@@ -84,7 +89,7 @@
)
// From activity A, start activity B.
- var activityA = monitorA.waitForActivityWithTimeout(3000) as NightModeActivity
+ val activityA = monitorA.waitForActivityWithTimeout(3000) as NightModeActivity
assertNotNull(activityA)
activityA.startActivity(
Intent(instr.context, NightModeActivityB::class.java).apply {
@@ -92,6 +97,9 @@
}
)
+ // Activity A is hidden, wait for it to stop.
+ waitUntilState(activityA, Lifecycle.State.CREATED)
+
// From activity B, start activity C.
val activityB = monitorB.waitForActivityWithTimeout(3000) as NightModeActivity
assertNotNull(activityB)
@@ -101,6 +109,9 @@
}
)
+ // Activity B is hidden, wait for it to stop.
+ waitUntilState(activityB, Lifecycle.State.CREATED)
+
// Toggle default night mode.
val activityC = monitorC.waitForActivityWithTimeout(3000) as NightModeActivity
assertNotNull(activityC)
@@ -111,39 +122,196 @@
// Activity C should receive a configuration change.
activityC.expectOnConfigurationChange(3000)
- // Activities A and B should recreate().
+ // Activities A and B should recreate() in the background.
val activityA2 = expectRecreate(monitorA, activityA) as NightModeActivity
val activityB2 = expectRecreate(monitorB, activityB) as NightModeActivity
// Activity C should have received a night mode configuration change.
- activityC.runOnUiThread {
- NightModeUtils.assertConfigurationNightModeEquals(
- "Activity A's effective configuration has night mode set",
- Configuration.UI_MODE_NIGHT_YES,
- activityC.effectiveConfiguration!!
- )
- }
-
- // Activity A should have been recreated in night mode.
- activityA2.runOnUiThread {
- NightModeUtils.assertConfigurationNightModeEquals(
- "Activity A's effective configuration has night mode set",
- Configuration.UI_MODE_NIGHT_YES,
- activityA2.effectiveConfiguration!!
- )
- }
-
- // Activity B should have been recreated in night mode.
- activityB2.runOnUiThread {
- NightModeUtils.assertConfigurationNightModeEquals(
- "Activity B's effective configuration has night mode set",
- Configuration.UI_MODE_NIGHT_YES,
- activityB2.effectiveConfiguration!!
- )
+ listOf(activityC, activityA2, activityB2).forEach { activity ->
+ activityC.runOnUiThread {
+ NightModeUtils.assertConfigurationNightModeEquals(
+ "Activity ${activity.title}'s effective configuration has night mode set",
+ Configuration.UI_MODE_NIGHT_YES,
+ activityC.effectiveConfiguration!!
+ )
+ }
}
}
- fun expectRecreate(monitor: ActivityMonitor, activity: Activity): Activity {
+ /**
+ * Regression test for the following scenario:
+ *
+ * If you have a stack of activities where every activity has `android:configChanges="uiMode"`
+ * and you call [AppCompatDelegate.setDefaultNightMode] from thread other than the top
+ * activity, then it can cause the bottom activity to not receive `onConfigurationChanged`.
+ *
+ * Eg:
+ * - Activity A DOES intercept uiMode config changes in manifest
+ * - Activity B DOES as well
+ * - Activity C DOES as well
+ *
+ * Here is your stack : A > B > C (C on top)
+ *
+ * Call [AppCompatDelegate.setDefaultNightMode] with a new mode on activity C (but not directly
+ * from this activity, ex with RX AndroidSchedulers.mainThread or an handler). Activity C
+ * receives both `onConfigurationChanged` and `onNightModeChanged`, but activities A and B
+ * may not receive either callback or change their configurations.
+ *
+ * Process:
+ * 1. A > B > C > setDefaultNightMode YES
+ * 2. Go back to A (B & C destroyed) > B > C > setDefaultNightMode NO (wrong config for A)
+ * 3. repeat (YES/NO/YES/NO...)
+ */
+ @Test
+ @SdkSuppress(minSdkVersion = 17)
+ public fun testDefaultNightModeWithStackedActivitiesAndNavigation() {
+ val instr = InstrumentationRegistry.getInstrumentation()
+ val result = Instrumentation.ActivityResult(0, Intent())
+ val monitorA = ActivityMonitor(
+ NightModeUiModeConfigChangesActivity::class.java.name,
+ result, false
+ )
+ val monitorB = ActivityMonitor(
+ NightModeUiModeConfigChangesActivityB::class.java.name,
+ result, false
+ )
+ val monitorC = ActivityMonitor(
+ NightModeUiModeConfigChangesActivityC::class.java.name,
+ result, false
+ )
+ instr.addMonitor(monitorA)
+ instr.addMonitor(monitorB)
+ instr.addMonitor(monitorC)
+
+ instr.runOnMainSync {
+ AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)
+ }
+
+ // Start activity A.
+ instr.startActivitySync(
+ Intent(instr.context, NightModeUiModeConfigChangesActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ putExtra(NightModeActivity.KEY_TITLE, "A")
+ }
+ )
+
+ // From activity A, start activity B.
+ val activityA = monitorA.waitForActivityWithTimeout(3000) as NightModeActivity
+ assertNotNull("Activity A started within 3000ms", activityA)
+ activityA.startActivity(
+ Intent(instr.context, NightModeUiModeConfigChangesActivityB::class.java).apply {
+ putExtra(NightModeActivity.KEY_TITLE, "B")
+ }
+ )
+
+ // Activity A is hidden, wait for it to stop.
+ waitUntilState(activityA, Lifecycle.State.CREATED)
+
+ // From activity B, start activity C.
+ val activityB = monitorB.waitForActivityWithTimeout(3000) as NightModeActivity
+ assertNotNull("Activity B started within 3000ms", activityB)
+ activityB.startActivity(
+ Intent(instr.context, NightModeUiModeConfigChangesActivityC::class.java).apply {
+ putExtra(NightModeActivity.KEY_TITLE, "C")
+ }
+ )
+
+ // Activity B is hidden, wait for it to stop.
+ waitUntilState(activityB, Lifecycle.State.CREATED)
+
+ // Wait for activity C to start.
+ val activityC = monitorC.waitForActivityWithTimeout(3000) as NightModeActivity
+ assertNotNull("Activity C started within 3000ms", activityC)
+
+ // Toggle default night mode from a non-UI thread.
+ Handler(Looper.getMainLooper()).post {
+ AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES)
+ }
+
+ // Activities A, B, and C should all receive configuration changes.
+ listOf(activityA, activityB, activityC).forEach { activity ->
+ activity.expectOnConfigurationChange(3000)
+ }
+
+ // Activities A, B, and C should have all received the new configuration.
+ listOf(activityA, activityB, activityC).forEach { activity ->
+ activity.runOnUiThread {
+ NightModeUtils.assertConfigurationNightModeEquals(
+ "Activity ${activity.title}'s effective configuration has night mode set",
+ Configuration.UI_MODE_NIGHT_YES,
+ activity.effectiveConfiguration!!
+ )
+ }
+ }
+
+ // Tear down activities C and B, in that order.
+ listOf(activityC, activityB).forEach { activity ->
+ activity.runOnUiThread {
+ activity.finish()
+ }
+ waitUntilState(activity, Lifecycle.State.DESTROYED)
+ }
+
+ // Activity A is in the foreground, wait for it to resume.
+ waitUntilState(activityA, Lifecycle.State.RESUMED)
+
+ // From activity A, start activity B again.
+ activityA.startActivity(
+ Intent(instr.context, NightModeUiModeConfigChangesActivityB::class.java).apply {
+ putExtra(NightModeActivity.KEY_TITLE, "B2")
+ }
+ )
+
+ // Activity A is hidden, wait for it to stop.
+ waitUntilState(activityA, Lifecycle.State.CREATED)
+
+ // From activity B, start activity C. Double-check the return, since the monitor could
+ // trigger on Activity B's lifecycle if the platform does something unexpected.
+ val activityB2 = monitorB.waitForActivityWithTimeout(3000) as NightModeActivity
+ assertNotSame("Monitor responded to activity B2 lifecycle", activityB, activityB2)
+ assertNotNull("Activity B2 started within 3000ms", activityB2)
+ activityB2.startActivity(
+ Intent(instr.context, NightModeUiModeConfigChangesActivityC::class.java).apply {
+ putExtra(NightModeActivity.KEY_TITLE, "C2")
+ }
+ )
+
+ // Activity B is hidden, wait for it to stop.
+ waitUntilState(activityB2, Lifecycle.State.CREATED)
+
+ // Wait for activity C to start. Double-check the return.
+ val activityC2 = monitorC.waitForActivityWithTimeout(3000) as NightModeActivity
+ assertNotSame("Monitor responded to Activity C2 lifecycle", activityC, activityC2)
+ assertNotNull("Activity C2 started within 3000ms", activityC2)
+
+ // Prepare activities A, B, and C to track configuration changes.
+ listOf(activityA, activityB2, activityC2).forEach { activity ->
+ activity.resetOnConfigurationChange()
+ }
+
+ // Toggle default night mode again from a non-UI thread.
+ Handler(Looper.getMainLooper()).post {
+ AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)
+ }
+
+ // Activities A, B, and C should all receive configuration changes.
+ listOf(activityA, activityB2, activityC2).forEach { activity ->
+ activity.expectOnConfigurationChange(3000)
+ }
+
+ // Activities A, B, and C should have all received the new configuration.
+ listOf(activityA, activityB2, activityC2).forEach { activity ->
+ activity.runOnUiThread {
+ NightModeUtils.assertConfigurationNightModeEquals(
+ "Activity ${activity.title}'s effective configuration has night mode set",
+ Configuration.UI_MODE_NIGHT_NO,
+ activity.effectiveConfiguration!!
+ )
+ }
+ }
+ }
+
+ private fun expectRecreate(monitor: ActivityMonitor, activity: Activity): Activity {
// The documentation says "Block until an Activity is created that matches this monitor."
// This statement is true, but there are some other true statements like: "Block until an
// Activity is destroyed" or "Block until an Activity is resumed"...
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityB.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityB.java
new file mode 100644
index 0000000..a5d27ef
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityB.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.app;
+
+/**
+ * An activity with DayNight theme that handles uiMode configuration changes.
+ */
+public class NightModeUiModeConfigChangesActivityB extends NightModeActivity {}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityC.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityC.java
new file mode 100644
index 0000000..83b473c
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesActivityC.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.app;
+
+/**
+ * An activity with DayNight theme that handles uiMode configuration changes.
+ */
+public class NightModeUiModeConfigChangesActivityC extends NightModeActivity {}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesTestCase.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesTestCase.kt
index 0cec4f9..fe35b47 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesTestCase.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeUiModeConfigChangesTestCase.kt
@@ -71,27 +71,51 @@
}
@Test
- fun testOnConfigurationChangeNotCalledWhenNotStarted() {
+ fun testOnConfigurationChangeCalledWhileStopped() {
+ scenario.moveToState(Lifecycle.State.RESUMED)
scenario.moveToState(Lifecycle.State.CREATED)
- // And clear any previous config changes
- scenario.onActivity { it.lastConfigurationChangeAndClear }
// Set local night mode to YES
scenario.onActivity { setNightMode(MODE_NIGHT_YES, it, setMode) }
- // Assert that the onConfigurationChange was not called with a new correct config
+ // Assert that the onConfigurationChange was called with a new correct config
scenario.onActivity {
- assertNull(it.lastConfigurationChangeAndClear)
+ val lastConfig = it.lastConfigurationChangeAndClear
+ assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_YES, lastConfig!!)
}
// Set local night mode back to NO
scenario.onActivity { setNightMode(MODE_NIGHT_NO, it, setMode) }
- // Assert that the onConfigurationChange was not called with a new correct config
+ // Assert that the onConfigurationChange was called with a new correct config
scenario.onActivity {
- assertNull(it.lastConfigurationChangeAndClear)
+ val lastConfig = it.lastConfigurationChangeAndClear
+ assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_NO, lastConfig!!)
}
}
@Test
+ fun testOnConfigurationChangeNotCalledWhileDestroyed() {
+ scenario.moveToState(Lifecycle.State.RESUMED)
+
+ lateinit var activity: NightModeUiModeConfigChangesActivity
+ scenario.onActivity { activity = it }
+
+ scenario.moveToState(Lifecycle.State.DESTROYED)
+
+ // And clear any previous config changes
+ activity.lastConfigurationChangeAndClear
+
+ // Set local night mode to YES
+ setNightMode(MODE_NIGHT_YES, activity, setMode)
+ // Assert that the onConfigurationChange was not called with a new correct config
+ assertNull(activity.lastConfigurationChangeAndClear)
+
+ // Set local night mode back to NO
+ setNightMode(MODE_NIGHT_NO, activity, setMode)
+ // Assert that the onConfigurationChange was not called with a new correct config
+ assertNull(activity.lastConfigurationChangeAndClear)
+ }
+
+ @Test
fun testResourcesUpdated() {
// Set local night mode to YES
scenario.onActivity { setNightMode(MODE_NIGHT_YES, it, setMode) }
@@ -128,7 +152,9 @@
@After
fun cleanup() {
// Reset the default night mode
- scenario.onActivity { setNightMode(MODE_NIGHT_NO, it, NightSetMode.DEFAULT) }
+ if (scenario.state != Lifecycle.State.DESTROYED) {
+ scenario.onActivity { setNightMode(MODE_NIGHT_NO, it, NightSetMode.DEFAULT) }
+ }
scenario.close()
}
diff --git a/appcompat/appcompat/src/androidTest/res/layout/dialog_layout.xml b/appcompat/appcompat/src/androidTest/res/layout/dialog_layout.xml
new file mode 100644
index 0000000..b97647f
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/res/layout/dialog_layout.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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:id="@+id/dialog_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="24sp"
+ android:textColor="@color/test_green"
+ android:text="@string/alert_dialog_custom_text1"
+ android:singleLine="false" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:textColor="@color/test_blue"
+ android:text="@string/alert_dialog_custom_text2"
+ android:singleLine="false" />
+</LinearLayout>
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index 08f06a8..7ca7f37 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -244,10 +244,10 @@
private boolean mLongPressBackDown;
private boolean mBaseContextAttached;
+ // true after the first call to onCreated.
private boolean mCreated;
- private boolean mStarted;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- boolean mIsDestroyed;
+ // true after the first (and only) call to onDestroyed.
+ boolean mDestroyed;
/**
* The configuration from the most recent call to either onConfigurationChanged or onCreate.
@@ -669,8 +669,6 @@
@Override
public void onStart() {
- mStarted = true;
-
// This will apply day/night if the time has changed, it will also call through to
// setupAutoNightModeIfNeeded()
applyDayNight();
@@ -678,8 +676,6 @@
@Override
public void onStop() {
- mStarted = false;
-
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setShowHideAnimationEnabled(false);
@@ -743,8 +739,7 @@
mWindow.getDecorView().removeCallbacks(mInvalidatePanelMenuRunnable);
}
- mStarted = false;
- mIsDestroyed = true;
+ mDestroyed = true;
if (mLocalNightMode != MODE_NIGHT_UNSPECIFIED
&& mHost instanceof Activity
@@ -844,7 +839,7 @@
// A pending invalidation will typically be resolved before the posted message
// would run normally in order to satisfy instance state restoration.
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (!mIsDestroyed && (st == null || st.menu == null)) {
+ if (!mDestroyed && (st == null || st.menu == null)) {
invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
}
}
@@ -1185,7 +1180,7 @@
@Override
public boolean onMenuItemSelected(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
final Window.Callback cb = getWindowCallback();
- if (cb != null && !mIsDestroyed) {
+ if (cb != null && !mDestroyed) {
final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
if (panel != null) {
return cb.onMenuItemSelected(panel.featureId, item);
@@ -1247,7 +1242,7 @@
}
ActionMode mode = null;
- if (mAppCompatCallback != null && !mIsDestroyed) {
+ if (mAppCompatCallback != null && !mDestroyed) {
try {
mode = mAppCompatCallback.onWindowStartingSupportActionMode(callback);
} catch (AbstractMethodError ame) {
@@ -1653,7 +1648,7 @@
private void openPanel(final PanelFeatureState st, KeyEvent event) {
// Already open, return
- if (st.isOpen || mIsDestroyed) {
+ if (st.isOpen || mDestroyed) {
return;
}
@@ -1765,7 +1760,7 @@
final Window.Callback cb = getWindowCallback();
if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
- if (cb != null && !mIsDestroyed) {
+ if (cb != null && !mDestroyed) {
// If we have a menu invalidation pending, do it now.
if (mInvalidatePanelMenuPosted &&
(mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {
@@ -1785,7 +1780,7 @@
}
} else {
mDecorContentParent.hideOverflowMenu();
- if (!mIsDestroyed) {
+ if (!mDestroyed) {
final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, st.menu);
}
@@ -1866,7 +1861,7 @@
}
private boolean preparePanel(PanelFeatureState st, KeyEvent event) {
- if (mIsDestroyed) {
+ if (mDestroyed) {
return false;
}
@@ -1977,7 +1972,7 @@
mClosingActionMenu = true;
mDecorContentParent.dismissPopups();
Window.Callback cb = getWindowCallback();
- if (cb != null && !mIsDestroyed) {
+ if (cb != null && !mDestroyed) {
cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, menu);
}
mClosingActionMenu = false;
@@ -2041,7 +2036,7 @@
mDecorContentParent.canShowOverflowMenu() &&
!ViewConfiguration.get(mContext).hasPermanentMenuKey()) {
if (!mDecorContentParent.isOverflowMenuShowing()) {
- if (!mIsDestroyed && preparePanel(st, event)) {
+ if (!mDestroyed && preparePanel(st, event)) {
handled = mDecorContentParent.showOverflowMenu();
}
} else {
@@ -2104,7 +2099,7 @@
return;
}
- if (!mIsDestroyed) {
+ if (!mDestroyed) {
// We need to be careful which callback we dispatch the call to. We can not dispatch
// this to the Window's callback since that will call back into this method and cause a
// crash. Instead we need to dispatch down to the original Activity/Dialog/etc.
@@ -2385,7 +2380,7 @@
@SuppressWarnings("deprecation")
private boolean applyDayNight(final boolean allowRecreation) {
- if (mIsDestroyed) {
+ if (mDestroyed) {
if (DEBUG) {
Log.d(TAG, "applyDayNight. Skipping because host is destroyed");
}
@@ -2614,14 +2609,15 @@
if (callOnConfigChange && mHost instanceof Activity) {
final Activity activity = (Activity) mHost;
if (activity instanceof LifecycleOwner) {
- // If the Activity is a LifecyleOwner, check that it is at least started
+ // If the Activity is a LifecyleOwner, check that it is after onCreate() and
+ // before onDestroy(), which includes STOPPED.
Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
- if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
+ if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.CREATED)) {
activity.onConfigurationChanged(conf);
}
} else {
- // Otherwise we'll fallback to our internal started flag.
- if (mStarted) {
+ // Otherwise, we'll fallback to our internal created and destroyed flags.
+ if (mCreated && !mDestroyed) {
activity.onConfigurationChanged(conf);
}
}
@@ -2777,7 +2773,7 @@
// Only dispatch for the root menu
if (subMenu == subMenu.getRootMenu() && mHasActionBar) {
Window.Callback cb = getWindowCallback();
- if (cb != null && !mIsDestroyed) {
+ if (cb != null && !mDestroyed) {
cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, subMenu);
}
}
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java
index feb05f0..48c6835 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java
@@ -24,6 +24,7 @@
import android.view.Window;
import android.view.WindowManager;
+import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
@@ -37,6 +38,16 @@
*/
public class AppCompatDialogFragment extends DialogFragment {
+ /** {@inheritDoc} **/
+ public AppCompatDialogFragment() {
+ super();
+ }
+
+ /** {@inheritDoc} **/
+ public AppCompatDialogFragment(@LayoutRes int contentLayoutId) {
+ super(contentLayoutId);
+ }
+
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXExtension.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXExtension.kt
index ea6c673..c17b7b7 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXExtension.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXExtension.kt
@@ -155,6 +155,8 @@
var legacyDisableKotlinStrictApiMode = false
+ var benchmarkRunAlsoInterpreted = false
+
fun shouldEnforceKotlinStrictApiMode(): Boolean {
return !legacyDisableKotlinStrictApiMode &&
shouldConfigureApiTasks()
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 212a24f..a2a0b1a0 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -48,7 +48,7 @@
val CONTENTPAGER = Version("1.1.0-alpha01")
val COMPOSE = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.0.0-beta08")
val COORDINATORLAYOUT = Version("1.2.0-alpha01")
- val CORE = Version("1.7.0-alpha01")
+ val CORE = Version("1.6.0-beta02")
val CORE_ANIMATION = Version("1.0.0-alpha03")
val CORE_ANIMATION_TESTING = Version("1.0.0-alpha03")
val CORE_APPDIGEST = Version("1.0.0-alpha01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/dackka/DackkaTask.kt b/buildSrc/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
index 3323df1..62196e2 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
@@ -22,7 +22,6 @@
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Classpath
-import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
@@ -53,10 +52,6 @@
@InputFiles
lateinit var sourcesDir: File
- // String representing names of .md files to be included in documentation
- @Input
- lateinit var includes: String
-
// Directory containing the docs project and package-lists
@InputFiles
lateinit var docsProjectDir: File
@@ -84,7 +79,9 @@
linksConfiguration +=
"${it.value}^${docsProjectDir.toPath()}/package-lists/${it.key}/package-list^^"
}
-
+ val includes = sourcesDir.walkTopDown()
+ .filter { it.name.endsWith("documentation.md") }
+ .joinToString(";")
val includesString = if (includes.isNotEmpty()) { "-includes $includes" } else { "" }
return listOf(
diff --git a/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
index da31e71..5ce9779 100644
--- a/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/docs/AndroidXDocsPlugin.kt
@@ -312,9 +312,6 @@
destinationDir = generatedDocsDir
samplesDir = unzippedSamplesSources
sourcesDir = unzippedDocsSources
- includes = unzippedDocsSources.walkTopDown()
- .filter { it.name.endsWith("documentation.md") }
- .joinToString(";")
docsProjectDir = File(project.rootDir, "docs-public")
dependenciesClasspath = androidJarFile(project) + dependencyClasspath
}
@@ -562,7 +559,7 @@
}
}
-private const val DACKKA_DEPENDENCY = "com.google.devsite:dackka:0.0.4"
+private const val DACKKA_DEPENDENCY = "com.google.devsite:dackka:0.0.5"
private const val DOCLAVA_DEPENDENCY = "com.android:doclava:1.0.6"
// Allowlist for directories that should be processed by Dackka
@@ -570,6 +567,7 @@
"androidx/benchmark/**",
"androidx/collection/**",
"androidx/compose/**",
+ "androidx/datastore/**",
"androidx/lifecycle/**",
"androidx/navigation/**",
"androidx/paging/**",
diff --git a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
index 17c29c3..9cd747d 100644
--- a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
@@ -67,6 +67,10 @@
abstract val hasBenchmarkPlugin: Property<Boolean>
@get:Input
+ @get:Optional
+ abstract val benchmarkRunAlsoInterpreted: Property<Boolean>
+
+ @get:Input
abstract val testRunner: Property<String>
@get:Input
@@ -127,6 +131,9 @@
if (hasBenchmarkPlugin.get()) {
configBuilder.isBenchmark(true)
if (configBuilder.isPostsubmit) {
+ if (benchmarkRunAlsoInterpreted.get()) {
+ configBuilder.tag("microbenchmarks_interpreted")
+ }
configBuilder.tag("microbenchmarks")
}
} else if (testProjectPath.get().endsWith("macrobenchmark")) {
diff --git a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
index ff5b301..e2a730e 100644
--- a/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
@@ -18,6 +18,7 @@
package androidx.build.testConfiguration
+import androidx.build.AndroidXExtension
import androidx.build.AndroidXPlugin
import androidx.build.AndroidXPlugin.Companion.ZIP_CONSTRAINED_TEST_CONFIGS_WITH_APKS_TASK
import androidx.build.AndroidXPlugin.Companion.ZIP_TEST_CONFIGS_WITH_APKS_TASK
@@ -82,7 +83,13 @@
} else {
task.minSdk.set(minSdk)
}
- task.hasBenchmarkPlugin.set(this.hasBenchmarkPlugin())
+ val hasBenchmarkPlugin = this.hasBenchmarkPlugin()
+ task.hasBenchmarkPlugin.set(hasBenchmarkPlugin)
+ if (hasBenchmarkPlugin) {
+ task.benchmarkRunAlsoInterpreted.set(
+ extensions.getByType<AndroidXExtension>().benchmarkRunAlsoInterpreted
+ )
+ }
task.testRunner.set(testRunner)
task.testProjectPath.set(this.path)
task.affectedModuleDetectorSubset.set(
diff --git a/busytown/androidx_incremental.sh b/busytown/androidx_incremental.sh
index 2c3383c1..f58af59 100755
--- a/busytown/androidx_incremental.sh
+++ b/busytown/androidx_incremental.sh
@@ -33,7 +33,7 @@
function zipKotlinMetadata() {
zipFile=kotlinMetadata.zip
echo "zipping kotlin metadata"
- (cd $OUT_DIR && find -name "*kotlin_metadata" | xargs zip "$DIST_DIR/$zipFile")
+ (cd $OUT_DIR && find -name "*kotlin_module" | xargs zip "$DIST_DIR/$zipFile")
echo done zipping kotlin metadata
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
index 8645cc2..f4f27b5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
@@ -140,7 +140,29 @@
object DefaultSessionOptionsUnpacker : SessionConfig.OptionUnpacker {
override fun unpack(config: UseCaseConfig<*>, builder: SessionConfig.Builder) {
- // Unused.
+ val defaultSessionConfig = config.getDefaultSessionConfig( /*valueIfMissing=*/null)
+
+ var implOptions: Config = OptionsBundle.emptyBundle()
+ var templateType = SessionConfig.defaultEmptySessionConfig().templateType
+
+ // Apply/extract defaults from session config
+ if (defaultSessionConfig != null) {
+ templateType = defaultSessionConfig.templateType
+ builder.addAllDeviceStateCallbacks(defaultSessionConfig.deviceStateCallbacks)
+ builder.addAllSessionStateCallbacks(defaultSessionConfig.sessionStateCallbacks)
+ builder.addAllRepeatingCameraCaptureCallbacks(
+ defaultSessionConfig.repeatingCameraCaptureCallbacks
+ )
+ implOptions = defaultSessionConfig.implementationOptions
+ }
+
+ // Set any additional implementation options
+ builder.setImplementationOptions(implOptions)
+
+ // Set the template type from default session config
+ builder.setTemplateType(templateType)
+
+ // TODO: Add Camera2 options and callbacks
}
}
}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapterTest.kt
index 8c846fc..78a488a 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapterTest.kt
@@ -16,6 +16,7 @@
package androidx.camera.camera2.pipe.integration.adapter
+import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraDevice
import android.os.Build
import android.view.Surface
@@ -24,6 +25,7 @@
import androidx.camera.core.impl.CaptureConfig
import androidx.camera.core.impl.ImageOutputConfig
import androidx.camera.core.impl.MutableOptionsBundle
+import androidx.camera.core.impl.SessionConfig
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -59,6 +61,60 @@
config.assertEquals(useCaseConfig.defaultCaptureConfig)
}
+ @Test
+ fun shouldApplySessionConfig_whenDefaultConfigSet() {
+ // Arrange
+ val defaultSessionCaptureConfig = SessionConfig.Builder()
+ .apply {
+ setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
+ addImplementationOptions(
+ MutableOptionsBundle.create()
+ .apply {
+ insertOption(
+ ImageOutputConfig.OPTION_TARGET_ROTATION,
+ Surface.ROTATION_180
+ )
+ }
+ )
+ addDeviceStateCallback(object : CameraDevice.StateCallback() {
+ override fun onOpened(camera: CameraDevice) {
+ // unused
+ }
+
+ override fun onDisconnected(camera: CameraDevice) {
+ // unused
+ }
+
+ override fun onError(camera: CameraDevice, error: Int) {
+ // unused
+ }
+ })
+ addSessionStateCallback(object : CameraCaptureSession.StateCallback() {
+ override fun onConfigured(session: CameraCaptureSession) {
+ // unused
+ }
+
+ override fun onConfigureFailed(session: CameraCaptureSession) {
+ // unused
+ }
+ })
+ addRepeatingCameraCaptureCallback(object : CameraCaptureCallback() {})
+ }
+ .build()
+
+ val useCaseConfig = ImageCapture.Builder()
+ .setDefaultSessionConfig(defaultSessionCaptureConfig)
+ .useCaseConfig
+ val builder = SessionConfig.Builder()
+
+ // Act
+ CameraUseCaseAdapter.DefaultSessionOptionsUnpacker.unpack(useCaseConfig, builder)
+
+ // Assert
+ val config = builder.build()
+ config.assertEquals(useCaseConfig.defaultSessionConfig)
+ }
+
private fun CaptureConfig.assertEquals(other: CaptureConfig) {
assertThat(templateType).isEqualTo(other.templateType)
assertThat(isUseRepeatingSurface).isEqualTo(other.isUseRepeatingSurface)
@@ -80,4 +136,21 @@
assertThat(tagBundle.getTag(key)).isEqualTo(other.tagBundle.getTag(key))
}
}
+
+ private fun SessionConfig.assertEquals(other: SessionConfig) {
+ assertThat(templateType).isEqualTo(other.templateType)
+ // Implementation options
+ assertThat(implementationOptions.listOptions())
+ .isEqualTo(other.implementationOptions.listOptions())
+ implementationOptions.listOptions().forEach { option ->
+ assertThat(implementationOptions.retrieveOption(option)).isEqualTo(
+ other.implementationOptions.retrieveOption(option)
+ )
+ }
+
+ // Verify callbacks
+ assertThat(deviceStateCallbacks).isEqualTo(other.deviceStateCallbacks)
+ assertThat(sessionStateCallbacks).isEqualTo(other.sessionStateCallbacks)
+ assertThat(repeatingCameraCaptureCallbacks).isEqualTo(other.repeatingCameraCaptureCallbacks)
+ }
}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
index e84383d..46ab8cd 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ExposureDeviceTest.java
@@ -41,12 +41,10 @@
import android.view.Surface;
import androidx.annotation.NonNull;
-import androidx.annotation.OptIn;
import androidx.camera.camera2.internal.compat.CameraManagerCompat;
import androidx.camera.camera2.internal.util.SemaphoreReleasingCamera2Callbacks;
import androidx.camera.camera2.interop.Camera2Interop;
import androidx.camera.core.CameraSelector;
-import androidx.camera.core.ExperimentalExposureCompensation;
import androidx.camera.core.ExposureState;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.impl.CameraControlInternal;
@@ -94,7 +92,6 @@
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
-@OptIn(markerClass = ExperimentalExposureCompensation.class)
public class ExposureDeviceTest {
@CameraSelector.LensFacing
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
index de30395..6417153 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
@@ -43,7 +43,6 @@
import androidx.camera.camera2.interop.Camera2CameraControl;
import androidx.camera.camera2.interop.CaptureRequestOptions;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
-import androidx.camera.core.ExperimentalExposureCompensation;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.FocusMeteringResult;
import androidx.camera.core.ImageCapture;
@@ -420,7 +419,6 @@
@NonNull
@Override
- @ExperimentalExposureCompensation
public ListenableFuture<Integer> setExposureCompensationIndex(int exposure) {
if (!isControlInUse()) {
return Futures.immediateFailedFuture(
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
index 66925a2..c6bd795 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
@@ -31,7 +31,6 @@
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraState;
-import androidx.camera.core.ExperimentalExposureCompensation;
import androidx.camera.core.ExposureState;
import androidx.camera.core.Logger;
import androidx.camera.core.ZoomState;
@@ -294,7 +293,6 @@
@NonNull
@Override
- @ExperimentalExposureCompensation
public ExposureState getExposureState() {
synchronized (mLock) {
if (mCamera2CameraControlImpl == null) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
index 8b87d66..bf9b6e9 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureControl.java
@@ -23,7 +23,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.OptIn;
import androidx.camera.camera2.impl.Camera2ImplConfig;
import androidx.camera.camera2.internal.annotation.CameraExecutor;
import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
@@ -53,7 +52,6 @@
* The task will fails with {@link CameraControl.OperationCanceledException} if the camera is
* closed.
*/
-@OptIn(markerClass = androidx.camera.core.ExperimentalExposureCompensation.class)
public class ExposureControl {
private static final int DEFAULT_EXPOSURE_COMPENSATION = 0;
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java
index bf44fdf..fb4a514 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java
@@ -23,13 +23,11 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
-import androidx.camera.core.ExperimentalExposureCompensation;
import androidx.camera.core.ExposureState;
/**
* An implementation of {@link ExposureState} where the values can be set.
*/
-@ExperimentalExposureCompensation
class ExposureStateImpl implements ExposureState {
private final Object mLock = new Object();
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
index 58f8e22..1909102 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ExposureControlTest.java
@@ -30,10 +30,8 @@
import android.util.Range;
import android.util.Rational;
-import androidx.annotation.OptIn;
import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
import androidx.camera.core.CameraControl;
-import androidx.camera.core.ExperimentalExposureCompensation;
import androidx.camera.core.impl.CameraControlInternal;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.test.core.app.ApplicationProvider;
@@ -57,7 +55,6 @@
@RunWith(RobolectricTestRunner.class)
@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
@DoNotInstrument
-@OptIn(markerClass = ExperimentalExposureCompensation.class)
public class ExposureControlTest {
private static final String CAMERA0_ID = "0";
diff --git a/camera/camera-core/api/current.ignore b/camera/camera-core/api/current.ignore
index 2f97885..17d923e 100644
--- a/camera/camera-core/api/current.ignore
+++ b/camera/camera-core/api/current.ignore
@@ -1,5 +1,9 @@
// Baseline format: 1.0
+AddedAbstractMethod: androidx.camera.core.CameraControl#setExposureCompensationIndex(int):
+ Added method androidx.camera.core.CameraControl.setExposureCompensationIndex(int)
AddedAbstractMethod: androidx.camera.core.CameraInfo#getCameraSelector():
Added method androidx.camera.core.CameraInfo.getCameraSelector()
AddedAbstractMethod: androidx.camera.core.CameraInfo#getCameraState():
Added method androidx.camera.core.CameraInfo.getCameraState()
+AddedAbstractMethod: androidx.camera.core.CameraInfo#getExposureState():
+ Added method androidx.camera.core.CameraInfo.getExposureState()
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index 4519564..46c69c1 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -14,6 +14,7 @@
public interface CameraControl {
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
@@ -29,6 +30,7 @@
public interface CameraInfo {
method public androidx.camera.core.CameraSelector getCameraSelector();
method public androidx.lifecycle.LiveData<androidx.camera.core.CameraState!> getCameraState();
+ method public androidx.camera.core.ExposureState getExposureState();
method public int getSensorRotationDegrees();
method public int getSensorRotationDegrees(int);
method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
@@ -122,6 +124,13 @@
ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraInfo, float, float);
}
+ public interface ExposureState {
+ method public int getExposureCompensationIndex();
+ method public android.util.Range<java.lang.Integer!> getExposureCompensationRange();
+ method public android.util.Rational getExposureCompensationStep();
+ method public boolean isExposureCompensationSupported();
+ }
+
public interface ExtendableBuilder<T> {
method public T build();
}
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
index ea9906c..daa3256 100644
--- a/camera/camera-core/api/public_plus_experimental_current.txt
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -14,7 +14,7 @@
public interface CameraControl {
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
- method @androidx.camera.core.ExperimentalExposureCompensation public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
@@ -30,7 +30,7 @@
public interface CameraInfo {
method public androidx.camera.core.CameraSelector getCameraSelector();
method public androidx.lifecycle.LiveData<androidx.camera.core.CameraState!> getCameraState();
- method @androidx.camera.core.ExperimentalExposureCompensation public androidx.camera.core.ExposureState getExposureState();
+ method public androidx.camera.core.ExposureState getExposureState();
method public int getSensorRotationDegrees();
method public int getSensorRotationDegrees(int);
method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
@@ -135,9 +135,6 @@
@RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCustomizableThreads {
}
- @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalExposureCompensation {
- }
-
@RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalGetImage {
}
@@ -147,7 +144,7 @@
@RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalUseCaseGroup {
}
- @androidx.camera.core.ExperimentalExposureCompensation public interface ExposureState {
+ public interface ExposureState {
method public int getExposureCompensationIndex();
method public android.util.Range<java.lang.Integer!> getExposureCompensationRange();
method public android.util.Rational getExposureCompensationStep();
diff --git a/camera/camera-core/api/restricted_current.ignore b/camera/camera-core/api/restricted_current.ignore
index 2f97885..17d923e 100644
--- a/camera/camera-core/api/restricted_current.ignore
+++ b/camera/camera-core/api/restricted_current.ignore
@@ -1,5 +1,9 @@
// Baseline format: 1.0
+AddedAbstractMethod: androidx.camera.core.CameraControl#setExposureCompensationIndex(int):
+ Added method androidx.camera.core.CameraControl.setExposureCompensationIndex(int)
AddedAbstractMethod: androidx.camera.core.CameraInfo#getCameraSelector():
Added method androidx.camera.core.CameraInfo.getCameraSelector()
AddedAbstractMethod: androidx.camera.core.CameraInfo#getCameraState():
Added method androidx.camera.core.CameraInfo.getCameraState()
+AddedAbstractMethod: androidx.camera.core.CameraInfo#getExposureState():
+ Added method androidx.camera.core.CameraInfo.getExposureState()
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index 4519564..46c69c1 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -14,6 +14,7 @@
public interface CameraControl {
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
@@ -29,6 +30,7 @@
public interface CameraInfo {
method public androidx.camera.core.CameraSelector getCameraSelector();
method public androidx.lifecycle.LiveData<androidx.camera.core.CameraState!> getCameraState();
+ method public androidx.camera.core.ExposureState getExposureState();
method public int getSensorRotationDegrees();
method public int getSensorRotationDegrees(int);
method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
@@ -122,6 +124,13 @@
ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraInfo, float, float);
}
+ public interface ExposureState {
+ method public int getExposureCompensationIndex();
+ method public android.util.Range<java.lang.Integer!> getExposureCompensationRange();
+ method public android.util.Rational getExposureCompensationStep();
+ method public boolean isExposureCompensationSupported();
+ }
+
public interface ExtendableBuilder<T> {
method public T build();
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
index e9df24a..9fa6afc 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
@@ -176,7 +176,6 @@
* </ul>
*/
@NonNull
- @ExperimentalExposureCompensation
ListenableFuture<Integer> setExposureCompensationIndex(int value);
/**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
index fa3d911..00f4c40 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
@@ -138,7 +138,6 @@
* <p>The {@link ExposureState} contains the current exposure related information.
*/
@NonNull
- @ExperimentalExposureCompensation
ExposureState getExposureState();
/**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalExposureCompensation.java b/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalExposureCompensation.java
deleted file mode 100644
index 6e22ab7..0000000
--- a/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalExposureCompensation.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.core;
-
-
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-import androidx.annotation.RequiresOptIn;
-
-import java.lang.annotation.Retention;
-
-/**
- * Denotes that the annotated method uses the experimental ExposureCompensation APIs that can
- * control the exposure compensation of the camera.
- *
- * <p>The feature allow the user to control the exposure compensation of the camera, it includes a
- * setter in {@link androidx.camera.core.CameraControl} and a getter in
- * {@link androidx.camera.core.CameraInfo}.
- */
-@Retention(CLASS)
-@RequiresOptIn
-public @interface ExperimentalExposureCompensation {
-}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ExposureState.java b/camera/camera-core/src/main/java/androidx/camera/core/ExposureState.java
index 919f708..7979a74 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ExposureState.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ExposureState.java
@@ -26,7 +26,6 @@
*
* <p>Applications can retrieve an instance via {@link CameraInfo#getExposureState()}.
*/
-@ExperimentalExposureCompensation
public interface ExposureState {
/**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
index d5354ed..ac35a05 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
@@ -22,7 +22,6 @@
import androidx.annotation.NonNull;
import androidx.camera.core.CameraControl;
-import androidx.camera.core.ExperimentalExposureCompensation;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.FocusMeteringResult;
import androidx.camera.core.ImageCapture.FlashMode;
@@ -82,7 +81,6 @@
*/
@NonNull
@Override
- @ExperimentalExposureCompensation
ListenableFuture<Integer> setExposureCompensationIndex(int exposure);
/**
@@ -157,7 +155,6 @@
@NonNull
@Override
- @ExperimentalExposureCompensation
public ListenableFuture<Integer> setExposureCompensationIndex(int exposure) {
return Futures.immediateFuture(0);
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
index 4645b33..28da413 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
@@ -27,14 +27,16 @@
* throws the AssumptionViolatedException to ignore the test if the test environment is not in the
* lab. Useful for the tests not needed to run on the PostSubmit.
*
- * To use this [TestRule] do the following. <br></br><br></br>
+ * To use this [TestRule], do the following. <br></br><br></br>
*
* Add the Rule to your JUnit test. <br></br><br></br>
* `LabTestRule mLabTestRule = new LabTestRule();
` *
* <br></br><br></br>
*
- * Add the [LabTestOnly] annotation to your test case. <br></br><br></br>
+ * Add only one of [LabTestOnly], [LabTestFrontCamera] or, [LabTestRearCamera] annotation to your
+ * test case.
+ * <br></br><br></br>
* `public void yourTestCase() {
*
* }
@@ -50,6 +52,22 @@
@Retention(AnnotationRetention.RUNTIME)
annotation class LabTestOnly()
+ /**
+ * The annotation for tests that only want to run on the CameraX lab environment with
+ * enabling front camera.
+ */
+ @Target(AnnotationTarget.FUNCTION)
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class LabTestFrontCamera()
+
+ /**
+ * The annotation for tests that only want to run on the CameraX lab environment with
+ * enabling rear camera.
+ */
+ @Target(AnnotationTarget.FUNCTION)
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class LabTestRearCamera()
+
class LabTestStatement(private val statement: Statement) :
Statement() {
@@ -63,9 +81,38 @@
}
}
+ class LabTestFrontCameraStatement(private val statement: Statement) :
+ Statement() {
+
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ // Only test in CameraX lab environment and the loggable tag will be set when running
+ // the CameraX e2e test with enabling front camera.
+ assumeTrue(Log.isLoggable("frontCameraE2E", Log.DEBUG))
+ statement.evaluate()
+ }
+ }
+
+ class LabTestRearCameraStatement(private val statement: Statement) :
+ Statement() {
+
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ // Only test in CameraX lab environment and the loggable tag will be set when running
+ // the CameraX e2e test with enabling rear camera.
+ assumeTrue(Log.isLoggable("rearCameraE2E", Log.DEBUG))
+ statement.evaluate()
+ }
+ }
+
override fun apply(base: Statement, description: Description): Statement {
+
return if (description.getAnnotation(LabTestOnly::class.java) != null) {
LabTestStatement(base)
+ } else if (description.getAnnotation(LabTestFrontCamera::class.java) != null) {
+ LabTestFrontCameraStatement(base)
+ } else if (description.getAnnotation(LabTestRearCamera::class.java) != null) {
+ LabTestRearCameraStatement(base)
} else {
base
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
index e42f23c..24f1a46 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
@@ -22,7 +22,6 @@
import android.graphics.Rect;
import androidx.annotation.NonNull;
-import androidx.camera.core.ExperimentalExposureCompensation;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.FocusMeteringResult;
import androidx.camera.core.ImageCapture;
@@ -136,7 +135,6 @@
@NonNull
@Override
- @ExperimentalExposureCompensation
public ListenableFuture<Integer> setExposureCompensationIndex(int exposure) {
return Futures.immediateFuture(null);
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
index ac45541..cec24f5 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
@@ -24,7 +24,6 @@
import androidx.annotation.Nullable;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraState;
-import androidx.camera.core.ExperimentalExposureCompensation;
import androidx.camera.core.ExposureState;
import androidx.camera.core.TorchState;
import androidx.camera.core.ZoomState;
@@ -138,7 +137,6 @@
@NonNull
@Override
- @ExperimentalExposureCompensation
public ExposureState getExposureState() {
return new ExposureState() {
@Override
diff --git a/camera/camera-view/api/current.txt b/camera/camera-view/api/current.txt
index d9c61fc..a56d9d4 100644
--- a/camera/camera-view/api/current.txt
+++ b/camera/camera-view/api/current.txt
@@ -4,13 +4,21 @@
public abstract class CameraController {
method @MainThread public void clearImageAnalysisAnalyzer();
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+ method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
method @MainThread public androidx.camera.core.CameraInfo? getCameraInfo();
method @MainThread public androidx.camera.core.CameraSelector getCameraSelector();
+ method @MainThread public java.util.concurrent.Executor? getImageAnalysisBackgroundExecutor();
method @MainThread public int getImageAnalysisBackpressureStrategy();
method @MainThread public int getImageAnalysisImageQueueDepth();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageAnalysisTargetSize();
method @MainThread public int getImageCaptureFlashMode();
+ method @MainThread public java.util.concurrent.Executor? getImageCaptureIoExecutor();
+ method @MainThread public int getImageCaptureMode();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageCaptureTargetSize();
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> getInitializationFuture();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getPreviewTargetSize();
method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getVideoCaptureTargetSize();
method @MainThread public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
method @MainThread public boolean hasCamera(androidx.camera.core.CameraSelector);
method @MainThread public boolean isImageAnalysisEnabled();
@@ -20,12 +28,19 @@
method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
method @MainThread public void setEnabledUseCases(int);
method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+ method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
method @MainThread public void setImageAnalysisBackpressureStrategy(int);
method @MainThread public void setImageAnalysisImageQueueDepth(int);
+ method @MainThread public void setImageAnalysisTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public void setImageCaptureFlashMode(int);
+ method @MainThread public void setImageCaptureIoExecutor(java.util.concurrent.Executor?);
+ method @MainThread public void setImageCaptureMode(int);
+ method @MainThread public void setImageCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
method @MainThread public void setPinchToZoomEnabled(boolean);
+ method @MainThread public void setPreviewTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public void setTapToFocusEnabled(boolean);
+ method @MainThread public void setVideoCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
method @MainThread public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
method @MainThread public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
@@ -33,6 +48,14 @@
field public static final int IMAGE_CAPTURE = 1; // 0x1
}
+ public static class CameraController.OutputSize {
+ ctor public CameraController.OutputSize(int);
+ ctor public CameraController.OutputSize(android.util.Size);
+ method public int getAspectRatio();
+ method public android.util.Size? getResolution();
+ field public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
+ }
+
public final class LifecycleCameraController extends androidx.camera.view.CameraController {
ctor public LifecycleCameraController(android.content.Context);
method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
diff --git a/camera/camera-view/api/public_plus_experimental_current.txt b/camera/camera-view/api/public_plus_experimental_current.txt
index a02bc5e..798e132 100644
--- a/camera/camera-view/api/public_plus_experimental_current.txt
+++ b/camera/camera-view/api/public_plus_experimental_current.txt
@@ -4,13 +4,21 @@
public abstract class CameraController {
method @MainThread public void clearImageAnalysisAnalyzer();
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+ method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
method @MainThread public androidx.camera.core.CameraInfo? getCameraInfo();
method @MainThread public androidx.camera.core.CameraSelector getCameraSelector();
+ method @MainThread public java.util.concurrent.Executor? getImageAnalysisBackgroundExecutor();
method @MainThread @androidx.camera.core.ImageAnalysis.BackpressureStrategy public int getImageAnalysisBackpressureStrategy();
method @MainThread public int getImageAnalysisImageQueueDepth();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageAnalysisTargetSize();
method @MainThread @androidx.camera.core.ImageCapture.FlashMode public int getImageCaptureFlashMode();
+ method @MainThread public java.util.concurrent.Executor? getImageCaptureIoExecutor();
+ method @MainThread public int getImageCaptureMode();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageCaptureTargetSize();
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> getInitializationFuture();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getPreviewTargetSize();
method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getVideoCaptureTargetSize();
method @MainThread public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
method @MainThread public boolean hasCamera(androidx.camera.core.CameraSelector);
method @MainThread public boolean isImageAnalysisEnabled();
@@ -22,12 +30,19 @@
method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
method @MainThread public void setEnabledUseCases(int);
method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+ method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
method @MainThread public void setImageAnalysisBackpressureStrategy(@androidx.camera.core.ImageAnalysis.BackpressureStrategy int);
method @MainThread public void setImageAnalysisImageQueueDepth(int);
+ method @MainThread public void setImageAnalysisTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public void setImageCaptureFlashMode(@androidx.camera.core.ImageCapture.FlashMode int);
+ method @MainThread public void setImageCaptureIoExecutor(java.util.concurrent.Executor?);
+ method @MainThread public void setImageCaptureMode(@androidx.camera.core.ImageCapture.CaptureMode int);
+ method @MainThread public void setImageCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
method @MainThread public void setPinchToZoomEnabled(boolean);
+ method @MainThread public void setPreviewTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public void setTapToFocusEnabled(boolean);
+ method @MainThread public void setVideoCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
method @MainThread @androidx.camera.view.video.ExperimentalVideo public void startRecording(androidx.camera.view.video.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.view.video.OnVideoSavedCallback);
method @MainThread @androidx.camera.view.video.ExperimentalVideo public void stopRecording();
@@ -38,6 +53,14 @@
field @androidx.camera.view.video.ExperimentalVideo public static final int VIDEO_CAPTURE = 4; // 0x4
}
+ public static class CameraController.OutputSize {
+ ctor public CameraController.OutputSize(@androidx.camera.core.AspectRatio.Ratio int);
+ ctor public CameraController.OutputSize(android.util.Size);
+ method public int getAspectRatio();
+ method public android.util.Size? getResolution();
+ field public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
+ }
+
public final class LifecycleCameraController extends androidx.camera.view.CameraController {
ctor public LifecycleCameraController(android.content.Context);
method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
diff --git a/camera/camera-view/api/restricted_current.txt b/camera/camera-view/api/restricted_current.txt
index a47bbc3..c880826 100644
--- a/camera/camera-view/api/restricted_current.txt
+++ b/camera/camera-view/api/restricted_current.txt
@@ -4,13 +4,21 @@
public abstract class CameraController {
method @MainThread public void clearImageAnalysisAnalyzer();
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+ method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
method @MainThread public androidx.camera.core.CameraInfo? getCameraInfo();
method @MainThread public androidx.camera.core.CameraSelector getCameraSelector();
+ method @MainThread public java.util.concurrent.Executor? getImageAnalysisBackgroundExecutor();
method @MainThread @androidx.camera.core.ImageAnalysis.BackpressureStrategy public int getImageAnalysisBackpressureStrategy();
method @MainThread public int getImageAnalysisImageQueueDepth();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageAnalysisTargetSize();
method @MainThread @androidx.camera.core.ImageCapture.FlashMode public int getImageCaptureFlashMode();
+ method @MainThread public java.util.concurrent.Executor? getImageCaptureIoExecutor();
+ method @MainThread public int getImageCaptureMode();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageCaptureTargetSize();
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> getInitializationFuture();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getPreviewTargetSize();
method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+ method @MainThread public androidx.camera.view.CameraController.OutputSize? getVideoCaptureTargetSize();
method @MainThread public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
method @MainThread public boolean hasCamera(androidx.camera.core.CameraSelector);
method @MainThread public boolean isImageAnalysisEnabled();
@@ -20,12 +28,19 @@
method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
method @MainThread public void setEnabledUseCases(int);
method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+ method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
method @MainThread public void setImageAnalysisBackpressureStrategy(@androidx.camera.core.ImageAnalysis.BackpressureStrategy int);
method @MainThread public void setImageAnalysisImageQueueDepth(int);
+ method @MainThread public void setImageAnalysisTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public void setImageCaptureFlashMode(@androidx.camera.core.ImageCapture.FlashMode int);
+ method @MainThread public void setImageCaptureIoExecutor(java.util.concurrent.Executor?);
+ method @MainThread public void setImageCaptureMode(@androidx.camera.core.ImageCapture.CaptureMode int);
+ method @MainThread public void setImageCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
method @MainThread public void setPinchToZoomEnabled(boolean);
+ method @MainThread public void setPreviewTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public void setTapToFocusEnabled(boolean);
+ method @MainThread public void setVideoCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
method @MainThread public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
method @MainThread public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
@@ -33,6 +48,14 @@
field public static final int IMAGE_CAPTURE = 1; // 0x1
}
+ public static class CameraController.OutputSize {
+ ctor public CameraController.OutputSize(@androidx.camera.core.AspectRatio.Ratio int);
+ ctor public CameraController.OutputSize(android.util.Size);
+ method public int getAspectRatio();
+ method public android.util.Size? getResolution();
+ field public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
+ }
+
public final class LifecycleCameraController extends androidx.camera.view.CameraController {
ctor public LifecycleCameraController(android.content.Context);
method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
index 98d607f..2fe7a79 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
@@ -16,12 +16,15 @@
package androidx.camera.view;
+import static androidx.camera.view.CameraController.OutputSize.UNASSIGNED_ASPECT_RATIO;
+
import android.annotation.SuppressLint;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
+import android.util.Size;
import android.view.Display;
import androidx.annotation.DoNotInline;
@@ -34,6 +37,7 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
+import androidx.camera.core.AspectRatio;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraControl;
import androidx.camera.core.CameraInfo;
@@ -56,6 +60,7 @@
import androidx.camera.core.VideoCapture;
import androidx.camera.core.ViewPort;
import androidx.camera.core.ZoomState;
+import androidx.camera.core.impl.ImageOutputConfig;
import androidx.camera.core.impl.utils.Threads;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.Futures;
@@ -150,32 +155,50 @@
// Synthetic access
@SuppressWarnings("WeakerAccess")
@NonNull
- final Preview mPreview;
+ Preview mPreview;
+
+ @Nullable
+ OutputSize mPreviewTargetSize;
// Synthetic access
@SuppressWarnings("WeakerAccess")
@NonNull
- final ImageCapture mImageCapture;
+ ImageCapture mImageCapture;
+
+ @Nullable
+ OutputSize mImageCaptureTargetSize;
+
+ @Nullable
+ Executor mImageCaptureIoExecutor;
@Nullable
private Executor mAnalysisExecutor;
@Nullable
+ private Executor mAnalysisBackgroundExecutor;
+
+ @Nullable
private ImageAnalysis.Analyzer mAnalysisAnalyzer;
@NonNull
ImageAnalysis mImageAnalysis;
+ @Nullable
+ OutputSize mImageAnalysisTargetSize;
+
// Synthetic access
@SuppressWarnings("WeakerAccess")
@NonNull
- final VideoCapture mVideoCapture;
+ VideoCapture mVideoCapture;
// Synthetic access
@SuppressWarnings("WeakerAccess")
@NonNull
final AtomicBoolean mVideoIsRecording = new AtomicBoolean(false);
+ @Nullable
+ OutputSize mVideoCaptureOutputSize;
+
// The latest bound camera.
// Synthetic access
@SuppressWarnings("WeakerAccess")
@@ -375,6 +398,35 @@
return (mEnabledUseCases & useCaseMask) != 0;
}
+ /**
+ * Sets the target aspect ratio or target resolution based on {@link OutputSize}.
+ */
+ private void setTargetOutputSize(@NonNull ImageOutputConfig.Builder<?> builder,
+ @Nullable OutputSize outputSize) {
+ if (outputSize == null) {
+ return;
+ }
+ if (outputSize.getResolution() != null) {
+ builder.setTargetResolution(outputSize.getResolution());
+ } else if (outputSize.getAspectRatio() != UNASSIGNED_ASPECT_RATIO) {
+ builder.setTargetAspectRatio(outputSize.getAspectRatio());
+ } else {
+ Logger.e(TAG, "Invalid target surface size. " + outputSize);
+ }
+ }
+
+ /**
+ * Checks if two {@link OutputSize} are equal.
+ */
+ private boolean isOutputSizeEqual(
+ @Nullable OutputSize currentSize,
+ @Nullable OutputSize newSize) {
+ if (currentSize == newSize) {
+ return true;
+ }
+ return currentSize != null && currentSize.equals(newSize);
+ }
+
// ------------------
// Preview use case.
// ------------------
@@ -433,6 +485,55 @@
return (DisplayManager) mAppContext.getSystemService(Context.DISPLAY_SERVICE);
}
+ /**
+ * Sets the intended output size for {@link Preview}.
+ *
+ * <p> The value is used as a hint when determining the resolution and aspect ratio of the
+ * preview. The actual output may differ from the requested value due to device constraints.
+ *
+ * <p> When set to null, the output will be based on the default config of {@link Preview}.
+ *
+ * <p> Changing the target size will reconfigure the camera which will cause additional latency.
+ * To avoid this, set the target size before controller is bound to lifecycle.
+ *
+ * @param targetSize the intended output size for {@link Preview}.
+ * @see Preview.Builder#setTargetAspectRatio(int)
+ * @see Preview.Builder#setTargetResolution(Size)
+ */
+ @MainThread
+ public void setPreviewTargetSize(@Nullable OutputSize targetSize) {
+ Threads.checkMainThread();
+ if (isOutputSizeEqual(mPreviewTargetSize, targetSize)) {
+ return;
+ }
+ mPreviewTargetSize = targetSize;
+ unbindPreviewAndRecreate();
+ startCameraAndTrackStates();
+ }
+
+ /**
+ * Returns the intended output size for {@link Preview} set by
+ * {@link #setPreviewTargetSize(OutputSize)}, or null if not set.
+ */
+ @MainThread
+ @Nullable
+ public OutputSize getPreviewTargetSize() {
+ Threads.checkMainThread();
+ return mPreviewTargetSize;
+ }
+
+ /**
+ * Unbinds {@link Preview} and recreates with the latest parameters.
+ */
+ private void unbindPreviewAndRecreate() {
+ if (isCameraInitialized()) {
+ mCameraProvider.unbind(mPreview);
+ }
+ Preview.Builder builder = new Preview.Builder();
+ setTargetOutputSize(builder, mPreviewTargetSize);
+ mPreview = builder.build();
+ }
+
// ----------------------
// ImageCapture UseCase.
// ----------------------
@@ -546,6 +647,120 @@
mImageCapture.takePicture(executor, callback);
}
+ /**
+ * Sets the image capture mode.
+ *
+ * <p>Valid capture modes are {@link ImageCapture.CaptureMode#CAPTURE_MODE_MINIMIZE_LATENCY},
+ * which prioritizes latency over image quality, or
+ * {@link ImageCapture.CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY},
+ * which prioritizes image quality over latency.
+ *
+ * @param captureMode the requested image capture mode.
+ */
+ @MainThread
+ public void setImageCaptureMode(@ImageCapture.CaptureMode int captureMode) {
+ Threads.checkMainThread();
+ if (mImageCapture.getCaptureMode() == captureMode) {
+ return;
+ }
+ unbindImageCaptureAndRecreate(captureMode);
+ startCameraAndTrackStates();
+ }
+
+ /**
+ * Returns the image capture mode.
+ *
+ * @see ImageCapture#getCaptureMode()
+ */
+ @MainThread
+ public int getImageCaptureMode() {
+ Threads.checkMainThread();
+ return mImageCapture.getCaptureMode();
+ }
+
+ /**
+ * Sets the intended image size for {@link ImageCapture}.
+ *
+ * <p> The value is used as a hint when determining the resolution and aspect ratio of
+ * the captured image. The actual output may differ from the requested value due to device
+ * constraints.
+ *
+ * <p> When set to null, the output will be based on the default config of {@link ImageCapture}.
+ *
+ * <p> Changing the target size will reconfigure the camera which will cause additional latency.
+ * To avoid this, set the target size before controller is bound to lifecycle.
+ *
+ * @param targetSize the intended image size for {@link ImageCapture}.
+ */
+ @MainThread
+ public void setImageCaptureTargetSize(@Nullable OutputSize targetSize) {
+ Threads.checkMainThread();
+ if (isOutputSizeEqual(mImageCaptureTargetSize, targetSize)) {
+ return;
+ }
+ mImageCaptureTargetSize = targetSize;
+ unbindImageCaptureAndRecreate(getImageCaptureMode());
+ startCameraAndTrackStates();
+ }
+
+ /**
+ * Returns the intended output size for {@link ImageCapture} set by
+ * {@link #setImageCaptureTargetSize(OutputSize)}, or null if not set.
+ */
+ @MainThread
+ @Nullable
+ public OutputSize getImageCaptureTargetSize() {
+ Threads.checkMainThread();
+ return mImageCaptureTargetSize;
+ }
+
+ /**
+ * Sets the default executor that will be used for {@link ImageCapture} IO tasks.
+ *
+ * <p> This executor will be used for any IO tasks specifically for {@link ImageCapture},
+ * such as {@link #takePicture(ImageCapture.OutputFileOptions, Executor,
+ * ImageCapture.OnImageSavedCallback)}. If no executor is set, then a default Executor
+ * specifically for IO will be used instead.
+ *
+ * @param executor The executor which will be used for IO tasks.
+ * TODO(b/187842789) add @see link for ImageCapture.
+ */
+ @MainThread
+ public void setImageCaptureIoExecutor(@Nullable Executor executor) {
+ Threads.checkMainThread();
+ if (mImageCaptureIoExecutor == executor) {
+ return;
+ }
+ mImageCaptureIoExecutor = executor;
+ unbindImageCaptureAndRecreate(mImageCapture.getCaptureMode());
+ startCameraAndTrackStates();
+ }
+
+ /**
+ * Gets the default executor for {@link ImageCapture} IO tasks.
+ */
+ @MainThread
+ @Nullable
+ public Executor getImageCaptureIoExecutor() {
+ Threads.checkMainThread();
+ return mImageCaptureIoExecutor;
+ }
+
+ /**
+ * Unbinds {@link ImageCapture} and recreates with the latest parameters.
+ */
+ private void unbindImageCaptureAndRecreate(int imageCaptureMode) {
+ if (isCameraInitialized()) {
+ mCameraProvider.unbind(mImageCapture);
+ }
+ ImageCapture.Builder builder = new ImageCapture.Builder().setCaptureMode(imageCaptureMode);
+ setTargetOutputSize(builder, mImageCaptureTargetSize);
+ if (mImageCaptureIoExecutor != null) {
+ builder.setIoExecutor(mImageCaptureIoExecutor);
+ }
+ mImageCapture = builder.build();
+ }
+
// -----------------
// Image analysis
// -----------------
@@ -673,19 +888,94 @@
}
/**
- * Unbinds {@link ImageAnalysis} and recreates with the given parameters.
+ * Sets the intended output size for {@link ImageAnalysis}.
*
- * <p> This is necessary because unlike other use cases, {@link ImageAnalysis}'s parameters
- * cannot be updated without recreating the use case.
+ * <p> The value is used as a hint when determining the resolution and aspect ratio of
+ * the output buffer. The actual output may differ from the requested value due to device
+ * constraints.
+ *
+ * <p> When set to null, the output will be based on the default config of
+ * {@link ImageAnalysis}.
+ *
+ * <p> Changing the target size will reconfigure the camera which will cause additional latency.
+ * To avoid this, set the target size before controller is bound to lifecycle.
+ *
+ * @param targetSize the intended output size for {@link ImageAnalysis}.
+ * @see ImageAnalysis.Builder#setTargetAspectRatio(int)
+ * @see ImageAnalysis.Builder#setTargetResolution(Size)
+ */
+ @MainThread
+ public void setImageAnalysisTargetSize(@Nullable OutputSize targetSize) {
+ Threads.checkMainThread();
+ if (isOutputSizeEqual(mImageAnalysisTargetSize, targetSize)) {
+ return;
+ }
+ mImageAnalysisTargetSize = targetSize;
+ unbindImageAnalysisAndRecreate(
+ mImageAnalysis.getBackpressureStrategy(),
+ mImageAnalysis.getImageQueueDepth());
+ startCameraAndTrackStates();
+ }
+
+ /**
+ * Returns the intended output size for {@link ImageAnalysis} set by
+ * {@link #setImageAnalysisTargetSize(OutputSize)}, or null if not set.
+ */
+ @MainThread
+ @Nullable
+ public OutputSize getImageAnalysisTargetSize() {
+ Threads.checkMainThread();
+ return mImageAnalysisTargetSize;
+ }
+
+ /**
+ * Sets the executor that will be used for {@link ImageAnalysis} background tasks.
+ *
+ * <p>If not set, the background executor will default to an automatically generated
+ * {@link Executor}.
+ *
+ * @param executor the executor for {@link ImageAnalysis} background tasks.
+ * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor)
+ */
+ @MainThread
+ public void setImageAnalysisBackgroundExecutor(@Nullable Executor executor) {
+ Threads.checkMainThread();
+ if (mAnalysisBackgroundExecutor == executor) {
+ return;
+ }
+ mAnalysisBackgroundExecutor = executor;
+ unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(),
+ mImageAnalysis.getImageQueueDepth());
+ startCameraAndTrackStates();
+ }
+
+ /**
+ * Gets the default executor for {@link ImageAnalysis} background tasks.
+ *
+ * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor)
+ */
+ @MainThread
+ @Nullable
+ public Executor getImageAnalysisBackgroundExecutor() {
+ Threads.checkMainThread();
+ return mAnalysisBackgroundExecutor;
+ }
+
+ /**
+ * Unbinds {@link ImageAnalysis} and recreates with the latest parameters.
*/
private void unbindImageAnalysisAndRecreate(int strategy, int imageQueueDepth) {
if (isCameraInitialized()) {
mCameraProvider.unbind(mImageAnalysis);
}
- mImageAnalysis = new ImageAnalysis.Builder()
+ ImageAnalysis.Builder builder = new ImageAnalysis.Builder()
.setBackpressureStrategy(strategy)
- .setImageQueueDepth(imageQueueDepth)
- .build();
+ .setImageQueueDepth(imageQueueDepth);
+ setTargetOutputSize(builder, mImageAnalysisTargetSize);
+ if (mAnalysisBackgroundExecutor != null) {
+ builder.setBackgroundExecutor(mAnalysisBackgroundExecutor);
+ }
+ mImageAnalysis = builder.build();
if (mAnalysisExecutor != null && mAnalysisAnalyzer != null) {
mImageAnalysis.setAnalyzer(mAnalysisExecutor, mAnalysisAnalyzer);
}
@@ -765,6 +1055,53 @@
return mVideoIsRecording.get();
}
+ /**
+ * Sets the intended video size for {@code VideoCapture}.
+ *
+ * <p> The value is used as a hint when determining the resolution and aspect ratio of
+ * the video. The actual output may differ from the requested value due to device constraints.
+ *
+ * <p> When set to null, the output will be based on the default config of {@code VideoCapture}.
+ *
+ * <p> Changing the target size will reconfigure the camera which will cause additional latency.
+ * To avoid this, set the target size before controller is bound to lifecycle.
+ *
+ * @param targetSize the intended video size for {@code VideoCapture}.
+ */
+ @MainThread
+ public void setVideoCaptureTargetSize(@Nullable OutputSize targetSize) {
+ Threads.checkMainThread();
+ if (isOutputSizeEqual(mVideoCaptureOutputSize, targetSize)) {
+ return;
+ }
+ mVideoCaptureOutputSize = targetSize;
+ unbindVideoAndRecreate();
+ startCameraAndTrackStates();
+ }
+
+ /**
+ * Returns the intended output size for {@code VideoCapture} set by
+ * {@link #setVideoCaptureTargetSize(OutputSize)}, or null if not set.
+ */
+ @MainThread
+ @Nullable
+ public OutputSize getVideoCaptureTargetSize() {
+ Threads.checkMainThread();
+ return mVideoCaptureOutputSize;
+ }
+
+ /**
+ * Unbinds VideoCapture and recreate with the latest parameters.
+ */
+ private void unbindVideoAndRecreate() {
+ if (isCameraInitialized()) {
+ mCameraProvider.unbind(mVideoCapture);
+ }
+ VideoCapture.Builder builder = new VideoCapture.Builder();
+ setTargetOutputSize(builder, mVideoCaptureOutputSize);
+ mVideoCapture = builder.build();
+ }
+
// -----------------
// Camera control
// -----------------
@@ -985,6 +1322,12 @@
/**
* Gets the {@link CameraInfo} of the currently attached camera.
*
+ * <p> For info available directly through CameraController as well as {@link CameraInfo},
+ * it's recommended to use the ones with CameraController, e.g. {@link #getTorchState()} v.s.
+ * {@link CameraInfo#getTorchState()}. {@link CameraInfo} is a lower-layer API and may
+ * require more steps to achieve the same effect, and will not maintain values when switching
+ * between cameras.
+ *
* @return the {@link CameraInfo} of the current camera. Returns null if camera is not ready.
* @see Camera#getCameraInfo()
*/
@@ -996,6 +1339,25 @@
}
/**
+ * Gets the {@link CameraControl} of the currently attached camera.
+ *
+ * <p> For controls available directly through CameraController as well as
+ * {@link CameraControl}, it's recommended to use the ones with CameraController, e.g.
+ * {@link #setLinearZoom(float)} v.s. {@link CameraControl#setLinearZoom(float)}.
+ * CameraControl is a lower-layer API and may require more steps to achieve the same effect,
+ * and will not maintain control values when switching between cameras.
+ *
+ * @return the {@link CameraControl} of the current camera. Returns null if camera is not ready.
+ * @see Camera#getCameraControl()
+ */
+ @Nullable
+ @MainThread
+ public CameraControl getCameraControl() {
+ Threads.checkMainThread();
+ return mCamera == null ? null : mCamera.getCameraControl();
+ }
+
+ /**
* Sets current zoom by ratio.
*
* <p>Valid zoom values range from {@link ZoomState#getMinZoomRatio()} to
@@ -1221,4 +1583,84 @@
return context.getAttributionTag();
}
}
+
+ /**
+ * Represents the output size of a {@link UseCase}.
+ *
+ * <p> This class is a preferred output size to be used with {@link CameraController}. The
+ * preferred output size can be based on either resolution or aspect ratio, but not both.
+ *
+ * @see #setImageAnalysisTargetSize(OutputSize)
+ * @see #setPreviewTargetSize(OutputSize)
+ * @see #setImageCaptureTargetSize(OutputSize)
+ * @see #setVideoCaptureTargetSize(OutputSize)
+ */
+ public static class OutputSize {
+
+ /**
+ * A value that represents the aspect ratio is not assigned.
+ */
+ public static final int UNASSIGNED_ASPECT_RATIO = -1;
+
+ /**
+ * Possible value for {@link #getAspectRatio()}
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {UNASSIGNED_ASPECT_RATIO, AspectRatio.RATIO_4_3, AspectRatio.RATIO_16_9})
+ public @interface OutputAspectRatio {
+ }
+
+ @OutputAspectRatio
+ private final int mAspectRatio;
+
+ @Nullable
+ private final Size mResolution;
+
+ /**
+ * Creates a {@link OutputSize} that is based on aspect ratio.
+ *
+ * @see Preview.Builder#setTargetAspectRatio(int)
+ * @see ImageAnalysis.Builder#setTargetAspectRatio(int)
+ */
+ public OutputSize(@AspectRatio.Ratio int aspectRatio) {
+ Preconditions.checkArgument(aspectRatio != UNASSIGNED_ASPECT_RATIO);
+ mAspectRatio = aspectRatio;
+ mResolution = null;
+ }
+
+ /**
+ * Creates a {@link OutputSize} that is based on resolution.
+ *
+ * @see Preview.Builder#setTargetResolution(Size)
+ * @see ImageAnalysis.Builder#setTargetResolution(Size)
+ */
+ public OutputSize(@NonNull Size resolution) {
+ Preconditions.checkNotNull(resolution);
+ mAspectRatio = UNASSIGNED_ASPECT_RATIO;
+ mResolution = resolution;
+ }
+
+ /**
+ * Gets the value of aspect ratio.
+ *
+ * @return {@link #UNASSIGNED_ASPECT_RATIO} if the size is not based on aspect ratio.
+ */
+ @OutputAspectRatio
+ public int getAspectRatio() {
+ return mAspectRatio;
+ }
+
+ /**
+ * Gets the value of resolution.
+ *
+ * @return null if the size is not based on resolution.
+ */
+ @Nullable
+ public Size getResolution() {
+ return mResolution;
+ }
+ }
}
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
index fe3a1c0..ce2e610 100644
--- a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
+++ b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
@@ -18,10 +18,17 @@
import android.content.Context
import android.os.Build
+import android.util.Size
import android.view.Surface
+import androidx.camera.core.AspectRatio
import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraX
import androidx.camera.core.CameraXConfig
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.impl.ImageAnalysisConfig
+import androidx.camera.core.impl.ImageCaptureConfig
+import androidx.camera.core.impl.ImageOutputConfig
import androidx.camera.testing.fakes.FakeAppConfig
import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
@@ -33,6 +40,7 @@
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.internal.DoNotInstrument
+import java.util.concurrent.Executors
/**
* Unit tests for [CameraController].
@@ -43,6 +51,11 @@
public class CameraControllerTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
+ private lateinit var controller: CameraController
+ private val targetSizeWithAspectRatio =
+ CameraController.OutputSize(AspectRatio.RATIO_16_9)
+ private val targetSizeWithResolution =
+ CameraController.OutputSize(Size(1080, 1960))
@Before
public fun setUp() {
@@ -50,6 +63,7 @@
FakeAppConfig.create()
).build()
CameraX.initialize(context, cameraXConfig).get()
+ controller = LifecycleCameraController(context)
}
@After
@@ -59,24 +73,139 @@
@UiThreadTest
@Test
- public fun sensorRotationChanges_useCaseTargetRotationUpdated() {
- // Arrange.
- val controller = LifecycleCameraController(context)
+ public fun setPreviewAspectRatio() {
+ controller.previewTargetSize = targetSizeWithAspectRatio
+ assertThat(controller.previewTargetSize).isEqualTo(targetSizeWithAspectRatio)
+ val config = controller.mPreview.currentConfig as ImageOutputConfig
+ assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setPreviewResolution() {
+ controller.previewTargetSize = targetSizeWithResolution
+ assertThat(controller.previewTargetSize).isEqualTo(targetSizeWithResolution)
+
+ val config = controller.mPreview.currentConfig as ImageOutputConfig
+ assertThat(config.targetResolution).isEqualTo(targetSizeWithResolution.resolution)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setAnalysisAspectRatio() {
+ controller.imageAnalysisTargetSize = targetSizeWithAspectRatio
+ assertThat(controller.imageAnalysisTargetSize).isEqualTo(targetSizeWithAspectRatio)
+
+ val config = controller.mImageAnalysis.currentConfig as ImageOutputConfig
+ assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setAnalysisBackgroundExecutor() {
+ val executor = Executors.newSingleThreadExecutor()
+ controller.imageAnalysisBackgroundExecutor = executor
+ assertThat(controller.imageAnalysisBackgroundExecutor).isEqualTo(executor)
+ val config = controller.mImageAnalysis.currentConfig as ImageAnalysisConfig
+ assertThat(config.backgroundExecutor).isEqualTo(executor)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setAnalysisQueueDepth() {
+ controller.imageAnalysisImageQueueDepth = 100
+ assertThat(controller.imageAnalysisImageQueueDepth).isEqualTo(100)
+ assertThat(controller.mImageAnalysis.imageQueueDepth).isEqualTo(100)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setAnalysisBackpressureStrategy() {
+ controller.imageAnalysisBackpressureStrategy = ImageAnalysis.STRATEGY_BLOCK_PRODUCER
+ assertThat(controller.imageAnalysisBackpressureStrategy)
+ .isEqualTo(ImageAnalysis.STRATEGY_BLOCK_PRODUCER)
+ assertThat(controller.mImageAnalysis.backpressureStrategy)
+ .isEqualTo(ImageAnalysis.STRATEGY_BLOCK_PRODUCER)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setImageCaptureResolution() {
+ controller.imageCaptureTargetSize = targetSizeWithResolution
+ assertThat(controller.imageCaptureTargetSize).isEqualTo(targetSizeWithResolution)
+
+ val config = controller.mImageCapture.currentConfig as ImageOutputConfig
+ assertThat(config.targetResolution).isEqualTo(targetSizeWithResolution.resolution)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setImageCaptureAspectRatio() {
+ controller.imageCaptureTargetSize = targetSizeWithAspectRatio
+ assertThat(controller.imageCaptureTargetSize).isEqualTo(targetSizeWithAspectRatio)
+
+ val config = controller.mImageCapture.currentConfig as ImageOutputConfig
+ assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setImageCaptureMode() {
+ controller.imageCaptureMode = ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
+ assertThat(controller.imageCaptureMode)
+ .isEqualTo(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+ assertThat(controller.mImageCapture.captureMode)
+ .isEqualTo(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setImageCaptureIoExecutor() {
+ val ioExecutor = Executors.newSingleThreadExecutor()
+ controller.imageCaptureIoExecutor = ioExecutor
+ assertThat(controller.imageCaptureIoExecutor).isEqualTo(ioExecutor)
+ val config = controller.mImageCapture.currentConfig as ImageCaptureConfig
+ assertThat(config.ioExecutor).isEqualTo(ioExecutor)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setVideoCaptureResolution() {
+ controller.videoCaptureTargetSize = targetSizeWithResolution
+ assertThat(controller.videoCaptureTargetSize).isEqualTo(targetSizeWithResolution)
+
+ val config = controller.mVideoCapture.currentConfig as ImageOutputConfig
+ assertThat(config.targetResolution).isEqualTo(targetSizeWithResolution.resolution)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun setVideoCaptureAspectRatio() {
+ controller.videoCaptureTargetSize = targetSizeWithAspectRatio
+ assertThat(controller.videoCaptureTargetSize).isEqualTo(targetSizeWithAspectRatio)
+
+ val config = controller.mVideoCapture.currentConfig as ImageOutputConfig
+ assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio)
+ }
+
+ @UiThreadTest
+ @Test
+ public fun sensorRotationChanges_useCaseTargetRotationUpdated() {
// Act.
controller.mRotationReceiver.onRotationChanged(Surface.ROTATION_180)
// Assert.
assertThat(controller.mImageAnalysis.targetRotation).isEqualTo(Surface.ROTATION_180)
assertThat(controller.mImageCapture.targetRotation).isEqualTo(Surface.ROTATION_180)
- // TODO(b/177276479): verify VideoCapture once it supports getTargetRotation().
+ val videoConfig = controller.mVideoCapture.currentConfig as ImageOutputConfig
+ assertThat(videoConfig.targetRotation).isEqualTo(Surface.ROTATION_180)
}
@UiThreadTest
@Test
public fun setSelectorBeforeBound_selectorSet() {
// Arrange.
- val controller = LifecycleCameraController(context)
assertThat(controller.cameraSelector.lensFacing).isEqualTo(CameraSelector.LENS_FACING_BACK)
// Act.
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
new file mode 100644
index 0000000..7a667a2
--- /dev/null
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/MLKitBarcodeTest.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.core
+
+import android.content.Context
+import android.util.Log
+import android.util.Size
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraX
+import androidx.camera.core.CameraXConfig
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.LabTestRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import com.google.mlkit.vision.barcode.Barcode.FORMAT_QR_CODE
+import com.google.mlkit.vision.barcode.BarcodeScanner
+import com.google.mlkit.vision.barcode.BarcodeScannerOptions
+import com.google.mlkit.vision.barcode.BarcodeScanning
+import com.google.mlkit.vision.common.InputImage
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.After
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+// The integration-tests for MLKit vision barcode component with CameraX ImageAnalysis use case.
+@LargeTest
+@RunWith(Parameterized::class)
+class MLKitBarcodeTest(
+ private val resolution: Size,
+ private val cameraConfig: CameraXConfig
+) {
+
+ @get:Rule
+ val cameraRule = CameraUtil.grantCameraPermissionAndPreTest()
+
+ @get:Rule
+ val labTest: LabTestRule = LabTestRule()
+
+ companion object {
+ private const val DETECT_TIMEOUT = 5_000L
+ private const val TAG = "MLKitVisionTest"
+
+ // TODO(b/189279877) Add different CameraXConfig (Camera2Config, CameraPipeConfig)
+ // as parameters.
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun data() = listOf(
+ arrayOf(Size(640, 480), Camera2Config.defaultConfig()),
+ arrayOf(Size(1280, 720), Camera2Config.defaultConfig())
+ )
+ }
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private lateinit var camera: CameraUseCaseAdapter
+ // For MK Kit Barcode scanner
+ private lateinit var barcodeScanner: BarcodeScanner
+
+ @Before
+ fun setup() {
+ CameraX.initialize(context, cameraConfig).get(10, TimeUnit.SECONDS)
+
+ barcodeScanner = BarcodeScanning.getClient(
+ BarcodeScannerOptions.Builder().setBarcodeFormats(FORMAT_QR_CODE).build()
+ )
+ }
+
+ @After
+ fun tearDown(): Unit = runBlocking {
+ if (::camera.isInitialized) {
+ // TODO: The removeUseCases() call might be removed after clarifying the
+ // abortCaptures() issue in b/162314023
+ withContext(Dispatchers.Main) {
+ camera.removeUseCases(camera.useCases)
+ }
+ }
+ CameraX.shutdown().get(10, TimeUnit.SECONDS)
+
+ if (::barcodeScanner.isInitialized) {
+ barcodeScanner.close()
+ }
+ }
+
+ @LabTestRule.LabTestFrontCamera
+ @Test
+ fun barcodeDetectViaFontCamera() {
+ val imageAnalysis = initImageAnalysis()
+
+ camera = CameraUtil.createCameraAndAttachUseCase(
+ context,
+ CameraSelector.DEFAULT_FRONT_CAMERA,
+ imageAnalysis
+ )
+ assertBarcodeDetect(imageAnalysis)
+ }
+
+ @LabTestRule.LabTestRearCamera
+ @Test
+ fun barcodeDetectViaRearCamera() {
+ val imageAnalysis = initImageAnalysis()
+
+ camera = CameraUtil.createCameraAndAttachUseCase(
+ context,
+ CameraSelector.DEFAULT_BACK_CAMERA,
+ imageAnalysis
+ )
+ assertBarcodeDetect(imageAnalysis)
+ }
+
+ private fun assertBarcodeDetect(imageAnalysis: ImageAnalysis) {
+ val latchForBarcodeDetect = CountDownLatch(4)
+
+ imageAnalysis.setAnalyzer(
+ Dispatchers.Main.asExecutor(),
+ { imageProxy ->
+ barcodeScanner.process(
+ InputImage.fromMediaImage(
+ imageProxy.image!!,
+ imageProxy.imageInfo.rotationDegrees
+ )
+ )
+ .addOnSuccessListener { barcodes ->
+ barcodes.forEach {
+ if ("Hi, CamX!" == it.displayValue) {
+ latchForBarcodeDetect.countDown()
+ Log.d(TAG, "barcode display value: {${it.displayValue}} ")
+ }
+ }
+ }
+ .addOnFailureListener { exception ->
+ Log.e(TAG, "processImage onFailure: $exception")
+ }
+ // When the image is from CameraX analysis use case, must call image.close() on
+ // received images when finished using them. Otherwise, new images may not be
+ // received or the camera may stall.
+ .addOnCompleteListener {
+ imageProxy.close()
+ }
+ }
+ )
+
+ // Verify it is the CameraX lab test environment and can detect qr-code.
+ assertTrue(latchForBarcodeDetect.await(DETECT_TIMEOUT, TimeUnit.MILLISECONDS))
+ }
+
+ private fun initImageAnalysis(): ImageAnalysis {
+ return ImageAnalysis.Builder()
+ .setTargetName("ImageAnalysis")
+ .setTargetResolution(resolution)
+ .build()
+ }
+}
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index c990c34..bf2dd58 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -195,7 +195,6 @@
private FutureCallback<Integer> mEVFutureCallback = new FutureCallback<Integer>() {
@Override
- @OptIn(markerClass = androidx.camera.core.ExperimentalExposureCompensation.class)
public void onSuccess(@Nullable Integer result) {
CameraInfo cameraInfo = getCameraInfo();
if (cameraInfo != null) {
@@ -324,7 +323,6 @@
return mPhotoToggle.isChecked() && cameraInfo != null && cameraInfo.hasFlashUnit();
}
- @OptIn(markerClass = androidx.camera.core.ExperimentalExposureCompensation.class)
private boolean isExposureCompensationSupported() {
CameraInfo cameraInfo = getCameraInfo();
return cameraInfo != null
@@ -469,7 +467,6 @@
});
}
- @OptIn(markerClass = androidx.camera.core.ExperimentalExposureCompensation.class)
private void setUpEVButton() {
mPlusEV.setOnClickListener(v -> {
Objects.requireNonNull(getCameraInfo());
diff --git a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
index 5ee2e66..e1ea9bb 100644
--- a/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
+++ b/camera/integration-tests/viewtestapp/src/androidTest/java/androidx/camera/integration/view/CameraControllerFragmentTest.kt
@@ -118,6 +118,14 @@
}
@Test
+ fun controllerBound_canGetCameraControl() {
+ fragment.assertPreviewIsStreaming()
+ instrumentation.runOnMainSync {
+ assertThat(fragment.cameraController.cameraControl).isNotNull()
+ }
+ }
+
+ @Test
fun controllerBound_canGetCameraInfo() {
fragment.assertPreviewIsStreaming()
instrumentation.runOnMainSync {
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java
index 100a797..860446f 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java
@@ -16,12 +16,15 @@
package androidx.car.app.sample.showcase.common.templates;
+import static androidx.car.app.CarToast.LENGTH_LONG;
import static androidx.car.app.model.Action.BACK;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
+import androidx.car.app.CarToast;
import androidx.car.app.Screen;
import androidx.car.app.model.Action;
+import androidx.car.app.model.ActionStrip;
import androidx.car.app.model.CarColor;
import androidx.car.app.model.CarIcon;
import androidx.car.app.model.MessageTemplate;
@@ -60,6 +63,21 @@
throw new RuntimeException("Error");
})
.build())
+
+ .setActionStrip(
+ new ActionStrip.Builder()
+ .addAction(
+ new Action.Builder()
+ .setTitle("Settings")
+ .setOnClickListener(
+ () ->
+ CarToast.makeText(
+ getCarContext(),
+ "Clicked Settings",
+ LENGTH_LONG)
+ .show())
+ .build())
+ .build())
.build();
}
}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
index 365b683..a545dcd 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
@@ -260,6 +260,8 @@
// .setClass(getCarContext(), SignInWithGoogleActivity.class)
// .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// .putExtras(extras));
+ CarToast.makeText(getCarContext(), "Sign-in with Google starts here", LENGTH_LONG)
+ .show();
}
private MessageTemplate getSignInCompletedMessageTemplate() {
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 3a71b2b..03237cc 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -55,6 +55,7 @@
field public static final String CAR_SERVICE = "car";
field @androidx.car.app.annotations.RequiresCarApi(2) public static final String CONSTRAINT_SERVICE = "constraints";
field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+ field @androidx.car.app.annotations.RequiresCarApi(3) public static final String HARDWARE_SERVICE = "hardware";
field public static final String NAVIGATION_SERVICE = "navigation";
field public static final String SCREEN_SERVICE = "screen";
}
@@ -189,16 +190,6 @@
field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
}
- @Deprecated public final class ConnectionToCar {
- ctor @Deprecated public ConnectionToCar(android.content.Context);
- method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
- field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
- field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
- field @Deprecated public static final int NATIVE = 1; // 0x1
- field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
- field @Deprecated public static final int PROJECTION = 2; // 0x2
- }
-
}
package androidx.car.app.constraints {
@@ -214,6 +205,285 @@
}
+package androidx.car.app.hardware {
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarHardwareManager {
+ method public default androidx.car.app.hardware.info.CarInfo getCarInfo();
+ method public default androidx.car.app.hardware.info.CarSensors getCarSensors();
+ }
+
+}
+
+package androidx.car.app.hardware.common {
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarUnit {
+ field public static final int KILOMETER = 3; // 0x3
+ field public static final int KILOMETERS_PER_HOUR = 102; // 0x66
+ field public static final int METER = 2; // 0x2
+ field public static final int METERS_PER_SEC = 101; // 0x65
+ field public static final int MILE = 4; // 0x4
+ field public static final int MILES_PER_HOUR = 103; // 0x67
+ field public static final int MILLIMETER = 1; // 0x1
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarValue<T> {
+ ctor public CarValue(T?, long, int);
+ method public int getStatus();
+ method public long getTimestampMillis();
+ method public T? getValue();
+ field public static final int STATUS_SUCCESS = 1; // 0x1
+ field public static final int STATUS_UNAVAILABLE = 3; // 0x3
+ field public static final int STATUS_UNIMPLEMENTED = 2; // 0x2
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public interface OnCarDataListener<T> {
+ method public void onCarData(T);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class UpdateRate {
+ field public static final int DEFAULT = 0; // 0x0
+ field public static final int FASTEST = 2; // 0x2
+ field public static final int UI = 1; // 0x1
+ }
+
+}
+
+package androidx.car.app.hardware.info {
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Accelerometer {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getAccelerometer();
+ }
+
+ public static final class Accelerometer.Builder {
+ ctor public Accelerometer.Builder();
+ method public androidx.car.app.hardware.info.Accelerometer build();
+ method public androidx.car.app.hardware.info.Accelerometer.Builder setAccelerometer(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Accelerometer.Params {
+ ctor public Accelerometer.Params(int);
+ method public static androidx.car.app.hardware.info.Accelerometer.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarHardwareLocation {
+ method public androidx.car.app.hardware.common.CarValue<android.location.Location!> getLocation();
+ }
+
+ public static final class CarHardwareLocation.Builder {
+ ctor public CarHardwareLocation.Builder();
+ method public androidx.car.app.hardware.info.CarHardwareLocation build();
+ method public androidx.car.app.hardware.info.CarHardwareLocation.Builder setLocation(androidx.car.app.hardware.common.CarValue<android.location.Location!>);
+ }
+
+ public static final class CarHardwareLocation.Params {
+ ctor public CarHardwareLocation.Params(int);
+ method public static androidx.car.app.hardware.info.CarHardwareLocation.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarInfo {
+ method public void addEnergyLevelListener(androidx.car.app.hardware.info.EnergyLevel.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method public void addMileageListener(androidx.car.app.hardware.info.Mileage.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void addSpeedListener(androidx.car.app.hardware.info.Speed.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+ method public void addTollListener(androidx.car.app.hardware.info.Toll.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+ method public void getEnergyProfile(androidx.car.app.hardware.info.EnergyProfile.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyProfile!>);
+ method public void getModel(androidx.car.app.hardware.info.Model.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Model!>);
+ method public void removeEnergyLevelListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method public void removeMileageListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void removeSpeedListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+ method public void removeTollListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarSensors {
+ method public void addAccelerometerListener(androidx.car.app.hardware.info.Accelerometer.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void addCarHardwareLocationListener(androidx.car.app.hardware.info.CarHardwareLocation.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void addCompassListener(androidx.car.app.hardware.info.Compass.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+ method public void addGyroscopeListener(androidx.car.app.hardware.info.Gyroscope.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+ method public void removeAccelerometerListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void removeCarHardwareLocationListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void removeCompassListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+ method public void removeGyroscopeListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Compass {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getCompass();
+ }
+
+ public static final class Compass.Builder {
+ ctor public Compass.Builder();
+ method public androidx.car.app.hardware.info.Compass build();
+ method public androidx.car.app.hardware.info.Compass.Builder setCompass(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Compass.Params {
+ ctor public Compass.Params(int);
+ method public static androidx.car.app.hardware.info.Compass.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyLevel {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getBatteryPercent();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEnergyIsLow();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getFuelPercent();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRangeRemaining();
+ }
+
+ public static final class EnergyLevel.Builder {
+ ctor public EnergyLevel.Builder();
+ method public androidx.car.app.hardware.info.EnergyLevel build();
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setBatteryPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setEnergyIsLow(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setRangeRemaining(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ public static final class EnergyLevel.Params {
+ ctor public EnergyLevel.Params(int);
+ method public static androidx.car.app.hardware.info.EnergyLevel.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyProfile {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getEvConnectorTypes();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getFuelTypes();
+ field public static final int EVCONNECTOR_TYPE_CHADEMO = 3; // 0x3
+ field public static final int EVCONNECTOR_TYPE_COMBO_1 = 4; // 0x4
+ field public static final int EVCONNECTOR_TYPE_COMBO_2 = 5; // 0x5
+ field public static final int EVCONNECTOR_TYPE_GBT = 9; // 0x9
+ field public static final int EVCONNECTOR_TYPE_GBT_DC = 10; // 0xa
+ field public static final int EVCONNECTOR_TYPE_J1772 = 1; // 0x1
+ field public static final int EVCONNECTOR_TYPE_MENNEKES = 2; // 0x2
+ field public static final int EVCONNECTOR_TYPE_OTHER = 101; // 0x65
+ field public static final int EVCONNECTOR_TYPE_SCAME = 11; // 0xb
+ field public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7; // 0x7
+ field public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6; // 0x6
+ field public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8; // 0x8
+ field public static final int EVCONNECTOR_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_BIODIESEL = 5; // 0x5
+ field public static final int FUEL_TYPE_CNG = 8; // 0x8
+ field public static final int FUEL_TYPE_DIESEL_1 = 3; // 0x3
+ field public static final int FUEL_TYPE_DIESEL_2 = 4; // 0x4
+ field public static final int FUEL_TYPE_E85 = 6; // 0x6
+ field public static final int FUEL_TYPE_ELECTRIC = 10; // 0xa
+ field public static final int FUEL_TYPE_HYDROGEN = 11; // 0xb
+ field public static final int FUEL_TYPE_LEADED = 2; // 0x2
+ field public static final int FUEL_TYPE_LNG = 9; // 0x9
+ field public static final int FUEL_TYPE_LPG = 7; // 0x7
+ field public static final int FUEL_TYPE_OTHER = 12; // 0xc
+ field public static final int FUEL_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_UNLEADED = 1; // 0x1
+ }
+
+ public static final class EnergyProfile.Builder {
+ ctor public EnergyProfile.Builder();
+ method public androidx.car.app.hardware.info.EnergyProfile build();
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setEvConnectorTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setFuelTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+ }
+
+ public static final class EnergyProfile.Params {
+ ctor public EnergyProfile.Params();
+ method public static androidx.car.app.hardware.info.EnergyProfile.Params getDefault();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Gyroscope {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getGyroscope();
+ }
+
+ public static final class Gyroscope.Builder {
+ ctor public Gyroscope.Builder();
+ method public androidx.car.app.hardware.info.Gyroscope build();
+ method public androidx.car.app.hardware.info.Gyroscope.Builder setGyroscope(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Gyroscope.Params {
+ ctor public Gyroscope.Params(int);
+ method public static androidx.car.app.hardware.info.Gyroscope.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Mileage {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getOdometer();
+ }
+
+ public static final class Mileage.Builder {
+ ctor public Mileage.Builder();
+ method public androidx.car.app.hardware.info.Mileage build();
+ method public androidx.car.app.hardware.info.Mileage.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.Mileage.Builder setOdometer(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ public static final class Mileage.Params {
+ ctor public Mileage.Params(int);
+ method public static androidx.car.app.hardware.info.Mileage.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Model {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getManufacturer();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getName();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getYear();
+ }
+
+ public static final class Model.Builder {
+ ctor public Model.Builder();
+ method public androidx.car.app.hardware.info.Model build();
+ method public androidx.car.app.hardware.info.Model.Builder setManufacturer(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setName(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setYear(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Model.Params {
+ ctor public Model.Params();
+ method public static androidx.car.app.hardware.info.Model.Params getDefault();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Speed {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getDisplaySpeed();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRawSpeed();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getSpeedDisplayUnit();
+ }
+
+ public static final class Speed.Builder {
+ ctor public Speed.Builder();
+ method public androidx.car.app.hardware.info.Speed build();
+ method public androidx.car.app.hardware.info.Speed.Builder setDisplaySpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setRawSpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setSpeedDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Speed.Params {
+ ctor public Speed.Params(int);
+ method public static androidx.car.app.hardware.info.Speed.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Toll {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getCardState();
+ field public static final int TOLLCARD_STATE_INVALID = 2; // 0x2
+ field public static final int TOLLCARD_STATE_NOT_INSERTED = 3; // 0x3
+ field public static final int TOLLCARD_STATE_UNKNOWN = 0; // 0x0
+ field public static final int TOLLCARD_STATE_VALID = 1; // 0x1
+ }
+
+ public static final class Toll.Builder {
+ ctor public Toll.Builder();
+ method public androidx.car.app.hardware.info.Toll build();
+ method public androidx.car.app.hardware.info.Toll.Builder setCardState(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Toll.Params {
+ ctor public Toll.Params();
+ method public static androidx.car.app.hardware.info.Toll.Params getDefault();
+ }
+
+}
+
package androidx.car.app.model {
@androidx.car.app.annotations.CarProtocol public final class Action {
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index dbcb362..a91d6a4 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -55,6 +55,7 @@
field public static final String CAR_SERVICE = "car";
field @androidx.car.app.annotations.RequiresCarApi(2) public static final String CONSTRAINT_SERVICE = "constraints";
field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+ field @androidx.car.app.annotations.RequiresCarApi(3) public static final String HARDWARE_SERVICE = "hardware";
field public static final String NAVIGATION_SERVICE = "navigation";
field public static final String SCREEN_SERVICE = "screen";
}
@@ -192,16 +193,6 @@
field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
}
- @Deprecated public final class ConnectionToCar {
- ctor @Deprecated public ConnectionToCar(android.content.Context);
- method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
- field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
- field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
- field @Deprecated public static final int NATIVE = 1; // 0x1
- field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
- field @Deprecated public static final int PROJECTION = 2; // 0x2
- }
-
}
package androidx.car.app.constraints {
@@ -217,6 +208,285 @@
}
+package androidx.car.app.hardware {
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarHardwareManager {
+ method public default androidx.car.app.hardware.info.CarInfo getCarInfo();
+ method public default androidx.car.app.hardware.info.CarSensors getCarSensors();
+ }
+
+}
+
+package androidx.car.app.hardware.common {
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarUnit {
+ field public static final int KILOMETER = 3; // 0x3
+ field public static final int KILOMETERS_PER_HOUR = 102; // 0x66
+ field public static final int METER = 2; // 0x2
+ field public static final int METERS_PER_SEC = 101; // 0x65
+ field public static final int MILE = 4; // 0x4
+ field public static final int MILES_PER_HOUR = 103; // 0x67
+ field public static final int MILLIMETER = 1; // 0x1
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarValue<T> {
+ ctor public CarValue(T?, long, int);
+ method public int getStatus();
+ method public long getTimestampMillis();
+ method public T? getValue();
+ field public static final int STATUS_SUCCESS = 1; // 0x1
+ field public static final int STATUS_UNAVAILABLE = 3; // 0x3
+ field public static final int STATUS_UNIMPLEMENTED = 2; // 0x2
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public interface OnCarDataListener<T> {
+ method public void onCarData(T);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class UpdateRate {
+ field public static final int DEFAULT = 0; // 0x0
+ field public static final int FASTEST = 2; // 0x2
+ field public static final int UI = 1; // 0x1
+ }
+
+}
+
+package androidx.car.app.hardware.info {
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Accelerometer {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getAccelerometer();
+ }
+
+ public static final class Accelerometer.Builder {
+ ctor public Accelerometer.Builder();
+ method public androidx.car.app.hardware.info.Accelerometer build();
+ method public androidx.car.app.hardware.info.Accelerometer.Builder setAccelerometer(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Accelerometer.Params {
+ ctor public Accelerometer.Params(int);
+ method public static androidx.car.app.hardware.info.Accelerometer.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarHardwareLocation {
+ method public androidx.car.app.hardware.common.CarValue<android.location.Location!> getLocation();
+ }
+
+ public static final class CarHardwareLocation.Builder {
+ ctor public CarHardwareLocation.Builder();
+ method public androidx.car.app.hardware.info.CarHardwareLocation build();
+ method public androidx.car.app.hardware.info.CarHardwareLocation.Builder setLocation(androidx.car.app.hardware.common.CarValue<android.location.Location!>);
+ }
+
+ public static final class CarHardwareLocation.Params {
+ ctor public CarHardwareLocation.Params(int);
+ method public static androidx.car.app.hardware.info.CarHardwareLocation.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarInfo {
+ method public void addEnergyLevelListener(androidx.car.app.hardware.info.EnergyLevel.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method public void addMileageListener(androidx.car.app.hardware.info.Mileage.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void addSpeedListener(androidx.car.app.hardware.info.Speed.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+ method public void addTollListener(androidx.car.app.hardware.info.Toll.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+ method public void getEnergyProfile(androidx.car.app.hardware.info.EnergyProfile.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyProfile!>);
+ method public void getModel(androidx.car.app.hardware.info.Model.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Model!>);
+ method public void removeEnergyLevelListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method public void removeMileageListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void removeSpeedListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+ method public void removeTollListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarSensors {
+ method public void addAccelerometerListener(androidx.car.app.hardware.info.Accelerometer.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void addCarHardwareLocationListener(androidx.car.app.hardware.info.CarHardwareLocation.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void addCompassListener(androidx.car.app.hardware.info.Compass.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+ method public void addGyroscopeListener(androidx.car.app.hardware.info.Gyroscope.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+ method public void removeAccelerometerListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void removeCarHardwareLocationListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void removeCompassListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+ method public void removeGyroscopeListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Compass {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getCompass();
+ }
+
+ public static final class Compass.Builder {
+ ctor public Compass.Builder();
+ method public androidx.car.app.hardware.info.Compass build();
+ method public androidx.car.app.hardware.info.Compass.Builder setCompass(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Compass.Params {
+ ctor public Compass.Params(int);
+ method public static androidx.car.app.hardware.info.Compass.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyLevel {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getBatteryPercent();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEnergyIsLow();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getFuelPercent();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRangeRemaining();
+ }
+
+ public static final class EnergyLevel.Builder {
+ ctor public EnergyLevel.Builder();
+ method public androidx.car.app.hardware.info.EnergyLevel build();
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setBatteryPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setEnergyIsLow(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setRangeRemaining(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ public static final class EnergyLevel.Params {
+ ctor public EnergyLevel.Params(int);
+ method public static androidx.car.app.hardware.info.EnergyLevel.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyProfile {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getEvConnectorTypes();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getFuelTypes();
+ field public static final int EVCONNECTOR_TYPE_CHADEMO = 3; // 0x3
+ field public static final int EVCONNECTOR_TYPE_COMBO_1 = 4; // 0x4
+ field public static final int EVCONNECTOR_TYPE_COMBO_2 = 5; // 0x5
+ field public static final int EVCONNECTOR_TYPE_GBT = 9; // 0x9
+ field public static final int EVCONNECTOR_TYPE_GBT_DC = 10; // 0xa
+ field public static final int EVCONNECTOR_TYPE_J1772 = 1; // 0x1
+ field public static final int EVCONNECTOR_TYPE_MENNEKES = 2; // 0x2
+ field public static final int EVCONNECTOR_TYPE_OTHER = 101; // 0x65
+ field public static final int EVCONNECTOR_TYPE_SCAME = 11; // 0xb
+ field public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7; // 0x7
+ field public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6; // 0x6
+ field public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8; // 0x8
+ field public static final int EVCONNECTOR_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_BIODIESEL = 5; // 0x5
+ field public static final int FUEL_TYPE_CNG = 8; // 0x8
+ field public static final int FUEL_TYPE_DIESEL_1 = 3; // 0x3
+ field public static final int FUEL_TYPE_DIESEL_2 = 4; // 0x4
+ field public static final int FUEL_TYPE_E85 = 6; // 0x6
+ field public static final int FUEL_TYPE_ELECTRIC = 10; // 0xa
+ field public static final int FUEL_TYPE_HYDROGEN = 11; // 0xb
+ field public static final int FUEL_TYPE_LEADED = 2; // 0x2
+ field public static final int FUEL_TYPE_LNG = 9; // 0x9
+ field public static final int FUEL_TYPE_LPG = 7; // 0x7
+ field public static final int FUEL_TYPE_OTHER = 12; // 0xc
+ field public static final int FUEL_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_UNLEADED = 1; // 0x1
+ }
+
+ public static final class EnergyProfile.Builder {
+ ctor public EnergyProfile.Builder();
+ method public androidx.car.app.hardware.info.EnergyProfile build();
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setEvConnectorTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setFuelTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+ }
+
+ public static final class EnergyProfile.Params {
+ ctor public EnergyProfile.Params();
+ method public static androidx.car.app.hardware.info.EnergyProfile.Params getDefault();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Gyroscope {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getGyroscope();
+ }
+
+ public static final class Gyroscope.Builder {
+ ctor public Gyroscope.Builder();
+ method public androidx.car.app.hardware.info.Gyroscope build();
+ method public androidx.car.app.hardware.info.Gyroscope.Builder setGyroscope(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Gyroscope.Params {
+ ctor public Gyroscope.Params(int);
+ method public static androidx.car.app.hardware.info.Gyroscope.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Mileage {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getOdometer();
+ }
+
+ public static final class Mileage.Builder {
+ ctor public Mileage.Builder();
+ method public androidx.car.app.hardware.info.Mileage build();
+ method public androidx.car.app.hardware.info.Mileage.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.Mileage.Builder setOdometer(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ public static final class Mileage.Params {
+ ctor public Mileage.Params(int);
+ method public static androidx.car.app.hardware.info.Mileage.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Model {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getManufacturer();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getName();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getYear();
+ }
+
+ public static final class Model.Builder {
+ ctor public Model.Builder();
+ method public androidx.car.app.hardware.info.Model build();
+ method public androidx.car.app.hardware.info.Model.Builder setManufacturer(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setName(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setYear(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Model.Params {
+ ctor public Model.Params();
+ method public static androidx.car.app.hardware.info.Model.Params getDefault();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Speed {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getDisplaySpeed();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRawSpeed();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getSpeedDisplayUnit();
+ }
+
+ public static final class Speed.Builder {
+ ctor public Speed.Builder();
+ method public androidx.car.app.hardware.info.Speed build();
+ method public androidx.car.app.hardware.info.Speed.Builder setDisplaySpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setRawSpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setSpeedDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Speed.Params {
+ ctor public Speed.Params(int);
+ method public static androidx.car.app.hardware.info.Speed.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Toll {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getCardState();
+ field public static final int TOLLCARD_STATE_INVALID = 2; // 0x2
+ field public static final int TOLLCARD_STATE_NOT_INSERTED = 3; // 0x3
+ field public static final int TOLLCARD_STATE_UNKNOWN = 0; // 0x0
+ field public static final int TOLLCARD_STATE_VALID = 1; // 0x1
+ }
+
+ public static final class Toll.Builder {
+ ctor public Toll.Builder();
+ method public androidx.car.app.hardware.info.Toll build();
+ method public androidx.car.app.hardware.info.Toll.Builder setCardState(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Toll.Params {
+ ctor public Toll.Params();
+ method public static androidx.car.app.hardware.info.Toll.Params getDefault();
+ }
+
+}
+
package androidx.car.app.model {
@androidx.car.app.annotations.CarProtocol public final class Action {
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 3a71b2b..03237cc 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -55,6 +55,7 @@
field public static final String CAR_SERVICE = "car";
field @androidx.car.app.annotations.RequiresCarApi(2) public static final String CONSTRAINT_SERVICE = "constraints";
field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+ field @androidx.car.app.annotations.RequiresCarApi(3) public static final String HARDWARE_SERVICE = "hardware";
field public static final String NAVIGATION_SERVICE = "navigation";
field public static final String SCREEN_SERVICE = "screen";
}
@@ -189,16 +190,6 @@
field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
}
- @Deprecated public final class ConnectionToCar {
- ctor @Deprecated public ConnectionToCar(android.content.Context);
- method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
- field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
- field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
- field @Deprecated public static final int NATIVE = 1; // 0x1
- field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
- field @Deprecated public static final int PROJECTION = 2; // 0x2
- }
-
}
package androidx.car.app.constraints {
@@ -214,6 +205,285 @@
}
+package androidx.car.app.hardware {
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarHardwareManager {
+ method public default androidx.car.app.hardware.info.CarInfo getCarInfo();
+ method public default androidx.car.app.hardware.info.CarSensors getCarSensors();
+ }
+
+}
+
+package androidx.car.app.hardware.common {
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarUnit {
+ field public static final int KILOMETER = 3; // 0x3
+ field public static final int KILOMETERS_PER_HOUR = 102; // 0x66
+ field public static final int METER = 2; // 0x2
+ field public static final int METERS_PER_SEC = 101; // 0x65
+ field public static final int MILE = 4; // 0x4
+ field public static final int MILES_PER_HOUR = 103; // 0x67
+ field public static final int MILLIMETER = 1; // 0x1
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarValue<T> {
+ ctor public CarValue(T?, long, int);
+ method public int getStatus();
+ method public long getTimestampMillis();
+ method public T? getValue();
+ field public static final int STATUS_SUCCESS = 1; // 0x1
+ field public static final int STATUS_UNAVAILABLE = 3; // 0x3
+ field public static final int STATUS_UNIMPLEMENTED = 2; // 0x2
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public interface OnCarDataListener<T> {
+ method public void onCarData(T);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class UpdateRate {
+ field public static final int DEFAULT = 0; // 0x0
+ field public static final int FASTEST = 2; // 0x2
+ field public static final int UI = 1; // 0x1
+ }
+
+}
+
+package androidx.car.app.hardware.info {
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Accelerometer {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getAccelerometer();
+ }
+
+ public static final class Accelerometer.Builder {
+ ctor public Accelerometer.Builder();
+ method public androidx.car.app.hardware.info.Accelerometer build();
+ method public androidx.car.app.hardware.info.Accelerometer.Builder setAccelerometer(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Accelerometer.Params {
+ ctor public Accelerometer.Params(int);
+ method public static androidx.car.app.hardware.info.Accelerometer.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarHardwareLocation {
+ method public androidx.car.app.hardware.common.CarValue<android.location.Location!> getLocation();
+ }
+
+ public static final class CarHardwareLocation.Builder {
+ ctor public CarHardwareLocation.Builder();
+ method public androidx.car.app.hardware.info.CarHardwareLocation build();
+ method public androidx.car.app.hardware.info.CarHardwareLocation.Builder setLocation(androidx.car.app.hardware.common.CarValue<android.location.Location!>);
+ }
+
+ public static final class CarHardwareLocation.Params {
+ ctor public CarHardwareLocation.Params(int);
+ method public static androidx.car.app.hardware.info.CarHardwareLocation.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarInfo {
+ method public void addEnergyLevelListener(androidx.car.app.hardware.info.EnergyLevel.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method public void addMileageListener(androidx.car.app.hardware.info.Mileage.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void addSpeedListener(androidx.car.app.hardware.info.Speed.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+ method public void addTollListener(androidx.car.app.hardware.info.Toll.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+ method public void getEnergyProfile(androidx.car.app.hardware.info.EnergyProfile.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyProfile!>);
+ method public void getModel(androidx.car.app.hardware.info.Model.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Model!>);
+ method public void removeEnergyLevelListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method public void removeMileageListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void removeSpeedListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Speed!>);
+ method public void removeTollListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Toll!>);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(3) public interface CarSensors {
+ method public void addAccelerometerListener(androidx.car.app.hardware.info.Accelerometer.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void addCarHardwareLocationListener(androidx.car.app.hardware.info.CarHardwareLocation.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void addCompassListener(androidx.car.app.hardware.info.Compass.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+ method public void addGyroscopeListener(androidx.car.app.hardware.info.Gyroscope.Params, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+ method public void removeAccelerometerListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void removeCarHardwareLocationListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void removeCompassListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Compass!>);
+ method public void removeGyroscopeListener(androidx.car.app.hardware.common.OnCarDataListener<androidx.car.app.hardware.info.Gyroscope!>);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Compass {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getCompass();
+ }
+
+ public static final class Compass.Builder {
+ ctor public Compass.Builder();
+ method public androidx.car.app.hardware.info.Compass build();
+ method public androidx.car.app.hardware.info.Compass.Builder setCompass(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Compass.Params {
+ ctor public Compass.Params(int);
+ method public static androidx.car.app.hardware.info.Compass.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyLevel {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getBatteryPercent();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEnergyIsLow();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getFuelPercent();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRangeRemaining();
+ }
+
+ public static final class EnergyLevel.Builder {
+ ctor public EnergyLevel.Builder();
+ method public androidx.car.app.hardware.info.EnergyLevel build();
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setBatteryPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setEnergyIsLow(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setRangeRemaining(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ public static final class EnergyLevel.Params {
+ ctor public EnergyLevel.Params(int);
+ method public static androidx.car.app.hardware.info.EnergyLevel.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyProfile {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getEvConnectorTypes();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!> getFuelTypes();
+ field public static final int EVCONNECTOR_TYPE_CHADEMO = 3; // 0x3
+ field public static final int EVCONNECTOR_TYPE_COMBO_1 = 4; // 0x4
+ field public static final int EVCONNECTOR_TYPE_COMBO_2 = 5; // 0x5
+ field public static final int EVCONNECTOR_TYPE_GBT = 9; // 0x9
+ field public static final int EVCONNECTOR_TYPE_GBT_DC = 10; // 0xa
+ field public static final int EVCONNECTOR_TYPE_J1772 = 1; // 0x1
+ field public static final int EVCONNECTOR_TYPE_MENNEKES = 2; // 0x2
+ field public static final int EVCONNECTOR_TYPE_OTHER = 101; // 0x65
+ field public static final int EVCONNECTOR_TYPE_SCAME = 11; // 0xb
+ field public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7; // 0x7
+ field public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6; // 0x6
+ field public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8; // 0x8
+ field public static final int EVCONNECTOR_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_BIODIESEL = 5; // 0x5
+ field public static final int FUEL_TYPE_CNG = 8; // 0x8
+ field public static final int FUEL_TYPE_DIESEL_1 = 3; // 0x3
+ field public static final int FUEL_TYPE_DIESEL_2 = 4; // 0x4
+ field public static final int FUEL_TYPE_E85 = 6; // 0x6
+ field public static final int FUEL_TYPE_ELECTRIC = 10; // 0xa
+ field public static final int FUEL_TYPE_HYDROGEN = 11; // 0xb
+ field public static final int FUEL_TYPE_LEADED = 2; // 0x2
+ field public static final int FUEL_TYPE_LNG = 9; // 0x9
+ field public static final int FUEL_TYPE_LPG = 7; // 0x7
+ field public static final int FUEL_TYPE_OTHER = 12; // 0xc
+ field public static final int FUEL_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_UNLEADED = 1; // 0x1
+ }
+
+ public static final class EnergyProfile.Builder {
+ ctor public EnergyProfile.Builder();
+ method public androidx.car.app.hardware.info.EnergyProfile build();
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setEvConnectorTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setFuelTypes(androidx.car.app.hardware.common.CarValue<java.lang.Integer![]!>);
+ }
+
+ public static final class EnergyProfile.Params {
+ ctor public EnergyProfile.Params();
+ method public static androidx.car.app.hardware.info.EnergyProfile.Params getDefault();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Gyroscope {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float![]!> getGyroscope();
+ }
+
+ public static final class Gyroscope.Builder {
+ ctor public Gyroscope.Builder();
+ method public androidx.car.app.hardware.info.Gyroscope build();
+ method public androidx.car.app.hardware.info.Gyroscope.Builder setGyroscope(androidx.car.app.hardware.common.CarValue<java.lang.Float![]!>);
+ }
+
+ public static final class Gyroscope.Params {
+ ctor public Gyroscope.Params(int);
+ method public static androidx.car.app.hardware.info.Gyroscope.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Mileage {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getOdometer();
+ }
+
+ public static final class Mileage.Builder {
+ ctor public Mileage.Builder();
+ method public androidx.car.app.hardware.info.Mileage build();
+ method public androidx.car.app.hardware.info.Mileage.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.Mileage.Builder setOdometer(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ public static final class Mileage.Params {
+ ctor public Mileage.Params(int);
+ method public static androidx.car.app.hardware.info.Mileage.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Model {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getManufacturer();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getName();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getYear();
+ }
+
+ public static final class Model.Builder {
+ ctor public Model.Builder();
+ method public androidx.car.app.hardware.info.Model build();
+ method public androidx.car.app.hardware.info.Model.Builder setManufacturer(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setName(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setYear(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Model.Params {
+ ctor public Model.Params();
+ method public static androidx.car.app.hardware.info.Model.Params getDefault();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Speed {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getDisplaySpeed();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRawSpeed();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getSpeedDisplayUnit();
+ }
+
+ public static final class Speed.Builder {
+ ctor public Speed.Builder();
+ method public androidx.car.app.hardware.info.Speed build();
+ method public androidx.car.app.hardware.info.Speed.Builder setDisplaySpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setRawSpeed(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setSpeedDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Speed.Params {
+ ctor public Speed.Params(int);
+ method public static androidx.car.app.hardware.info.Speed.Params getDefault();
+ method public int getRate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Toll {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getCardState();
+ field public static final int TOLLCARD_STATE_INVALID = 2; // 0x2
+ field public static final int TOLLCARD_STATE_NOT_INSERTED = 3; // 0x3
+ field public static final int TOLLCARD_STATE_UNKNOWN = 0; // 0x0
+ field public static final int TOLLCARD_STATE_VALID = 1; // 0x1
+ }
+
+ public static final class Toll.Builder {
+ ctor public Toll.Builder();
+ method public androidx.car.app.hardware.info.Toll build();
+ method public androidx.car.app.hardware.info.Toll.Builder setCardState(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ public static final class Toll.Params {
+ ctor public Toll.Params();
+ method public static androidx.car.app.hardware.info.Toll.Params getDefault();
+ }
+
+}
+
package androidx.car.app.model {
@androidx.car.app.annotations.CarProtocol public final class Action {
diff --git a/car/app/app/src/main/java/androidx/car/app/CarContext.java b/car/app/app/src/main/java/androidx/car/app/CarContext.java
index 624a38a..2220466 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarContext.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarContext.java
@@ -39,10 +39,12 @@
import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.StringDef;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.hardware.CarHardwareManager;
import androidx.car.app.navigation.NavigationManager;
import androidx.car.app.notification.CarPendingIntent;
import androidx.car.app.utils.RemoteUtils;
@@ -93,7 +95,8 @@
*
* @hide
*/
- @StringDef({APP_SERVICE, CAR_SERVICE, NAVIGATION_SERVICE, SCREEN_SERVICE, CONSTRAINT_SERVICE})
+ @StringDef({APP_SERVICE, CAR_SERVICE, NAVIGATION_SERVICE, SCREEN_SERVICE, CONSTRAINT_SERVICE,
+ HARDWARE_SERVICE})
@Retention(RetentionPolicy.SOURCE)
@RestrictTo(LIBRARY)
public @interface CarServiceType {
@@ -120,6 +123,10 @@
*/
public static final String CAR_SERVICE = "car";
+ /** Manages access to androidx.car.app.hardware properties, sensors and actions. */
+ @RequiresCarApi(3)
+ public static final String HARDWARE_SERVICE = "hardware";
+
/**
* Key for including a IStartCarApp in the notification {@link Intent}, for starting the app
* if it has not been opened yet.
@@ -151,10 +158,18 @@
static final String EXTRA_ON_REQUEST_PERMISSIONS_RESULT_CALLBACK_KEY =
"androidx.car.app.action.EXTRA_ON_REQUEST_PERMISSIONS_RESULT_CALLBACK_KEY";
+ /**
+ * Holds an exception to be thrown when accessing {@link CarHardwareManager} if there is an
+ * error during initialization.
+ */
+ @Nullable
+ private final IllegalStateException mCarHardwareManagerException;
+
private final AppManager mAppManager;
private final NavigationManager mNavigationManager;
private final ScreenManager mScreenManager;
private final ConstraintManager mConstraintManager;
+ private final CarHardwareManager mCarHardwareManager;
private final OnBackPressedDispatcher mOnBackPressedDispatcher;
private final HostDispatcher mHostDispatcher;
private final Lifecycle mLifecycle;
@@ -208,6 +223,11 @@
return mScreenManager;
case CONSTRAINT_SERVICE:
return mConstraintManager;
+ case HARDWARE_SERVICE:
+ if (mCarHardwareManagerException != null) {
+ throw mCarHardwareManagerException;
+ }
+ return mCarHardwareManager;
default: // fall out
}
@@ -258,6 +278,8 @@
return SCREEN_SERVICE;
} else if (serviceClass.isInstance(mConstraintManager)) {
return CONSTRAINT_SERVICE;
+ } else if (serviceClass.isInstance(mCarHardwareManager)) {
+ return HARDWARE_SERVICE;
}
throw new IllegalArgumentException("The class does not correspond to a car service");
@@ -624,6 +646,24 @@
mNavigationManager = NavigationManager.create(this, hostDispatcher, lifecycle);
mScreenManager = ScreenManager.create(this, lifecycle);
mConstraintManager = ConstraintManager.create(this, hostDispatcher);
+
+ // Try to instantiate a CarHardwareManager.
+ CarHardwareManager carHardwareManager = null;
+ IllegalStateException carHardwareManagerException = null;
+ try {
+ carHardwareManager = CarHardwareManager.create(this, hostDispatcher);
+ if (carHardwareManager == null) {
+ carHardwareManagerException = new IllegalStateException("CarHardwareManager not "
+ + "configured. Did you forget to add a dependency on automotive or "
+ + "projected artifacts?");
+ }
+ } catch (IllegalStateException e) {
+ carHardwareManager = new CarHardwareManager() { };
+ carHardwareManagerException = e;
+ }
+ mCarHardwareManager = carHardwareManager;
+ mCarHardwareManagerException = carHardwareManagerException;
+
mOnBackPressedDispatcher =
new OnBackPressedDispatcher(() -> getCarService(ScreenManager.class).pop());
mLifecycle = lifecycle;
diff --git a/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java b/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java
deleted file mode 100644
index 4caabbe..0000000
--- a/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.connection;
-
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-import static androidx.car.app.utils.CommonUtils.isAutomotiveOS;
-
-import static java.util.Objects.requireNonNull;
-
-import android.content.Context;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.LiveData;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-// TODO(b/169537526): Remove this class
-/**
- * A class that allows retrieval of information about connection to a car head unit.
- *
- * @deprecated use {@link CarConnection} instead.
- */
-@Deprecated
-public final class ConnectionToCar {
- /**
- * Defines current car connection state.
- *
- * <p>This is used for communication with the car host's content provider on queries for
- * connection type.
- */
- public static final String CAR_CONNECTION_STATE = "CarConnectionState";
-
- /**
- * Broadcast action that notifies that the car connection has changed and needs to be updated.
- */
- public static final String ACTION_CAR_CONNECTION_UPDATED =
- "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
-
- /**
- * Represents the types of connections that exist to a car head unit.
- *
- * @hide
- */
- @IntDef({NOT_CONNECTED, NATIVE, PROJECTION})
- @Retention(RetentionPolicy.SOURCE)
- @Target({ElementType.TYPE_USE})
- @RestrictTo(LIBRARY)
- public @interface ConnectionType {
- }
-
- /**
- * Not connected to any car head unit.
- */
- public static final int NOT_CONNECTED = 0;
-
- /**
- * Natively running on a head unit (Android Automotive OS).
- */
- public static final int NATIVE = 1;
-
- /**
- * Connected to a car head unit by projecting to it.
- */
- public static final int PROJECTION = 2;
-
- private final LiveData<Integer> mConnectionTypeLiveData;
-
- /**
- * Constructs a {@link ConnectionToCar} that can be used to get connection information.
- *
- * @throws NullPointerException if {@code context} is {@code null}
- */
- public ConnectionToCar(@NonNull Context context) {
- requireNonNull(context);
- mConnectionTypeLiveData = isAutomotiveOS(context)
- ? new AutomotiveCarConnectionTypeLiveData()
- : new CarConnectionTypeLiveData(context);
- }
-
- /**
- * Returns a {@link LiveData} that can be observed to get current connection type.
- *
- * <p>The recommended pattern is to observe the {@link LiveData} with the activity's
- * lifecycle in order to get updates on the state change throughout the activity's lifetime.
- *
- * <p>Connection types are:
- * <ol>
- * <li>{@link #NOT_CONNECTED}
- * <li>{@link #NATIVE}
- * <li>{@link #PROJECTION}
- * </ol>
- */
- @NonNull
- public LiveData<@ConnectionType Integer> getType() {
- return mConnectionTypeLiveData;
- }
-}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/CarHardwareManager.java b/car/app/app/src/main/java/androidx/car/app/hardware/CarHardwareManager.java
new file mode 100644
index 0000000..7825bb3
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/CarHardwareManager.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.HostDispatcher;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.info.CarInfo;
+import androidx.car.app.hardware.info.CarSensors;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Manages access to car hardware specific properties and sensors.
+ */
+@RequiresCarApi(3)
+public interface CarHardwareManager {
+ /**
+ * Returns the {@link CarInfo} that can be used to query the car hardware information
+ * such as make, model, etc.
+ */
+ default @NonNull CarInfo getCarInfo() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the {@link CarSensors} that can be used to access sensor information from the
+ * car hardware.
+ */
+ default @NonNull CarSensors getCarSensors() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns an instance of {@link CarHardwareManager} depending on linked library including an
+ * optional {@link HostDispatcher}.
+ *
+ * @throws IllegalStateException if none of the supported classes are found or if a supported
+ * class was found but the constructor was mismatched
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
+ @NonNull
+ static CarHardwareManager create(@NonNull Context context,
+ @Nullable HostDispatcher hostDispatcher) throws IllegalStateException {
+
+ try { // Check for automotive library first.
+ Class<?> c = Class.forName("androidx.car.app.hardware.CarHardwareManagerAutomotive");
+ Constructor<?> ctor = c.getConstructor(Context.class);
+ Object object = ctor.newInstance(context);
+ return (CarHardwareManager) object;
+ } catch (ClassNotFoundException e) {
+ // No Automotive. Fall through.
+ } catch (NoSuchMethodException | IllegalAccessException | InstantiationException
+ | InvocationTargetException e) {
+ // Something went wrong with accessing the constructor or calling newInstance().
+ throw new IllegalStateException("Mismatch with app-automotive artifact", e);
+ }
+
+ try { // Check for automotive library first.
+ Class<?> c = Class.forName("androidx.car.app.hardware.CarHardwareManagerProjected");
+ Constructor<?> ctor = c.getConstructor(HostDispatcher.class);
+ Object object = ctor.newInstance(hostDispatcher);
+ return (CarHardwareManager) object;
+ } catch (ClassNotFoundException e) {
+ // No Projected. Fall through.
+ } catch (NoSuchMethodException | IllegalAccessException | InstantiationException
+ | InvocationTargetException e) {
+ // Something went wrong with accessing the constructor or calling newInstance().
+ throw new IllegalStateException("Mismatch with app-projected artifact", e);
+ }
+
+ throw new IllegalStateException("Vehicle Manager not "
+ + "configured. Did you forget to add a dependency on automotive or "
+ + "projected artifacts?");
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java
new file mode 100644
index 0000000..50a8995
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarUnit.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.car.app.hardware.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Units such as speed and distance for car hardware measurements and display. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class CarUnit {
+ /**
+ * Defines the possible distance units from car hardware.
+ *
+ * @hide
+ */
+ @IntDef({
+ MILLIMETER,
+ METER,
+ KILOMETER,
+ MILE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY)
+ public @interface CarDistanceUnit {
+ }
+
+ /** Millimeter unit. */
+ @CarDistanceUnit
+ public static final int MILLIMETER = 1;
+
+ /** Meter unit. */
+ @CarDistanceUnit
+ public static final int METER = 2;
+
+ @CarDistanceUnit
+ /** Kilometer unit. */
+ public static final int KILOMETER = 3;
+
+ /** Miles unit. */
+ @CarDistanceUnit
+ public static final int MILE = 4;
+
+ /**
+ * Defines the possible distance units from car hardware.
+ *
+ * @hide
+ */
+ @IntDef({
+ METERS_PER_SEC,
+ KILOMETERS_PER_HOUR,
+ MILES_PER_HOUR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY)
+ public @interface CarSpeedUnit {
+ }
+
+ /** Meters per second unit. */
+ @CarSpeedUnit
+ public static final int METERS_PER_SEC = 101;
+
+ /** Kilometers per hour unit. */
+ @CarSpeedUnit
+ public static final int KILOMETERS_PER_HOUR = 102;
+
+ /** Miles per hour unit. */
+ @CarSpeedUnit
+ public static final int MILES_PER_HOUR = 103;
+
+ private CarUnit() {}
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/CarValue.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarValue.java
new file mode 100644
index 0000000..ede436a
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/CarValue.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A data value object returned from car hardware with associated metadata including status,
+ * timestamp, and the actual value.
+ *
+ * @param <T> data type which is returned by the {@link CarValue}
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public final class CarValue<T> {
+ /**
+ * Defines the possible status codes when trying to access car hardware properties, sensors,
+ * and actions.
+ *
+ * @hide
+ */
+ @IntDef({
+ STATUS_UNKNOWN,
+ STATUS_SUCCESS,
+ STATUS_UNIMPLEMENTED,
+ STATUS_UNAVAILABLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY)
+ public @interface StatusCode {
+ }
+
+ /**
+ * {@link CarValue} has unknown status.
+ */
+ @StatusCode
+ public static final int STATUS_UNKNOWN = 0;
+
+ /**
+ * {@link CarValue} was obtained successfully.
+ */
+ @StatusCode
+ public static final int STATUS_SUCCESS = 1;
+
+ /**
+ * {@link CarValue} attempted for unimplemented property, sensor, or action.
+ *
+ * <p>For example, the car hardware might not be able to return a value such as speed or
+ * energy level and will set the status to this value.
+ */
+ @StatusCode
+ public static final int STATUS_UNIMPLEMENTED = 2;
+
+ /**
+ * {@link CarValue} attempted for unavailable property, sensor, or action.
+ *
+ * <p>For example, the car hardware might not be able to return a value such as climate at the
+ * current time because the engine is off and will set the status to this value.
+ */
+ @StatusCode
+ public static final int STATUS_UNAVAILABLE = 3;
+
+ @Nullable
+ private final T mValue;
+ private final long mTimestampMillis;
+ @StatusCode
+ private final int mStatus;
+
+ private static <T> CarValue<T> unimplemented() {
+ return new CarValue<>(null, 0, CarValue.STATUS_UNIMPLEMENTED);
+ }
+
+ /** @hide */
+ @RestrictTo(LIBRARY)
+ public static final CarValue<Integer> UNIMPLEMENTED_INTEGER = unimplemented();
+
+ /** @hide */
+ @RestrictTo(LIBRARY)
+ public static final CarValue<Boolean> UNIMPLEMENTED_BOOLEAN = unimplemented();
+
+ /** @hide */
+ @RestrictTo(LIBRARY)
+ public static final CarValue<Float> UNIMPLEMENTED_FLOAT = unimplemented();
+
+ /** @hide */
+ @RestrictTo(LIBRARY)
+ public static final CarValue<String> UNIMPLEMENTED_STRING = unimplemented();
+
+ /** @hide */
+ @RestrictTo(LIBRARY)
+ public static final CarValue<Float[]> UNIMPLEMENTED_FLOAT_ARRAY = unimplemented();
+
+ /** @hide */
+ @RestrictTo(LIBRARY)
+ public static final CarValue<Integer[]> UNIMPLEMENTED_INTEGER_ARRAY = unimplemented();
+
+ /**
+ * Returns a the data value or {@code null} if the status is not successful.
+ *
+ * @see #getStatus
+ */
+ @Nullable
+ public T getValue() {
+ return mValue;
+ }
+
+ /**
+ * Returns the time in milliseconds at which the event happened.
+ *
+ * <p>For a given property, sensor, or action, each new value's timestamp should be
+ * monotonically increasing using the same time base as SystemClock.elapsedRealtime().
+ */
+ public long getTimestampMillis() {
+ return mTimestampMillis;
+ }
+
+ /**
+ * Returns the status of this particular result such as success, unavailable, or unimplemented.
+ */
+ @StatusCode
+ public int getStatus() {
+ return mStatus;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[value: "
+ + mValue
+ + ", timestamp: "
+ + mTimestampMillis
+ + ", Status: "
+ + mStatus
+ + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mValue, mTimestampMillis, mStatus);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof CarValue<?>)) {
+ return false;
+ }
+ CarValue<?> otherCarValue = (CarValue<?>) other;
+ return Objects.equals(mValue, otherCarValue.mValue)
+ && mTimestampMillis == otherCarValue.mTimestampMillis
+ && mStatus == otherCarValue.mStatus;
+ }
+
+ /**
+ * Constructs a new instance of a {@link CarValue}.
+ *
+ * @param value data to be returned with the result
+ * @param timestampMillis the time in milliseconds when the value was generated. See
+ * {@link #getTimestampMillis}
+ * @param status the status code associated with this value
+ */
+ public CarValue(@Nullable T value, long timestampMillis, @StatusCode int status) {
+ mValue = value;
+ mTimestampMillis = timestampMillis;
+ mStatus = status;
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private CarValue() {
+ mValue = null;
+ mTimestampMillis = 0;
+ mStatus = STATUS_UNKNOWN;
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/OnCarDataListener.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/OnCarDataListener.java
new file mode 100644
index 0000000..191efff
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/OnCarDataListener.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.car.app.hardware.common;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+
+/**
+ * A listener for data being returned about from the car hardware.
+ *
+ * @param <T> data type returned by the listener
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public interface OnCarDataListener<T> {
+ /**
+ * Notifies that the requested data is available.
+ *
+ * @param data car hardware data that was requested.
+ */
+ void onCarData(@NonNull T data);
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/common/UpdateRate.java b/car/app/app/src/main/java/androidx/car/app/hardware/common/UpdateRate.java
new file mode 100644
index 0000000..6c27cb7
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/common/UpdateRate.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.car.app.hardware.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Defines the possible update rates that properties, sensors, and actions can be requested with
+ * . */
+@CarProtocol
+@RequiresCarApi(3)
+public final class UpdateRate {
+ /**
+ * Defines the possible update rates that properties, sensors, and actions can be requested
+ * with.
+ *
+ * @hide
+ */
+ @IntDef({
+ DEFAULT,
+ UI,
+ FASTEST,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY)
+ public @interface Value {
+ }
+
+ /**
+ * Car hardware property, sensor, or action should be fetched at its default rate.
+ */
+ @Value
+ public static final int DEFAULT = 0;
+
+ /**
+ * Car hardware property, sensor, or action should be fetched at a rate consistent with
+ * drawing UI to a screen.
+ */
+ @Value
+ public static final int UI = 1;
+
+ /**
+ * Car hardware property, sensor, or action should be fetched at its fastest possible rate.
+ */
+ @Value
+ public static final int FASTEST = 2;
+
+ private UpdateRate() {}
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Accelerometer.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Accelerometer.java
new file mode 100644
index 0000000..8518764
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Accelerometer.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car specific accelerometers available from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Accelerometer {
+
+ /** Accelerometer request parameters. */
+ public static final class Params {
+ private final @UpdateRate.Value int mRate;
+
+ /**
+ * Construct accelerometer parameter instance.
+ */
+ public Params(@UpdateRate.Value int rate) {
+ mRate = rate;
+ }
+
+ /** Gets the requested data rate for the accelerometer. */
+ public @UpdateRate.Value int getRate() {
+ return mRate;
+ }
+
+ /** Gets an {@link Accelerometer.Params} instance with default values set. */
+ public static @NonNull Accelerometer.Params getDefault() {
+ return new Params(UpdateRate.DEFAULT);
+ }
+ }
+
+ @Keep
+ @NonNull
+ private final CarValue<Float[]> mAccelerometer;
+
+ /**
+ * Returns the raw accelerometer data from the car sensor.
+ *
+ * <p>Follows the same format as {@link android.hardware.SensorEvent#values}.
+ */
+ @NonNull
+ public CarValue<Float[]> getAccelerometer() {
+ return requireNonNull(mAccelerometer);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ accelerometer: " + mAccelerometer + " ]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAccelerometer);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Accelerometer)) {
+ return false;
+ }
+ Accelerometer otherAccelerometer = (Accelerometer) other;
+
+ return Objects.equals(mAccelerometer, otherAccelerometer.mAccelerometer);
+ }
+
+ Accelerometer(Builder builder) {
+ mAccelerometer = requireNonNull(builder.mAccelerometer);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private Accelerometer() {
+ mAccelerometer = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+ }
+
+ /** A builder of {@link Accelerometer}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Float[]> mAccelerometer;
+
+ /**
+ * Sets the raw accelerometer data.
+ *
+ * @throws NullPointerException if {@code accelerometer} is {@code null}
+ */
+ @NonNull
+ public Builder setAccelerometer(@NonNull CarValue<Float[]> accelerometer) {
+ mAccelerometer = requireNonNull(accelerometer);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Accelerometer} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public Accelerometer build() {
+ if (mAccelerometer == null) {
+ mAccelerometer = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+ }
+ return new Accelerometer(this);
+ }
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/CarHardwareLocation.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarHardwareLocation.java
new file mode 100644
index 0000000..2f88b62
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarHardwareLocation.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import android.location.Location;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car specific car location available from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class CarHardwareLocation {
+
+ /** {@link CarHardwareLocation} request parameters. */
+ public static final class Params {
+ private final @UpdateRate.Value int mRate;
+
+ /**
+ * Construct car location parameter instance.
+ */
+ public Params(@UpdateRate.Value int rate) {
+ mRate = rate;
+ }
+
+ /** Gets the requested data rate for the location. */
+ public @UpdateRate.Value int getRate() {
+ return mRate;
+ }
+
+ /** Gets an {@link CarHardwareLocation.Params} instance with default values set. */
+ public static @NonNull CarHardwareLocation.Params getDefault() {
+ return new Params(UpdateRate.DEFAULT);
+ }
+ }
+
+ // Not private because needed in builder.
+ static final CarValue<Location> UNIMPLEMENTED_LOCATION = new CarValue<>(null, 0,
+ CarValue.STATUS_UNAVAILABLE);
+
+ @Keep
+ @NonNull
+ private final CarValue<Location> mLocation;
+
+ /** Returns the raw location data from the car sensor. */
+ @NonNull
+ public CarValue<Location> getLocation() {
+ return requireNonNull(mLocation);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ location: " + mLocation + " ]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLocation);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof CarHardwareLocation)) {
+ return false;
+ }
+ CarHardwareLocation otherCarHardwareLocation = (CarHardwareLocation) other;
+
+ return Objects.equals(mLocation, otherCarHardwareLocation.mLocation);
+ }
+
+ CarHardwareLocation(Builder builder) {
+ mLocation = requireNonNull(builder.mLocation);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private CarHardwareLocation() {
+ mLocation = UNIMPLEMENTED_LOCATION;
+ }
+
+ /** A builder of {@link CarHardwareLocation}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Location> mLocation;
+
+ /**
+ * Sets the raw car location data.
+ *
+ * @throws NullPointerException if {@code location} is {@code null}
+ */
+ @NonNull
+ public Builder setLocation(@NonNull CarValue<Location> location) {
+ mLocation = requireNonNull(location);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link CarHardwareLocation} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public CarHardwareLocation build() {
+ if (mLocation == null) {
+ mLocation = UNIMPLEMENTED_LOCATION;
+ }
+ return new CarHardwareLocation(this);
+ }
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/CarInfo.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarInfo.java
new file mode 100644
index 0000000..1f04ce6
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarInfo.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.OnCarDataListener;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages access to car hardware specific info such as model, energy, and speed info.
+ */
+@RequiresCarApi(3)
+public interface CarInfo {
+ /**
+ * Request the {@link Model} information about the car hardware.
+ *
+ * @param params the parameters for this specific request. Use
+ * {@link Model.Params#getDefault()} as a default.
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void getModel(@NonNull Model.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<Model> listener);
+
+ /**
+ * Reguest the {@link EnergyProfile} information about the car hardware.
+ *
+ * @param params the parameters for this request. Use {@link EnergyProfile.Params#getDefault}
+ * as a default.
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void getEnergyProfile(@NonNull EnergyProfile.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<EnergyProfile> listener);
+
+ /**
+ * Setup an ongoing listener to receive {@link Toll} information from the car hardware.
+ *
+ * <p>If the listener was added previously then previous params are updated with the new params.
+ *
+ * @param params the parameters for this request. Use {@link Toll.Params#getDefault}
+ * as a default.
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void addTollListener(@NonNull Toll.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<Toll> listener);
+
+ /**
+ * Remove an ongoing listener for {@link Toll} information.
+ *
+ * <p>If the listener is not currently added, this call will be ignored.
+ *
+ * @param listener the listener to use
+ */
+ void removeTollListener(@NonNull OnCarDataListener<Toll> listener);
+
+ /**
+ * Setup an ongoing listener to receive {@link EnergyLevel} information from the car hardware.
+ *
+ * <p>If the listener was added previously then previous params are updated with the new params.
+ *
+ * @param params the parameters for this request. Use {@link EnergyLevel.Params#getDefault}
+ * as a default.
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void addEnergyLevelListener(@NonNull EnergyLevel.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<EnergyLevel> listener);
+
+ /**
+ * Remove an ongoing listener for {@link EnergyLevel} information.
+ *
+ * <p>If the listener is not currently added, this call will be ignored.
+ *
+ * @param listener the listener to use
+ */
+ void removeEnergyLevelListener(@NonNull OnCarDataListener<EnergyLevel> listener);
+
+ /**
+ * Setup an ongoing listener to receive {@link Speed} information from the car hardware.
+ *
+ * <p>If the listener was added previously then previous params are updated with the new params.
+ *
+ * @param params the parameters for this request. Use {@link Speed.Params#getDefault}
+ * as a default.
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void addSpeedListener(@NonNull Speed.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<Speed> listener);
+
+ /**
+ * Remove an ongoing listener for {@link Speed} information.
+ *
+ * <p>If the listener is not currently added, this call will be ignored.
+ *
+ * @param listener the listener to use
+ */
+ void removeSpeedListener(@NonNull OnCarDataListener<Speed> listener);
+
+ /**
+ * Setup an ongoing listener to receive {@link Mileage} information from the car hardware.
+ *
+ * <p>If the listener was added previously then previous params are updated with the new params.
+ *
+ * @param params the parameters for this request. Use {@link Mileage.Params#getDefault}
+ * as a default.
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use.
+ */
+ void addMileageListener(@NonNull Mileage.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<Mileage> listener);
+
+ /**
+ * Remove an ongoing listener for {@link Mileage} information.
+ *
+ * <p>If the listener is not currently added, this call will be ignored.
+ *
+ * @param listener the listener to use
+ */
+ void removeMileageListener(@NonNull OnCarDataListener<Mileage> listener);
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/CarSensors.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarSensors.java
new file mode 100644
index 0000000..3f82d41
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/CarSensors.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.OnCarDataListener;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages access to androidx.car.app.hardware specific sensors such as compass, accelerometer,
+ * and gyroscope.
+ */
+@RequiresCarApi(3)
+public interface CarSensors {
+ /**
+ * Setup an ongoing listener to receive {@link Accelerometer} data from the car hardware.
+ *
+ * <p>If the listener was added previously then previous params are updated with the new params.
+ *
+ * @param params the parameters for this request. Use {@link Accelerometer.Params#getDefault}
+ * as a default
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void addAccelerometerListener(@NonNull Accelerometer.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<Accelerometer> listener);
+
+ /**
+ * Remove an ongoing listener for {@link Accelerometer} information.
+ *
+ * <p>If the listener is not currently added, this call will be ignored.
+ *
+ * @param listener the listener to use
+ */
+ void removeAccelerometerListener(@NonNull OnCarDataListener<Accelerometer> listener);
+
+ /**
+ * Setup an ongoing listener to receive {@link Gyroscope} data from the car hardware.
+ *
+ * <p>If the listener was added previously then previous params are updated with the new params.
+ *
+ * @param params the parameters for this request. Use {@link Gyroscope.Params#getDefault}
+ * as a default
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void addGyroscopeListener(@NonNull Gyroscope.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<Gyroscope> listener);
+
+ /**
+ * Remove an ongoing listener for {@link Gyroscope} information.
+ *
+ * <p>If the listener is not currently added, this call will be ignored.
+ *
+ * @param listener the listener to use
+ */
+ void removeGyroscopeListener(@NonNull OnCarDataListener<Gyroscope> listener);
+
+ /**
+ * Setup an ongoing listener to receive {@link Compass} data from the car hardware.
+ *
+ * <p>If the listener was added previously then previous params are updated with the new params.
+ *
+ * @param params the parameters for this request. Use {@link Compass.Params#getDefault}
+ * as a default
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void addCompassListener(@NonNull Compass.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<Compass> listener);
+
+ /**
+ * Remove an ongoing listener for {@link Compass} information.
+ *
+ * <p>If the listener is not currently added, this call will be ignored.
+ *
+ * @param listener the listener to use
+ */
+ void removeCompassListener(@NonNull OnCarDataListener<Compass> listener);
+
+ /**
+ * Setup an ongoing listener to receive {@link CarHardwareLocation} data from the car hardware.
+ *
+ * <p>If the listener was added previously then previous params are updated with the new params.
+ *
+ * @param params the parameters for this request. Use
+ * {@link CarHardwareLocation.Params#getDefault} as a default
+ * @param executor the executor which will be used for invoking the listener
+ * @param listener the listener to use
+ */
+ void addCarHardwareLocationListener(@NonNull CarHardwareLocation.Params params,
+ @NonNull Executor executor,
+ @NonNull OnCarDataListener<CarHardwareLocation> listener);
+
+ /**
+ * Remove an ongoing listener for {@link CarHardwareLocation} information.
+ *
+ * <p>If the listener is not currently added, this call will be ignored.
+ *
+ * @param listener the listener to use
+ */
+ void removeCarHardwareLocationListener(
+ @NonNull OnCarDataListener<CarHardwareLocation> listener);
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Compass.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Compass.java
new file mode 100644
index 0000000..1ed0fd9
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Compass.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car specific compass available from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Compass {
+
+ /** Compass request parameters. */
+ public static final class Params {
+ private final @UpdateRate.Value int mRate;
+
+ /**
+ * Construct compass parameter instance.
+ */
+ public Params(@UpdateRate.Value int rate) {
+ mRate = rate;
+ }
+
+ /** Gets the requested data rate for the compass. */
+ public @UpdateRate.Value int getRate() {
+ return mRate;
+ }
+
+ /** Gets an {@link Compass.Params} instance with default values set. */
+ public static @NonNull Compass.Params getDefault() {
+ return new Params(UpdateRate.DEFAULT);
+ }
+ }
+
+ @Keep
+ @NonNull
+ private final CarValue<Float[]> mCompass;
+
+ /**
+ * Returns the raw compass data from the car sensor.
+ *
+ * <p>Follows the same format as {@link android.hardware.SensorEvent#values}.
+ */
+ @NonNull
+ public CarValue<Float[]> getCompass() {
+ return requireNonNull(mCompass);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ compass: " + mCompass + " ]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCompass);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Compass)) {
+ return false;
+ }
+ Compass otherCompass = (Compass) other;
+
+ return Objects.equals(mCompass, otherCompass.mCompass);
+ }
+
+ Compass(Builder builder) {
+ mCompass = requireNonNull(builder.mCompass);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private Compass() {
+ mCompass = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+ }
+
+ /** A builder of {@link Compass}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Float[]> mCompass;
+
+ /**
+ * Sets the raw compass data.
+ *
+ * @throws NullPointerException if {@code compass} is {@code null}
+ */
+ @NonNull
+ public Builder setCompass(@NonNull CarValue<Float[]> compass) {
+ mCompass = requireNonNull(compass);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Compass} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public Compass build() {
+ if (mCompass == null) {
+ mCompass = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+ }
+ return new Compass(this);
+ }
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java
new file mode 100644
index 0000000..86955d8
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyLevel.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarUnit;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information of the energy (fuel and battery) levels from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class EnergyLevel {
+
+ /** Energy level request parameters. */
+ public static final class Params {
+ private final @UpdateRate.Value int mRate;
+
+ private static final EnergyLevel.Params DEFAULT = new Params(UpdateRate.DEFAULT);
+
+ /** Construct {@link EnergyLevel} parameter instance. */
+ public Params(@UpdateRate.Value int rate) {
+ mRate = rate;
+ }
+
+ /** Gets the requested data rate for the energy level. */
+ public @UpdateRate.Value int getRate() {
+ return mRate;
+ }
+
+ /** Gets an {@link EnergyLevel.Params} instance with default values set. */
+ public static @NonNull EnergyLevel.Params getDefault() {
+ return DEFAULT;
+ }
+ }
+
+ @Keep
+ @NonNull
+ private final CarValue<Float> mBatteryPercent;
+
+ @Keep
+ @NonNull
+ private final CarValue<Float> mFuelPercent;
+
+ @Keep
+ @NonNull
+ private final CarValue<Boolean> mEnergyIsLow;
+
+ @Keep
+ @NonNull
+ private final CarValue<Float> mRangeRemaining;
+
+ @Keep
+ @NonNull
+ private final CarValue<Integer> mDistanceDisplayUnit;
+
+ /** Returns the battery percentage remaining from the car hardware. */
+ @NonNull
+ public CarValue<Float> getBatteryPercent() {
+ return requireNonNull(mBatteryPercent);
+ }
+
+ /** Returns the fuel percentage remaining from the car hardware. */
+ @NonNull
+ public CarValue<Float> getFuelPercent() {
+ return requireNonNull(mFuelPercent);
+ }
+
+ /** Returns if the remaining car energy is low from the car hardware. */
+ @NonNull
+ public CarValue<Boolean> getEnergyIsLow() {
+ return requireNonNull(mEnergyIsLow);
+ }
+
+ /** Returns the range remaining from the car hardware in meters. */
+ @NonNull
+ public CarValue<Float> getRangeRemaining() {
+ return requireNonNull(mRangeRemaining);
+ }
+
+ /**
+ * Returns the distance display unit from the car hardware.
+ *
+ * <p>See {@link CarUnit} for possible distance values.
+ */
+ @NonNull
+ public CarValue<Integer> getDistanceDisplayUnit() {
+ return requireNonNull(mDistanceDisplayUnit);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ battery percent: "
+ + mBatteryPercent
+ + ", fuel percent: "
+ + mFuelPercent
+ + ", energyIsLow: "
+ + mEnergyIsLow
+ + ", range remaining: "
+ + mRangeRemaining
+ + ", distance display unit: "
+ + mDistanceDisplayUnit
+ + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mBatteryPercent, mFuelPercent, mEnergyIsLow, mRangeRemaining,
+ mDistanceDisplayUnit);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof EnergyLevel)) {
+ return false;
+ }
+ EnergyLevel otherEnergyLevel = (EnergyLevel) other;
+
+ return Objects.equals(mBatteryPercent, otherEnergyLevel.mBatteryPercent)
+ && Objects.equals(mFuelPercent, otherEnergyLevel.mFuelPercent)
+ && Objects.equals(mEnergyIsLow, otherEnergyLevel.mEnergyIsLow)
+ && Objects.equals(mRangeRemaining, otherEnergyLevel.mRangeRemaining)
+ && Objects.equals(mDistanceDisplayUnit, otherEnergyLevel.mDistanceDisplayUnit);
+ }
+
+ EnergyLevel(Builder builder) {
+ mBatteryPercent = requireNonNull(builder.mBatteryPercent);
+ mFuelPercent = requireNonNull(builder.mFuelPercent);
+ mEnergyIsLow = requireNonNull(builder.mEnergyIsLow);
+ mRangeRemaining = requireNonNull(builder.mRangeRemaining);
+ mDistanceDisplayUnit = requireNonNull(builder.mDistanceDisplayUnit);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private EnergyLevel() {
+ mBatteryPercent = CarValue.UNIMPLEMENTED_FLOAT;
+ mFuelPercent = CarValue.UNIMPLEMENTED_FLOAT;
+ mEnergyIsLow = CarValue.UNIMPLEMENTED_BOOLEAN;
+ mRangeRemaining = CarValue.UNIMPLEMENTED_FLOAT;
+ mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+
+ /** A builder of {@link EnergyLevel}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Float> mBatteryPercent;
+ @Nullable
+ CarValue<Float> mFuelPercent;
+
+ @Nullable
+ CarValue<Boolean> mEnergyIsLow;
+
+ @Nullable
+ CarValue<Float> mRangeRemaining;
+
+ @Nullable
+ CarValue<Integer> mDistanceDisplayUnit;
+
+ /** Sets the remaining batter percentage. */
+ @NonNull
+ public Builder setBatteryPercent(@NonNull CarValue<Float> batteryPercent) {
+ mBatteryPercent = requireNonNull(batteryPercent);
+ return this;
+ }
+
+ /**
+ * Sets the remaining fuel percentage.
+ *
+ * @throws NullPointerException if {@code fuelPercent} is {@code null}
+ */
+ @NonNull
+ public Builder setFuelPercent(@NonNull CarValue<Float> fuelPercent) {
+ mFuelPercent = requireNonNull(fuelPercent);
+ return this;
+ }
+
+ /**
+ * Sets if the remaining energy is low.
+ *
+ * @throws NullPointerException if {@code energyIsLow} is {@code null}
+ */
+ @NonNull
+ public Builder setEnergyIsLow(@NonNull CarValue<Boolean> energyIsLow) {
+ mEnergyIsLow = requireNonNull(energyIsLow);
+ return this;
+ }
+
+ /**
+ * Sets the range of the remaining fuel in meters.
+ *
+ * @throws NullPointerException if {@code rangeRemaining} is {@code null}
+ */
+ @NonNull
+ public Builder setRangeRemaining(@NonNull CarValue<Float> rangeRemaining) {
+ mRangeRemaining = requireNonNull(rangeRemaining);
+ return this;
+ }
+
+ /**
+ * Sets the distance display unit.
+ *
+ * <p>Valid values are in {@link CarUnit}.
+ *
+ * @throws NullPointerException if {@code distanceDisplayUnit} is {@code null}
+ */
+ @NonNull
+ public Builder setDistanceDisplayUnit(@NonNull CarValue<Integer> distanceDisplayUnit) {
+ mDistanceDisplayUnit = requireNonNull(distanceDisplayUnit);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link EnergyLevel} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public EnergyLevel build() {
+ if (mBatteryPercent == null) {
+ mBatteryPercent = CarValue.UNIMPLEMENTED_FLOAT;
+ }
+ if (mFuelPercent == null) {
+ mFuelPercent = CarValue.UNIMPLEMENTED_FLOAT;
+ }
+ if (mEnergyIsLow == null) {
+ mEnergyIsLow = CarValue.UNIMPLEMENTED_BOOLEAN;
+ }
+ if (mRangeRemaining == null) {
+ mRangeRemaining = CarValue.UNIMPLEMENTED_FLOAT;
+ }
+ if (mDistanceDisplayUnit == null) {
+ mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+ return new EnergyLevel(this);
+ }
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java
new file mode 100644
index 0000000..f9590f5
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/EnergyProfile.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/** Information about car hardware fuel profile such as fuel types and connector ports. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class EnergyProfile {
+
+ /**
+ * Possible EV Connector types.
+ *
+ * @hide
+ */
+ @IntDef({
+ EVCONNECTOR_TYPE_UNKNOWN,
+ EVCONNECTOR_TYPE_J1772,
+ EVCONNECTOR_TYPE_MENNEKES,
+ EVCONNECTOR_TYPE_CHADEMO,
+ EVCONNECTOR_TYPE_COMBO_1,
+ EVCONNECTOR_TYPE_COMBO_2,
+ EVCONNECTOR_TYPE_TESLA_ROADSTER,
+ EVCONNECTOR_TYPE_TESLA_HPWC,
+ EVCONNECTOR_TYPE_TESLA_SUPERCHARGER,
+ EVCONNECTOR_TYPE_GBT,
+ EVCONNECTOR_TYPE_GBT_DC,
+ EVCONNECTOR_TYPE_SCAME,
+ EVCONNECTOR_TYPE_OTHER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY)
+ public @interface EvConnectorType {}
+
+ /** Unknown connector type. */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_UNKNOWN = 0;
+
+ /** Connector type SAE J1772 */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_J1772 = 1;
+
+ /** IEC 62196 Type 2 connector */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_MENNEKES = 2;
+
+ /** CHAdeMo fast charger connector */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_CHADEMO = 3;
+
+ /** Combined Charging System Combo 1 */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_COMBO_1 = 4;
+
+ /** Combined Charging System Combo 2 */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_COMBO_2 = 5;
+
+ /** Connector of Tesla Roadster */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6;
+
+ /** High Power Wall Charger of Tesla */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7;
+
+ /** Supercharger of Tesla */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8;
+
+ /** GBT_AC Fast Charging Standard */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_GBT = 9;
+
+ /** GBT_DC Fast Charging Standard */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_GBT_DC = 10;
+
+ /** IEC_TYPE_3_AC connector */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_SCAME = 11;
+
+ /**
+ * Connector type to use when no other types apply.
+ */
+ @EvConnectorType
+ public static final int EVCONNECTOR_TYPE_OTHER = 101;
+
+ /** Energy profile request parameters. */
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public static final class Params {
+ public static @NonNull Params getDefault() {
+ return new Params();
+ }
+ }
+
+ /**
+ * Possible Fual types.
+ *
+ * @hide
+ */
+ @IntDef({
+ FUEL_TYPE_UNKNOWN,
+ FUEL_TYPE_UNLEADED,
+ FUEL_TYPE_LEADED,
+ FUEL_TYPE_DIESEL_1,
+ FUEL_TYPE_DIESEL_2,
+ FUEL_TYPE_BIODIESEL,
+ FUEL_TYPE_E85,
+ FUEL_TYPE_LPG,
+ FUEL_TYPE_CNG,
+ FUEL_TYPE_LNG,
+ FUEL_TYPE_ELECTRIC,
+ FUEL_TYPE_HYDROGEN,
+ FUEL_TYPE_OTHER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY)
+ public @interface FuelType {}
+
+ /** Unknown fuel type */
+ @FuelType public static final int FUEL_TYPE_UNKNOWN = 0;
+ /** Unleaded gasoline */
+ @FuelType public static final int FUEL_TYPE_UNLEADED = 1;
+ /** Leaded gasoline */
+ @FuelType public static final int FUEL_TYPE_LEADED = 2;
+ /** #1 Grade Diesel */
+ @FuelType public static final int FUEL_TYPE_DIESEL_1 = 3;
+ /** #2 Grade Diesel */
+ @FuelType public static final int FUEL_TYPE_DIESEL_2 = 4;
+ /** Biodiesel */
+ @FuelType public static final int FUEL_TYPE_BIODIESEL = 5;
+ /** 85% ethanol/gasoline blend */
+ @FuelType public static final int FUEL_TYPE_E85 = 6;
+ /** Liquified petroleum gas */
+ @FuelType public static final int FUEL_TYPE_LPG = 7;
+ /** Compressed natural gas */
+ @FuelType public static final int FUEL_TYPE_CNG = 8;
+ /** Liquified natural gas */
+ @FuelType public static final int FUEL_TYPE_LNG = 9;
+ /** Electric */
+ @FuelType public static final int FUEL_TYPE_ELECTRIC = 10;
+ /** Hydrogen fuel cell */
+ @FuelType public static final int FUEL_TYPE_HYDROGEN = 11;
+ /** Fuel type to use when no other types apply. */
+ @FuelType public static final int FUEL_TYPE_OTHER = 12;
+
+ @Keep
+ @NonNull
+ private final CarValue<Integer[]> mEvConnectorTypes;
+
+ @Keep
+ @NonNull
+ private final CarValue<Integer[]> mFuelTypes;
+
+ /**
+ * Returns an array of the available EV connectors.
+ *
+ * <p>If a vehicle does not know the EV connector type it will return
+ * {@link #EVCONNECTOR_TYPE_UNKNOWN} or {@link CarValue#STATUS_UNIMPLEMENTED}. If the value
+ * is known but not in the current list {@link #EVCONNECTOR_TYPE_UNKNOWN} will be returned.
+ */
+ @NonNull
+ public CarValue<Integer[]> getEvConnectorTypes() {
+ return requireNonNull(mEvConnectorTypes);
+ }
+
+ /**
+ * Returns an array of the available fuel types.
+ *
+ * <p>If a vehicle does not know the fuel type it will return {@link #FUEL_TYPE_UNKNOWN} or
+ * {@link CarValue#STATUS_UNIMPLEMENTED}. If the value is known but not in the current list
+ * {@link #EVCONNECTOR_TYPE_UNKNOWN} will be returned.
+ */
+ @NonNull
+ public CarValue<Integer[]> getFuelTypes() {
+ return requireNonNull(mFuelTypes);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ evConnectorTypes: " + mEvConnectorTypes + ", fuelTypes: " + mFuelTypes + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mEvConnectorTypes, mFuelTypes);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof EnergyProfile)) {
+ return false;
+ }
+ EnergyProfile otherProfile = (EnergyProfile) other;
+
+ return Objects.equals(mEvConnectorTypes, otherProfile.mEvConnectorTypes)
+ && Objects.equals(mFuelTypes, otherProfile.mFuelTypes);
+ }
+
+ EnergyProfile(Builder builder) {
+ mEvConnectorTypes = requireNonNull(builder.mEvConnectorTypes);
+ mFuelTypes = requireNonNull(builder.mFuelTypes);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private EnergyProfile() {
+ mEvConnectorTypes = CarValue.UNIMPLEMENTED_INTEGER_ARRAY;
+ mFuelTypes = CarValue.UNIMPLEMENTED_INTEGER_ARRAY;
+ }
+
+ /** A builder of {@link EnergyProfile}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Integer[]> mEvConnectorTypes;
+ @Nullable
+ CarValue<Integer[]> mFuelTypes;
+
+ /**
+ * Sets the cars EV connector types.
+ *
+ * @throws NullPointerException if {@code evConnectorTypes} is {@code null}
+ */
+ @NonNull
+ public Builder setEvConnectorTypes(@NonNull CarValue<Integer[]> evConnectorTypes) {
+ mEvConnectorTypes = requireNonNull(evConnectorTypes);
+ return this;
+ }
+
+ /**
+ * Sets the cars fuel types.
+ *
+ * @throws NullPointerException if {@code fuelTypes} is {@code null}
+ */
+ @NonNull
+ public Builder setFuelTypes(@NonNull CarValue<Integer[]> fuelTypes) {
+ mFuelTypes = requireNonNull(fuelTypes);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link EnergyProfile} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public EnergyProfile build() {
+ if (mEvConnectorTypes == null) {
+ mEvConnectorTypes = CarValue.UNIMPLEMENTED_INTEGER_ARRAY;
+ }
+ if (mFuelTypes == null) {
+ mFuelTypes = CarValue.UNIMPLEMENTED_INTEGER_ARRAY;
+ }
+ return new EnergyProfile(this);
+ }
+
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Gyroscope.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Gyroscope.java
new file mode 100644
index 0000000..38eee39
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Gyroscope.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car specific gyroscopes available from the car hardware. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Gyroscope {
+
+ /** Gyroscope request parameters. */
+ public static final class Params {
+ private final @UpdateRate.Value int mRate;
+
+ /**
+ * Construct gyroscope parameter instance.
+ */
+ public Params(@UpdateRate.Value int rate) {
+ mRate = rate;
+ }
+
+ /** Gets the requested data rate for the gyroscope. */
+ public @UpdateRate.Value int getRate() {
+ return mRate;
+ }
+
+ /** Gets an {@link Gyroscope.Params} instance with default values set. */
+ public static @NonNull Gyroscope.Params getDefault() {
+ return new Params(UpdateRate.DEFAULT);
+ }
+ }
+
+ @Keep
+ @NonNull
+ private final CarValue<Float[]> mGyroscope;
+
+ /** Returns the raw gyroscope data from the car sensor. */
+ @NonNull
+ public CarValue<Float[]> getGyroscope() {
+ return requireNonNull(mGyroscope);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ gyroscope: " + mGyroscope + " ]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGyroscope);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Gyroscope)) {
+ return false;
+ }
+ Gyroscope otherGyroscope = (Gyroscope) other;
+
+ return Objects.equals(mGyroscope, otherGyroscope.mGyroscope);
+ }
+
+ Gyroscope(Builder builder) {
+ mGyroscope = requireNonNull(builder.mGyroscope);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private Gyroscope() {
+ mGyroscope = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+ }
+
+ /** A builder of {@link Gyroscope}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Float[]> mGyroscope;
+
+ /**
+ * Sets the raw gyroscope data.
+ *
+ * @throws NullPointerException if {@code gyroscope} is {@code null}
+ */
+ @NonNull
+ public Builder setGyroscope(@NonNull CarValue<Float[]> gyroscope) {
+ mGyroscope = requireNonNull(gyroscope);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Gyroscope} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public Gyroscope build() {
+ if (mGyroscope == null) {
+ mGyroscope = CarValue.UNIMPLEMENTED_FLOAT_ARRAY;
+ }
+ return new Gyroscope(this);
+ }
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java
new file mode 100644
index 0000000..fb93d0d
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Mileage.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarUnit;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/** Information about car mileage. */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Mileage {
+
+ /** Mileage request parameters. */
+ public static final class Params {
+ private final @UpdateRate.Value int mRate;
+
+ public Params(@UpdateRate.Value int rate) {
+ mRate = rate;
+ }
+
+ public @UpdateRate.Value int getRate() {
+ return mRate;
+ }
+
+ public static @NonNull Mileage.Params getDefault() {
+ return new Params(UpdateRate.DEFAULT);
+ }
+ }
+
+ @Keep
+ @NonNull
+ private final CarValue<Float> mOdometer;
+
+ @Keep
+ @NonNull
+ private final CarValue<Integer> mDistanceDisplayUnit;
+
+ /** Returns the value of the odometer from the car hardware in meters. */
+ @NonNull
+ public CarValue<Float> getOdometer() {
+ return requireNonNull(mOdometer);
+ }
+
+ /**
+ * Returns the distance display unit from the car hardware.
+ *
+ * <p>See {@link CarUnit} for possible distance values.
+ */
+ @NonNull
+ public CarValue<Integer> getDistanceDisplayUnit() {
+ return requireNonNull(mDistanceDisplayUnit);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ odometer: "
+ + mOdometer
+ + ", distance display unit: "
+ + mDistanceDisplayUnit
+ + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mOdometer, mDistanceDisplayUnit);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Mileage)) {
+ return false;
+ }
+ Mileage otherMileage = (Mileage) other;
+
+ return Objects.equals(mOdometer, otherMileage.mOdometer)
+ && Objects.equals(mDistanceDisplayUnit, otherMileage.mDistanceDisplayUnit);
+ }
+
+ Mileage(Builder builder) {
+ mOdometer = requireNonNull(builder.mOdometer);
+ mDistanceDisplayUnit = requireNonNull(builder.mDistanceDisplayUnit);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private Mileage() {
+ mOdometer = CarValue.UNIMPLEMENTED_FLOAT;
+ mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+
+ /** A builder of {@link Mileage}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Float> mOdometer;
+ @Nullable
+ CarValue<Integer> mDistanceDisplayUnit;
+
+ /**
+ * Sets the odometer value in meters.
+ *
+ * @throws NullPointerException if {@code odometer} is {@code null}
+ */
+ @NonNull
+ public Builder setOdometer(@NonNull CarValue<Float> odometer) {
+ mOdometer = requireNonNull(odometer);
+ return this;
+ }
+
+ /**
+ * Sets the mileage display unit.
+ *
+ * <p>Valid values are in {@link CarUnit}.
+ *
+ * @throws NullPointerException if {@code mileageDisplayUnit} is {@code null}
+ */
+ @NonNull
+ public Builder setDistanceDisplayUnit(@NonNull CarValue<Integer> mileageDisplayUnit) {
+ mDistanceDisplayUnit = requireNonNull(mileageDisplayUnit);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Mileage} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public Mileage build() {
+ if (mOdometer == null) {
+ mOdometer = CarValue.UNIMPLEMENTED_FLOAT;
+ }
+ if (mDistanceDisplayUnit == null) {
+ mDistanceDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+ return new Mileage(this);
+ }
+
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Model.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Model.java
new file mode 100644
index 0000000..5af402b
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Model.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+
+import java.util.Objects;
+
+/**
+ * Information about the androidx.car.app.hardware model such as name, year and manufacturer.
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Model {
+
+ /** Model request parameters. */
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public static final class Params {
+ public static @NonNull Params getDefault() {
+ return new Params();
+ }
+ }
+
+ @Keep
+ @NonNull
+ private final CarValue<String> mName;
+
+ @Keep
+ @NonNull
+ private final CarValue<Integer> mYear;
+
+ @Keep
+ @NonNull
+ private final CarValue<String> mManufacturer;
+
+ /** Returns the car model name. */
+ @NonNull
+ public CarValue<String> getName() {
+ return requireNonNull(mName);
+ }
+
+ /** Returns the car model year. */
+ @NonNull
+ public CarValue<Integer> getYear() {
+ return requireNonNull(mYear);
+ }
+
+ /** Returns the car manufacturer. */
+ @NonNull
+ public CarValue<String> getManufacturer() {
+ return requireNonNull(mManufacturer);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ name: " + mName + ", year: " + mYear + ", manufacturer: " + mManufacturer + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mYear, mManufacturer);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Model)) {
+ return false;
+ }
+ Model otherModel = (Model) other;
+
+ return Objects.equals(mName, otherModel.mName)
+ && Objects.equals(mYear, otherModel.mYear)
+ && Objects.equals(mManufacturer, otherModel.mManufacturer);
+ }
+
+ Model(Builder builder) {
+ mName = requireNonNull(builder.mName);
+ mManufacturer = requireNonNull(builder.mManufacturer);
+ mYear = requireNonNull(builder.mYear);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private Model() {
+ mName = CarValue.UNIMPLEMENTED_STRING;
+ mManufacturer = CarValue.UNIMPLEMENTED_STRING;
+ mYear = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+
+ /** A builder of {@link Model}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<String> mName;
+ @Nullable
+ CarValue<Integer> mYear;
+ @Nullable
+ CarValue<String> mManufacturer;
+
+ /**
+ * Sets the car model name.
+ *
+ * @throws NullPointerException if {@code name} is {@code null}
+ */
+ @NonNull
+ public Builder setName(@NonNull CarValue<String> name) {
+ mName = requireNonNull(name);
+ return this;
+ }
+
+ /**
+ * Sets the car model year.
+ *
+ * @throws NullPointerException if {@code year} is {@code null}
+ */
+ @NonNull
+ public Builder setYear(@NonNull CarValue<Integer> year) {
+ mYear = requireNonNull(year);
+ return this;
+ }
+
+ /**
+ * Sets the car manufacturer.
+ *
+ * @throws NullPointerException if {@code manufacturer} is {@code null}
+ */
+ @NonNull
+ public Builder setManufacturer(@NonNull CarValue<String> manufacturer) {
+ mManufacturer = requireNonNull(manufacturer);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Model} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public Model build() {
+ if (mName == null) {
+ mName = CarValue.UNIMPLEMENTED_STRING;
+ }
+ if (mYear == null) {
+ mYear = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+ if (mManufacturer == null) {
+ mManufacturer = CarValue.UNIMPLEMENTED_STRING;
+ }
+ return new Model(this);
+ }
+
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java
new file mode 100644
index 0000000..bfbc856
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Speed.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarUnit;
+import androidx.car.app.hardware.common.CarValue;
+import androidx.car.app.hardware.common.UpdateRate;
+
+import java.util.Objects;
+
+/**
+ * Information about the current car speed.
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Speed {
+
+ /**
+ * Parameters for speed requests.
+ */
+ public static final class Params {
+ private final @UpdateRate.Value int mRate;
+
+ public Params(@UpdateRate.Value int rate) {
+ mRate = rate;
+ }
+
+ public @UpdateRate.Value int getRate() {
+ return mRate;
+ }
+
+ public static @NonNull Speed.Params getDefault() {
+ return new Params(UpdateRate.DEFAULT);
+ }
+ }
+
+ @Keep
+ @NonNull
+ private final CarValue<Float> mRawSpeed;
+
+ @Keep
+ @NonNull
+ private final CarValue<Float> mDisplaySpeed;
+
+ @Keep
+ @NonNull
+ private final CarValue<Integer> mSpeedDisplayUnit;
+
+ /** Returns the raw speed of the car in meters/second. */
+ @NonNull
+ public CarValue<Float> getRawSpeed() {
+ return requireNonNull(mRawSpeed);
+ }
+
+ /** Returns the display speed of the car in meters/second. */
+ @NonNull
+ public CarValue<Float> getDisplaySpeed() {
+ return requireNonNull(mDisplaySpeed);
+ }
+
+ /**
+ * Returns the units used to display speed from the car settings.
+ *
+ * <p>See {@link CarUnit} for valid speed units.
+ */
+ @NonNull
+ public CarValue<Integer> getSpeedDisplayUnit() {
+ return requireNonNull(mSpeedDisplayUnit);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ raw speed: "
+ + mRawSpeed
+ + ", display speed: "
+ + mDisplaySpeed
+ + ", speed display unit: "
+ + mSpeedDisplayUnit
+ + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRawSpeed, mDisplaySpeed, mSpeedDisplayUnit);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Speed)) {
+ return false;
+ }
+ Speed otherSpeed = (Speed) other;
+
+ return Objects.equals(mRawSpeed, otherSpeed.mRawSpeed)
+ && Objects.equals(mDisplaySpeed, otherSpeed.mDisplaySpeed)
+ && Objects.equals(mSpeedDisplayUnit, otherSpeed.mSpeedDisplayUnit);
+ }
+
+ Speed(Builder builder) {
+ mRawSpeed = requireNonNull(builder.mRawSpeed);
+ mDisplaySpeed = requireNonNull(builder.mDisplaySpeed);
+ mSpeedDisplayUnit = requireNonNull(builder.mSpeedDisplayUnit);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private Speed() {
+ mRawSpeed = CarValue.UNIMPLEMENTED_FLOAT;
+ mDisplaySpeed = CarValue.UNIMPLEMENTED_FLOAT;
+ mSpeedDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+
+ /** A builder of {@link Speed}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Float> mRawSpeed;
+ @Nullable
+ CarValue<Float> mDisplaySpeed;
+
+ @Nullable
+ CarValue<Integer> mSpeedDisplayUnit;
+
+ /**
+ * Sets the raw speed.
+ *
+ * @throws NullPointerException if {@code rawSpeed} is {@code null}
+ */
+ @NonNull
+ public Builder setRawSpeed(@NonNull CarValue<Float> rawSpeed) {
+ mRawSpeed = requireNonNull(rawSpeed);
+ return this;
+ }
+
+ /**
+ * Sets the display speed. *
+ *
+ * @throws NullPointerException if {@code displaySpeed} is {@code null}
+ */
+ @NonNull
+ public Builder setDisplaySpeed(@NonNull CarValue<Float> displaySpeed) {
+ mDisplaySpeed = requireNonNull(displaySpeed);
+ return this;
+ }
+
+ /**
+ * Sets the units used to display speed from the car hardware settings.
+ *
+ * <p>See {@link CarUnit} for valid speed units.
+ *
+ * @throws NullPointerException if {@code speedDisplayUnit} is {@code null}
+ */
+ @NonNull
+ public Builder setSpeedDisplayUnit(@NonNull CarValue<Integer> speedDisplayUnit) {
+ mSpeedDisplayUnit = requireNonNull(speedDisplayUnit);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Speed} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public Speed build() {
+ if (mRawSpeed == null) {
+ mRawSpeed = CarValue.UNIMPLEMENTED_FLOAT;
+ }
+ if (mDisplaySpeed == null) {
+ mDisplaySpeed = CarValue.UNIMPLEMENTED_FLOAT;
+ }
+ if (mSpeedDisplayUnit == null) {
+ mSpeedDisplayUnit = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+ return new Speed(this);
+ }
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/hardware/info/Toll.java b/car/app/app/src/main/java/androidx/car/app/hardware/info/Toll.java
new file mode 100644
index 0000000..17de543
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/hardware/info/Toll.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.car.app.hardware.info;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.hardware.common.CarValue;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Information about toll card capabilities in a car.
+ */
+@CarProtocol
+@RequiresCarApi(3)
+public final class Toll {
+
+ /**
+ * Possible toll card states.
+ *
+ * @hide
+ */
+ @IntDef({
+ TOLLCARD_STATE_UNKNOWN,
+ TOLLCARD_STATE_VALID,
+ TOLLCARD_STATE_INVALID,
+ TOLLCARD_STATE_NOT_INSERTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @RestrictTo(LIBRARY)
+ public @interface State {
+ }
+
+ /**
+ * Toll card state is unknown.
+ */
+ @State
+ public static final int TOLLCARD_STATE_UNKNOWN = 0;
+
+ /**
+ * Toll card state is valid.
+ */
+ @State
+ public static final int TOLLCARD_STATE_VALID = 1;
+
+ /**
+ * Toll card state invalid.
+ *
+ * <p>On some vehicles this may be that the toll card is inserted but not valid while other
+ * vehicles might not have a toll card inserted.
+ */
+ @State
+ public static final int TOLLCARD_STATE_INVALID = 2;
+
+ /**
+ * Toll card state is not inserted.
+ *
+ * <p>Will be returned if the car hardware is able to detect that the card is not inserted.
+ */
+ @State
+ public static final int TOLLCARD_STATE_NOT_INSERTED = 3;
+
+ /** Toll card request parameters. */
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public static final class Params {
+ public static @NonNull Params getDefault() {
+ return new Params();
+ }
+ }
+
+ @Keep
+ @NonNull
+ private final CarValue<Integer> mCardState;
+
+ /** Returns the toll card state if available. */
+ @NonNull
+ public CarValue<Integer> getCardState() {
+ return requireNonNull(mCardState);
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "[ card state: " + mCardState + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCardState);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Toll)) {
+ return false;
+ }
+ Toll otherToll = (Toll) other;
+
+ return Objects.equals(mCardState, otherToll.mCardState);
+ }
+
+ Toll(Builder builder) {
+ mCardState = requireNonNull(builder.mCardState);
+ }
+
+ /** Constructs an empty instance, used by serialization code. */
+ private Toll() {
+ mCardState = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+
+ /** A builder of {@link Toll}. */
+ public static final class Builder {
+ @Nullable
+ CarValue<Integer> mCardState;
+
+ /**
+ * Sets the toll card state.
+ *
+ * @throws NullPointerException if {@code cardState} is {@code null}
+ */
+ @NonNull
+ public Builder setCardState(@NonNull CarValue<Integer> cardState) {
+ mCardState = requireNonNull(cardState);
+ return this;
+ }
+
+ /**
+ * Constructs the {@link Toll} defined by this builder.
+ *
+ * <p>Any fields which have not been set are added with {@code null} value and
+ * {@link CarValue#STATUS_UNIMPLEMENTED}.
+ */
+ @NonNull
+ public Toll build() {
+ if (mCardState == null) {
+ mCardState = CarValue.UNIMPLEMENTED_INTEGER;
+ }
+ return new Toll(this);
+ }
+
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
index 1003326..3e42b54f 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
@@ -51,9 +51,9 @@
* <p>Similar to Android devices, car screens cover a wide range of sizes and densities. To
* ensure that icons and images render well across all car screens, use vector assets whenever
* possible to avoid scaling issues. If your app relies on bitmaps or other non-vector
- * assets, you should ensure that you have resources that address multiple size and pixel density
- * buckets using configuration qualifiers in your resource folders (e.g. "large", "xlarge", "mdpi",
- * "hdpi", etc). See {@link androidx.car.app.CarContext} for more details.
+ * assets, you should ensure that you have resources that address multiple pixel density
+ * buckets using configuration qualifiers in your resource folders (e.g. "mdpi", "hdpi", etc).
+ * See {@link androidx.car.app.CarContext} for more details.
*
* <h4>Themed Drawables</h4>
*
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
index 57be50d..fb829cb 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
@@ -162,7 +162,7 @@
*
* Images in the cue of the {@link Step} object, set with {@link Step.Builder#setCue}, can
* contain image spans. To minimize scaling artifacts across a wide range of car screens,
- * apps should provide images targeting a 250 x 83 dp bounding box. If necessary, those
+ * apps should provide images targeting a 216 x 72 dp bounding box. If necessary, those
* images in the spans will be scaled down to fit the bounding box while preserving their
* aspect ratios.
*
@@ -189,7 +189,7 @@
*
* Images in the cue of the {@link Step} object, set with {@link Step.Builder#setCue}, can
* contain image spans. To minimize scaling artifacts across a wide range of car screens,
- * apps should provide images targeting a 250 x 83 dp bounding box. If necessary, those
+ * apps should provide images targeting a 216 x 72 dp bounding box. If necessary, those
* images in the spans will be scaled down to fit the bounding box while preserving their
* aspect ratios.
*
diff --git a/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java b/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java
index 881a154..7accc41 100644
--- a/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java
+++ b/car/app/app/src/main/java/androidx/car/app/versioning/CarAppApiLevels.java
@@ -31,7 +31,7 @@
/**
* API level 3.
*
- * <p>Includes a vehicle manager for access to sensors and other vehicle properties.
+ * <p>Includes a car hardware manager for access to sensors and other vehicle properties.
*/
@CarAppApiLevel
public static final int LEVEL_3 = 3;
diff --git a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
index 502ea8bc..e37c8c7 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
@@ -42,6 +42,7 @@
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.Nullable;
+import androidx.car.app.hardware.CarHardwareManager;
import androidx.car.app.navigation.NavigationManager;
import androidx.car.app.testing.TestLifecycleOwner;
import androidx.lifecycle.Lifecycle.Event;
@@ -69,6 +70,7 @@
private static final String APP_SERVICE = "app";
private static final String NAVIGATION_SERVICE = "navigation";
private static final String SCREEN_SERVICE = "screen";
+ private static final String HARDWARE_SERVICE = "hardware";
@Mock
private ICarHost mMockCarHost;
@@ -144,6 +146,12 @@
}
@Test
+ public void getCarService_hardwareManager() {
+ assertThrows(IllegalStateException.class, () ->
+ mCarContext.getCarService(CarContext.HARDWARE_SERVICE));
+ }
+
+ @Test
public void getCarService_unknown_throws() {
assertThrows(IllegalArgumentException.class, () -> mCarContext.getCarService("foo"));
}
@@ -173,6 +181,12 @@
}
@Test
+ public void getCarServiceName_hardwareManager_throws() {
+ assertThat(mCarContext.getCarServiceName(CarHardwareManager.class)).isEqualTo(
+ HARDWARE_SERVICE);
+ }
+
+ @Test
public void getCarServiceName_unexpectedClass_throws() {
assertThrows(
IllegalArgumentException.class,
diff --git a/car/app/app/src/test/java/androidx/car/app/hardware/common/CarValueTest.java b/car/app/app/src/test/java/androidx/car/app/hardware/common/CarValueTest.java
new file mode 100644
index 0000000..7ac9981
--- /dev/null
+++ b/car/app/app/src/test/java/androidx/car/app/hardware/common/CarValueTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.car.app.hardware.common;
+
+/** Tests for {@link VehicleException}. */
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class CarValueTest {
+
+ @Test
+ public void createInstance() {
+ String value = "VALUE";
+ long timeStampMillis = 10;
+ int status = CarValue.STATUS_UNIMPLEMENTED;
+ CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+
+ assertThat(value).isEqualTo(carValue.getValue());
+ assertThat(timeStampMillis).isEqualTo(carValue.getTimestampMillis());
+ assertThat(status).isEqualTo(carValue.getStatus());
+ }
+
+ @Test
+ public void equals() {
+ String value = "VALUE";
+ long timeStampMillis = 10;
+ int status = CarValue.STATUS_UNIMPLEMENTED;
+ CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+ assertThat(new CarValue<>(value, timeStampMillis, status)).isEqualTo(carValue);
+ }
+
+ @Test
+ public void notEquals_differentValue() {
+ String value = "VALUE";
+ long timeStampMillis = 10;
+ int status = CarValue.STATUS_UNIMPLEMENTED;
+ CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+ assertThat(new CarValue<>("other", timeStampMillis, status))
+ .isNotEqualTo(carValue);
+ }
+
+ @Test
+ public void notEquals_differentTimestamp() {
+ String value = "VALUE";
+ long timeStampMillis = 10;
+ int status = CarValue.STATUS_UNIMPLEMENTED;
+ CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+ assertThat(new CarValue<>(value, 20, status)).isNotEqualTo(carValue);
+ }
+
+ @Test
+ public void notEquals_differentStatus() {
+ String value = "VALUE";
+ long timeStampMillis = 10;
+ int status = CarValue.STATUS_UNIMPLEMENTED;
+ CarValue<String> carValue = new CarValue<>(value, timeStampMillis, status);
+ assertThat(new CarValue<>(value, timeStampMillis,
+ CarValue.STATUS_UNAVAILABLE)).isNotEqualTo(carValue);
+ }
+
+}
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
index dc84d26..2591678 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/AppContent.jvm.kt
@@ -53,12 +53,12 @@
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
-import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.window.Notifier
import androidx.compose.ui.window.Popup
-import androidx.compose.ui.window.Tray
import androidx.compose.ui.window.WindowDraggableArea
+import androidx.compose.ui.window.v1.Dialog
+import androidx.compose.ui.window.v1.DialogProperties
+import androidx.compose.ui.window.v1.Tray
import java.awt.Toolkit
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.jvm.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.jvm.kt
index 0ace057..fd8dd5c 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.jvm.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/Main.jvm.kt
@@ -21,8 +21,8 @@
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.window.Menu
-import androidx.compose.ui.window.MenuBar
+import androidx.compose.ui.window.v1.Menu
+import androidx.compose.ui.window.v1.MenuBar
import javax.swing.SwingUtilities
fun main() = SwingUtilities.invokeLater {
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/MenuItems.jvm.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/MenuItems.jvm.kt
index e43c1d2..54c7476 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/MenuItems.jvm.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/popupexample/MenuItems.jvm.kt
@@ -17,9 +17,9 @@
import androidx.compose.desktop.AppManager
import androidx.compose.ui.input.key.Key
-import androidx.compose.ui.window.MenuItem
-import androidx.compose.ui.window.KeyStroke
import androidx.compose.ui.window.Notifier
+import androidx.compose.ui.window.v1.KeyStroke
+import androidx.compose.ui.window.v1.MenuItem
object MenuItems {
val Exit = MenuItem(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt
index 637e42cc..f3b8f16 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt
@@ -16,10 +16,13 @@
package androidx.compose.foundation.demos.text
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@@ -34,6 +37,7 @@
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.intl.LocaleList
import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.unit.dp
@Composable
fun TextSelectionDemo() {
@@ -54,6 +58,20 @@
TagLine(tag = "enable and disable selection")
TextDemoSelectionEnableAndDisable()
}
+ item {
+ TagLine(tag = "fix crashing of longpress in the blank area")
+ SelectionContainer {
+ Text(
+ text = "Hello World\nHello",
+ modifier = Modifier.fillMaxWidth()
+ .border(BorderStroke(1.dp, color = Color.Black))
+ .height(80.dp)
+ )
+ }
+ }
+ item {
+ TagLine(tag = "")
+ }
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
index 5803e10..dcbbe84 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
@@ -1474,6 +1474,65 @@
)
}
+ @Test
+ fun initialScrollPositionIsCorrectWhenItemsAreLoadedAsynchronously() {
+ lateinit var state: LazyListState
+ var itemsCount by mutableStateOf(0)
+ rule.setContent {
+ state = rememberLazyListState(2, 10)
+ LazyColumn(Modifier.fillMaxSize(), state) {
+ items(itemsCount) {
+ Box(Modifier.size(20.dp))
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ itemsCount = 100
+ }
+
+ rule.runOnIdle {
+ assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+ assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+ }
+ }
+
+ @Test
+ fun restoredScrollPositionIsCorrectWhenItemsAreLoadedAsynchronously() {
+ lateinit var state: LazyListState
+ var itemsCount = 100
+ val recomposeCounter = mutableStateOf(0)
+ val tester = StateRestorationTester(rule)
+ tester.setContent {
+ state = rememberLazyListState()
+ LazyColumn(Modifier.fillMaxSize(), state) {
+ recomposeCounter.value
+ items(itemsCount) {
+ Box(Modifier.size(20.dp))
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollToItem(2, 10)
+ }
+ itemsCount = 0
+ }
+
+ tester.emulateSavedInstanceStateRestore()
+
+ rule.runOnIdle {
+ itemsCount = 100
+ recomposeCounter.value = 1
+ }
+
+ rule.runOnIdle {
+ assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+ assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+ }
+ }
+
private fun SemanticsNodeInteraction.assertTopPositionIsAlmost(expected: Dp) {
getUnclippedBoundsInRoot().top.assertIsEqualTo(expected, tolerance = 1.dp)
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
index e2b6fc5..9ad18ab 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
@@ -1169,6 +1169,65 @@
)
}
+ @Test
+ fun initialScrollPositionIsCorrectWhenItemsAreLoadedAsynchronously() {
+ lateinit var state: LazyListState
+ var itemsCount by mutableStateOf(0)
+ rule.setContent {
+ state = rememberLazyListState(2, 10)
+ LazyRow(Modifier.fillMaxSize(), state) {
+ items(itemsCount) {
+ Box(Modifier.size(20.dp))
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ itemsCount = 100
+ }
+
+ rule.runOnIdle {
+ assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+ assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+ }
+ }
+
+ @Test
+ fun restoredScrollPositionIsCorrectWhenItemsAreLoadedAsynchronously() {
+ lateinit var state: LazyListState
+ var itemsCount = 100
+ val recomposeCounter = mutableStateOf(0)
+ val tester = StateRestorationTester(rule)
+ tester.setContent {
+ state = rememberLazyListState()
+ LazyRow(Modifier.fillMaxSize(), state) {
+ recomposeCounter.value
+ items(itemsCount) {
+ Box(Modifier.size(20.dp))
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollToItem(2, 10)
+ }
+ itemsCount = 0
+ }
+
+ tester.emulateSavedInstanceStateRestore()
+
+ rule.runOnIdle {
+ itemsCount = 100
+ recomposeCounter.value = 1
+ }
+
+ rule.runOnIdle {
+ assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+ assertThat(state.firstVisibleItemScrollOffset).isEqualTo(10)
+ }
+ }
+
private fun LazyListState.scrollBy(offset: Dp) {
runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
animateScrollBy(with(rule.density) { offset.roundToPx().toFloat() }, snap())
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
index 00401f3..58285ba 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/shape/CornerBasedShapeTest.kt
@@ -88,44 +88,9 @@
val impl = Impl(
topStart = CornerSize(10.0f),
- topEnd = CornerSize(6.dp),
- bottomEnd = CornerSize(1.0f),
- bottomStart = CornerSize(2.0f),
- onOutlineRequested = assertSizes
- )
-
- impl.createOutline(sizeWithLargerWidth, LayoutDirection.Ltr, density)
- impl.createOutline(sizeWithLargerHeight, LayoutDirection.Ltr, density)
-
- assertThat(sizesList).isEqualTo(mutableListOf(sizeWithLargerWidth, sizeWithLargerHeight))
- }
-
- @Test
- fun largerBottomCornersUseRemainingFromMinDimensionSize() {
- val density = Density(2f, 1f)
- val sizeWithLargerWidth = Size(6.0f, 4.0f)
- val sizeWithLargerHeight = Size(4.0f, 6.0f)
-
- val sizesList = mutableListOf<Size>()
- val assertSizes = { size: Size,
- topStart: Float,
- topEnd: Float,
- bottomEnd: Float,
- bottomStart: Float,
- ld: LayoutDirection ->
- sizesList.add(size)
- assertThat(topStart).isEqualTo(1.0f)
- assertThat(topEnd).isEqualTo(1.0f)
- assertThat(bottomEnd).isEqualTo(3.0f)
- assertThat(bottomStart).isEqualTo(3.0f)
- assertThat(ld).isEqualTo(LayoutDirection.Ltr)
- }
-
- val impl = Impl(
- topStart = CornerSize(1.0f),
- topEnd = CornerSize(0.5f.dp),
- bottomEnd = CornerSize(10f),
- bottomStart = CornerSize(100),
+ topEnd = CornerSize(10.0f),
+ bottomEnd = CornerSize(0f),
+ bottomStart = CornerSize(0f),
onOutlineRequested = assertSizes
)
@@ -221,6 +186,76 @@
assertThat(assertionExecuted).isTrue()
}
+
+ @Test
+ fun overSizedEqualCornerSizes() {
+ val density = Density(2f, 1f)
+ val sizeWithLargerWidth = Size(6.0f, 4.0f)
+ val sizeWithLargerHeight = Size(4.0f, 6.0f)
+
+ val sizesList = mutableListOf<Size>()
+ val assertSizes = { size: Size,
+ topStart: Float,
+ topEnd: Float,
+ bottomEnd: Float,
+ bottomStart: Float,
+ ld: LayoutDirection ->
+ sizesList.add(size)
+ assertThat(topStart).isEqualTo(2.0f)
+ assertThat(topEnd).isEqualTo(2.0f)
+ assertThat(bottomEnd).isEqualTo(2.0f)
+ assertThat(bottomStart).isEqualTo(2.0f)
+ assertThat(ld).isEqualTo(LayoutDirection.Ltr)
+ }
+
+ val impl = Impl(
+ topStart = CornerSize(75),
+ topEnd = CornerSize(75),
+ bottomEnd = CornerSize(75),
+ bottomStart = CornerSize(75),
+ onOutlineRequested = assertSizes
+ )
+
+ impl.createOutline(sizeWithLargerWidth, LayoutDirection.Ltr, density)
+ impl.createOutline(sizeWithLargerHeight, LayoutDirection.Ltr, density)
+
+ assertThat(sizesList).isEqualTo(mutableListOf(sizeWithLargerWidth, sizeWithLargerHeight))
+ }
+
+ @Test
+ fun overSizedCornerSizesShouldProportionallyScale() {
+ val density = Density(2f, 1f)
+ val sizeWithLargerWidth = Size(15.0f, 10.0f)
+ val sizeWithLargerHeight = Size(10.0f, 15.0f)
+
+ val sizesList = mutableListOf<Size>()
+ val assertSizes = { size: Size,
+ topStart: Float,
+ topEnd: Float,
+ bottomEnd: Float,
+ bottomStart: Float,
+ ld: LayoutDirection ->
+ sizesList.add(size)
+ assertThat(topStart).isEqualTo(7.5f)
+ assertThat(topEnd).isEqualTo(2.5f)
+ assertThat(bottomEnd).isEqualTo(7.5f)
+ assertThat(bottomStart).isEqualTo(2.5f)
+ assertThat(ld).isEqualTo(LayoutDirection.Ltr)
+ }
+
+ val impl = Impl(
+ topStart = CornerSize(90),
+ topEnd = CornerSize(30),
+ bottomEnd = CornerSize(90),
+ bottomStart = CornerSize(30),
+ onOutlineRequested = assertSizes
+ )
+
+ impl.createOutline(sizeWithLargerWidth, LayoutDirection.Ltr, density)
+ impl.createOutline(sizeWithLargerHeight, LayoutDirection.Ltr, density)
+
+ assertThat(sizesList).isEqualTo(mutableListOf(sizeWithLargerWidth, sizeWithLargerHeight))
+ }
}
private class Impl(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index bbcdc14..62548ff 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -28,12 +28,16 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.Drag
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.Fling
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.Relocate
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.debugInspectorInfo
@@ -207,6 +211,13 @@
}
}
+ fun performRelocationScroll(scroll: Offset): Offset {
+ nestedScrollDispatcher.value.coroutineScope.launch {
+ scrollableState.animateScrollBy(scroll.toFloat().reverseIfNeeded())
+ }
+ return scroll
+ }
+
suspend fun onDragStopped(axisVelocity: Float) {
val velocity = axisVelocity.toVelocity()
val preConsumedByParent = nestedScrollDispatcher.value.dispatchPreFling(velocity)
@@ -219,8 +230,7 @@
var result: Velocity = available
scrollableState.scroll {
val outerScopeScroll: (Float) -> Float = { delta ->
- delta - this.dispatchScroll(delta.reverseIfNeeded(), NestedScrollSource.Fling)
- .reverseIfNeeded()
+ delta - this.dispatchScroll(delta.reverseIfNeeded(), Fling).reverseIfNeeded()
}
val scope = object : ScrollScope {
override fun scrollBy(pixels: Float): Float {
@@ -246,7 +256,7 @@
override fun dragBy(pixels: Float) {
with(scrollLogic.value) {
with(latestScrollScope) {
- dispatchScroll(pixels, NestedScrollSource.Drag)
+ dispatchScroll(pixels, Drag)
}
}
}
@@ -279,7 +289,13 @@
available: Offset,
source: NestedScrollSource
): Offset = if (enabled) {
- scrollLogic.value.performRawScroll(available)
+ @Suppress("DEPRECATION")
+ when (source) {
+ Drag, Fling -> scrollLogic.value.performRawScroll(available)
+ @OptIn(ExperimentalComposeUiApi::class)
+ Relocate -> scrollLogic.value.performRelocationScroll(available)
+ else -> error("$source scroll not supported.")
+ }
} else {
Offset.Zero
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index 082c787..f592586 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -182,12 +182,7 @@
}
internal fun snapToItemIndexInternal(index: Int, scrollOffset: Int) {
- scrollPosition.update(
- index = DataIndex(index),
- scrollOffset = scrollOffset,
- // `true` will be replaced with the real value during the forceRemeasure() execution
- canScrollForward = true
- )
+ scrollPosition.update(DataIndex(index), scrollOffset)
remeasurement.forceRemeasure()
}
@@ -275,11 +270,7 @@
* Updates the state with the new calculated scroll position and consumed scroll.
*/
internal fun applyMeasureResult(measureResult: LazyListMeasureResult) {
- scrollPosition.update(
- index = measureResult.firstVisibleItemIndex,
- scrollOffset = measureResult.firstVisibleItemScrollOffset,
- canScrollForward = measureResult.canScrollForward
- )
+ scrollPosition.update(measureResult)
lastVisibleItemIndexNonObservable = DataIndex(
measureResult.visibleItemsInfo.lastOrNull()?.index ?: 0
)
@@ -335,11 +326,27 @@
private val scrollOffsetState = mutableStateOf(scrollOffset)
val observableScrollOffset get() = scrollOffsetState.value
- val canScrollBackward: Boolean get() = index.value != 0 || scrollOffset != 0
+ var canScrollBackward: Boolean = false
+ private set
var canScrollForward: Boolean = false
private set
- fun update(index: DataIndex, scrollOffset: Int, canScrollForward: Boolean) {
+ private var hadFirstNotEmptyLayout = false
+
+ fun update(measureResult: LazyListMeasureResult) {
+ // we ignore the index and offset from measureResult until we get at least one
+ // measurement with real items. otherwise the initial index and scroll passed to the
+ // state would be lost and overridden with zeros.
+ if (hadFirstNotEmptyLayout || measureResult.totalItemsCount > 0) {
+ hadFirstNotEmptyLayout = true
+ update(measureResult.firstVisibleItemIndex, measureResult.firstVisibleItemScrollOffset)
+ }
+ this.canScrollForward = measureResult.canScrollForward
+ this.canScrollBackward = measureResult.firstVisibleItemIndex.value != 0 ||
+ measureResult.firstVisibleItemScrollOffset != 0
+ }
+
+ fun update(index: DataIndex, scrollOffset: Int) {
require(index.value >= 0f) { "Index should be non-negative (${index.value})" }
require(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
if (index != this.index) {
@@ -350,7 +357,6 @@
this.scrollOffset = scrollOffset
scrollOffsetState.value = scrollOffset
}
- this.canScrollForward = canScrollForward
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
index 2792bb6..710e524 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerBasedShape.kt
@@ -21,7 +21,6 @@
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
-import kotlin.math.min
/**
* Base class for [Shape]s defined by four [CornerSize]s.
@@ -45,11 +44,21 @@
layoutDirection: LayoutDirection,
density: Density
): Outline {
+ var topStart = topStart.toPx(size, density)
+ var topEnd = topEnd.toPx(size, density)
+ var bottomEnd = bottomEnd.toPx(size, density)
+ var bottomStart = bottomStart.toPx(size, density)
val minDimension = size.minDimension
- val topStart = min(topStart.toPx(size, density), minDimension)
- val topEnd = min(topEnd.toPx(size, density), minDimension)
- val bottomEnd = min(bottomEnd.toPx(size, density), minDimension - topEnd)
- val bottomStart = min(bottomStart.toPx(size, density), minDimension - topStart)
+ if (topStart + bottomStart > minDimension) {
+ val scale = minDimension / (topStart + bottomStart)
+ topStart *= scale
+ bottomStart *= scale
+ }
+ if (topEnd + bottomEnd > minDimension) {
+ val scale = minDimension / (topEnd + bottomEnd)
+ topEnd *= scale
+ bottomEnd *= scale
+ }
require(topStart >= 0.0f && topEnd >= 0.0f && bottomEnd >= 0.0f && bottomStart >= 0.0f) {
"Corner size in Px can't be negative(topStart = $topStart, topEnd = $topEnd, " +
"bottomEnd = $bottomEnd, bottomStart = $bottomStart)!"
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
index c0c452c..4d4e184 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
@@ -372,11 +372,17 @@
state.layoutCoordinates?.let {
if (!it.isAttached) return
- selectionRegistrar?.notifySelectionUpdateStart(
- layoutCoordinates = it,
- startPosition = startPoint,
- adjustment = SelectionAdjustment.WORD
- )
+ if (outOfBoundary(startPoint, startPoint)) {
+ selectionRegistrar?.notifySelectionUpdateSelectAll(
+ selectableId = state.selectableId
+ )
+ } else {
+ selectionRegistrar?.notifySelectionUpdateStart(
+ layoutCoordinates = it,
+ startPosition = startPoint,
+ adjustment = SelectionAdjustment.WORD
+ )
+ }
dragBeginPosition = startPoint
}
@@ -394,12 +400,14 @@
dragTotalDistance += delta
- selectionRegistrar?.notifySelectionUpdate(
- layoutCoordinates = it,
- startPosition = dragBeginPosition,
- endPosition = dragBeginPosition + dragTotalDistance,
- adjustment = SelectionAdjustment.CHARACTER
- )
+ if (!outOfBoundary(dragBeginPosition, dragBeginPosition + dragTotalDistance)) {
+ selectionRegistrar?.notifySelectionUpdate(
+ layoutCoordinates = it,
+ startPosition = dragBeginPosition,
+ endPosition = dragBeginPosition + dragTotalDistance,
+ adjustment = SelectionAdjustment.CHARACTER
+ )
+ }
}
}
@@ -484,6 +492,18 @@
}
}
+ private fun outOfBoundary(start: Offset, end: Offset): Boolean {
+ state.layoutResult?.let {
+ val lastOffset = it.layoutInput.text.text.length
+ val rawStartOffset = it.getOffsetForPosition(start)
+ val rawEndOffset = it.getOffsetForPosition(end)
+
+ return rawStartOffset >= lastOffset - 1 && rawEndOffset >= lastOffset - 1 ||
+ rawStartOffset < 0 && rawEndOffset < 0
+ }
+ return false
+ }
+
/**
* Draw the given selection on the canvas.
*/
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
index 84c367d..eb1704b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
@@ -57,6 +57,18 @@
)
}
+ override fun getSelectAllSelection(): Selection? {
+ val textLayoutResult = layoutResultCallback() ?: return null
+
+ return getAssembledSelectionInfo(
+ startOffset = 0,
+ endOffset = textLayoutResult.layoutInput.text.length,
+ handlesCrossed = false,
+ selectableId = selectableId,
+ textLayoutResult = textLayoutResult
+ )
+ }
+
override fun getHandlePosition(selection: Selection, isStartHandle: Boolean): Offset {
// Check if the selection handles's selectable is the current selectable.
if (isStartHandle && selection.start.selectableId != this.selectableId ||
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/Selectable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/Selectable.kt
index 0e33360..ebad9c2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/Selectable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/Selectable.kt
@@ -60,6 +60,15 @@
): Selection?
/**
+ * Returns selectAll [Selection] information for a selectable composable. If no selection can be
+ * provided null should be returned.
+ *
+ * @return selectAll [Selection] information for a selectable composable. If no selection can be
+ * provided null should be returned.
+ */
+ fun getSelectAllSelection(): Selection?
+
+ /**
* Return the [Offset] of a [SelectionHandle].
*
* @param selection [Selection] contains the [SelectionHandle]
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
index 25e8c89..1774fd4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
@@ -203,6 +203,21 @@
hideSelectionToolbar()
}
+ selectionRegistrar.onSelectionUpdateSelectAll =
+ { selectableId ->
+ val (newSelection, newSubselection) = mergeSelections(
+ selectableId = selectableId,
+ previousSelection = selection,
+ )
+ if (newSelection != selection) {
+ selectionRegistrar.subselections = newSubselection
+ onSelectionChange(newSelection)
+ }
+
+ focusRequester.requestFocus()
+ hideSelectionToolbar()
+ }
+
selectionRegistrar.onSelectionUpdateCallback =
{ layoutCoordinates, startPosition, endPosition, selectionMode ->
val startPositionOrCurrent = if (startPosition == null) {
@@ -356,6 +371,24 @@
return Pair(newSelection, subselections)
}
+ internal fun mergeSelections(
+ previousSelection: Selection? = null,
+ selectableId: Long
+ ): Pair<Selection?, Map<Long, Selection>> {
+ val subselections = mutableMapOf<Long, Selection>()
+ val newSelection = selectionRegistrar.sort(requireContainerCoordinates())
+ .fastFold(null) { mergedSelection: Selection?, selectable: Selectable ->
+ val selection = if (selectable.selectableId == selectableId)
+ selectable.getSelectAllSelection() else null
+ selection?.let { subselections[selectable.selectableId] = it }
+ merge(mergedSelection, selection)
+ }
+ if (previousSelection != newSelection) hapticFeedBack?.performHapticFeedback(
+ HapticFeedbackType.TextHandleMove
+ )
+ return Pair(newSelection, subselections)
+ }
+
internal fun getSelectedText(): AnnotatedString? {
val selectables = selectionRegistrar.sort(requireContainerCoordinates())
var selectedText: AnnotatedString? = null
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
index f9bc6af..554371a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
@@ -97,6 +97,14 @@
)
/**
+ * Call this method to notify the [SelectionContainer] that the selection has been initiated
+ * with selectAll [Selection].
+ *
+ * @param selectableId [selectableId] of the [Selectable]
+ */
+ fun notifySelectionUpdateSelectAll(selectableId: Long)
+
+ /**
* Call this method to notify the [SelectionContainer] that the selection has been updated.
* The caller of this method should make sure that [notifySelectionUpdateStart] is always
* called once before calling this function. And [notifySelectionUpdateEnd] is always called
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
index 143361f..7aea413 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
@@ -69,6 +69,13 @@
)? = null
/**
+ * The callback to be invoked when the selection is initiated with selectAll [Selection].
+ */
+ internal var onSelectionUpdateSelectAll: (
+ (Long) -> Unit
+ )? = null
+
+ /**
* The callback to be invoked when the selection is updated.
* If the first offset is null it means that the start of selection is unknown for the caller.
*/
@@ -170,6 +177,10 @@
onSelectionUpdateStartCallback?.invoke(layoutCoordinates, startPosition, adjustment)
}
+ override fun notifySelectionUpdateSelectAll(selectableId: Long) {
+ onSelectionUpdateSelectAll?.invoke(selectableId)
+ }
+
override fun notifySelectionUpdate(
layoutCoordinates: LayoutCoordinates,
endPosition: Offset,
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
index 0444795..9c742d1 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
@@ -22,7 +22,16 @@
import androidx.compose.foundation.text.selection.SelectionRegistrarImpl
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextLayoutInput
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.ResolvedTextDirection
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.spy
@@ -70,6 +79,22 @@
state = TextState(mock(), selectableId)
state.layoutCoordinates = layoutCoordinates
+ state.layoutResult = TextLayoutResult(
+ TextLayoutInput(
+ text = AnnotatedString.Builder("Hello, World").toAnnotatedString(),
+ style = TextStyle(),
+ placeholders = listOf(),
+ maxLines = 1,
+ softWrap = true,
+ overflow = TextOverflow.Ellipsis,
+ density = Density(1.0f),
+ layoutDirection = LayoutDirection.Ltr,
+ resourceLoader = mock(),
+ constraints = Constraints.fixedWidth(100)
+ ),
+ multiParagraph = mock(),
+ size = IntSize(50, 50)
+ )
val controller = TextController(state).also {
it.update(selectionRegistrar)
@@ -80,6 +105,7 @@
@Test
fun longPressDragObserver_onLongPress_calls_notifySelectionInitiated() {
val position = Offset(100f, 100f)
+ whenever(state.layoutResult?.getOffsetForPosition(position)).thenReturn("Hello".length)
gesture.onStart(position)
@@ -91,12 +117,34 @@
}
@Test
+ fun longPressDragObserver_onLongPress_out_of_boundary_calls_notifySelectionUpdateSelectAll() {
+ val position = Offset(100f, 100f)
+ whenever(state.layoutResult?.getOffsetForPosition(position))
+ .thenReturn("Hello, World".length)
+
+ gesture.onStart(position)
+
+ verify(selectionRegistrar, times(1)).notifySelectionUpdateSelectAll(
+ selectableId = selectableId
+ )
+ }
+
+ @Test
fun longPressDragObserver_onDragStart_reset_dragTotalDistance() {
// Setup. Make sure selectionManager.dragTotalDistance is not 0.
val dragDistance1 = Offset(15f, 10f)
val beginPosition1 = Offset(30f, 20f)
val dragDistance2 = Offset(100f, 300f)
val beginPosition2 = Offset(300f, 200f)
+ whenever(state.layoutResult?.getOffsetForPosition(beginPosition1))
+ .thenReturn("Hello".length)
+ whenever(state.layoutResult?.getOffsetForPosition(beginPosition1 + dragDistance1))
+ .thenReturn("Hello".length)
+ whenever(state.layoutResult?.getOffsetForPosition(beginPosition2))
+ .thenReturn("Hello".length)
+ whenever(state.layoutResult?.getOffsetForPosition(beginPosition2 + dragDistance2))
+ .thenReturn("Hello".length)
+
gesture.onStart(beginPosition1)
gesture.onDrag(dragDistance1)
// Setup. Cancel selection and reselect.
@@ -122,6 +170,10 @@
fun longPressDragObserver_onDrag_calls_notifySelectionDrag() {
val dragDistance = Offset(15f, 10f)
val beginPosition = Offset(30f, 20f)
+ whenever(state.layoutResult?.getOffsetForPosition(beginPosition))
+ .thenReturn("Hello".length)
+ whenever(state.layoutResult?.getOffsetForPosition(beginPosition + dragDistance))
+ .thenReturn("Hello".length)
gesture.onStart(beginPosition)
selectionRegistrar.subselections = mapOf(selectableId to fakeSelection)
@@ -136,6 +188,27 @@
}
@Test
+ fun longPressDragObserver_onDrag_out_of_boundary_not_call_notifySelectionDrag() {
+ val dragDistance = Offset(15f, 10f)
+ val beginPosition = Offset(30f, 20f)
+ whenever(state.layoutResult?.getOffsetForPosition(beginPosition))
+ .thenReturn("Hello, World".length)
+ whenever(state.layoutResult?.getOffsetForPosition(beginPosition + dragDistance))
+ .thenReturn("Hello, World".length)
+ gesture.onStart(beginPosition)
+ selectionRegistrar.subselections = mapOf(selectableId to fakeSelection)
+
+ gesture.onDrag(dragDistance)
+ verify(selectionRegistrar, times(0))
+ .notifySelectionUpdate(
+ layoutCoordinates = layoutCoordinates,
+ startPosition = beginPosition,
+ endPosition = beginPosition + dragDistance,
+ adjustment = SelectionAdjustment.CHARACTER
+ )
+ }
+
+ @Test
fun longPressDragObserver_onStop_calls_notifySelectionEnd() {
val beginPosition = Offset(30f, 20f)
gesture.onStart(beginPosition)
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
index 9c480e54..b30cf4a 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
@@ -210,6 +210,20 @@
var selectionToReturn: Selection? = null
var textToReturn: AnnotatedString? = null
+ private val selectableKey = 1L
+ private val fakeSelectAllSelection: Selection = Selection(
+ start = Selection.AnchorInfo(
+ direction = ResolvedTextDirection.Ltr,
+ offset = 0,
+ selectableId = selectableKey
+ ),
+ end = Selection.AnchorInfo(
+ direction = ResolvedTextDirection.Ltr,
+ offset = 10,
+ selectableId = selectableKey
+ )
+ )
+
override fun getSelection(
startPosition: Offset,
endPosition: Offset,
@@ -228,6 +242,10 @@
return selectionToReturn
}
+ override fun getSelectAllSelection(): Selection? {
+ return fakeSelectAllSelection
+ }
+
override fun getText(): AnnotatedString {
getTextCalledTimes++
return textToReturn!!
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
index fa5092d..59736b1 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
@@ -193,6 +193,26 @@
}
@Test
+ fun mergeSelections_selectAll() {
+ val anotherSelectableId = 100L
+ val selectableAnother = mock<Selectable>()
+ whenever(selectableAnother.selectableId).thenReturn(anotherSelectableId)
+
+ selectionRegistrar.subscribe(selectableAnother)
+
+ selectionManager.mergeSelections(
+ selectableId = selectableId,
+ previousSelection = fakeSelection
+ )
+
+ verify(selectableAnother, times(0)).getSelectAllSelection()
+ verify(
+ hapticFeedback,
+ times(1)
+ ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
+ }
+
+ @Test
fun getSelectedText_selection_null_return_null() {
selectionManager.selection = null
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/sideeffects/SideEffects.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/sideeffects/SideEffects.kt
index e37067e..98c63c6 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/sideeffects/SideEffects.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/sideeffects/SideEffects.kt
@@ -210,7 +210,7 @@
// Creates a State<T> with Result.Loading as initial value
// If either `url` or `imageRepository` changes, the running producer
// will cancel and will be re-launched with the new inputs.
- return produceState(initialValue = Result.Loading, url, imageRepository) {
+ return produceState<Result<Image>>(initialValue = Result.Loading, url, imageRepository) {
// In a coroutine, can make suspend calls
val image = imageRepository.load(url)
diff --git a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
index 0a9f875..b077db6 100644
--- a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
+++ b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt
@@ -24,8 +24,8 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
-import androidx.compose.ui.window.DialogProperties
+import androidx.compose.ui.window.v1.Dialog
+import androidx.compose.ui.window.v1.DialogProperties
/**
* Alert dialog is a [Dialog] which interrupts the user with urgent information, details or actions.
diff --git a/compose/runtime/runtime/api/1.0.0-beta08.txt b/compose/runtime/runtime/api/1.0.0-beta08.txt
index 32b5d46..665a5a5 100644
--- a/compose/runtime/runtime/api/1.0.0-beta08.txt
+++ b/compose/runtime/runtime/api/1.0.0-beta08.txt
@@ -370,7 +370,7 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
- method public static inline operator <T> T! getValue(androidx.compose.runtime.State<T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 32b5d46..665a5a5 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -370,7 +370,7 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
- method public static inline operator <T> T! getValue(androidx.compose.runtime.State<T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
diff --git a/compose/runtime/runtime/api/public_plus_experimental_1.0.0-beta08.txt b/compose/runtime/runtime/api/public_plus_experimental_1.0.0-beta08.txt
index bbd47ef..2c12ec0 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_1.0.0-beta08.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_1.0.0-beta08.txt
@@ -388,7 +388,7 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
- method public static inline operator <T> T! getValue(androidx.compose.runtime.State<T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index bbd47ef..2c12ec0 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -388,7 +388,7 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
- method public static inline operator <T> T! getValue(androidx.compose.runtime.State<T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
diff --git a/compose/runtime/runtime/api/restricted_1.0.0-beta08.txt b/compose/runtime/runtime/api/restricted_1.0.0-beta08.txt
index 8ee23f1..d2a86ac 100644
--- a/compose/runtime/runtime/api/restricted_1.0.0-beta08.txt
+++ b/compose/runtime/runtime/api/restricted_1.0.0-beta08.txt
@@ -396,7 +396,7 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
- method public static inline operator <T> T! getValue(androidx.compose.runtime.State<T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 8ee23f1..d2a86ac 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -396,7 +396,7 @@
method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
- method public static inline operator <T> T! getValue(androidx.compose.runtime.State<T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/build.gradle b/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
index 460dabe..2bcb7e7 100644
--- a/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
+++ b/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
@@ -56,3 +56,7 @@
androidTestImplementation("androidx.activity:activity:1.2.0")
androidTestImplementation(project(":activity:activity-compose"))
}
+
+androidx {
+ benchmarkRunAlsoInterpreted = true
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
index 0b48ed3..dfee58f 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
@@ -76,7 +76,7 @@
* @see [mutableStateOf]
*/
@Stable
-interface State<T> {
+interface State<out T> {
val value: T
}
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
index edcae42..6580236 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
@@ -20,6 +20,7 @@
import android.view.ViewGroup
import android.view.inspector.WindowInspector
import android.widget.TextView
+import androidx.compose.animation.Crossfade
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@@ -45,6 +46,9 @@
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.InternalComposeApi
import androidx.compose.runtime.currentComposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.tooling.CompositionData
import androidx.compose.runtime.tooling.LocalInspectionTables
import androidx.compose.ui.Alignment
@@ -89,7 +93,7 @@
import java.util.WeakHashMap
import kotlin.math.roundToInt
-private const val DEBUG = false
+private const val DEBUG = true
private const val ROOT_ID = 3L
private const val MAX_RECURSIONS = 2
private const val MAX_ITERABLE_SIZE = 5
@@ -706,6 +710,43 @@
}
}
+ @Composable
+ fun First() {
+ Text("First")
+ }
+
+ @Composable
+ fun Second() {
+ Text("Second")
+ }
+
+ @Test
+ fun testCrossfade() {
+ val slotTableRecord = CompositionDataRecord.create()
+
+ show {
+ Inspectable(slotTableRecord) {
+ val showFirst by remember { mutableStateOf(true) }
+ Crossfade(showFirst) {
+ when (it) {
+ true -> First()
+ false -> Second()
+ }
+ }
+ }
+ }
+ val androidComposeView = findAndroidComposeView()
+ androidComposeView.setTag(R.id.inspection_slot_table_set, slotTableRecord.store)
+ val builder = LayoutInspectorTree()
+ builder.hideSystemNodes = false
+ val first = builder.convert(androidComposeView)
+ .flatMap { flatten(it) }
+ .first { it.name == "First" }
+ val hash = packageNameHash(this.javaClass.name.substringBeforeLast('.'))
+ assertThat(first.fileName).isEqualTo("LayoutInspectorTreeTest.kt")
+ assertThat(first.packageHash).isEqualTo(hash)
+ }
+
@Suppress("SameParameterValue")
private fun validate(
result: List<InspectorNode>,
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
index a6779e4..2dca1dd 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
@@ -17,6 +17,7 @@
package androidx.compose.ui.inspection.inspector
import android.view.View
+import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.InternalComposeApi
import androidx.compose.runtime.tooling.CompositionData
import androidx.compose.ui.ExperimentalComposeUiApi
@@ -74,7 +75,8 @@
"ProvideCommonCompositionLocals",
)
-private fun packageNameHash(packageName: String) =
+@VisibleForTesting
+fun packageNameHash(packageName: String) =
packageName.fold(0) { hash, char -> hash * 31 + char.code }.absoluteValue
/**
diff --git a/compose/ui/ui-text/api/1.0.0-beta08.txt b/compose/ui/ui-text/api/1.0.0-beta08.txt
index d0abc49..d50cd19 100644
--- a/compose/ui/ui-text/api/1.0.0-beta08.txt
+++ b/compose/ui/ui-text/api/1.0.0-beta08.txt
@@ -222,24 +222,43 @@
}
@androidx.compose.runtime.Immutable public final class Placeholder {
- method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+ method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
method public operator boolean equals(Object? other);
method public long getHeight-XSAIIZE();
- method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+ method public int getPlaceholderVerticalAlign-J6kI3mc();
method public long getWidth-XSAIIZE();
property public final long height;
- property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+ property public final int placeholderVerticalAlign;
property public final long width;
}
- public enum PlaceholderVerticalAlign {
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+ public final inline class PlaceholderVerticalAlign {
+ ctor public PlaceholderVerticalAlign();
+ method public static int constructor-impl(int value);
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public int getValue();
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ property public final int value;
+ field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+ }
+
+ public static final class PlaceholderVerticalAlign.Companion {
+ method public int getAboveBaseline-J6kI3mc();
+ method public int getBottom-J6kI3mc();
+ method public int getCenter-J6kI3mc();
+ method public int getTextBottom-J6kI3mc();
+ method public int getTextCenter-J6kI3mc();
+ method public int getTextTop-J6kI3mc();
+ method public int getTop-J6kI3mc();
+ property public final int AboveBaseline;
+ property public final int Bottom;
+ property public final int Center;
+ property public final int TextBottom;
+ property public final int TextCenter;
+ property public final int TextTop;
+ property public final int Top;
}
public final class SaversKt {
diff --git a/compose/ui/ui-text/api/current.ignore b/compose/ui/ui-text/api/current.ignore
index 5bb1213..221b006 100644
--- a/compose/ui/ui-text/api/current.ignore
+++ b/compose/ui/ui-text/api/current.ignore
@@ -3,6 +3,8 @@
Added method androidx.compose.ui.text.input.InputEventCallback.onImeAction-KlQnJC8(int)
+ChangedSuperclass: androidx.compose.ui.text.PlaceholderVerticalAlign:
+ Class androidx.compose.ui.text.PlaceholderVerticalAlign superclass changed from java.lang.Enum to java.lang.Object
ChangedSuperclass: androidx.compose.ui.text.input.ImeAction:
Class androidx.compose.ui.text.input.ImeAction superclass changed from java.lang.Enum to java.lang.Object
ChangedSuperclass: androidx.compose.ui.text.input.KeyboardType:
@@ -13,6 +15,8 @@
Attempted to remove @NonNull annotation from Field ImeOptions.imeAction
InvalidNullConversion: Field ImeOptions.keyboardType:
Attempted to remove @NonNull annotation from Field ImeOptions.keyboardType
+InvalidNullConversion: Field Placeholder.placeholderVerticalAlign:
+ Attempted to remove @NonNull annotation from Field Placeholder.placeholderVerticalAlign
RemovedClass: androidx.compose.ui.text.JvmCharHelpers_jvmKt:
@@ -23,6 +27,20 @@
Removed deprecated method androidx.compose.ui.text.input.InputEventCallback.onImeAction(androidx.compose.ui.text.input.ImeAction)
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#AboveBaseline:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.AboveBaseline
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Bottom:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Bottom
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Center:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Center
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextBottom:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextBottom
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextCenter:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextCenter
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextTop:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextTop
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Top:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Top
RemovedField: androidx.compose.ui.text.input.ImeAction#Default:
Removed enum constant androidx.compose.ui.text.input.ImeAction.Default
RemovedField: androidx.compose.ui.text.input.ImeAction#Done:
@@ -57,6 +75,10 @@
Removed enum constant androidx.compose.ui.text.input.KeyboardType.Uri
+RemovedMethod: androidx.compose.ui.text.Placeholder#copy-KJSDsNM(long, long, androidx.compose.ui.text.PlaceholderVerticalAlign):
+ Removed method androidx.compose.ui.text.Placeholder.copy-KJSDsNM(long,long,androidx.compose.ui.text.PlaceholderVerticalAlign)
+RemovedMethod: androidx.compose.ui.text.Placeholder#getPlaceholderVerticalAlign():
+ Removed method androidx.compose.ui.text.Placeholder.getPlaceholderVerticalAlign()
RemovedMethod: androidx.compose.ui.text.input.ImeOptions#ImeOptions(boolean, androidx.compose.ui.text.input.KeyboardCapitalization, boolean, androidx.compose.ui.text.input.KeyboardType, androidx.compose.ui.text.input.ImeAction):
Removed constructor androidx.compose.ui.text.input.ImeOptions(boolean,androidx.compose.ui.text.input.KeyboardCapitalization,boolean,androidx.compose.ui.text.input.KeyboardType,androidx.compose.ui.text.input.ImeAction)
RemovedMethod: androidx.compose.ui.text.input.ImeOptions#copy(boolean, androidx.compose.ui.text.input.KeyboardCapitalization, boolean, androidx.compose.ui.text.input.KeyboardType, androidx.compose.ui.text.input.ImeAction):
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index d0abc49..d50cd19 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -222,24 +222,43 @@
}
@androidx.compose.runtime.Immutable public final class Placeholder {
- method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+ method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
method public operator boolean equals(Object? other);
method public long getHeight-XSAIIZE();
- method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+ method public int getPlaceholderVerticalAlign-J6kI3mc();
method public long getWidth-XSAIIZE();
property public final long height;
- property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+ property public final int placeholderVerticalAlign;
property public final long width;
}
- public enum PlaceholderVerticalAlign {
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+ public final inline class PlaceholderVerticalAlign {
+ ctor public PlaceholderVerticalAlign();
+ method public static int constructor-impl(int value);
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public int getValue();
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ property public final int value;
+ field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+ }
+
+ public static final class PlaceholderVerticalAlign.Companion {
+ method public int getAboveBaseline-J6kI3mc();
+ method public int getBottom-J6kI3mc();
+ method public int getCenter-J6kI3mc();
+ method public int getTextBottom-J6kI3mc();
+ method public int getTextCenter-J6kI3mc();
+ method public int getTextTop-J6kI3mc();
+ method public int getTop-J6kI3mc();
+ property public final int AboveBaseline;
+ property public final int Bottom;
+ property public final int Center;
+ property public final int TextBottom;
+ property public final int TextCenter;
+ property public final int TextTop;
+ property public final int Top;
}
public final class SaversKt {
diff --git a/compose/ui/ui-text/api/public_plus_experimental_1.0.0-beta08.txt b/compose/ui/ui-text/api/public_plus_experimental_1.0.0-beta08.txt
index 2f0a4c8..7b15f2b 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_1.0.0-beta08.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_1.0.0-beta08.txt
@@ -231,24 +231,43 @@
}
@androidx.compose.runtime.Immutable public final class Placeholder {
- method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+ method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
method public operator boolean equals(Object? other);
method public long getHeight-XSAIIZE();
- method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+ method public int getPlaceholderVerticalAlign-J6kI3mc();
method public long getWidth-XSAIIZE();
property public final long height;
- property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+ property public final int placeholderVerticalAlign;
property public final long width;
}
- public enum PlaceholderVerticalAlign {
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+ public final inline class PlaceholderVerticalAlign {
+ ctor public PlaceholderVerticalAlign();
+ method public static int constructor-impl(int value);
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public int getValue();
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ property public final int value;
+ field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+ }
+
+ public static final class PlaceholderVerticalAlign.Companion {
+ method public int getAboveBaseline-J6kI3mc();
+ method public int getBottom-J6kI3mc();
+ method public int getCenter-J6kI3mc();
+ method public int getTextBottom-J6kI3mc();
+ method public int getTextCenter-J6kI3mc();
+ method public int getTextTop-J6kI3mc();
+ method public int getTop-J6kI3mc();
+ property public final int AboveBaseline;
+ property public final int Bottom;
+ property public final int Center;
+ property public final int TextBottom;
+ property public final int TextCenter;
+ property public final int TextTop;
+ property public final int Top;
}
public final class SaversKt {
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index 2f0a4c8..7b15f2b 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -231,24 +231,43 @@
}
@androidx.compose.runtime.Immutable public final class Placeholder {
- method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+ method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
method public operator boolean equals(Object? other);
method public long getHeight-XSAIIZE();
- method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+ method public int getPlaceholderVerticalAlign-J6kI3mc();
method public long getWidth-XSAIIZE();
property public final long height;
- property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+ property public final int placeholderVerticalAlign;
property public final long width;
}
- public enum PlaceholderVerticalAlign {
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+ public final inline class PlaceholderVerticalAlign {
+ ctor public PlaceholderVerticalAlign();
+ method public static int constructor-impl(int value);
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public int getValue();
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ property public final int value;
+ field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+ }
+
+ public static final class PlaceholderVerticalAlign.Companion {
+ method public int getAboveBaseline-J6kI3mc();
+ method public int getBottom-J6kI3mc();
+ method public int getCenter-J6kI3mc();
+ method public int getTextBottom-J6kI3mc();
+ method public int getTextCenter-J6kI3mc();
+ method public int getTextTop-J6kI3mc();
+ method public int getTop-J6kI3mc();
+ property public final int AboveBaseline;
+ property public final int Bottom;
+ property public final int Center;
+ property public final int TextBottom;
+ property public final int TextCenter;
+ property public final int TextTop;
+ property public final int Top;
}
public final class SaversKt {
diff --git a/compose/ui/ui-text/api/restricted_1.0.0-beta08.txt b/compose/ui/ui-text/api/restricted_1.0.0-beta08.txt
index d0abc49..d50cd19 100644
--- a/compose/ui/ui-text/api/restricted_1.0.0-beta08.txt
+++ b/compose/ui/ui-text/api/restricted_1.0.0-beta08.txt
@@ -222,24 +222,43 @@
}
@androidx.compose.runtime.Immutable public final class Placeholder {
- method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+ method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
method public operator boolean equals(Object? other);
method public long getHeight-XSAIIZE();
- method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+ method public int getPlaceholderVerticalAlign-J6kI3mc();
method public long getWidth-XSAIIZE();
property public final long height;
- property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+ property public final int placeholderVerticalAlign;
property public final long width;
}
- public enum PlaceholderVerticalAlign {
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+ public final inline class PlaceholderVerticalAlign {
+ ctor public PlaceholderVerticalAlign();
+ method public static int constructor-impl(int value);
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public int getValue();
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ property public final int value;
+ field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+ }
+
+ public static final class PlaceholderVerticalAlign.Companion {
+ method public int getAboveBaseline-J6kI3mc();
+ method public int getBottom-J6kI3mc();
+ method public int getCenter-J6kI3mc();
+ method public int getTextBottom-J6kI3mc();
+ method public int getTextCenter-J6kI3mc();
+ method public int getTextTop-J6kI3mc();
+ method public int getTop-J6kI3mc();
+ property public final int AboveBaseline;
+ property public final int Bottom;
+ property public final int Center;
+ property public final int TextBottom;
+ property public final int TextCenter;
+ property public final int TextTop;
+ property public final int Top;
}
public final class SaversKt {
diff --git a/compose/ui/ui-text/api/restricted_current.ignore b/compose/ui/ui-text/api/restricted_current.ignore
index 5bb1213..221b006 100644
--- a/compose/ui/ui-text/api/restricted_current.ignore
+++ b/compose/ui/ui-text/api/restricted_current.ignore
@@ -3,6 +3,8 @@
Added method androidx.compose.ui.text.input.InputEventCallback.onImeAction-KlQnJC8(int)
+ChangedSuperclass: androidx.compose.ui.text.PlaceholderVerticalAlign:
+ Class androidx.compose.ui.text.PlaceholderVerticalAlign superclass changed from java.lang.Enum to java.lang.Object
ChangedSuperclass: androidx.compose.ui.text.input.ImeAction:
Class androidx.compose.ui.text.input.ImeAction superclass changed from java.lang.Enum to java.lang.Object
ChangedSuperclass: androidx.compose.ui.text.input.KeyboardType:
@@ -13,6 +15,8 @@
Attempted to remove @NonNull annotation from Field ImeOptions.imeAction
InvalidNullConversion: Field ImeOptions.keyboardType:
Attempted to remove @NonNull annotation from Field ImeOptions.keyboardType
+InvalidNullConversion: Field Placeholder.placeholderVerticalAlign:
+ Attempted to remove @NonNull annotation from Field Placeholder.placeholderVerticalAlign
RemovedClass: androidx.compose.ui.text.JvmCharHelpers_jvmKt:
@@ -23,6 +27,20 @@
Removed deprecated method androidx.compose.ui.text.input.InputEventCallback.onImeAction(androidx.compose.ui.text.input.ImeAction)
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#AboveBaseline:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.AboveBaseline
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Bottom:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Bottom
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Center:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Center
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextBottom:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextBottom
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextCenter:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextCenter
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#TextTop:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.TextTop
+RemovedField: androidx.compose.ui.text.PlaceholderVerticalAlign#Top:
+ Removed enum constant androidx.compose.ui.text.PlaceholderVerticalAlign.Top
RemovedField: androidx.compose.ui.text.input.ImeAction#Default:
Removed enum constant androidx.compose.ui.text.input.ImeAction.Default
RemovedField: androidx.compose.ui.text.input.ImeAction#Done:
@@ -57,6 +75,10 @@
Removed enum constant androidx.compose.ui.text.input.KeyboardType.Uri
+RemovedMethod: androidx.compose.ui.text.Placeholder#copy-KJSDsNM(long, long, androidx.compose.ui.text.PlaceholderVerticalAlign):
+ Removed method androidx.compose.ui.text.Placeholder.copy-KJSDsNM(long,long,androidx.compose.ui.text.PlaceholderVerticalAlign)
+RemovedMethod: androidx.compose.ui.text.Placeholder#getPlaceholderVerticalAlign():
+ Removed method androidx.compose.ui.text.Placeholder.getPlaceholderVerticalAlign()
RemovedMethod: androidx.compose.ui.text.input.ImeOptions#ImeOptions(boolean, androidx.compose.ui.text.input.KeyboardCapitalization, boolean, androidx.compose.ui.text.input.KeyboardType, androidx.compose.ui.text.input.ImeAction):
Removed constructor androidx.compose.ui.text.input.ImeOptions(boolean,androidx.compose.ui.text.input.KeyboardCapitalization,boolean,androidx.compose.ui.text.input.KeyboardType,androidx.compose.ui.text.input.ImeAction)
RemovedMethod: androidx.compose.ui.text.input.ImeOptions#copy(boolean, androidx.compose.ui.text.input.KeyboardCapitalization, boolean, androidx.compose.ui.text.input.KeyboardType, androidx.compose.ui.text.input.ImeAction):
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index d0abc49..d50cd19 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -222,24 +222,43 @@
}
@androidx.compose.runtime.Immutable public final class Placeholder {
- method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign);
+ method public androidx.compose.ui.text.Placeholder copy-KJSDsNM(optional long width, optional long height, optional int placeholderVerticalAlign);
method public operator boolean equals(Object? other);
method public long getHeight-XSAIIZE();
- method public androidx.compose.ui.text.PlaceholderVerticalAlign getPlaceholderVerticalAlign();
+ method public int getPlaceholderVerticalAlign-J6kI3mc();
method public long getWidth-XSAIIZE();
property public final long height;
- property public final androidx.compose.ui.text.PlaceholderVerticalAlign placeholderVerticalAlign;
+ property public final int placeholderVerticalAlign;
property public final long width;
}
- public enum PlaceholderVerticalAlign {
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign AboveBaseline;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Bottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Center;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextBottom;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextCenter;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign TextTop;
- enum_constant public static final androidx.compose.ui.text.PlaceholderVerticalAlign Top;
+ public final inline class PlaceholderVerticalAlign {
+ ctor public PlaceholderVerticalAlign();
+ method public static int constructor-impl(int value);
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public int getValue();
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ property public final int value;
+ field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+ }
+
+ public static final class PlaceholderVerticalAlign.Companion {
+ method public int getAboveBaseline-J6kI3mc();
+ method public int getBottom-J6kI3mc();
+ method public int getCenter-J6kI3mc();
+ method public int getTextBottom-J6kI3mc();
+ method public int getTextCenter-J6kI3mc();
+ method public int getTextTop-J6kI3mc();
+ method public int getTop-J6kI3mc();
+ property public final int AboveBaseline;
+ property public final int Bottom;
+ property public final int Center;
+ property public final int TextBottom;
+ property public final int TextCenter;
+ property public final int TextTop;
+ property public final int Top;
}
public final class SaversKt {
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt
index ee998a9..94d26db 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt
@@ -84,4 +84,5 @@
PlaceholderVerticalAlign.TextTop -> PlaceholderSpan.ALIGN_TEXT_TOP
PlaceholderVerticalAlign.TextBottom -> PlaceholderSpan.ALIGN_TEXT_BOTTOM
PlaceholderVerticalAlign.TextCenter -> PlaceholderSpan.ALIGN_TEXT_CENTER
+ else -> error("Invalid PlaceholderVerticalAlign")
}
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
index cf6b935..eca4d3d 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt
@@ -84,34 +84,51 @@
* The settings used to specify how a placeholder is vertically aligned within a text line.
* @see Placeholder
*/
-enum class PlaceholderVerticalAlign {
- /** Align the bottom of the placeholder with the baseline. */
- AboveBaseline,
- /** Align the top of the placeholder with the top of the entire line. */
- Top,
- /** Align the bottom of the placeholder with the bottom of the entire line. */
- Bottom,
- /** Align the center of the placeholder with the center of the entire line. */
- Center,
- /**
- * Align the top of the placeholder with the top of the proceeding text.
- * It is different from the [Top] when there are texts with different font size, font or other
- * styles in the same line. This option will use the proceeding text's top instead of the
- * whole line's top.
- */
- TextTop,
- /**
- * Align the bottom of the placeholder with the bottom of the proceeding text.
- * It is different from the [TextBottom] when there are texts with different font size, font or
- * other styles in the same line. This option will use the proceeding text's bottom instead of
- * the whole line's bottom.
- */
- TextBottom,
- /**
- * Align the center of the placeholder with the center of the proceeding text.
- * It is different from the [Center] when there are texts with different font size, font or
- * other styles in the same line. This option will use the proceeding text's center instead of
- * the whole line's center.
- */
- TextCenter,
+@Suppress("INLINE_CLASS_DEPRECATED")
+inline class PlaceholderVerticalAlign(val value: Int) {
+
+ override fun toString(): String {
+ return when (this) {
+ AboveBaseline -> "AboveBaseline"
+ Top -> "Top"
+ Bottom -> "Bottom"
+ Center -> "Center"
+ TextTop -> "TextTop"
+ TextBottom -> "TextBottom"
+ TextCenter -> "TextCenter"
+ else -> "Invalid"
+ }
+ }
+
+ companion object {
+ /** Align the bottom of the placeholder with the baseline. */
+ val AboveBaseline = PlaceholderVerticalAlign(1)
+ /** Align the top of the placeholder with the top of the entire line. */
+ val Top = PlaceholderVerticalAlign(2)
+ /** Align the bottom of the placeholder with the bottom of the entire line. */
+ val Bottom = PlaceholderVerticalAlign(3)
+ /** Align the center of the placeholder with the center of the entire line. */
+ val Center = PlaceholderVerticalAlign(4)
+ /**
+ * Align the top of the placeholder with the top of the proceeding text.
+ * It is different from the [Top] when there are texts with different font size, font or other
+ * styles in the same line. This option will use the proceeding text's top instead of the
+ * whole line's top.
+ */
+ val TextTop = PlaceholderVerticalAlign(5)
+ /**
+ * Align the bottom of the placeholder with the bottom of the proceeding text.
+ * It is different from the [TextBottom] when there are texts with different font size, font or
+ * other styles in the same line. This option will use the proceeding text's bottom instead of
+ * the whole line's bottom.
+ */
+ val TextBottom = PlaceholderVerticalAlign(6)
+ /**
+ * Align the center of the placeholder with the center of the proceeding text.
+ * It is different from the [Center] when there are texts with different font size, font or
+ * other styles in the same line. This option will use the proceeding text's center instead of
+ * the whole line's center.
+ */
+ val TextCenter = PlaceholderVerticalAlign(7)
+ }
}
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.desktop.kt b/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.desktop.kt
index 5934cd7..9105a83 100644
--- a/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.desktop.kt
+++ b/compose/ui/ui-text/src/desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopParagraph.desktop.kt
@@ -866,6 +866,7 @@
PlaceholderVerticalAlign.Top -> PlaceholderAlignment.TOP
PlaceholderVerticalAlign.Bottom -> PlaceholderAlignment.BOTTOM
PlaceholderVerticalAlign.Center -> PlaceholderAlignment.MIDDLE
+ else -> error("Invalid PlaceholderVerticalAlign.")
}
}
diff --git a/compose/ui/ui-tooling-data/src/main/java/androidx/compose/ui/tooling/data/SlotTree.kt b/compose/ui/ui-tooling-data/src/main/java/androidx/compose/ui/tooling/data/SlotTree.kt
index 0817e3f..9a00966 100644
--- a/compose/ui/ui-tooling-data/src/main/java/androidx/compose/ui/tooling/data/SlotTree.kt
+++ b/compose/ui/ui-tooling-data/src/main/java/androidx/compose/ui/tooling/data/SlotTree.kt
@@ -432,7 +432,7 @@
return SourceInformationContext(
name = name,
sourceFile = sourceFile ?: parent?.sourceFile,
- packageHash = packageHash,
+ packageHash = if (sourceFile != null) packageHash else parent?.packageHash ?: packageHash,
locations = sourceLocations,
repeatOffset = repeatOffset,
parameters = parameters,
diff --git a/compose/ui/ui/api/1.0.0-beta08.txt b/compose/ui/ui/api/1.0.0-beta08.txt
index fa7c70b..492584d 100644
--- a/compose/ui/ui/api/1.0.0-beta08.txt
+++ b/compose/ui/ui/api/1.0.0-beta08.txt
@@ -1286,9 +1286,9 @@
public interface NestedScrollConnection {
method public default suspend Object? onPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPostScroll-61dr0bI(long consumed, long available, int source);
method public default suspend Object? onPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPreScroll-ScU7Jk8(long available, int source);
}
public final class NestedScrollDelegatingWrapperKt {
@@ -1297,9 +1297,9 @@
public final class NestedScrollDispatcher {
ctor public NestedScrollDispatcher();
method public suspend Object? dispatchPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPostScroll-61dr0bI(long consumed, long available, int source);
method public suspend Object? dispatchPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPreScroll-ScU7Jk8(long available, int source);
method public kotlinx.coroutines.CoroutineScope getCoroutineScope();
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
}
@@ -1308,9 +1308,22 @@
method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
}
- public enum NestedScrollSource {
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Drag;
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Fling;
+ public final inline class NestedScrollSource {
+ ctor public NestedScrollSource();
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
+ }
+
+ public static final class NestedScrollSource.Companion {
+ method public int getDrag-WNlRxjI();
+ method public int getFling-WNlRxjI();
+ method @Deprecated public int getRelocate-WNlRxjI();
+ property public final int Drag;
+ property public final int Fling;
+ property @Deprecated public final int Relocate;
}
}
diff --git a/compose/ui/ui/api/current.ignore b/compose/ui/ui/api/current.ignore
index 209860c..76b5c43 100644
--- a/compose/ui/ui/api/current.ignore
+++ b/compose/ui/ui/api/current.ignore
@@ -11,6 +11,8 @@
Class androidx.compose.ui.focus.FocusState changed class/interface declaration
+ChangedSuperclass: androidx.compose.ui.input.nestedscroll.NestedScrollSource:
+ Class androidx.compose.ui.input.nestedscroll.NestedScrollSource superclass changed from java.lang.Enum to java.lang.Object
ChangedSuperclass: androidx.compose.ui.input.pointer.PointerType:
Class androidx.compose.ui.input.pointer.PointerType superclass changed from java.lang.Enum to java.lang.Object
@@ -77,6 +79,10 @@
Removed enum constant androidx.compose.ui.focus.FocusState.Disabled
RemovedField: androidx.compose.ui.focus.FocusState#Inactive:
Removed enum constant androidx.compose.ui.focus.FocusState.Inactive
+RemovedField: androidx.compose.ui.input.nestedscroll.NestedScrollSource#Drag:
+ Removed enum constant androidx.compose.ui.input.nestedscroll.NestedScrollSource.Drag
+RemovedField: androidx.compose.ui.input.nestedscroll.NestedScrollSource#Fling:
+ Removed enum constant androidx.compose.ui.input.nestedscroll.NestedScrollSource.Fling
RemovedField: androidx.compose.ui.input.pointer.PointerType#Eraser:
Removed enum constant androidx.compose.ui.input.pointer.PointerType.Eraser
RemovedField: androidx.compose.ui.input.pointer.PointerType#Mouse:
@@ -105,6 +111,14 @@
Removed method androidx.compose.ui.graphics.vector.VectorPath.getStrokeLineCap()
RemovedMethod: androidx.compose.ui.graphics.vector.VectorPath#getStrokeLineJoin():
Removed method androidx.compose.ui.graphics.vector.VectorPath.getStrokeLineJoin()
+RemovedMethod: androidx.compose.ui.input.nestedscroll.NestedScrollConnection#onPostScroll-61dr0bI(long, long, androidx.compose.ui.input.nestedscroll.NestedScrollSource):
+ Removed method androidx.compose.ui.input.nestedscroll.NestedScrollConnection.onPostScroll-61dr0bI(long,long,androidx.compose.ui.input.nestedscroll.NestedScrollSource)
+RemovedMethod: androidx.compose.ui.input.nestedscroll.NestedScrollConnection#onPreScroll-ScU7Jk8(long, androidx.compose.ui.input.nestedscroll.NestedScrollSource):
+ Removed method androidx.compose.ui.input.nestedscroll.NestedScrollConnection.onPreScroll-ScU7Jk8(long,androidx.compose.ui.input.nestedscroll.NestedScrollSource)
+RemovedMethod: androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher#dispatchPostScroll-61dr0bI(long, long, androidx.compose.ui.input.nestedscroll.NestedScrollSource):
+ Removed method androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher.dispatchPostScroll-61dr0bI(long,long,androidx.compose.ui.input.nestedscroll.NestedScrollSource)
+RemovedMethod: androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher#dispatchPreScroll-ScU7Jk8(long, androidx.compose.ui.input.nestedscroll.NestedScrollSource):
+ Removed method androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher.dispatchPreScroll-ScU7Jk8(long,androidx.compose.ui.input.nestedscroll.NestedScrollSource)
RemovedMethod: androidx.compose.ui.input.pointer.PointerInputChange#copy-aJ2ieLA(long, long, long, boolean, long, long, boolean, androidx.compose.ui.input.pointer.ConsumedData, androidx.compose.ui.input.pointer.PointerType):
Removed method androidx.compose.ui.input.pointer.PointerInputChange.copy-aJ2ieLA(long,long,long,boolean,long,long,boolean,androidx.compose.ui.input.pointer.ConsumedData,androidx.compose.ui.input.pointer.PointerType)
RemovedMethod: androidx.compose.ui.input.pointer.PointerInputChange#getType():
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index fa7c70b..492584d 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -1286,9 +1286,9 @@
public interface NestedScrollConnection {
method public default suspend Object? onPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPostScroll-61dr0bI(long consumed, long available, int source);
method public default suspend Object? onPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPreScroll-ScU7Jk8(long available, int source);
}
public final class NestedScrollDelegatingWrapperKt {
@@ -1297,9 +1297,9 @@
public final class NestedScrollDispatcher {
ctor public NestedScrollDispatcher();
method public suspend Object? dispatchPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPostScroll-61dr0bI(long consumed, long available, int source);
method public suspend Object? dispatchPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPreScroll-ScU7Jk8(long available, int source);
method public kotlinx.coroutines.CoroutineScope getCoroutineScope();
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
}
@@ -1308,9 +1308,22 @@
method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
}
- public enum NestedScrollSource {
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Drag;
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Fling;
+ public final inline class NestedScrollSource {
+ ctor public NestedScrollSource();
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
+ }
+
+ public static final class NestedScrollSource.Companion {
+ method public int getDrag-WNlRxjI();
+ method public int getFling-WNlRxjI();
+ method @Deprecated public int getRelocate-WNlRxjI();
+ property public final int Drag;
+ property public final int Fling;
+ property @Deprecated public final int Relocate;
}
}
diff --git a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
index 22062ae..29c786f 100644
--- a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
+++ b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
@@ -1388,9 +1388,9 @@
public interface NestedScrollConnection {
method public default suspend Object? onPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPostScroll-61dr0bI(long consumed, long available, int source);
method public default suspend Object? onPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPreScroll-ScU7Jk8(long available, int source);
}
public final class NestedScrollDelegatingWrapperKt {
@@ -1399,9 +1399,9 @@
public final class NestedScrollDispatcher {
ctor public NestedScrollDispatcher();
method public suspend Object? dispatchPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPostScroll-61dr0bI(long consumed, long available, int source);
method public suspend Object? dispatchPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPreScroll-ScU7Jk8(long available, int source);
method public kotlinx.coroutines.CoroutineScope getCoroutineScope();
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
}
@@ -1410,9 +1410,22 @@
method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
}
- public enum NestedScrollSource {
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Drag;
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Fling;
+ public final inline class NestedScrollSource {
+ ctor public NestedScrollSource();
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
+ }
+
+ public static final class NestedScrollSource.Companion {
+ method public int getDrag-WNlRxjI();
+ method public int getFling-WNlRxjI();
+ method @Deprecated public int getRelocate-WNlRxjI();
+ property public final int Drag;
+ property public final int Fling;
+ property @Deprecated public final int Relocate;
}
}
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 22062ae..29c786f 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -1388,9 +1388,9 @@
public interface NestedScrollConnection {
method public default suspend Object? onPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPostScroll-61dr0bI(long consumed, long available, int source);
method public default suspend Object? onPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPreScroll-ScU7Jk8(long available, int source);
}
public final class NestedScrollDelegatingWrapperKt {
@@ -1399,9 +1399,9 @@
public final class NestedScrollDispatcher {
ctor public NestedScrollDispatcher();
method public suspend Object? dispatchPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPostScroll-61dr0bI(long consumed, long available, int source);
method public suspend Object? dispatchPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPreScroll-ScU7Jk8(long available, int source);
method public kotlinx.coroutines.CoroutineScope getCoroutineScope();
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
}
@@ -1410,9 +1410,22 @@
method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
}
- public enum NestedScrollSource {
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Drag;
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Fling;
+ public final inline class NestedScrollSource {
+ ctor public NestedScrollSource();
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
+ }
+
+ public static final class NestedScrollSource.Companion {
+ method public int getDrag-WNlRxjI();
+ method public int getFling-WNlRxjI();
+ method @Deprecated public int getRelocate-WNlRxjI();
+ property public final int Drag;
+ property public final int Fling;
+ property @Deprecated public final int Relocate;
}
}
diff --git a/compose/ui/ui/api/restricted_1.0.0-beta08.txt b/compose/ui/ui/api/restricted_1.0.0-beta08.txt
index 60cf61c..189254a 100644
--- a/compose/ui/ui/api/restricted_1.0.0-beta08.txt
+++ b/compose/ui/ui/api/restricted_1.0.0-beta08.txt
@@ -1286,9 +1286,9 @@
public interface NestedScrollConnection {
method public default suspend Object? onPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPostScroll-61dr0bI(long consumed, long available, int source);
method public default suspend Object? onPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPreScroll-ScU7Jk8(long available, int source);
}
public final class NestedScrollDelegatingWrapperKt {
@@ -1297,9 +1297,9 @@
public final class NestedScrollDispatcher {
ctor public NestedScrollDispatcher();
method public suspend Object? dispatchPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPostScroll-61dr0bI(long consumed, long available, int source);
method public suspend Object? dispatchPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPreScroll-ScU7Jk8(long available, int source);
method public kotlinx.coroutines.CoroutineScope getCoroutineScope();
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
}
@@ -1308,9 +1308,22 @@
method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
}
- public enum NestedScrollSource {
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Drag;
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Fling;
+ public final inline class NestedScrollSource {
+ ctor public NestedScrollSource();
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
+ }
+
+ public static final class NestedScrollSource.Companion {
+ method public int getDrag-WNlRxjI();
+ method public int getFling-WNlRxjI();
+ method @Deprecated public int getRelocate-WNlRxjI();
+ property public final int Drag;
+ property public final int Fling;
+ property @Deprecated public final int Relocate;
}
}
diff --git a/compose/ui/ui/api/restricted_current.ignore b/compose/ui/ui/api/restricted_current.ignore
index 209860c..76b5c43 100644
--- a/compose/ui/ui/api/restricted_current.ignore
+++ b/compose/ui/ui/api/restricted_current.ignore
@@ -11,6 +11,8 @@
Class androidx.compose.ui.focus.FocusState changed class/interface declaration
+ChangedSuperclass: androidx.compose.ui.input.nestedscroll.NestedScrollSource:
+ Class androidx.compose.ui.input.nestedscroll.NestedScrollSource superclass changed from java.lang.Enum to java.lang.Object
ChangedSuperclass: androidx.compose.ui.input.pointer.PointerType:
Class androidx.compose.ui.input.pointer.PointerType superclass changed from java.lang.Enum to java.lang.Object
@@ -77,6 +79,10 @@
Removed enum constant androidx.compose.ui.focus.FocusState.Disabled
RemovedField: androidx.compose.ui.focus.FocusState#Inactive:
Removed enum constant androidx.compose.ui.focus.FocusState.Inactive
+RemovedField: androidx.compose.ui.input.nestedscroll.NestedScrollSource#Drag:
+ Removed enum constant androidx.compose.ui.input.nestedscroll.NestedScrollSource.Drag
+RemovedField: androidx.compose.ui.input.nestedscroll.NestedScrollSource#Fling:
+ Removed enum constant androidx.compose.ui.input.nestedscroll.NestedScrollSource.Fling
RemovedField: androidx.compose.ui.input.pointer.PointerType#Eraser:
Removed enum constant androidx.compose.ui.input.pointer.PointerType.Eraser
RemovedField: androidx.compose.ui.input.pointer.PointerType#Mouse:
@@ -105,6 +111,14 @@
Removed method androidx.compose.ui.graphics.vector.VectorPath.getStrokeLineCap()
RemovedMethod: androidx.compose.ui.graphics.vector.VectorPath#getStrokeLineJoin():
Removed method androidx.compose.ui.graphics.vector.VectorPath.getStrokeLineJoin()
+RemovedMethod: androidx.compose.ui.input.nestedscroll.NestedScrollConnection#onPostScroll-61dr0bI(long, long, androidx.compose.ui.input.nestedscroll.NestedScrollSource):
+ Removed method androidx.compose.ui.input.nestedscroll.NestedScrollConnection.onPostScroll-61dr0bI(long,long,androidx.compose.ui.input.nestedscroll.NestedScrollSource)
+RemovedMethod: androidx.compose.ui.input.nestedscroll.NestedScrollConnection#onPreScroll-ScU7Jk8(long, androidx.compose.ui.input.nestedscroll.NestedScrollSource):
+ Removed method androidx.compose.ui.input.nestedscroll.NestedScrollConnection.onPreScroll-ScU7Jk8(long,androidx.compose.ui.input.nestedscroll.NestedScrollSource)
+RemovedMethod: androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher#dispatchPostScroll-61dr0bI(long, long, androidx.compose.ui.input.nestedscroll.NestedScrollSource):
+ Removed method androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher.dispatchPostScroll-61dr0bI(long,long,androidx.compose.ui.input.nestedscroll.NestedScrollSource)
+RemovedMethod: androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher#dispatchPreScroll-ScU7Jk8(long, androidx.compose.ui.input.nestedscroll.NestedScrollSource):
+ Removed method androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher.dispatchPreScroll-ScU7Jk8(long,androidx.compose.ui.input.nestedscroll.NestedScrollSource)
RemovedMethod: androidx.compose.ui.input.pointer.PointerInputChange#copy-aJ2ieLA(long, long, long, boolean, long, long, boolean, androidx.compose.ui.input.pointer.ConsumedData, androidx.compose.ui.input.pointer.PointerType):
Removed method androidx.compose.ui.input.pointer.PointerInputChange.copy-aJ2ieLA(long,long,long,boolean,long,long,boolean,androidx.compose.ui.input.pointer.ConsumedData,androidx.compose.ui.input.pointer.PointerType)
RemovedMethod: androidx.compose.ui.input.pointer.PointerInputChange#getType():
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 60cf61c..189254a 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -1286,9 +1286,9 @@
public interface NestedScrollConnection {
method public default suspend Object? onPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPostScroll-61dr0bI(long consumed, long available, int source);
method public default suspend Object? onPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public default long onPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public default long onPreScroll-ScU7Jk8(long available, int source);
}
public final class NestedScrollDelegatingWrapperKt {
@@ -1297,9 +1297,9 @@
public final class NestedScrollDispatcher {
ctor public NestedScrollDispatcher();
method public suspend Object? dispatchPostFling-k5p9STU(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPostScroll-61dr0bI(long consumed, long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPostScroll-61dr0bI(long consumed, long available, int source);
method public suspend Object? dispatchPreFling-bHeVL4A(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity> p);
- method public long dispatchPreScroll-ScU7Jk8(long available, androidx.compose.ui.input.nestedscroll.NestedScrollSource source);
+ method public long dispatchPreScroll-ScU7Jk8(long available, int source);
method public kotlinx.coroutines.CoroutineScope getCoroutineScope();
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
}
@@ -1308,9 +1308,22 @@
method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
}
- public enum NestedScrollSource {
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Drag;
- enum_constant public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource Fling;
+ public final inline class NestedScrollSource {
+ ctor public NestedScrollSource();
+ method public static inline boolean equals-impl(int p, Object? p1);
+ method public static boolean equals-impl0(int p1, int p2);
+ method public static inline int hashCode-impl(int p);
+ method public static String toString-impl(int $this);
+ field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
+ }
+
+ public static final class NestedScrollSource.Companion {
+ method public int getDrag-WNlRxjI();
+ method public int getFling-WNlRxjI();
+ method @Deprecated public int getRelocate-WNlRxjI();
+ property public final int Drag;
+ property public final int Fling;
+ property @Deprecated public final int Relocate;
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
index 6f94cbe..72f5d5a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifier.kt
@@ -18,6 +18,7 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
@@ -237,16 +238,39 @@
/**
* Possible sources of scroll events in the [NestedScrollConnection]
*/
-enum class NestedScrollSource {
- /**
- * Dragging via mouse/touch/etc events
- */
- Drag,
+@Suppress("INLINE_CLASS_DEPRECATED", "EXPERIMENTAL_FEATURE_WARNING")
+inline class NestedScrollSource internal constructor(
+ @Suppress("unused") private val value: Int
+) {
+ override fun toString(): String {
+ @Suppress("DEPRECATION")
+ return when (this) {
+ Drag -> "Drag"
+ Fling -> "Fling"
+ @OptIn(ExperimentalComposeUiApi::class)
+ Relocate -> "Relocate"
+ else -> "Invalid"
+ }
+ }
- /**
- * Flinging after the drag has ended with velocity
- */
- Fling
+ companion object {
+ /**
+ * Dragging via mouse/touch/etc events.
+ */
+ val Drag: NestedScrollSource = NestedScrollSource(1)
+
+ /**
+ * Flinging after the drag has ended with velocity.
+ */
+ val Fling: NestedScrollSource = NestedScrollSource(2)
+
+ /**
+ * Relocating when a component asks parents to scroll to bring it into view.
+ */
+ @ExperimentalComposeUiApi
+ @Deprecated("Do not use. Will be removed in the future.")
+ val Relocate: NestedScrollSource = NestedScrollSource(3)
+ }
}
/**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
index 7d7b737..627c392 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
@@ -24,7 +24,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Offset.Companion.Zero
import androidx.compose.ui.input.nestedscroll.NestedScrollDelegatingWrapper
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Drag
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.Relocate
import androidx.compose.ui.node.LayoutNodeWrapper
import androidx.compose.ui.platform.debugInspectorInfo
import kotlin.math.abs
@@ -88,7 +88,8 @@
// TODO(b/187432148): We ideally shouldn't be using internal connection functions. We need to
// build a better system with a more granular API that allows us to send scroll requests to
// specific parents.
- modifier.connection.onPostScroll(Zero, offset, Drag)
+ @Suppress("DEPRECATION")
+ modifier.connection.onPostScroll(Zero, offset, @OptIn(ExperimentalComposeUiApi::class) Relocate)
wrappedBy?.findPreviousNestedScrollWrapper()?.bringIntoView(child)
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
index 92a69d7..9c509bc 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
@@ -202,7 +202,8 @@
val itemIndex = root.foldedChildren.indexOf(node)
if (itemIndex < currentIndex) {
throw IllegalArgumentException(
- "$slotId was already used with subcompose during this measuring pass"
+ "Key $slotId was already used. If you are using LazyColumn/Row please make sure " +
+ "you provide a unique key for each item."
)
}
if (currentIndex != itemIndex) {
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.desktop.kt
index cf41892..b2fe299 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppFrame.desktop.kt
@@ -17,7 +17,7 @@
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.window.MenuBar
+import androidx.compose.ui.window.v1.MenuBar
import java.awt.event.MouseListener
import java.awt.event.MouseMotionListener
import java.awt.image.BufferedImage
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppManager.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppManager.desktop.kt
index 035aff4..2cdac33 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppManager.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppManager.desktop.kt
@@ -15,7 +15,7 @@
*/
package androidx.compose.desktop
-import androidx.compose.ui.window.MenuBar
+import androidx.compose.ui.window.v1.MenuBar
object AppManager {
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.desktop.kt
index ac4cfd6..80252f0 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.desktop.kt
@@ -22,7 +22,7 @@
import androidx.compose.ui.platform.Keyboard
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.window.MenuBar
+import androidx.compose.ui.window.v1.MenuBar
import java.awt.Container
import java.awt.Frame
import java.awt.event.ComponentAdapter
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/DesktopDialog.desktop.kt
similarity index 97%
rename from compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.desktop.kt
rename to compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/DesktopDialog.desktop.kt
index 4a9174a..a16f12a 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/DesktopDialog.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/DesktopDialog.desktop.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.ui.window
+package androidx.compose.ui.window.v1
import androidx.compose.desktop.AppWindow
import androidx.compose.desktop.LocalAppWindow
@@ -22,8 +22,8 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.rememberCompositionContext
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCompositionContext
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import java.awt.image.BufferedImage
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuBar.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuBar.desktop.kt
similarity index 96%
rename from compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuBar.desktop.kt
rename to compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuBar.desktop.kt
index 3672743..8f65fcf 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuBar.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuBar.desktop.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package androidx.compose.ui.window
+package androidx.compose.ui.window.v1
import org.jetbrains.skiko.Library
-import java.awt.event.ActionListener
import java.awt.event.ActionEvent
+import java.awt.event.ActionListener
import javax.swing.JMenu
import javax.swing.JMenuBar
import javax.swing.JMenuItem
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuItem.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuItem.desktop.kt
similarity index 95%
rename from compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuItem.desktop.kt
rename to compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuItem.desktop.kt
index d7c732a..031c03b8 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/MenuItem.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/MenuItem.desktop.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package androidx.compose.ui.window
+package androidx.compose.ui.window.v1
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.nativeKeyCode
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Tray.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/Tray.desktop.kt
similarity index 97%
rename from compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Tray.desktop.kt
rename to compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/Tray.desktop.kt
index 5e51ec8..6affd42 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Tray.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v1/Tray.desktop.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package androidx.compose.ui.window
+package androidx.compose.ui.window.v1
-import java.awt.event.ActionListener
-import java.awt.event.ActionEvent
import java.awt.Image
import java.awt.PopupMenu
import java.awt.SystemTray
import java.awt.TrayIcon
import java.awt.TrayIcon.MessageType
+import java.awt.event.ActionEvent
+import java.awt.event.ActionListener
/**
* Tray is class for working with the system tray.
diff --git a/core/core-google-shortcuts/build.gradle b/core/core-google-shortcuts/build.gradle
index 7f7bf68..ea77000 100644
--- a/core/core-google-shortcuts/build.gradle
+++ b/core/core-google-shortcuts/build.gradle
@@ -32,7 +32,7 @@
}
dependencies {
- api("androidx.core:core:1.6.0-beta01")
+ api(project(":core:core"))
implementation("com.google.firebase:firebase-appindexing:19.2.0")
implementation("com.google.crypto.tink:tink-android:1.5.0")
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 83d6a2d..2cf275c 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1542,7 +1542,6 @@
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
- method @ChecksSdkIntAtLeast(codename="T") public static boolean isAtLeastT();
}
public final class CancellationSignal {
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index ebb5dfb..3934ab1 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -1540,7 +1540,6 @@
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
- method @ChecksSdkIntAtLeast(codename="T") public static boolean isAtLeastT();
}
public final class CancellationSignal {
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 34557d9..46113a6 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1861,7 +1861,6 @@
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
method @ChecksSdkIntAtLeast(codename="S") public static boolean isAtLeastS();
- method @ChecksSdkIntAtLeast(codename="T") public static boolean isAtLeastT();
}
public final class CancellationSignal {
diff --git a/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java b/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java
new file mode 100644
index 0000000..a333e5e
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.os;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link BuildCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BuildCompatTest {
+ @Test
+ public void isAtLeastPreReleaseCodename() {
+ assertTrue(BuildCompat.isAtLeastPreReleaseCodename("S", "S"));
+ assertTrue(BuildCompat.isAtLeastPreReleaseCodename("T", "S"));
+ assertFalse(BuildCompat.isAtLeastPreReleaseCodename("S", "T"));
+
+ assertTrue(BuildCompat.isAtLeastPreReleaseCodename("OMR1", "O"));
+ assertFalse(BuildCompat.isAtLeastPreReleaseCodename("O", "OMR1"));
+
+ assertTrue(BuildCompat.isAtLeastPreReleaseCodename("OMR1", "OMR1"));
+ assertTrue(BuildCompat.isAtLeastPreReleaseCodename("OMR2", "OMR1"));
+ assertFalse(BuildCompat.isAtLeastPreReleaseCodename("OMR1", "OMR2"));
+
+ assertFalse(BuildCompat.isAtLeastPreReleaseCodename("S", "REL"));
+
+ assertFalse(BuildCompat.isAtLeastPreReleaseCodename("RMR1", "REL"));
+ }
+
+}
diff --git a/core/core/src/main/java/androidx/core/os/BuildCompat.java b/core/core/src/main/java/androidx/core/os/BuildCompat.java
index 389622f..7df4319 100644
--- a/core/core/src/main/java/androidx/core/os/BuildCompat.java
+++ b/core/core/src/main/java/androidx/core/os/BuildCompat.java
@@ -20,6 +20,8 @@
import android.os.Build.VERSION;
import androidx.annotation.ChecksSdkIntAtLeast;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
/**
* This class contains additional platform version checking methods for targeting pre-release
@@ -30,6 +32,28 @@
}
/**
+ * Checks if the codename is a matching or higher version than the given build value.
+ * @param codename the requested build codename, e.g. {@code "O"} or {@code "OMR1"}
+ * @param buildCodename the value of {@link VERSION.CODENAME}
+ *
+ * @return {@code true} if APIs from the requested codename are available in the build.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.TESTS)
+ protected static boolean isAtLeastPreReleaseCodename(@NonNull String codename,
+ @NonNull String buildCodename) {
+
+ // Special case "REL", which means the build is not a pre-release build.
+ if ("REL".equals(buildCodename)) {
+ return false;
+ }
+
+ // Otherwise lexically compare them.
+ return codename.compareTo(buildCodename) >= 0;
+ }
+
+ /**
* Checks if the device is running on the Android N release or newer.
*
* @return {@code true} if N APIs are available for use
@@ -139,21 +163,6 @@
*/
@ChecksSdkIntAtLeast(codename = "S")
public static boolean isAtLeastS() {
- return VERSION.CODENAME.equals("S") || VERSION.CODENAME.equals("T");
- }
-
- /**
- * Checks if the device is running on a pre-release version of Android T or a release version of
- * Android T or newer.
- * <p>
- * <strong>Note:</strong> When Android T is finalized for release, this method will be
- * deprecated and all calls should be replaced with {@code Build.VERSION.SDK_INT >=
- * Build.VERSION_CODES.T}.
- *
- * @return {@code true} if T APIs are available for use, {@code false} otherwise
- */
- @ChecksSdkIntAtLeast(codename = "T")
- public static boolean isAtLeastT() {
- return VERSION.CODENAME.equals("T");
+ return isAtLeastPreReleaseCodename("S", VERSION.CODENAME);
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 767eb12..5493c52 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -18,7 +18,7 @@
autoValue = "1.6.3"
dexmaker = "2.25.0"
espresso = "3.3.0"
-hilt = "2.35"
+hilt = "2.36"
incap = "0.2"
kotlin = "1.5.10"
kotlinCompileTesting = "1.4.0"
diff --git a/jetifier/jetifier/processor/build.gradle b/jetifier/jetifier/processor/build.gradle
index 39eeeaa..71565e7 100644
--- a/jetifier/jetifier/processor/build.gradle
+++ b/jetifier/jetifier/processor/build.gradle
@@ -32,8 +32,11 @@
api("org.ow2.asm:asm-commons:8.0.1")
api("org.jdom:jdom2:2.0.6")
api(KOTLIN_STDLIB)
+ api(KOTLIN_METADATA_JVM)
testImplementation("junit:junit:4.12")
testImplementation(TRUTH)
+ testImplementation(KOTLIN_REFLECT)
+ testImplementation(KOTLIN_COMPILE_TESTING)
}
androidx {
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
index 7c32c73..2fb2197 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
@@ -24,10 +24,11 @@
import com.android.tools.build.jetifier.processor.archive.ArchiveFile
import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
import com.android.tools.build.jetifier.processor.archive.FileSearchResult
-import com.android.tools.build.jetifier.processor.com.android.tools.build.jetifier.processor.transform.java.JavaTransformer
import com.android.tools.build.jetifier.processor.transform.TransformationContext
import com.android.tools.build.jetifier.processor.transform.Transformer
import com.android.tools.build.jetifier.processor.transform.bytecode.ByteCodeTransformer
+import com.android.tools.build.jetifier.processor.transform.java.JavaTransformer
+import com.android.tools.build.jetifier.processor.transform.metainf.KotlinModuleTransformer
import com.android.tools.build.jetifier.processor.transform.metainf.MetaInfTransformer
import com.android.tools.build.jetifier.processor.transform.pom.PomDocument
import com.android.tools.build.jetifier.processor.transform.pom.PomScanner
@@ -59,7 +60,8 @@
ByteCodeTransformer(context),
XmlResourcesTransformer(context),
ProGuardTransformer(context),
- JavaTransformer(context)
+ JavaTransformer(context),
+ KotlinModuleTransformer(context)
)
/**
@@ -70,7 +72,8 @@
ByteCodeTransformer(context),
XmlResourcesTransformer(context),
ProGuardTransformer(context),
- MetaInfTransformer(context)
+ MetaInfTransformer(context),
+ KotlinModuleTransformer(context)
)
/**
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
index 9b5f12e2..1546bed 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
@@ -119,4 +119,8 @@
)
}
}
+
+ fun reportUnreadableKotlinModule(tag: String, filePath: Path) {
+ Log.e(tag, "Unreadable kotlin module medata file: %s", filePath)
+ }
}
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
index a03c041..6427c83 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
@@ -21,7 +21,9 @@
import com.android.tools.build.jetifier.core.utils.Log
import com.android.tools.build.jetifier.processor.transform.TransformationContext
import com.android.tools.build.jetifier.processor.transform.bytecode.asm.CustomRemapper
+import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.Opcodes
import org.objectweb.asm.commons.ClassRemapper
import java.nio.file.Path
@@ -49,12 +51,30 @@
)
}
- private val typesMap = context.config.typesMap
-
var changesDone = false
private set
- val classRemapper = ClassRemapper(visitor, CustomRemapper(this))
+ val remapper = CustomRemapper(this)
+ val classRemapper = object : ClassRemapper(visitor, remapper) {
+ override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
+ val annotationVisitor = super.visitAnnotation(descriptor, visible)
+ return if (descriptor == "Lkotlin/Metadata;")
+ KotlinMetadataVisitor(annotationVisitor) else annotationVisitor
+ }
+ }
+
+ inner class KotlinMetadataVisitor(
+ visitor: AnnotationVisitor
+ ) : AnnotationVisitor(Opcodes.ASM8, visitor) {
+ init {
+ remapper.onKotlinAnnotationVisitStart()
+ }
+
+ override fun visitEnd() {
+ remapper.onKotlinAnnotationVisitEnd()
+ super.visitEnd()
+ }
+ }
override fun rewriteType(type: JavaType): JavaType {
val result = context.typeRewriter.rewriteType(type)
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
index 7e34af8..3d0ed43 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
@@ -24,6 +24,17 @@
* Extends [Remapper] to allow further customizations.
*/
class CustomRemapper(private val remapper: CoreRemapper) : Remapper() {
+ private var inKotlinMetadata = false
+
+ fun onKotlinAnnotationVisitStart() {
+ require(!inKotlinMetadata)
+ inKotlinMetadata = true
+ }
+
+ fun onKotlinAnnotationVisitEnd() {
+ require(inKotlinMetadata)
+ inKotlinMetadata = false
+ }
override fun map(typeName: String): String {
return remapper.rewriteType(JavaType(typeName)).fullName
@@ -72,6 +83,10 @@
return "L" + mapPoolReferenceType(typeDeclaration) + ";"
}
+ if (inKotlinMetadata) {
+ return rewriteIfMethodSignature(stringVal, ::mapPoolReferenceType)
+ ?: remapper.rewriteString(stringVal)
+ }
return remapper.rewriteString(stringVal)
}
}
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/KotlinMetadataUtil.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/KotlinMetadataUtil.kt
new file mode 100644
index 0000000..5e4ef1f
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/KotlinMetadataUtil.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.build.jetifier.processor.transform.bytecode.asm
+
+/**
+ * If the given string [signature] is in format `(ILFoo;LBar;)LResult;` it maps
+ * referenced types via [mapDeclaration]. If the given string doesn't follow this pattern,
+ * `null` is returned.
+ *
+ * Such strings occur in kotlin's [Metadata] annotation for property getters and setters and
+ * data classes.
+ */
+internal fun rewriteIfMethodSignature(
+ signature: String,
+ mapDeclaration: (String) -> String
+): String? {
+ val mapType = { declaration: String ->
+ val type = if (isArrayDeclaration(declaration)) declaration.trim('[') else declaration
+ val mapped = if (isTypeDeclaration(type)) {
+ "L${mapDeclaration(type.substring(1, type.length - 1))};"
+ } else {
+ type
+ }
+ "${"[".repeat(declaration.length - type.length)}$mapped"
+ }
+ // trying to match strings in the format `(ILFoo;LBar;)LResult;`
+ if (!signature.startsWith('(')) return null
+ val index = signature.indexOf(')')
+ if (index == -1) return null
+ val params = splitParameters(signature.substring(1, index)).joinToString("") {
+ mapType(it)
+ }
+ val returnType = signature.substring(index + 1)
+ return "($params)${mapType(returnType)}"
+}
+
+private fun splitParameters(parameters: String): List<String> {
+ val result = mutableListOf<String>()
+ val currentParam = StringBuilder(parameters.length)
+ var inClassName = false
+ for (c in parameters) {
+ currentParam.append(c)
+ inClassName = if (inClassName) c != ';' else c == 'L'
+ // add a parameter if we're no longer in class and not in array start
+ if (!inClassName && c != '[') {
+ result.add(currentParam.toString())
+ currentParam.clear()
+ }
+ }
+ return result
+}
+
+private fun isTypeDeclaration(string: String) = string.startsWith("L") && string.endsWith(";")
+private fun isArrayDeclaration(string: String) = string.startsWith("[")
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/java/JavaTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/java/JavaTransformer.kt
index 69f19fa..e8a6b6d 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/java/JavaTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/java/JavaTransformer.kt
@@ -1,4 +1,4 @@
-package com.android.tools.build.jetifier.processor.com.android.tools.build.jetifier.processor.transform.java
+package com.android.tools.build.jetifier.processor.transform.java
import com.android.tools.build.jetifier.processor.archive.ArchiveFile
import com.android.tools.build.jetifier.processor.transform.TransformationContext
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/KotlinModuleTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/KotlinModuleTransformer.kt
new file mode 100644
index 0000000..5632348
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/KotlinModuleTransformer.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.build.jetifier.processor.transform.metainf
+
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.PackageName
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
+import kotlinx.metadata.jvm.KmPackageParts
+import kotlinx.metadata.jvm.KotlinModuleMetadata
+
+class KotlinModuleTransformer internal constructor(
+ private val context: TransformationContext
+) : Transformer {
+ override fun canTransform(file: ArchiveFile): Boolean {
+ return file.relativePath.toString().startsWith(META_INF_DIR) &&
+ file.fileName.endsWith(KOTLIN_MODULE_SUFFIX) &&
+ !file.isSingleFile
+ }
+
+ override fun runTransform(file: ArchiveFile) {
+ val module = KotlinModuleMetadata.read(file.data)?.toKmModule()
+ ?: return context.reportUnreadableKotlinModule(TAG, file.relativePath)
+ val newPackageParts = module.packageParts.map { (packageName, packageParts) ->
+ val pckg = PackageName.fromDotVersion(packageName)
+ val result = context.config.packageMap.getPackageFor(pckg)
+ val newPackageName = result?.toDotNotation()
+ if (newPackageName == null && context.config.isEligibleForRewrite(pckg)) {
+ context.reportNoPackageMappingFoundFailure(TAG, packageName, file.relativePath)
+ }
+
+ val newSingleFacades = packageParts.fileFacades.map(this::mapType).toMutableList()
+ val newMultiFacades = packageParts.multiFileClassParts.map { (key, singleFile) ->
+ mapType(key) to mapType(singleFile)
+ }.toMap().toMutableMap()
+
+ val newPackageParts = KmPackageParts(
+ newSingleFacades,
+ newMultiFacades,
+ )
+ (newPackageName ?: packageName) to newPackageParts
+ }.toMap()
+ module.packageParts.clear()
+ module.packageParts.putAll(newPackageParts)
+ file.setNewData(KotlinModuleMetadata.Writer().apply(module::accept).write().bytes)
+ }
+
+ private fun mapType(packageName: String): String {
+ val javaType = JavaType(packageName)
+ val newType = context.typeRewriter.rewriteType(javaType)
+ if (newType == null) {
+ context.reportNoMappingFoundFailure(TAG, javaType)
+ }
+ return newType?.fullName ?: packageName
+ }
+}
+
+private const val META_INF_DIR = "META-INF"
+private const val KOTLIN_MODULE_SUFFIX = ".kotlin_module"
+private const val TAG = "KotlinModuleTransformer"
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestDataClass.kt b/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestDataClass.kt
new file mode 100644
index 0000000..6e8dbde
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestDataClass.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fake.lib
+
+// used in KotlinByteCodeTransformerTest as a resource
+@Suppress("unused")
+data class TestDataClass(val property: String, var children: TestDataClass? = null)
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestProperty.kt b/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestProperty.kt
new file mode 100644
index 0000000..3a86d06
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/androidx/fake/lib/TestProperty.kt
@@ -0,0 +1,7 @@
+package androidx.fake.lib
+
+// used in KotlinByteCodeTransformerTest as a resource
+@Suppress("unused")
+class TestProperty {
+ var property: TestProperty? = null
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinModuleRewriteTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinModuleRewriteTest.kt
new file mode 100644
index 0000000..a0eb8a5
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinModuleRewriteTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.build.jetifier.processor.transform
+
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
+import com.google.common.truth.Truth.assertThat
+import com.tschuchort.compiletesting.KotlinCompilation
+import com.tschuchort.compiletesting.SourceFile
+import com.tschuchort.compiletesting.SourceFile.Companion.kotlin
+import org.intellij.lang.annotations.Language
+import org.junit.Test
+import java.io.File
+import java.nio.file.Files
+
+/**
+ * Test that verifies that *.kotlin_module file is correctly rewritten.
+ */
+class KotlinModuleRewriteTest {
+ @Test
+ fun topLevel() {
+ val jars = originalJar(
+ kotlin(
+ "toplevel.kt",
+ """
+ package androidx.fake.lib;
+
+ fun topLevel(): Int = 10
+ """
+ )
+ )
+ testKotlinCompilation(
+ jars,
+ """
+ import android.old.fake.topLevel
+
+ fun hello() {
+ println(topLevel())
+ }
+ """
+ )
+ }
+
+ @Test
+ fun multifile() {
+ val originalJar = originalJar(
+ kotlin(
+ "mutlifile1.kt",
+ """
+ @file:JvmName("Single")
+ @file:JvmMultifileClass
+
+ package androidx.fake.lib
+
+ fun stringMethod(): String = "one"
+ """
+ ),
+ kotlin(
+ "mutlifile2.kt",
+ """
+ @file:JvmName("Single")
+ @file:JvmMultifileClass
+
+ package androidx.fake.lib
+
+ fun intMethod(): Int = 5
+ """
+ )
+ )
+ testKotlinCompilation(
+ originalJar,
+ """
+ import android.old.fake.stringMethod
+ import android.old.fake.intMethod
+
+ fun hello() {
+ println(stringMethod())
+ println(intMethod())
+ }
+ """
+ )
+ }
+}
+
+private fun testKotlinCompilation(originalJar: File, @Language("kotlin") content: String) {
+ val processor = Processor.createProcessor4(KotlinTestConfig)
+ val output = Files.createTempFile("out", ".jar").toFile()
+ processor.transform2(setOf(FileMapping(originalJar, output)))
+
+ val kotlinCompilation = KotlinCompilation()
+ kotlinCompilation.sources = listOf(kotlin("test.kt", content))
+ kotlinCompilation.classpaths = listOf(output)
+ assertThat(kotlinCompilation.compile().exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
+}
+
+fun originalJar(vararg sources: SourceFile): File {
+ val originalJar = Files.createTempFile("original", ".jar").toFile()
+ val kotlinCompilation = KotlinCompilation()
+ kotlinCompilation.kotlincArguments = listOf("-d", originalJar.absolutePath)
+ kotlinCompilation.sources = sources.toList()
+ kotlinCompilation.compile()
+ assertThat(kotlinCompilation.compile().exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
+ return originalJar
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinTestConfig.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinTestConfig.kt
new file mode 100644
index 0000000..9d31e3d
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/KotlinTestConfig.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.build.jetifier.processor.transform
+
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+
+val KotlinTestConfig = Config.fromOptional(
+ restrictToPackagePrefixes = setOf("androidx/fake"),
+ reversedRestrictToPackagesPrefixes = setOf("android/old"),
+ rulesMap = RewriteRulesMap(
+ RewriteRule("androidx/fake/lib/(.*)", "android/old/fake/{0}")
+ ),
+
+ packageMap = PackageMap(
+ listOf(PackageMap.PackageRule("androidx/fake/lib", "android/old/fake"))
+ )
+)
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/KotlinByteCodeTransformerTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/KotlinByteCodeTransformerTest.kt
new file mode 100644
index 0000000..2a76b4a
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/KotlinByteCodeTransformerTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.build.jetifier.processor.transform.bytecode
+
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.KotlinTestConfig
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import java.io.File
+import java.net.URLClassLoader
+import java.nio.file.Paths
+import kotlin.reflect.KClass
+import kotlin.reflect.full.memberProperties
+import kotlin.reflect.full.primaryConstructor
+
+class KotlinByteCodeTransformerTest {
+ @Test
+ fun propertyTest() {
+ val testClass = transformAndLoadClass("TestProperty")
+ val testInstance = testClass.primaryConstructor!!.call()
+
+ assertThat(testClass.memberProperties).hasSize(1)
+ val property = testClass.memberProperties.first()
+ // this call will fail if metadata for this property is incorrect
+ property.call(testInstance)
+ }
+
+ @Test
+ fun dataClassTest() {
+ val testClass = transformAndLoadClass("TestDataClass")
+ // this call will fail if metadata for this data class is incorrect
+ testClass.primaryConstructor!!.call("a", null)
+ }
+}
+
+private fun transformAndLoadClass(className: String): KClass<*> {
+ val inputClassPath = "/androidx/fake/lib/$className.class"
+ val inputFile = File(KotlinByteCodeTransformerTest::class.java.getResource(inputClassPath).file)
+ val archiveFile = ArchiveFile(
+ Paths.get("androidx/fake/lib", "$className.class"),
+ inputFile.readBytes()
+ )
+
+ val context = TransformationContext(config = KotlinTestConfig)
+ val transformer = ByteCodeTransformer(context)
+ transformer.runTransform(archiveFile)
+ val bytes = archiveFile.data
+
+ val classLoader = object : URLClassLoader(emptyArray()) {
+ override fun findClass(name: String): Class<*> =
+ if (name == "android.old.fake.$className") defineClass(name, bytes, 0, bytes.size)
+ else super.findClass(name)
+ }
+ return classLoader.loadClass("android.old.fake.$className").kotlin
+}
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/SignatureMappingTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/SignatureMappingTest.kt
new file mode 100644
index 0000000..6722eeb
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/SignatureMappingTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.build.jetifier.processor.transform.bytecode
+
+import com.android.tools.build.jetifier.processor.transform.bytecode.asm.rewriteIfMethodSignature
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class SignatureMappingTest {
+ @Test
+ fun remapSignatureString_inKotlinMetadata() {
+ val typesMap = mapOf(
+ "android/old/Foo" to "androidx/fancy/Foo",
+ "android/old/Bar" to "androidx/fancy/Bar",
+ )
+
+ fun assertRewrite(oldSignature: String, newSignature: String) =
+ assertThat(rewriteIfMethodSignature(oldSignature) { type -> typesMap[type] ?: type })
+ .isEqualTo(newSignature)
+
+ assertRewrite("()V", "()V")
+ assertRewrite("(Landroid/old/Foo;)V", "(Landroidx/fancy/Foo;)V")
+ assertRewrite("()Landroid/old/Foo;", "()Landroidx/fancy/Foo;")
+
+ assertRewrite("(Landroid/unmapped/Foo;)V", "(Landroid/unmapped/Foo;)V")
+ assertRewrite(
+ "(Landroid/old/Bar;JLandroid/unmapped/Foo;)V",
+ "(Landroidx/fancy/Bar;JLandroid/unmapped/Foo;)V"
+ )
+ assertRewrite(
+ "(JLandroid/unmapped/Foo;I[[I)Landroid/old/Bar;",
+ "(JLandroid/unmapped/Foo;I[[I)Landroidx/fancy/Bar;"
+ )
+ assertRewrite("([Landroid/old/Foo;)V", "([Landroidx/fancy/Foo;)V")
+ assertRewrite("()[[Landroid/old/Foo;", "()[[Landroidx/fancy/Foo;")
+ }
+}
\ No newline at end of file
diff --git a/lint-checks/integration-tests/expected-lint-results.xml b/lint-checks/integration-tests/expected-lint-results.xml
index 6328019..ce6e261 100644
--- a/lint-checks/integration-tests/expected-lint-results.xml
+++ b/lint-checks/integration-tests/expected-lint-results.xml
@@ -148,7 +148,7 @@
<issue
id="ClassVerificationFailure"
severity="Error"
- message="This call references a method added in API level 19; however, the containing class androidx.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification."
+ message="This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification."
category="Correctness"
priority="5"
summary="Even in cases where references to new APIs are gated on SDK_INT checks, run-time class verification will still fail on references to APIs that may not be available at run time, including platform APIs introduced after a library's minSdkVersion."
@@ -164,7 +164,7 @@
<issue
id="ClassVerificationFailure"
severity="Error"
- message="This call references a method added in API level 19; however, the containing class androidx.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification."
+ message="This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification."
category="Correctness"
priority="5"
summary="Even in cases where references to new APIs are gated on SDK_INT checks, run-time class verification will still fail on references to APIs that may not be available at run time, including platform APIs introduced after a library's minSdkVersion."
diff --git a/lint-checks/integration-tests/src/main/AndroidManifest.xml b/lint-checks/integration-tests/src/main/AndroidManifest.xml
index 1a39913..98ea13f 100644
--- a/lint-checks/integration-tests/src/main/AndroidManifest.xml
+++ b/lint-checks/integration-tests/src/main/AndroidManifest.xml
@@ -13,4 +13,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<manifest package="androidx.lint.integration.tests" />
+<manifest package="androidx.lint.integration.tests"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <application>
+ <!-- Application-level metadata is not allowed. -->
+ <meta-data android:name="name" android:value="value" />
+
+ <service android:name="androidx.core.app.JobIntentService">
+ <!-- Service-level metadata is allowed. -->
+ <meta-data android:name="name" android:value="value" />
+ </service>
+ </application>
+</manifest>
diff --git a/lint-checks/integration-tests/src/main/java/androidx/ConcurrentHashMapUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/ConcurrentHashMapUsageJava.java
new file mode 100644
index 0000000..db30af2
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/ConcurrentHashMapUsageJava.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx;
+
+import androidx.annotation.NonNull;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@SuppressWarnings("unused")
+public class ConcurrentHashMapUsageJava {
+
+ private final ConcurrentHashMap<?, ?> mMap = new ConcurrentHashMap<>();
+
+ @NonNull
+ public <V, K> Map<V, K> createMap() {
+ return new ConcurrentHashMap<>();
+ }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageJava.java
new file mode 100644
index 0000000..964459f
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageJava.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx;
+
+import androidx.annotation.Keep;
+
+@Keep
+public class KeepAnnotationUsageJava {
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageJava.java
new file mode 100644
index 0000000..04aafe1
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageJava.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+@SuppressWarnings("unused")
+public class ParcelableUsageJava implements Parcelable {
+
+ protected ParcelableUsageJava(@NonNull Parcel in) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ }
+
+ public static final Creator<ParcelableUsageJava> CREATOR = new Creator<ParcelableUsageJava>() {
+ @Override
+ public ParcelableUsageJava createFromParcel(Parcel in) {
+ return new ParcelableUsageJava(in);
+ }
+
+ @Override
+ public ParcelableUsageJava[] newArray(int size) {
+ return new ParcelableUsageJava[size];
+ }
+ };
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/SynchronizedMethodJava.java b/lint-checks/integration-tests/src/main/java/androidx/SynchronizedMethodJava.java
new file mode 100644
index 0000000..73e6365
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/SynchronizedMethodJava.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx;
+
+@SuppressWarnings("unused")
+public class SynchronizedMethodJava {
+
+ public synchronized void someMethod() {
+ }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageJava.java
new file mode 100644
index 0000000..ff0d71d
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageJava.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx;
+
+import android.annotation.TargetApi;
+
+@SuppressWarnings("unused")
+@TargetApi(29)
+public class TargetApiUsageJava {
+
+ @TargetApi(30)
+ public void someMethod() {
+ }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreator.java b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreator.java
new file mode 100644
index 0000000..8790171
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreator.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sample.core.app;
+
+import static android.os.Build.VERSION.SDK_INT;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * The goal here is to get common (and correct) behavior around Activity recreation for all API
+ * versions up until P, where the behavior was specified to be useful and implemented to match the
+ * specification. On API 26 and 27, recreate() doesn't actually recreate the Activity if it's
+ * not in the foreground; it will be recreated when the user next interacts with it. This has a few
+ * undesirable consequences:
+ *
+ * <p>1. It's impossible to recreate multiple activities at once, which means that activities in the
+ * background will observe the new configuration before they're recreated. If we keep them on the
+ * old configuration, we have two conflicting configurations active in the app, which leads to
+ * logging skew.
+ *
+ * <p>2. Recreation occurs in the critical path of user interaction - re-inflating a bunch of views
+ * isn't free, and we'd rather do it when we're in the background than when the user is staring at
+ * the screen waiting to see us.
+ *
+ * <p>On API < 26, recreate() was implemented with a single call to a private method on
+ * ActivityThread. That method still exists in 26 and 27, so we can use reflection to call it and
+ * get the exact same behavior as < 26. However, that behavior has problems itself. When
+ * an Activity in the background is recreated, it goes through: destroy -> create -> start ->
+ * resume -> pause and doesn't stop. This is a violation of the contract for onStart/onStop,
+ * but that might be palatable if it didn't also have the effect of preventing new configurations
+ * from being applied - since the Activity doesn't go through onStop, our tracking of whether
+ * our app is visible thinks we're always visible, and thus can't do another recreation later.
+ *
+ * <p>The fix for this is to add the missing onStop() call, by using reflection to call into
+ * ActivityThread.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+@SuppressWarnings({"PrivateApi", "JavaReflectionMemberAccess", "unused"})
+final class ActivityRecreator {
+ private ActivityRecreator() {}
+
+ private static final String LOG_TAG = "ActivityRecreator";
+
+ // Activity.mMainThread
+ protected static final Field mainThreadField;
+ // Activity.mToken. This object is an identifier that is the same between multiple instances of
+ //the same underlying Activity.
+ protected static final Field tokenField;
+ // On API 25, a third param was added to performStopActivity
+ protected static final Method performStopActivity3ParamsMethod;
+ // Before API 25, performStopActivity had two params
+ protected static final Method performStopActivity2ParamsMethod;
+ // ActivityThread.requestRelaunchActivity
+ protected static final Method requestRelaunchActivityMethod;
+
+ private static final Handler mainHandler = new Handler(Looper.getMainLooper());
+
+ static {
+ Class<?> activityThreadClass = getActivityThreadClass();
+ mainThreadField = getMainThreadField();
+ tokenField = getTokenField();
+ performStopActivity3ParamsMethod = getPerformStopActivity3Params(activityThreadClass);
+ performStopActivity2ParamsMethod = getPerformStopActivity2Params(activityThreadClass);
+ requestRelaunchActivityMethod = getRequestRelaunchActivityMethod(activityThreadClass);
+ }
+
+ /**
+ * Equivalent to {@link Activity#recreate}, but working around a number of platform bugs.
+ *
+ * @return true if a recreate() task was successfully scheduled.
+ */
+ static boolean recreate(@NonNull final Activity activity) {
+ // On Android O and later we can rely on the platform recreate()
+ if (SDK_INT >= 28) {
+ activity.recreate();
+ return true;
+ }
+
+ // API 26 needs this workaround but it's not possible because our reflective lookup failed.
+ if (needsRelaunchCall() && requestRelaunchActivityMethod == null) {
+ return false;
+ }
+ // All versions of android so far need this workaround, but it's not possible because our
+ // reflective lookup failed.
+ if (performStopActivity2ParamsMethod == null && performStopActivity3ParamsMethod == null) {
+ return false;
+ }
+ try {
+ final Object token = tokenField.get(activity);
+ if (token == null) {
+ return false;
+ }
+ Object activityThread = mainThreadField.get(activity);
+ if (activityThread == null) {
+ return false;
+ }
+
+ final Application application = activity.getApplication();
+ final LifecycleCheckCallbacks callbacks = new LifecycleCheckCallbacks(activity);
+ application.registerActivityLifecycleCallbacks(callbacks);
+
+ /*
+ * Runnables scheduled before/after recreate() will run before and after the Runnables
+ * scheduled by recreate(). This allows us to bound the time where mActivity lifecycle
+ * events that could be caused by recreate() run - that way we can detect onPause()
+ * from the new Activity instance, and schedule onStop to run immediately after it.
+ */
+ mainHandler.post(() -> callbacks.currentlyRecreatingToken = token);
+
+ try {
+ if (needsRelaunchCall()) {
+ requestRelaunchActivityMethod.invoke(activityThread,
+ token, null, null, 0, false, null, null, false, false);
+ } else {
+ activity.recreate();
+ }
+ return true;
+ } finally {
+ mainHandler.post(() -> {
+ // Since we're calling hidden API, it's entirely possible for it to
+ // simply do nothing;
+ // if that's the case, make sure to unregister so we don't leak memory
+ // waiting for an event that will never happen.
+ application.unregisterActivityLifecycleCallbacks(callbacks);
+ });
+ }
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ private static final class LifecycleCheckCallbacks implements ActivityLifecycleCallbacks {
+ Object currentlyRecreatingToken;
+
+ private Activity mActivity;
+ private final int mRecreatingHashCode;
+
+ // Whether the activity on which recreate() was called went through onStart after
+ // recreate() was called (and thus the callback was registered).
+ private boolean mStarted = false;
+
+ // Whether the activity on which recreate() was called went through onDestroy after
+ // recreate() was called. This means we successfully initiated a recreate().
+ private boolean mDestroyed = false;
+
+ // Whether we'll force the activity on which recreate() was called to go through an
+ // onStop()
+ private boolean mStopQueued = false;
+
+ LifecycleCheckCallbacks(@NonNull Activity aboutToRecreate) {
+ mActivity = aboutToRecreate;
+ mRecreatingHashCode = mActivity.hashCode();
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ // If we see a start call on the original mActivity instance, then the mActivity
+ // starting event executed between our call to recreate() and the actual
+ // recreation of the mActivity. In that case, a stop() call should not be scheduled.
+ if (mActivity == activity) {
+ mStarted = true;
+ }
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ if (mDestroyed // Original mActivity must be gone
+ && !mStopQueued // Don't schedule stop twice for one recreate() call
+ && !mStarted
+ // Don't schedule stop if the original instance starting raced with recreate()
+ && queueOnStopIfNecessary(
+ currentlyRecreatingToken, mRecreatingHashCode, activity)) {
+ mStopQueued = true;
+ // Don't retain this object longer than necessary
+ currentlyRecreatingToken = null;
+ }
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ // Not possible to get a start/stop pair in the same UI thread loop
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ if (mActivity == activity) {
+ // Once the original mActivity instance is mDestroyed, we don't need to compare to
+ // it any
+ // longer, and we don't want to retain it any longer than necessary.
+ mActivity = null;
+ mDestroyed = true;
+ }
+ }
+ }
+
+ /**
+ * Returns true if a stop call was scheduled successfully
+ */
+ protected static boolean queueOnStopIfNecessary(
+ Object currentlyRecreatingToken, int currentlyRecreatingHashCode, Activity activity) {
+ try {
+ final Object token = tokenField.get(activity);
+ if (token != currentlyRecreatingToken
+ || activity.hashCode() != currentlyRecreatingHashCode) {
+ // We're looking at a different activity, don't try to make it stop! Note that
+ // tokens are reused on SDK 21-23 but Activity objects (and thus hashCode, in
+ // all likelihood) are not, so we need to check both.
+ return false;
+ }
+ final Object activityThread = mainThreadField.get(activity);
+ // These operations are posted at the front of the queue, so that operations
+ // scheduled from onCreate, onStart etc run after the onStop call - this should
+ // cause any redundant loads to be immediately cancelled.
+ mainHandler.postAtFrontOfQueue(() -> {
+ try {
+ if (performStopActivity3ParamsMethod != null) {
+ performStopActivity3ParamsMethod.invoke(activityThread,
+ token, false, "AppCompat recreation");
+ } else {
+ performStopActivity2ParamsMethod.invoke(activityThread,
+ token, false);
+ }
+ } catch (RuntimeException e) {
+ // If an Activity throws from onStop, don't swallow it
+ if (e.getClass() == RuntimeException.class
+ && e.getMessage() != null
+ && e.getMessage().startsWith("Unable to stop")) {
+ throw e;
+ }
+ // Otherwise just swallow it - we're calling random private methods,
+ // there's no guarantee on how they'll behave.
+ } catch (Throwable t) {
+ Log.e(LOG_TAG, "Exception while invoking performStopActivity", t);
+ }
+ });
+ return true;
+ } catch (Throwable t) {
+ Log.e(LOG_TAG, "Exception while fetching field values", t);
+ return false;
+ }
+ }
+
+ private static Method getPerformStopActivity3Params(Class<?> activityThreadClass) {
+ if (activityThreadClass == null) {
+ return null;
+ }
+ try {
+ Method performStop = activityThreadClass.getDeclaredMethod("performStopActivity",
+ IBinder.class, boolean.class, String.class);
+ performStop.setAccessible(true);
+ return performStop;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Method getPerformStopActivity2Params(Class<?> activityThreadClass) {
+ if (activityThreadClass == null) {
+ return null;
+ }
+ try {
+ Method performStop = activityThreadClass.getDeclaredMethod("performStopActivity",
+ IBinder.class, boolean.class);
+ performStop.setAccessible(true);
+ return performStop;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static boolean needsRelaunchCall() {
+ return SDK_INT == 26 || SDK_INT == 27;
+ }
+
+ private static Method getRequestRelaunchActivityMethod(Class<?> activityThreadClass) {
+ if (!needsRelaunchCall() || activityThreadClass == null) {
+ return null;
+ }
+ try {
+ Method relaunch = activityThreadClass.getDeclaredMethod(
+ "requestRelaunchActivity",
+ IBinder.class,
+ List.class,
+ List.class,
+ int.class,
+ boolean.class,
+ Configuration.class,
+ Configuration.class,
+ boolean.class,
+ boolean.class);
+ relaunch.setAccessible(true);
+ return relaunch;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Field getMainThreadField() {
+ try {
+ Field mainThreadField = Activity.class.getDeclaredField("mMainThread");
+ mainThreadField.setAccessible(true);
+ return mainThreadField;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Field getTokenField() {
+ try {
+ Field tokenField = Activity.class.getDeclaredField("mToken");
+ tokenField.setAccessible(true);
+ return tokenField;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Class<?> getActivityThreadClass() {
+ try {
+ return Class.forName("android.app.ActivityThread");
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorChecked.java b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorChecked.java
new file mode 100644
index 0000000..0523d2d
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorChecked.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sample.core.app;
+
+import static android.os.Build.VERSION.SDK_INT;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.annotation.ChecksSdkIntAtLeast;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * The goal here is to get common (and correct) behavior around Activity recreation for all API
+ * versions up until P, where the behavior was specified to be useful and implemented to match the
+ * specification. On API 26 and 27, recreate() doesn't actually recreate the Activity if it's
+ * not in the foreground; it will be recreated when the user next interacts with it. This has a few
+ * undesirable consequences:
+ *
+ * <p>1. It's impossible to recreate multiple activities at once, which means that activities in the
+ * background will observe the new configuration before they're recreated. If we keep them on the
+ * old configuration, we have two conflicting configurations active in the app, which leads to
+ * logging skew.
+ *
+ * <p>2. Recreation occurs in the critical path of user interaction - re-inflating a bunch of views
+ * isn't free, and we'd rather do it when we're in the background than when the user is staring at
+ * the screen waiting to see us.
+ *
+ * <p>On API < 26, recreate() was implemented with a single call to a private method on
+ * ActivityThread. That method still exists in 26 and 27, so we can use reflection to call it and
+ * get the exact same behavior as < 26. However, that behavior has problems itself. When
+ * an Activity in the background is recreated, it goes through: destroy -> create -> start ->
+ * resume -> pause and doesn't stop. This is a violation of the contract for onStart/onStop,
+ * but that might be palatable if it didn't also have the effect of preventing new configurations
+ * from being applied - since the Activity doesn't go through onStop, our tracking of whether
+ * our app is visible thinks we're always visible, and thus can't do another recreation later.
+ *
+ * <p>The fix for this is to add the missing onStop() call, by using reflection to call into
+ * ActivityThread.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+@SuppressWarnings({"PrivateApi", "JavaReflectionMemberAccess", "unused"})
+final class ActivityRecreatorChecked {
+ private ActivityRecreatorChecked() {}
+
+ private static final String LOG_TAG = "ActivityRecreatorChecked";
+
+ // Activity.mMainThread
+ protected static final Field mainThreadField;
+ // Activity.mToken. This object is an identifier that is the same between multiple instances of
+ //the same underlying Activity.
+ protected static final Field tokenField;
+ // On API 25, a third param was added to performStopActivity
+ protected static final Method performStopActivity3ParamsMethod;
+ // Before API 25, performStopActivity had two params
+ protected static final Method performStopActivity2ParamsMethod;
+ // ActivityThread.requestRelaunchActivity
+ protected static final Method requestRelaunchActivityMethod;
+
+ private static final Handler mainHandler = new Handler(Looper.getMainLooper());
+
+ static {
+ Class<?> activityThreadClass = getActivityThreadClass();
+ mainThreadField = getMainThreadField();
+ tokenField = getTokenField();
+ performStopActivity3ParamsMethod = getPerformStopActivity3Params(activityThreadClass);
+ performStopActivity2ParamsMethod = getPerformStopActivity2Params(activityThreadClass);
+ requestRelaunchActivityMethod = getRequestRelaunchActivityMethod(activityThreadClass);
+ }
+
+ /**
+ * Equivalent to {@link Activity#recreate}, but working around a number of platform bugs.
+ *
+ * @return true if a recreate() task was successfully scheduled.
+ */
+ static boolean recreate(@NonNull final Activity activity) {
+ // On Android O and later we can rely on the platform recreate()
+ if (SDK_INT >= 28) {
+ activity.recreate();
+ return true;
+ }
+
+ // API 26 needs this workaround but it's not possible because our reflective lookup failed.
+ if (needsRelaunchCall() && requestRelaunchActivityMethod == null) {
+ return false;
+ }
+ // All versions of android so far need this workaround, but it's not possible because our
+ // reflective lookup failed.
+ if (performStopActivity2ParamsMethod == null && performStopActivity3ParamsMethod == null) {
+ return false;
+ }
+ try {
+ final Object token = tokenField.get(activity);
+ if (token == null) {
+ return false;
+ }
+ Object activityThread = mainThreadField.get(activity);
+ if (activityThread == null) {
+ return false;
+ }
+
+ final Application application = activity.getApplication();
+ final LifecycleCheckCallbacks callbacks = new LifecycleCheckCallbacks(activity);
+ application.registerActivityLifecycleCallbacks(callbacks);
+
+ /*
+ * Runnables scheduled before/after recreate() will run before and after the Runnables
+ * scheduled by recreate(). This allows us to bound the time where mActivity lifecycle
+ * events that could be caused by recreate() run - that way we can detect onPause()
+ * from the new Activity instance, and schedule onStop to run immediately after it.
+ */
+ mainHandler.post(() -> callbacks.currentlyRecreatingToken = token);
+
+ try {
+ if (needsRelaunchCall()) {
+ requestRelaunchActivityMethod.invoke(activityThread,
+ token, null, null, 0, false, null, null, false, false);
+ } else {
+ activity.recreate();
+ }
+ return true;
+ } finally {
+ mainHandler.post(() -> {
+ // Since we're calling hidden API, it's entirely possible for it to
+ // simply do nothing;
+ // if that's the case, make sure to unregister so we don't leak memory
+ // waiting for an event that will never happen.
+ application.unregisterActivityLifecycleCallbacks(callbacks);
+ });
+ }
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ // Only reachable on SDK_INT < 28
+ private static final class LifecycleCheckCallbacks implements ActivityLifecycleCallbacks {
+ Object currentlyRecreatingToken;
+
+ private Activity mActivity;
+ private final int mRecreatingHashCode;
+
+ // Whether the activity on which recreate() was called went through onStart after
+ // recreate() was called (and thus the callback was registered).
+ private boolean mStarted = false;
+
+ // Whether the activity on which recreate() was called went through onDestroy after
+ // recreate() was called. This means we successfully initiated a recreate().
+ private boolean mDestroyed = false;
+
+ // Whether we'll force the activity on which recreate() was called to go through an
+ // onStop()
+ private boolean mStopQueued = false;
+
+ LifecycleCheckCallbacks(@NonNull Activity aboutToRecreate) {
+ mActivity = aboutToRecreate;
+ mRecreatingHashCode = mActivity.hashCode();
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ // If we see a start call on the original mActivity instance, then the mActivity
+ // starting event executed between our call to recreate() and the actual
+ // recreation of the mActivity. In that case, a stop() call should not be scheduled.
+ if (mActivity == activity) {
+ mStarted = true;
+ }
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ if (mDestroyed // Original mActivity must be gone
+ && !mStopQueued // Don't schedule stop twice for one recreate() call
+ && !mStarted
+ // Don't schedule stop if the original instance starting raced with recreate()
+ && queueOnStopIfNecessary(
+ currentlyRecreatingToken, mRecreatingHashCode, activity)) {
+ mStopQueued = true;
+ // Don't retain this object longer than necessary
+ currentlyRecreatingToken = null;
+ }
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ // Not possible to get a start/stop pair in the same UI thread loop
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ if (mActivity == activity) {
+ // Once the original mActivity instance is mDestroyed, we don't need to compare to
+ // it any
+ // longer, and we don't want to retain it any longer than necessary.
+ mActivity = null;
+ mDestroyed = true;
+ }
+ }
+ }
+
+ /**
+ * Returns true if a stop call was scheduled successfully.
+ *
+ * Only reachable on SDK < 28.
+ */
+ protected static boolean queueOnStopIfNecessary(
+ Object currentlyRecreatingToken, int currentlyRecreatingHashCode, Activity activity) {
+ try {
+ final Object token = tokenField.get(activity);
+ if (token != currentlyRecreatingToken
+ || activity.hashCode() != currentlyRecreatingHashCode) {
+ // We're looking at a different activity, don't try to make it stop! Note that
+ // tokens are reused on SDK 21-23 but Activity objects (and thus hashCode, in
+ // all likelihood) are not, so we need to check both.
+ return false;
+ }
+ final Object activityThread = mainThreadField.get(activity);
+ // These operations are posted at the front of the queue, so that operations
+ // scheduled from onCreate, onStart etc run after the onStop call - this should
+ // cause any redundant loads to be immediately cancelled.
+ mainHandler.postAtFrontOfQueue(() -> {
+ try {
+ if (SDK_INT < 28) {
+ if (performStopActivity3ParamsMethod != null) {
+ performStopActivity3ParamsMethod.invoke(activityThread,
+ token, false, "AppCompat recreation");
+ } else {
+ performStopActivity2ParamsMethod.invoke(activityThread,
+ token, false);
+ }
+ }
+ } catch (RuntimeException e) {
+ // If an Activity throws from onStop, don't swallow it
+ if (e.getClass() == RuntimeException.class
+ && e.getMessage() != null
+ && e.getMessage().startsWith("Unable to stop")) {
+ throw e;
+ }
+ // Otherwise just swallow it - we're calling random private methods,
+ // there's no guarantee on how they'll behave.
+ } catch (Throwable t) {
+ Log.e(LOG_TAG, "Exception while invoking performStopActivity", t);
+ }
+ });
+ return true;
+ } catch (Throwable t) {
+ Log.e(LOG_TAG, "Exception while fetching field values", t);
+ return false;
+ }
+ }
+
+ private static Method getPerformStopActivity3Params(Class<?> activityThreadClass) {
+ if (activityThreadClass == null) {
+ return null;
+ }
+ try {
+ Method performStop = activityThreadClass.getDeclaredMethod("performStopActivity",
+ IBinder.class, boolean.class, String.class);
+ performStop.setAccessible(true);
+ return performStop;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Method getPerformStopActivity2Params(Class<?> activityThreadClass) {
+ if (activityThreadClass == null) {
+ return null;
+ }
+ try {
+ Method performStop = activityThreadClass.getDeclaredMethod("performStopActivity",
+ IBinder.class, boolean.class);
+ performStop.setAccessible(true);
+ return performStop;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ @ChecksSdkIntAtLeast(api = 26)
+ private static boolean needsRelaunchCall() {
+ return SDK_INT == 26 || SDK_INT == 27;
+ }
+
+ private static Method getRequestRelaunchActivityMethod(Class<?> activityThreadClass) {
+ if (!needsRelaunchCall() || activityThreadClass == null) {
+ return null;
+ }
+ try {
+ Method relaunch = activityThreadClass.getDeclaredMethod(
+ "requestRelaunchActivity",
+ IBinder.class,
+ List.class,
+ List.class,
+ int.class,
+ boolean.class,
+ Configuration.class,
+ Configuration.class,
+ boolean.class,
+ boolean.class);
+ relaunch.setAccessible(true);
+ return relaunch;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Field getMainThreadField() {
+ try {
+ Field mainThreadField = Activity.class.getDeclaredField("mMainThread");
+ mainThreadField.setAccessible(true);
+ return mainThreadField;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Field getTokenField() {
+ try {
+ Field tokenField = Activity.class.getDeclaredField("mToken");
+ tokenField.setAccessible(true);
+ return tokenField;
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Class<?> getActivityThreadClass() {
+ try {
+ return Class.forName("android.app.ActivityThread");
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/core/widget/ListViewCompat.java b/lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompat.java
similarity index 97%
rename from lint-checks/integration-tests/src/main/java/androidx/core/widget/ListViewCompat.java
rename to lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompat.java
index 1d51e41..216ef20 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/core/widget/ListViewCompat.java
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.core.widget;
+package androidx.sample.core.widget;
import android.os.Build;
import android.view.View;
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
index 5bc2e75..4ee07b7 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
@@ -22,13 +22,14 @@
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.Incident
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.intellij.psi.PsiCompiledElement
import org.jetbrains.uast.UAnnotated
import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UClass
import org.jetbrains.uast.UElement
import org.jetbrains.uast.resolveToUElement
@@ -45,11 +46,28 @@
private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
override fun visitAnnotation(node: UAnnotation) {
+ if (DEBUG) {
+ if (APPLICABLE_ANNOTATIONS.contains(node.qualifiedName) && node.sourcePsi != null) {
+ (node.uastParent as? UClass)?.let { annotation ->
+ println(
+ "${context.driver.mode}: declared ${annotation.qualifiedName} in " +
+ "${context.project}"
+ )
+ }
+ }
+ }
+
+ // If we find an usage of an experimentally-declared annotation, check it.
val annotation = node.resolveToUElement()
if (annotation is UAnnotated) {
val annotations = context.evaluator.getAllAnnotations(annotation, false)
- val isOptIn = annotations.any { APPLICABLE_ANNOTATIONS.contains(it.qualifiedName) }
- if (isOptIn) {
+ if (annotations.any { APPLICABLE_ANNOTATIONS.contains(it.qualifiedName) }) {
+ if (DEBUG) {
+ println(
+ "${context.driver.mode}: used ${node.qualifiedName} in " +
+ "${context.project}"
+ )
+ }
verifyUsageOfElementIsWithinSameGroup(context, node, annotation, ISSUE)
}
}
@@ -64,28 +82,28 @@
) {
val evaluator = context.evaluator
val usageCoordinates = evaluator.getLibrary(usage) ?: context.project.mavenCoordinate
- val annotationCoordinates = evaluator.getLibrary(annotation) ?: run {
- // Is the annotation defined in source code?
- if (usageCoordinates != null && annotation !is PsiCompiledElement) {
- annotation.sourcePsi?.let { sourcePsi ->
- evaluator.getProject(sourcePsi)?.mavenCoordinate
- }
- } else {
- null
- }
- }
val usageGroupId = usageCoordinates?.groupId
- val annotationGroupId = annotationCoordinates?.groupId
+ val annotationGroupId = evaluator.getLibrary(annotation)?.groupId
if (annotationGroupId != usageGroupId && annotationGroupId != null) {
- context.report(
- issue, usage, context.getNameLocation(usage),
- "`Experimental` and `RequiresOptIn` APIs may only be used within the same-version" +
- " group where they were defined."
- )
+ if (DEBUG) {
+ println(
+ "${context.driver.mode}: report usage of $annotationGroupId in $usageGroupId"
+ )
+ }
+ Incident(context)
+ .issue(issue)
+ .at(usage)
+ .message(
+ "`Experimental` and `RequiresOptIn` APIs may only be used within the " +
+ "same-version group where they were defined."
+ )
+ .report()
}
}
companion object {
+ private const val DEBUG = false
+
private const val KOTLIN_EXPERIMENTAL_ANNOTATION = "kotlin.Experimental"
private const val KOTLIN_REQUIRES_OPT_IN_ANNOTATION = "kotlin.RequiresOptIn"
private const val JAVA_EXPERIMENTAL_ANNOTATION =
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt b/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
index 32c2133..725f322c 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
@@ -16,6 +16,7 @@
@file:Suppress("UnstableApiUsage")
package androidx.build.lint
+
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
@@ -23,47 +24,55 @@
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.checks.VersionChecks.Companion.isWithinVersionCheckConditional
-import com.android.sdklib.SdkVersionInfo
+import com.android.sdklib.SdkVersionInfo.HIGHEST_KNOWN_API
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
-const val METHOD_REFLECTION_CLASS = "java.lang.reflect.Method"
-class BanUncheckedReflection : Detector(), SourceCodeScanner {
- override fun getApplicableMethodNames() = listOf("invoke")
- override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
- // We are not really monitoring if the reflection call is within the right API check
- // we leave that to the user, and so we check for any API check really. That means
- // any check with an upper bound of the highest known API or a with a lower bound of 1
- // (which should technically include every check) is good enough.
- // Return if not reflection
- if (!context.evaluator.isMemberInClass(method, METHOD_REFLECTION_CLASS)) return
- // If not within an SDK check, flag
- if (!isWithinVersionCheckConditional(
- context, node, SdkVersionInfo.HIGHEST_KNOWN_API, false
- ) && !isWithinVersionCheckConditional(
- context, node, 1, true
- )
- ) {
+class BanUncheckedReflection : Detector(), SourceCodeScanner {
+
+ override fun getApplicableMethodNames() = listOf(
+ METHOD_INVOKE_NAME
+ )
+
+ override fun visitMethodCall(
+ context: JavaContext,
+ node: UCallExpression,
+ method: PsiMethod
+ ) {
+ // We don't care if the invocation is correct -- there's another lint for that. We're
+ // just enforcing the "all reflection on the platform SDK must be gated on SDK_INT checks"
+ // policy. Also -- since we're not actually checking whether the invocation is on the
+ // platform SDK -- we're discouraging reflection in general.
+
+ // Skip if this isn't a call to `Method.invoke`.
+ if (!context.evaluator.isMemberInClass(method, METHOD_REFLECTION_CLASS)) return
+
+ // Flag if the call isn't inside an SDK_INT check.
+ if (!isWithinVersionCheckConditional(context, node, HIGHEST_KNOWN_API, false) &&
+ !isWithinVersionCheckConditional(context, node, 1, true)
+ ) {
context.report(
ISSUE, node, context.getLocation(node),
- "Calling Method.invoke without an SDK check"
+ "Calling `Method.invoke` without an SDK check"
)
}
}
+
companion object {
val ISSUE = Issue.create(
"BanUncheckedReflection",
"Reflection that is not within an SDK check",
- "Use of reflection can be risky and there is never a" +
- " reason to use reflection without" +
- " having to check for the device's SDK (either through SDK_INT comparison or " +
- "methods such as isAtLeastP etc...)" +
- ". Please surround the Method.invoke" +
- " call with the appropriate SDK_INT check.",
+ "Jetpack policy discourages reflection. In cases where reflection is used on " +
+ "platform SDK classes, it must be used within an `SDK_INT` check that delegates " +
+ "to an equivalent public API on the latest version of the platform. If no " +
+ "equivalent public API exists, reflection must not be used.",
Category.CORRECTNESS, 5, Severity.ERROR,
Implementation(BanUncheckedReflection::class.java, Scope.JAVA_FILE_SCOPE)
)
+
+ const val METHOD_REFLECTION_CLASS = "java.lang.reflect.Method"
+ const val METHOD_INVOKE_NAME = "invoke"
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt
new file mode 100644
index 0000000..8bcfd3e
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.ProjectDescription
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestLintResult
+import com.android.tools.lint.checks.infrastructure.TestMode
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import java.io.FileNotFoundException
+
+/**
+ * Implementation of [LintDetectorTest] that's slightly more Kotlin-friendly.
+ */
+abstract class AbstractLintDetectorTest(
+ val useDetector: Detector,
+ val useIssues: List<Issue>,
+ val stubs: Array<TestFile> = emptyArray(),
+) : LintDetectorTest() {
+ override fun getDetector(): Detector = useDetector
+
+ override fun getIssues(): List<Issue> = useIssues
+
+ fun check(
+ vararg projects: ProjectDescription
+ ): TestLintResult {
+ // If we have stubs, push those into a virtual project and pass them through the call to
+ // projects(), since attempting to call files() would overwrite the call to projects().
+ val projectsWithStubs = if (stubs.isNotEmpty()) {
+ arrayOf(*projects, project().files(*stubs))
+ } else {
+ projects
+ }
+
+ return lint()
+ .projects(*projectsWithStubs).testModes(TestMode.DEFAULT, TestMode.PARTIAL)
+ .run()
+ }
+
+ fun check(
+ vararg files: TestFile,
+ ): TestLintResult {
+ return lint()
+ .files(
+ *stubs,
+ *files
+ )
+ .run()
+ }
+}
+
+/**
+ * Creates a new [ProjectDescription].
+ */
+fun project(): ProjectDescription = ProjectDescription()
+
+/**
+ * Loads a [TestFile] from `AndroidManifest.xml` included in the JAR resources.
+ */
+fun manifestSample(): TestFile = TestFiles.manifest(
+ Stubs::class.java.getResource(
+ "/AndroidManifest.xml"
+ )?.readText() ?: throw FileNotFoundException(
+ "Could not find AndroidManifest.xml in the integration test project"
+ )
+)
+
+/**
+ * Loads a [TestFile] from Java source code included in the JAR resources.
+ */
+fun javaSample(className: String): TestFile = TestFiles.java(
+ Stubs::class.java.getResource(
+ "/java/${className.replace('.', '/')}.java"
+ )?.readText() ?: throw FileNotFoundException(
+ "Could not find Java sources for $className in the integration test project"
+ )
+)
+
+/**
+ * Loads a [TestFile] from Kotlin source code included in the JAR resources.
+ */
+fun ktSample(className: String): TestFile = TestFiles.kotlin(
+ Stubs::class.java.getResource(
+ "/java/${className.replace('.', '/')}.kt"
+ )?.readText() ?: throw FileNotFoundException(
+ "Could not find Kotlin sources for $className in the integration test project"
+ )
+)
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt
new file mode 100644
index 0000000..a01e86e
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanConcurrentHashMapTest : AbstractLintDetectorTest(
+ useDetector = BanConcurrentHashMap(),
+ useIssues = listOf(BanConcurrentHashMap.ISSUE),
+) {
+
+ @Test
+ fun `Detection of ConcurrentHashMap usage in Java sources`() {
+ val input = arrayOf(
+ javaSample("androidx.ConcurrentHashMapUsageJava"),
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/ConcurrentHashMapUsageJava.java:22: Error: Detected ConcurrentHashMap usage. [BanConcurrentHashMap]
+import java.util.concurrent.ConcurrentHashMap;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input).expect(expected)
+ }
+}
\ No newline at end of file
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
index 0c7e01e..64e10d8 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
@@ -14,20 +14,22 @@
* limitations under the License.
*/
-@file:Suppress("UnstableApiUsage")
+@file:Suppress("UnstableApiUsage", "GroovyUnusedAssignment")
package androidx.build.lint
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestFiles
-import com.android.tools.lint.checks.infrastructure.TestFiles.gradle
-import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import com.android.tools.lint.checks.infrastructure.ProjectDescription
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanInappropriateExperimentalUsageTest {
+class BanInappropriateExperimentalUsageTest : AbstractLintDetectorTest(
+ useDetector = BanInappropriateExperimentalUsage(),
+ useIssues = listOf(BanInappropriateExperimentalUsage.ISSUE),
+ stubs = arrayOf(Stubs.OptIn),
+) {
@Test
fun `Test within-module Experimental usage via Gradle model`() {
@@ -42,24 +44,23 @@
group=sample.annotation.provider
"""
).indented(),
- OPT_IN_KT,
)
- lint()
- .projects(provider)
- .issues(BanInappropriateExperimentalUsage.ISSUE)
- .run()
- .expect(
- """
- No warnings.
- """.trimIndent()
- )
+ /* ktlint-disable max-line-length */
+ val expected = """
+No warnings.
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(provider).expect(expected)
}
+ @Ignore("b/188814760")
@Test
fun `Test cross-module Experimental usage via Gradle model`() {
val provider = project()
.name("provider")
+ .type(ProjectDescription.Type.LIBRARY)
.report(false)
.files(
ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
@@ -70,11 +71,11 @@
group=sample.annotation.provider
"""
).indented(),
- OPT_IN_KT,
)
val consumer = project()
.name("consumer")
+ .type(ProjectDescription.Type.LIBRARY)
.dependsOn(provider)
.files(
ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"),
@@ -83,69 +84,18 @@
apply plugin: 'com.android.library'
group=androidx.sample.consumer
"""
- ).indented()
+ ).indented(),
)
- lint()
- .projects(provider, consumer)
- .issues(BanInappropriateExperimentalUsage.ISSUE)
- .run()
- .expect(
- /* ktlint-enable max-line-length */
- """
- src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:25: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
- @ExperimentalSampleAnnotationJava
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
- """.trimIndent()
- /* ktlint-enable max-line-length */
- )
+ /* ktlint-disable max-line-length */
+ val expected = """
+../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:25: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
+ @ExperimentalSampleAnnotationJava
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(provider, consumer).expect(expected)
}
}
-
-/* ktlint-disable max-line-length */
-
-/**
- * [TestFile] containing OptIn.kt from the Kotlin standard library.
- *
- * This is a workaround for the Kotlin standard library used by the Lint test harness not
- * including the Experimental annotation by default.
- */
-private val OPT_IN_KT: TestFile = TestFiles.kotlin(
- """
- package kotlin
-
- import kotlin.annotation.AnnotationRetention.BINARY
- import kotlin.annotation.AnnotationRetention.SOURCE
- import kotlin.annotation.AnnotationTarget.*
- import kotlin.internal.RequireKotlin
- import kotlin.internal.RequireKotlinVersionKind
- import kotlin.reflect.KClass
-
- @Target(ANNOTATION_CLASS)
- @Retention(BINARY)
- @SinceKotlin("1.3")
- @RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
- public annotation class RequiresOptIn(
- val message: String = "",
- val level: Level = Level.ERROR
- ) {
- public enum class Level {
- WARNING,
- ERROR,
- }
- }
-
- @Target(
- CLASS, PROPERTY, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, EXPRESSION, FILE, TYPEALIAS
- )
- @Retention(SOURCE)
- @SinceKotlin("1.3")
- @RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
- public annotation class OptIn(
- vararg val markerClass: KClass<out Annotation>
- )
- """.trimIndent()
-)
-
-/* ktlint-enable max-line-length */
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
index 4b5b504..dceb43b 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
@@ -14,55 +14,36 @@
* limitations under the License.
*/
+@file:Suppress("UnstableApiUsage")
+
package androidx.build.lint
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-import com.android.tools.lint.checks.infrastructure.TestFiles.java
-import com.android.tools.lint.checks.infrastructure.TestLintResult
-import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
-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
-class BanKeepAnnotationTest : LintDetectorTest() {
- override fun getDetector(): Detector = BanKeepAnnotation()
+@RunWith(JUnit4::class)
+class BanKeepAnnotationTest : AbstractLintDetectorTest(
+ useDetector = BanKeepAnnotation(),
+ useIssues = listOf(BanKeepAnnotation.ISSUE),
+ stubs = arrayOf(Stubs.Keep),
+) {
- override fun getIssues(): List<Issue> = listOf(
- BanKeepAnnotation.ISSUE
- )
-
- private fun check(code: String): TestLintResult {
- return lint().files(
- java(annotationSource),
- java(code)
+ @Test
+ fun `Detection of Keep annotation in Java sources`() {
+ val input = arrayOf(
+ javaSample("androidx.KeepAnnotationUsageJava"),
)
- .run()
- }
- private val annotationSource = """
- package androidx.annotation;
-
- public @interface Keep {
- }
- """
-
- @Test fun testAnnotatedUnreferencedClass() {
- val input = """
- package androidx.sample;
-
- import androidx.annotation.Keep;
- @Keep
- public class SampleClass {
- }
- """
+ /* ktlint-disable max-line-length */
val expected = """
- src/androidx/sample/SampleClass.java:4: Error: Uses @Keep annotation [BanKeepAnnotation]
- @Keep
- ~~~~~
- 1 errors, 0 warnings
- """
- check(input.trimIndent())
- .expect(expected.trimIndent())
+src/androidx/KeepAnnotationUsageJava.java:21: Error: Uses @Keep annotation [BanKeepAnnotation]
+@Keep
+~~~~~
+1 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input).expect(expected)
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt
new file mode 100644
index 0000000..b2cc7d4
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanParcelableUsageTest : AbstractLintDetectorTest(
+ useDetector = BanParcelableUsage(),
+ useIssues = listOf(BanParcelableUsage.ISSUE),
+) {
+
+ @Test
+ fun `Detection of Parcelable usage in Java sources`() {
+ val input = arrayOf(
+ javaSample("androidx.ParcelableUsageJava"),
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/ParcelableUsageJava.java:25: Error: Class implements android.os.Parcelable [BanParcelableUsage]
+public class ParcelableUsageJava implements Parcelable {
+ ~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input).expect(expected)
+ }
+}
\ No newline at end of file
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt
new file mode 100644
index 0000000..702b681
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanSynchronizedMethodsTest : AbstractLintDetectorTest(
+ useDetector = BanSynchronizedMethods(),
+ useIssues = listOf(BanSynchronizedMethods.ISSUE),
+) {
+
+ @Test
+ fun `Detection of synchronized methods in Java sources`() {
+ val input = arrayOf(
+ javaSample("androidx.SynchronizedMethodJava"),
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/SynchronizedMethodJava.java:22: Error: Use of synchronized methods is not recommended [BanSynchronizedMethods]
+ public synchronized void someMethod() {
+ ^
+1 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input).expect(expected)
+ }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt
new file mode 100644
index 0000000..49429e0
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanTargetApiAnnotationTest : AbstractLintDetectorTest(
+ useDetector = BanTargetApiAnnotation(),
+ useIssues = listOf(BanTargetApiAnnotation.ISSUE),
+) {
+
+ @Test
+ fun `Detection of TargetApi usage in Java sources`() {
+ val input = arrayOf(
+ javaSample("androidx.TargetApiUsageJava"),
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/TargetApiUsageJava.java:22: Error: Uses @TargetApi annotation [BanTargetApiAnnotation]
+@TargetApi(29)
+~~~~~~~~~~~~~~
+src/androidx/TargetApiUsageJava.java:25: Error: Uses @TargetApi annotation [BanTargetApiAnnotation]
+ @TargetApi(30)
+ ~~~~~~~~~~~~~~
+2 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input).expect(expected)
+ }
+}
\ No newline at end of file
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt
new file mode 100644
index 0000000..6c66b16
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanUncheckedReflectionTest : AbstractLintDetectorTest(
+ useDetector = BanUncheckedReflection(),
+ useIssues = listOf(BanUncheckedReflection.ISSUE),
+ stubs = arrayOf(Stubs.ChecksSdkIntAtLeast),
+) {
+
+ @Test
+ fun `Detection of unchecked reflection in real-world Java sources`() {
+ val input = arrayOf(
+ javaSample("androidx.sample.core.app.ActivityRecreator"),
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/sample/core/app/ActivityRecreator.java:145: Error: Calling Method.invoke without an SDK check [BanUncheckedReflection]
+ requestRelaunchActivityMethod.invoke(activityThread,
+ ^
+src/androidx/sample/core/app/ActivityRecreator.java:262: Error: Calling Method.invoke without an SDK check [BanUncheckedReflection]
+ performStopActivity3ParamsMethod.invoke(activityThread,
+ ^
+src/androidx/sample/core/app/ActivityRecreator.java:265: Error: Calling Method.invoke without an SDK check [BanUncheckedReflection]
+ performStopActivity2ParamsMethod.invoke(activityThread,
+ ^
+3 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input).expect(expected)
+ }
+
+ @Test
+ fun `Checked reflection in real-world Java sources`() {
+ val input = arrayOf(
+ javaSample("androidx.sample.core.app.ActivityRecreatorChecked"),
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+No warnings.
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input).expect(expected)
+ }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
index 253df56..c1006d3 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
@@ -14,38 +14,28 @@
* limitations under the License.
*/
+@file:Suppress("UnstableApiUsage")
+
package androidx.build.lint
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest.manifest
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestFiles
-import com.android.tools.lint.checks.infrastructure.TestLintResult
-import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-@Suppress("UnstableApiUsage")
@RunWith(JUnit4::class)
-class ClassVerificationFailureDetectorTest {
-
- private fun check(
- vararg testFiles: TestFile,
- minSdkVersion: Int = 14,
- ): TestLintResult {
- return lint()
- .files(
- manifest().minSdk(minSdkVersion),
- *testFiles,
- )
- .issues(ClassVerificationFailureDetector.ISSUE)
- .run()
- }
+class ClassVerificationFailureDetectorTest : AbstractLintDetectorTest(
+ useDetector = ClassVerificationFailureDetector(),
+ useIssues = listOf(ClassVerificationFailureDetector.ISSUE),
+ stubs = arrayOf(
+ // AndroidManifest with minSdkVersion=14
+ manifest().minSdk(14),
+ ),
+) {
@Test
fun `Detection of unsafe references in Java sources`() {
val input = arrayOf(
- javaSample("androidx.ClassVerificationFailureFromJava")
+ javaSample("androidx.ClassVerificationFailureFromJava"),
)
/* ktlint-disable max-line-length */
@@ -72,22 +62,22 @@
@Test
fun `Detection and auto-fix of unsafe references in real-world Java sources`() {
val input = arrayOf(
- javaSample("androidx.core.widget.ListViewCompat")
+ javaSample("androidx.sample.core.widget.ListViewCompat"),
)
/* ktlint-disable max-line-length */
val expected = """
-src/androidx/core/widget/ListViewCompat.java:39: Error: This call references a method added in API level 19; however, the containing class androidx.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
+src/androidx/sample/core/widget/ListViewCompat.java:39: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
listView.scrollListBy(y);
~~~~~~~~~~~~
-src/androidx/core/widget/ListViewCompat.java:69: Error: This call references a method added in API level 19; however, the containing class androidx.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
+src/androidx/sample/core/widget/ListViewCompat.java:69: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return listView.canScrollList(direction);
~~~~~~~~~~~~~
2 errors, 0 warnings
""".trimIndent()
val expectedFix = """
-Fix for src/androidx/core/widget/ListViewCompat.java line 39: Extract to static inner class:
+Fix for src/androidx/sample/core/widget/ListViewCompat.java line 39: Extract to static inner class:
@@ -39 +39
- listView.scrollListBy(y);
+ Api19Impl.scrollListBy(listView, y);
@@ -106,7 +96,7 @@
@@ -93 +102
+ }}
+
-Fix for src/androidx/core/widget/ListViewCompat.java line 69: Extract to static inner class:
+Fix for src/androidx/sample/core/widget/ListViewCompat.java line 69: Extract to static inner class:
@@ -69 +69
- return listView.canScrollList(direction);
+ return Api19Impl.canScrollList(listView, direction);
@@ -134,7 +124,7 @@
@Test
fun `Auto-fix unsafe void-type method reference in Java source`() {
val input = arrayOf(
- javaSample("androidx.AutofixUnsafeVoidMethodReferenceJava")
+ javaSample("androidx.AutofixUnsafeVoidMethodReferenceJava"),
)
/* ktlint-disable max-line-length */
@@ -167,7 +157,7 @@
@Test
fun `Auto-fix unsafe constructor reference in Java source`() {
val input = arrayOf(
- javaSample("androidx.AutofixUnsafeConstructorReferenceJava")
+ javaSample("androidx.AutofixUnsafeConstructorReferenceJava"),
)
/* ktlint-disable max-line-length */
@@ -200,7 +190,7 @@
@Test
fun `Auto-fix unsafe static method reference in Java source`() {
val input = arrayOf(
- javaSample("androidx.AutofixUnsafeStaticMethodReferenceJava")
+ javaSample("androidx.AutofixUnsafeStaticMethodReferenceJava"),
)
/* ktlint-disable max-line-length */
@@ -233,7 +223,7 @@
@Test
fun `Auto-fix unsafe generic-type method reference in Java source`() {
val input = arrayOf(
- javaSample("androidx.AutofixUnsafeGenericMethodReferenceJava")
+ javaSample("androidx.AutofixUnsafeGenericMethodReferenceJava"),
)
/* ktlint-disable max-line-length */
@@ -266,7 +256,7 @@
@Test
fun `Auto-fix unsafe reference in Java source with existing inner class`() {
val input = arrayOf(
- javaSample("androidx.AutofixUnsafeReferenceWithExistingClassJava")
+ javaSample("androidx.AutofixUnsafeReferenceWithExistingClassJava"),
)
/* ktlint-disable max-line-length */
@@ -295,18 +285,4 @@
check(*input).expectFixDiffs(expectedFix)
}
-
- /**
- * Loads a [TestFile] from Java source code included in the JAR resources.
- */
- private fun javaSample(className: String): TestFile = TestFiles.java(
- javaClass.getResource("/java/${className.replace('.', '/')}.java").readText()
- )
-
- /**
- * Loads a [TestFile] from Kotlin source code included in the JAR resources.
- */
- private fun ktSample(className: String): TestFile = TestFiles.kotlin(
- javaClass.getResource("/java/${className.replace('.', '/')}.kt").readText()
- )
}
\ No newline at end of file
diff --git a/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
index d3879db..0ed74a4 100644
--- a/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
@@ -14,30 +14,19 @@
* limitations under the License.
*/
+@file:Suppress("UnstableApiUsage")
+
package androidx.build.lint
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestFiles
-import com.android.tools.lint.checks.infrastructure.TestLintResult
-import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-@Suppress("UnstableApiUsage")
@RunWith(JUnit4::class)
-class IdeaSuppressionDetectorTest {
-
- private fun check(
- vararg testFiles: TestFile,
- ): TestLintResult {
- return lint()
- .files(
- *testFiles,
- )
- .issues(IdeaSuppressionDetector.ISSUE)
- .run()
- }
+class IdeaSuppressionDetectorTest : AbstractLintDetectorTest(
+ useDetector = IdeaSuppressionDetector(),
+ useIssues = listOf(IdeaSuppressionDetector.ISSUE),
+) {
@Test
fun `Detection of IDEA-specific suppression in Java sources`() {
@@ -56,11 +45,4 @@
check(*input).expect(expected)
}
-
- /**
- * Loads a [TestFile] from Java source code included in the JAR resources.
- */
- private fun javaSample(className: String): TestFile = TestFiles.java(
- javaClass.getResource("/java/${className.replace('.', '/')}.java").readText()
- )
-}
\ No newline at end of file
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt
new file mode 100644
index 0000000..f587614
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class MetadataTagInsideApplicationTagDetectorTest : AbstractLintDetectorTest(
+ useDetector = MetadataTagInsideApplicationTagDetector(),
+ useIssues = listOf(MetadataTagInsideApplicationTagDetector.ISSUE),
+) {
+
+ @Test
+ fun `Detect usage of metadata tag insice application tag`() {
+ val input = arrayOf(
+ manifestSample()
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+AndroidManifest.xml:20: Error: Detected <application>-level meta-data tag. [MetadataTagInsideApplicationTag]
+ <meta-data android:name="name" android:value="value" />
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input).expect(expected)
+ }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
index deaab57..874cc32 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
@@ -14,40 +14,27 @@
* limitations under the License.
*/
-package androidx.build.lint
+@file:Suppress("UnstableApiUsage")
-import com.android.tools.lint.checks.infrastructure.TestFiles.java
-import com.android.tools.lint.checks.infrastructure.TestLintResult
-import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+package androidx.build.lint
import org.junit.Ignore
import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
@Ignore("ANDROID_HOME not available on CI")
-class ObsoleteBuildCompatUsageDetectorTest {
- private val buildCompatStub = java(
- """
- package androidx.core.os;
- public class BuildCompat {
- public static boolean isAtLeastN() { return false; }
- public static boolean isAtLeastNMR1() { return false; }
- public static boolean isAtLeastO() { return false; }
- public static boolean isAtLeastOMR1() { return false; }
- public static boolean isAtLeastP() { return false; }
- public static boolean isAtLeastQ() { return false; }
- }
- """.trimIndent()
- )
+@RunWith(JUnit4::class)
+class ObsoleteBuildCompatUsageDetectorTest : AbstractLintDetectorTest(
+ useDetector = ObsoleteBuildCompatUsageDetector(),
+ useIssues = listOf(ObsoleteBuildCompatUsageDetector.ISSUE),
+ stubs = arrayOf(BuildCompat),
+) {
- private fun check(vararg code: String): TestLintResult {
- return lint().files(buildCompatStub, *code.map(::java).toTypedArray())
- .allowMissingSdk(true)
- .issues(ObsoleteBuildCompatUsageDetector.ISSUE)
- .run()
- }
-
- @Test fun isAtLeastN() {
- val input = """
+ @Test
+ fun isAtLeastN() {
+ val input = java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -57,26 +44,34 @@
}
}
}
- """
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
val expected = """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastN()) {
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
+
val expectedDiff = """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 24:
@@ -5 +5
- if (BuildCompat.isAtLeastN()) {
+ if (Build.VERSION.SDK_INT >= 24) {
"""
- check(input.trimIndent())
+ /* ktlint-enable max-line-length */
+
+ check(input)
.expect(expected.trimIndent())
.expectFixDiffs(expectedDiff.trimIndent())
}
- @Test fun isAtLeastNStaticImport() {
- val input = """
+ @Test
+ fun isAtLeastNStaticImport() {
+ val input = java(
+ """
package foo;
import static androidx.core.os.BuildCompat.isAtLeastN;
public class Example {
@@ -86,26 +81,34 @@
}
}
}
- """
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
val expected = """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (isAtLeastN()) {
~~~~~~~~~~~~
1 errors, 0 warnings
"""
+
val expectedDiff = """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 24:
@@ -5 +5
- if (isAtLeastN()) {
+ if (Build.VERSION.SDK_INT >= 24) {
"""
- check(input.trimIndent())
+ /* ktlint-enable max-line-length */
+
+ check(input)
.expect(expected.trimIndent())
.expectFixDiffs(expectedDiff.trimIndent())
}
- @Test fun isAtLeastNMR1() {
- val input = """
+ @Test
+ fun isAtLeastNMR1() {
+ val input = java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -115,26 +118,34 @@
}
}
}
- """
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
val expected = """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastNMR1()) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
+
val expectedDiff = """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 25:
@@ -5 +5
- if (BuildCompat.isAtLeastNMR1()) {
+ if (Build.VERSION.SDK_INT >= 25) {
"""
- check(input.trimIndent())
+ /* ktlint-enable max-line-length */
+
+ check(input)
.expect(expected.trimIndent())
.expectFixDiffs(expectedDiff.trimIndent())
}
- @Test fun isAtLeastO() {
- val input = """
+ @Test
+ fun isAtLeastO() {
+ val input = java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -144,26 +155,34 @@
}
}
}
- """
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
val expected = """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastO()) {
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
+
val expectedDiff = """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 26:
@@ -5 +5
- if (BuildCompat.isAtLeastO()) {
+ if (Build.VERSION.SDK_INT >= 26) {
"""
- check(input.trimIndent())
+ /* ktlint-enable max-line-length */
+
+ check(input)
.expect(expected.trimIndent())
.expectFixDiffs(expectedDiff.trimIndent())
}
- @Test fun isAtLeastOMR1() {
- val input = """
+ @Test
+ fun isAtLeastOMR1() {
+ val input = java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -173,26 +192,34 @@
}
}
}
- """
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
val expected = """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastOMR1()) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
+
val expectedDiff = """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 27:
@@ -5 +5
- if (BuildCompat.isAtLeastOMR1()) {
+ if (Build.VERSION.SDK_INT >= 27) {
"""
- check(input.trimIndent())
+ /* ktlint-enable max-line-length */
+
+ check(input)
.expect(expected.trimIndent())
.expectFixDiffs(expectedDiff.trimIndent())
}
- @Test fun isAtLeastP() {
- val input = """
+ @Test
+ fun isAtLeastP() {
+ val input = java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -202,26 +229,34 @@
}
}
}
- """
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
val expected = """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastP()) {
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
+
val expectedDiff = """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 28:
@@ -5 +5
- if (BuildCompat.isAtLeastP()) {
+ if (Build.VERSION.SDK_INT >= 28) {
"""
- check(input.trimIndent())
+ /* ktlint-enable max-line-length */
+
+ check(input)
.expect(expected.trimIndent())
.expectFixDiffs(expectedDiff.trimIndent())
}
- @Test fun isAtLeastQ() {
- val input = """
+ @Test
+ fun isAtLeastQ() {
+ val input = java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -231,21 +266,43 @@
}
}
}
- """
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
val expected = """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastQ()) {
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
+
val expectedDiff = """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 29:
@@ -5 +5
- if (BuildCompat.isAtLeastQ()) {
+ if (Build.VERSION.SDK_INT >= 29) {
"""
- check(input.trimIndent())
+ /* ktlint-enable max-line-length */
+
+ check(input)
.expect(expected.trimIndent())
.expectFixDiffs(expectedDiff.trimIndent())
}
+
+ companion object {
+ private val BuildCompat = java(
+ """
+ package androidx.core.os;
+ public class BuildCompat {
+ public static boolean isAtLeastN() { return false; }
+ public static boolean isAtLeastNMR1() { return false; }
+ public static boolean isAtLeastO() { return false; }
+ public static boolean isAtLeastOMR1() { return false; }
+ public static boolean isAtLeastP() { return false; }
+ public static boolean isAtLeastQ() { return false; }
+ }
+ """.trimIndent()
+ )
+ }
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
index 0d51308..b8ac0d9 100644
--- a/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
@@ -18,24 +18,15 @@
package androidx.build.lint
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestLintResult
-import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class PrivateConstructorForUtilityClassDetectorTest {
-
- private fun check(
- vararg testFiles: TestFile,
- ): TestLintResult {
- return lint()
- .files(*testFiles)
- .issues(PrivateConstructorForUtilityClassDetector.ISSUE)
- .run()
- }
+class PrivateConstructorForUtilityClassDetectorTest : AbstractLintDetectorTest(
+ useDetector = PrivateConstructorForUtilityClassDetector(),
+ useIssues = listOf(PrivateConstructorForUtilityClassDetector.ISSUE),
+) {
@Test
fun testInnerClassVisibilityJava() {
diff --git a/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt b/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
index 2c638cb..0e6943f 100644
--- a/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:Suppress("KDocUnresolvedReference")
+@file:Suppress("KDocUnresolvedReference", "UnstableApiUsage")
package androidx.build.lint
diff --git a/lint-checks/src/test/java/androidx/build/lint/Stubs.kt b/lint-checks/src/test/java/androidx/build/lint/Stubs.kt
new file mode 100644
index 0000000..06a6353
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/Stubs.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+
+class Stubs {
+
+ companion object {
+
+ /* ktlint-disable max-line-length */
+
+ /**
+ * [TestFile] containing Keep.java from the annotation library.
+ */
+ val Keep = TestFiles.java(
+ """
+package androidx.annotation;
+
+public @interface Keep {
+}
+ """
+ )
+
+ val RunWith = TestFiles.kotlin(
+ """
+package org.junit.runner
+
+annotation class RunWith(val value: KClass<*>)
+ """
+ )
+
+ val JUnit4Runner = TestFiles.kotlin(
+ """
+package org.junit.runners
+
+class JUnit4
+ """
+ )
+
+ val ParameterizedRunner = TestFiles.kotlin(
+ """
+package org.junit.runners
+
+class Parameterized
+ """
+ )
+
+ val AndroidJUnit4Runner = TestFiles.kotlin(
+ """
+package androidx.test.ext.junit.runners
+
+class AndroidJUnit4
+ """
+ )
+
+ val TestSizeAnnotations = TestFiles.kotlin(
+ """
+package androidx.test.filters
+
+annotation class SmallTest
+annotation class MediumTest
+annotation class LargeTest
+ """
+ )
+
+ val TestAnnotation = TestFiles.kotlin(
+ """
+package org.junit
+
+annotation class Test
+ """
+ )
+
+ /**
+ * [TestFile] containing OptIn.kt from the Kotlin standard library.
+ *
+ * This is a workaround for the Kotlin standard library used by the Lint test harness not
+ * including the Experimental annotation by default.
+ */
+ val OptIn = TestFiles.kotlin(
+ """
+package kotlin
+
+import kotlin.annotation.AnnotationRetention.BINARY
+import kotlin.annotation.AnnotationRetention.SOURCE
+import kotlin.annotation.AnnotationTarget.*
+import kotlin.internal.RequireKotlin
+import kotlin.internal.RequireKotlinVersionKind
+import kotlin.reflect.KClass
+
+@Target(ANNOTATION_CLASS)
+@Retention(BINARY)
+@SinceKotlin("1.3")
+@RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
+public annotation class RequiresOptIn(
+ val message: String = "",
+ val level: Level = Level.ERROR
+) {
+ public enum class Level {
+ WARNING,
+ ERROR,
+ }
+}
+
+@Target(
+ CLASS, PROPERTY, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, EXPRESSION, FILE, TYPEALIAS
+)
+@Retention(SOURCE)
+@SinceKotlin("1.3")
+@RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
+public annotation class OptIn(
+ vararg val markerClass: KClass<out Annotation>
+)
+ """
+ )
+
+ /**
+ * [TestFile] containing ChecksSdkIntAtLeast.java from the annotation library.
+ */
+ val ChecksSdkIntAtLeast = TestFiles.java(
+ """
+package androidx.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Documented
+@Retention(CLASS)
+@Target({METHOD, FIELD})
+public @interface ChecksSdkIntAtLeast {
+ int api() default -1;
+ String codename() default "";
+ int parameter() default -1;
+ int lambda() default -1;
+}
+ """
+ )
+
+ /* ktlint-enable max-line-length */
+ }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt b/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
index 08933de3..fe6ce7d 100644
--- a/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
@@ -19,7 +19,6 @@
package androidx.build.lint
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import org.junit.Test
@@ -53,7 +52,7 @@
}
"""
).within("src/test"),
- *Stubs
+ *StubClasses
)
.run()
.expectClean()
@@ -85,7 +84,7 @@
}
"""
).within("src/test"),
- *Stubs
+ *StubClasses
)
.run()
.expect(
@@ -114,7 +113,7 @@
class Test
"""
).within("src/androidTest"),
- *Stubs
+ *StubClasses
)
.run()
.expect(
@@ -148,7 +147,7 @@
}
"""
).within("src/androidTest"),
- *Stubs
+ *StubClasses
)
.run()
.expectClean()
@@ -171,7 +170,7 @@
}
"""
).within("src/androidTest"),
- *Stubs
+ *StubClasses
)
.run()
.expectClean()
@@ -218,7 +217,7 @@
}
"""
).within("src/androidTest"),
- *Stubs
+ *StubClasses
)
.run()
.expectClean()
@@ -249,7 +248,7 @@
}
"""
).within("src/androidTest"),
- *Stubs
+ *StubClasses
)
.run()
.expect(
@@ -287,7 +286,7 @@
}
"""
).within("src/androidTest"),
- *Stubs
+ *StubClasses
)
.run()
.expect(
@@ -332,68 +331,18 @@
}
"""
).within("src/androidTest"),
- *Stubs
+ *StubClasses
)
.run()
.expectClean()
}
+
+ private val StubClasses = arrayOf(
+ Stubs.RunWith,
+ Stubs.JUnit4Runner,
+ Stubs.ParameterizedRunner,
+ Stubs.AndroidJUnit4Runner,
+ Stubs.TestSizeAnnotations,
+ Stubs.TestAnnotation
+ )
}
-
-private val RunWith = kotlin(
- """
- package org.junit.runner
-
- annotation class RunWith(val value: KClass<*>)
- """
-)
-
-private val JUnit4Runner = kotlin(
- """
- package org.junit.runners
-
- class JUnit4
- """
-)
-
-private val ParameterizedRunner = kotlin(
- """
- package org.junit.runners
-
- class Parameterized
- """
-)
-
-private val AndroidJUnit4Runner = kotlin(
- """
- package androidx.test.ext.junit.runners
-
- class AndroidJUnit4
- """
-)
-
-private val TestSizeAnnotations = kotlin(
- """
- package androidx.test.filters
-
- annotation class SmallTest
- annotation class MediumTest
- annotation class LargeTest
- """
-)
-
-private val TestAnnotation = kotlin(
- """
- package org.junit
-
- annotation class Test
- """
-)
-
-private val Stubs = arrayOf(
- RunWith,
- JUnit4Runner,
- ParameterizedRunner,
- AndroidJUnit4Runner,
- TestSizeAnnotations,
- TestAnnotation
-)
diff --git a/lint-checks/src/test/java/androidx/build/lint/TestUtils.kt b/lint-checks/src/test/java/androidx/build/lint/TestUtils.kt
deleted file mode 100644
index b863b8b..0000000
--- a/lint-checks/src/test/java/androidx/build/lint/TestUtils.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:Suppress("UnstableApiUsage")
-
-package androidx.build.lint
-
-import com.android.tools.lint.checks.infrastructure.ProjectDescription
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestFiles
-import java.io.FileNotFoundException
-
-private class TestUtils
-
-fun project(): ProjectDescription = ProjectDescription()
-
-/**
- * Loads a [TestFile] from Java source code included in the JAR resources.
- */
-fun javaSample(className: String): TestFile = TestFiles.java(
- TestUtils::class.java.getResource(
- "/java/${className.replace('.', '/')}.java"
- )?.readText() ?: throw FileNotFoundException(
- "Could not find Java sources for $className in the integration test project"
- )
-)
-
-/**
- * Loads a [TestFile] from Kotlin source code included in the JAR resources.
- */
-fun ktSample(className: String): TestFile = TestFiles.kotlin(
- TestUtils::class.java.getResource(
- "/java/${className.replace('.', '/')}.kt"
- )?.readText() ?: throw FileNotFoundException(
- "Could not find Kotlin sources for $className in the integration test project"
- )
-)
\ No newline at end of file
diff --git a/media/media/build.gradle b/media/media/build.gradle
index 561b834..214cdab 100644
--- a/media/media/build.gradle
+++ b/media/media/build.gradle
@@ -26,6 +26,7 @@
dependencies {
api("androidx.core:core:1.3.0")
+ implementation("androidx.annotation:annotation:1.2.0")
implementation("androidx.collection:collection:1.1.0")
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/media/media/lint-baseline.xml b/media/media/lint-baseline.xml
index 1fe0359..2386b46 100644
--- a/media/media/lint-baseline.xml
+++ b/media/media/lint-baseline.xml
@@ -2,1135 +2,13 @@
<issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mFwkBuilder = new AudioAttributes.Builder();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
- line="133"
- column="27"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mFwkBuilder = new AudioAttributes.Builder((AudioAttributes) aa);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
- line="137"
- column="27"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return new AudioAttributesImplApi21(mFwkBuilder.build());"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
- line="143"
- column="61"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mFwkBuilder.setUsage(usage);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
- line="153"
- column="25"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mFwkBuilder.setContentType(contentType);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
- line="160"
- column="25"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mFwkBuilder.setFlags(flags);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
- line="167"
- column="25"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi21.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mFwkBuilder.setLegacyStreamType(streamType);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi21.java"
- line="174"
- column="25"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi26.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return new AudioAttributesImplApi26(mFwkBuilder.build());"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi26.java"
- line="65"
- column="61"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.AudioAttributesImplApi26.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mFwkBuilder.setUsage(usage);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioAttributesImplApi26.java"
- line="71"
- column="25"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" new AudioFocusRequest.Builder(mFocusGain)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
- line="84"
- column="21"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" .setAudioAttributes(getAudioAttributes())"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
- line="85"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" .setWillPauseWhenDucked(mPauseOnDuck)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
- line="86"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" .setOnAudioFocusChangeListener("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
- line="87"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 26; however, the containing class androidx.media.AudioFocusRequestCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" .build();"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
- line="89"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 26; however, the containing class androidx.media.AudioManagerCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return audioManager.requestAudioFocus(focusRequest.getAudioFocusRequest());"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioManagerCompat.java"
- line="91"
- column="33"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 26; however, the containing class androidx.media.AudioManagerCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return audioManager.abandonAudioFocusRequest(focusRequest.getAudioFocusRequest());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioManagerCompat.java"
- line="120"
- column="33"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 28; however, the containing class androidx.media.AudioManagerCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return audioManager.getStreamMinVolume(streamType);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/AudioManagerCompat.java"
- line="148"
- column="33"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaBrowserCompat.MediaItem is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" int flags = itemFwk.getFlags();"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="499"
- column="33"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaBrowserCompat.MediaItem is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" MediaDescriptionCompat.fromMediaDescription(itemFwk.getDescription());"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="501"
- column="73"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mServiceFwk.setSessionToken((MediaSession.Token) token.getToken());"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="335"
- column="33"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.MediaBrowserServiceCompat.MediaBrowserServiceImplApi21.MediaBrowserServiceApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return browserRootCompat == null ? null : new MediaBrowserService.BrowserRoot("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="506"
- column="59"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 26; however, the containing class androidx.media.session.MediaButtonReceiver is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" context.startForegroundService(intent);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="301"
- column="21"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaControllerCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" controllerFwk = new MediaController(activity, (MediaSession.Token) sessionTokenObj);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="169"
- column="33"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaControllerCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" activity.setMediaController(controllerFwk);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="171"
- column="22"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaControllerCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" MediaController controllerFwk = activity.getMediaController();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="192"
- column="54"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaControllerCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" MediaSession.Token sessionTokenFwk = controllerFwk.getSessionToken();"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="196"
- column="64"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 29; however, the containing class android.support.v4.media.session.MediaControllerCompat.MediaControllerImplApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mSessionInfo = mControllerFwk.getSessionInfo();"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="2270"
- column="47"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 24; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mControlsFwk.prepare();"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="2394"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 24; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mControlsFwk.prepareFromMediaId(mediaId, extras);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="2403"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 24; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mControlsFwk.prepareFromSearch(query, extras);"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="2415"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 24; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mControlsFwk.prepareFromUri(uri, extras);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="2427"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 29; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mControlsFwk.setPlaybackSpeed(speed);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="2495"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 23; however, the containing class android.support.v4.media.session.MediaControllerCompat.TransportControlsApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mControlsFwk.playFromUri(uri, extras);"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="2537"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" MediaDescription.Builder bob = new MediaDescription.Builder();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="339"
- column="40"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setMediaId(mMediaId);"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="340"
- column="13"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setTitle(mTitle);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="341"
- column="13"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setSubtitle(mSubtitle);"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="342"
- column="13"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setDescription(mDescription);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="343"
- column="13"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setIconBitmap(mIcon);"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="344"
- column="13"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setIconUri(mIconUri);"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="345"
- column="13"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setExtras(extras);"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="358"
- column="13"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 23; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setMediaUri(mMediaUri);"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="360"
- column="17"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mDescriptionFwk = bob.build();"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="362"
- column="31"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setMediaId(description.getMediaId());"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="383"
- column="40"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setTitle(description.getTitle());"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="384"
- column="38"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setSubtitle(description.getSubtitle());"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="385"
- column="41"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setDescription(description.getDescription());"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="386"
- column="44"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setIconBitmap(description.getIconBitmap());"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="387"
- column="43"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setIconUri(description.getIconUri());"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="388"
- column="40"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" Bundle extras = description.getExtras();"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="389"
- column="41"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 23; however, the containing class android.support.v4.media.MediaDescriptionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bob.setMediaUri(description.getMediaUri());"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="415"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 29; however, the containing class android.support.v4.media.session.MediaSessionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return new MediaSession(context, tag, sessionInfo);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="613"
- column="20"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaSessionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return new MediaSession(context, tag);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="615"
- column="20"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaSessionCompat.QueueItem is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mItemFwk = new MediaSession.QueueItem("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2231"
- column="24"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaSessionCompat.QueueItem is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" Object descriptionObj = queueItemObj.getDescription();"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2252"
- column="50"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.MediaSessionCompat.QueueItem is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" long id = queueItemObj.getQueueId();"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2255"
- column="36"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 22; however, the containing class android.support.v4.media.session.MediaSessionCompat.MediaSessionImplApi21 is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mSessionFwk.setRatingType(type);"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="3974"
- column="29"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 16; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.getBuilder().setStyle("
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="211"
- column="38"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" fillInMediaStyle(new Notification.MediaStyle()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="212"
- column="42"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" style.setShowActionsInCompactView(mActionsToShowInCompact);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="221"
- column="23"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" style.setMediaSession((MediaSession.Token) mToken.getToken());"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="224"
- column="23"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 15; however, the containing class androidx.media.app.NotificationCompat.MediaStyle is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" button.setContentDescription(R.id.action0, action.getTitle());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="288"
- column="24"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 16; however, the containing class androidx.media.app.NotificationCompat.DecoratedMediaCustomViewStyle is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.getBuilder().setStyle("
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="389"
- column="38"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 24; however, the containing class androidx.media.app.NotificationCompat.DecoratedMediaCustomViewStyle is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" fillInMediaStyle(new Notification.DecoratedMediaCustomViewStyle()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="390"
- column="42"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" List<PlaybackState.CustomAction> customActionFwks = stateFwk.getCustomActions();"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="809"
- column="74"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 22; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" extras = stateFwk.getExtras();"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="819"
- column="35"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" stateFwk.getState(),"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="825"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" stateFwk.getPosition(),"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="826"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" stateFwk.getBufferedPosition(),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="827"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" stateFwk.getPlaybackSpeed(),"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="828"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" stateFwk.getActions(),"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="829"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" stateFwk.getErrorMessage(),"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="831"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" stateFwk.getLastPositionUpdateTime(),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="832"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" stateFwk.getActiveQueueItemId(),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="834"
- column="30"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" PlaybackState.Builder builder = new PlaybackState.Builder();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="853"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.setState(mState, mPosition, mSpeed, mUpdateTime);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="854"
- column="21"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.setBufferedPosition(mBufferedPosition);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="855"
- column="21"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.setActions(mActions);"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="856"
- column="21"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.setErrorMessage(mErrorMessage);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="857"
- column="21"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.addCustomAction("
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="859"
- column="25"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.setActiveQueueItemId(mActiveItemId);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="862"
- column="21"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 22; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.setExtras(mExtras);"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="864"
- column="25"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mStateFwk = builder.build();"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="866"
- column="33"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" Bundle extras = customActionFwk.getExtras();"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="945"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" customActionFwk.getAction(),"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="949"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" customActionFwk.getName(),"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="950"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" customActionFwk.getIcon(),"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="951"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" PlaybackState.CustomAction.Builder builder = new PlaybackState.CustomAction.Builder("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="972"
- column="58"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.setExtras(mExtras);"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="974"
- column="21"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class android.support.v4.media.session.PlaybackStateCompat.CustomAction is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return builder.build();"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="975"
- column="28"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" final int ratingStyle = ((Rating) ratingObj).getRatingStyle();"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="334"
- column="58"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (((Rating) ratingObj).isRated()) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="336"
- column="38"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" rating = newHeartRating(((Rating) ratingObj).hasHeart());"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="339"
- column="70"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" rating = newThumbRating(((Rating) ratingObj).isThumbUp());"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="342"
- column="70"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" ((Rating) ratingObj).getStarRating());"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="348"
- column="54"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" ((Rating) ratingObj).getPercentRating());"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="352"
- column="54"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mRatingObj = Rating.newHeartRating(hasHeart());"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="380"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mRatingObj = Rating.newThumbRating(isThumbUp());"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="383"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mRatingObj = Rating.newStarRating(mRatingStyle,"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="388"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mRatingObj = Rating.newPercentageRating(getPercentRating());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="392"
- column="45"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class android.support.v4.media.RatingCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mRatingObj = Rating.newUnratedRating(mRatingStyle);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="398"
- column="37"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.media.VolumeProviderCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" volumeProviderFwk.setCurrentVolume(currentVolume);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/media/VolumeProviderCompat.java"
- line="146"
- column="31"/>
- </issue>
-
- <issue
id="LambdaLast"
message="Functional interface parameters (such as parameter 1, "listener", in androidx.media.AudioFocusRequestCompat.Builder.setOnAudioFocusChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
errorLine1=" @NonNull OnAudioFocusChangeListener listener, @NonNull Handler handler) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
- line="310"
+ line="306"
column="63"/>
</issue>
@@ -1229,7 +107,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/AudioFocusRequestCompat.java"
- line="370"
+ line="366"
column="16"/>
</issue>
@@ -1240,7 +118,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="190"
+ line="192"
column="31"/>
</issue>
@@ -1251,7 +129,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="190"
+ line="192"
column="48"/>
</issue>
@@ -1262,7 +140,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="191"
+ line="193"
column="13"/>
</issue>
@@ -1273,7 +151,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="191"
+ line="193"
column="42"/>
</issue>
@@ -1284,7 +162,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="410"
+ line="412"
column="59"/>
</issue>
@@ -1295,7 +173,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="432"
+ line="434"
column="58"/>
</issue>
@@ -1306,7 +184,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="494"
+ line="496"
column="23"/>
</issue>
@@ -1317,7 +195,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="494"
+ line="496"
column="47"/>
</issue>
@@ -1328,7 +206,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="515"
+ line="517"
column="23"/>
</issue>
@@ -1339,7 +217,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="515"
+ line="517"
column="57"/>
</issue>
@@ -1350,7 +228,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="557"
+ line="559"
column="35"/>
</issue>
@@ -1361,7 +239,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="883"
+ line="885"
column="34"/>
</issue>
@@ -1372,7 +250,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="922"
+ line="924"
column="59"/>
</issue>
@@ -1383,7 +261,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="933"
+ line="935"
column="52"/>
</issue>
@@ -1394,7 +272,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="949"
+ line="951"
column="38"/>
</issue>
@@ -1405,7 +283,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="949"
+ line="951"
column="53"/>
</issue>
@@ -1416,7 +294,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="949"
+ line="951"
column="68"/>
</issue>
@@ -1427,7 +305,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="959"
+ line="961"
column="30"/>
</issue>
@@ -1438,7 +316,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="959"
+ line="961"
column="45"/>
</issue>
@@ -1449,7 +327,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="959"
+ line="961"
column="60"/>
</issue>
@@ -1460,7 +338,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="970"
+ line="972"
column="29"/>
</issue>
@@ -1471,7 +349,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="970"
+ line="972"
column="44"/>
</issue>
@@ -1482,7 +360,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"
- line="970"
+ line="972"
column="59"/>
</issue>
@@ -1537,7 +415,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1306"
+ line="1311"
column="37"/>
</issue>
@@ -1548,7 +426,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1328"
+ line="1333"
column="12"/>
</issue>
@@ -1559,7 +437,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1328"
+ line="1333"
column="27"/>
</issue>
@@ -1570,7 +448,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1333"
+ line="1338"
column="22"/>
</issue>
@@ -1581,7 +459,7 @@
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1333"
+ line="1338"
column="41"/>
</issue>
@@ -1592,7 +470,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1333"
+ line="1338"
column="61"/>
</issue>
@@ -1603,7 +481,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1424"
+ line="1429"
column="29"/>
</issue>
@@ -1614,7 +492,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1424"
+ line="1429"
column="40"/>
</issue>
@@ -1625,7 +503,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1434"
+ line="1439"
column="31"/>
</issue>
@@ -1636,7 +514,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1455"
+ line="1460"
column="28"/>
</issue>
@@ -1647,7 +525,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1481"
+ line="1486"
column="49"/>
</issue>
@@ -1658,7 +536,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1506"
+ line="1511"
column="56"/>
</issue>
@@ -1669,7 +547,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1519"
+ line="1524"
column="33"/>
</issue>
@@ -1680,7 +558,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1554"
+ line="1559"
column="18"/>
</issue>
@@ -1691,7 +569,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1962"
+ line="1967"
column="16"/>
</issue>
@@ -1702,7 +580,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"
- line="1969"
+ line="1974"
column="16"/>
</issue>
@@ -1713,7 +591,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="106"
+ line="107"
column="27"/>
</issue>
@@ -1724,7 +602,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="106"
+ line="107"
column="44"/>
</issue>
@@ -1735,7 +613,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="191"
+ line="192"
column="19"/>
</issue>
@@ -1746,7 +624,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="191"
+ line="192"
column="41"/>
</issue>
@@ -1757,7 +635,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="191"
+ line="192"
column="80"/>
</issue>
@@ -1768,7 +646,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="224"
+ line="225"
column="19"/>
</issue>
@@ -1779,7 +657,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="224"
+ line="225"
column="63"/>
</issue>
@@ -1790,7 +668,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="258"
+ line="259"
column="19"/>
</issue>
@@ -1801,7 +679,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="258"
+ line="259"
column="63"/>
</issue>
@@ -1812,7 +690,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="259"
+ line="260"
column="13"/>
</issue>
@@ -1823,7 +701,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="283"
+ line="284"
column="19"/>
</issue>
@@ -1834,7 +712,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/androidx/media/session/MediaButtonReceiver.java"
- line="283"
+ line="284"
column="65"/>
</issue>
@@ -1856,7 +734,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="186"
+ line="181"
column="19"/>
</issue>
@@ -1867,7 +745,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="235"
+ line="224"
column="34"/>
</issue>
@@ -1878,7 +756,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="254"
+ line="245"
column="34"/>
</issue>
@@ -1889,7 +767,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="272"
+ line="263"
column="12"/>
</issue>
@@ -1900,7 +778,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="283"
+ line="274"
column="45"/>
</issue>
@@ -1911,7 +789,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="300"
+ line="291"
column="12"/>
</issue>
@@ -1922,7 +800,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="309"
+ line="300"
column="12"/>
</issue>
@@ -1933,7 +811,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="319"
+ line="310"
column="12"/>
</issue>
@@ -1944,7 +822,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="335"
+ line="326"
column="30"/>
</issue>
@@ -1955,7 +833,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="355"
+ line="346"
column="30"/>
</issue>
@@ -1966,7 +844,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="372"
+ line="363"
column="33"/>
</issue>
@@ -1977,7 +855,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="402"
+ line="393"
column="12"/>
</issue>
@@ -1988,7 +866,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="409"
+ line="400"
column="12"/>
</issue>
@@ -1999,7 +877,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="489"
+ line="480"
column="12"/>
</issue>
@@ -2010,7 +888,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="499"
+ line="490"
column="12"/>
</issue>
@@ -2021,7 +899,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="508"
+ line="499"
column="12"/>
</issue>
@@ -2032,7 +910,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="576"
+ line="567"
column="62"/>
</issue>
@@ -2043,7 +921,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="654"
+ line="645"
column="12"/>
</issue>
@@ -2054,7 +932,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="684"
+ line="675"
column="12"/>
</issue>
@@ -2065,7 +943,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="729"
+ line="720"
column="36"/>
</issue>
@@ -2076,7 +954,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="729"
+ line="720"
column="50"/>
</issue>
@@ -2087,7 +965,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="737"
+ line="728"
column="44"/>
</issue>
@@ -2098,7 +976,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="746"
+ line="737"
column="39"/>
</issue>
@@ -2109,7 +987,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="757"
+ line="748"
column="36"/>
</issue>
@@ -2120,7 +998,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="767"
+ line="758"
column="41"/>
</issue>
@@ -2131,7 +1009,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="776"
+ line="767"
column="37"/>
</issue>
@@ -2142,7 +1020,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="784"
+ line="775"
column="40"/>
</issue>
@@ -2153,7 +1031,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="823"
+ line="814"
column="16"/>
</issue>
@@ -2164,7 +1042,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1174"
+ line="1165"
column="49"/>
</issue>
@@ -2175,7 +1053,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1174"
+ line="1165"
column="65"/>
</issue>
@@ -2186,7 +1064,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1186"
+ line="1177"
column="48"/>
</issue>
@@ -2197,7 +1075,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1186"
+ line="1177"
column="62"/>
</issue>
@@ -2208,7 +1086,7 @@
errorLine2=" ~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1197"
+ line="1188"
column="45"/>
</issue>
@@ -2219,7 +1097,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1197"
+ line="1188"
column="54"/>
</issue>
@@ -2230,7 +1108,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1211"
+ line="1202"
column="46"/>
</issue>
@@ -2241,7 +1119,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1211"
+ line="1202"
column="62"/>
</issue>
@@ -2252,7 +1130,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1222"
+ line="1213"
column="45"/>
</issue>
@@ -2263,7 +1141,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1222"
+ line="1213"
column="59"/>
</issue>
@@ -2274,7 +1152,7 @@
errorLine2=" ~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1231"
+ line="1222"
column="42"/>
</issue>
@@ -2285,7 +1163,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1231"
+ line="1222"
column="51"/>
</issue>
@@ -2296,7 +1174,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1287"
+ line="1278"
column="40"/>
</issue>
@@ -2307,7 +1185,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1301"
+ line="1292"
column="40"/>
</issue>
@@ -2318,7 +1196,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1301"
+ line="1292"
column="61"/>
</issue>
@@ -2329,7 +1207,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1347"
+ line="1338"
column="47"/>
</issue>
@@ -2340,7 +1218,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1348"
+ line="1339"
column="17"/>
</issue>
@@ -2351,7 +1229,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1366"
+ line="1357"
column="47"/>
</issue>
@@ -2362,7 +1240,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"
- line="1366"
+ line="1357"
column="62"/>
</issue>
@@ -2373,7 +1251,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="304"
+ line="306"
column="31"/>
</issue>
@@ -2384,7 +1262,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="335"
+ line="337"
column="12"/>
</issue>
@@ -2395,7 +1273,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="379"
+ line="381"
column="19"/>
</issue>
@@ -2406,7 +1284,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="379"
+ line="381"
column="63"/>
</issue>
@@ -2417,7 +1295,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="468"
+ line="470"
column="16"/>
</issue>
@@ -2428,7 +1306,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="479"
+ line="481"
column="16"/>
</issue>
@@ -2439,7 +1317,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="490"
+ line="492"
column="16"/>
</issue>
@@ -2450,7 +1328,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="502"
+ line="504"
column="16"/>
</issue>
@@ -2461,7 +1339,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="514"
+ line="516"
column="16"/>
</issue>
@@ -2472,7 +1350,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="526"
+ line="528"
column="16"/>
</issue>
@@ -2483,7 +1361,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="537"
+ line="539"
column="16"/>
</issue>
@@ -2494,7 +1372,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="548"
+ line="550"
column="16"/>
</issue>
@@ -2505,7 +1383,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"
- line="559"
+ line="561"
column="16"/>
</issue>
@@ -2890,7 +1768,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="629"
+ line="619"
column="29"/>
</issue>
@@ -2901,7 +1779,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="645"
+ line="635"
column="29"/>
</issue>
@@ -2912,7 +1790,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="645"
+ line="635"
column="48"/>
</issue>
@@ -2923,7 +1801,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="661"
+ line="651"
column="36"/>
</issue>
@@ -2934,7 +1812,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="678"
+ line="668"
column="40"/>
</issue>
@@ -2945,7 +1823,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="720"
+ line="710"
column="37"/>
</issue>
@@ -2956,7 +1834,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="764"
+ line="754"
column="34"/>
</issue>
@@ -2967,7 +1845,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="764"
+ line="754"
column="48"/>
</issue>
@@ -2978,7 +1856,7 @@
errorLine2=" ~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="794"
+ line="784"
column="12"/>
</issue>
@@ -2989,7 +1867,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="804"
+ line="794"
column="12"/>
</issue>
@@ -3000,7 +1878,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="813"
+ line="803"
column="34"/>
</issue>
@@ -3011,7 +1889,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="825"
+ line="815"
column="29"/>
</issue>
@@ -3022,7 +1900,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="841"
+ line="831"
column="26"/>
</issue>
@@ -3033,7 +1911,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="865"
+ line="855"
column="31"/>
</issue>
@@ -3044,7 +1922,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="934"
+ line="924"
column="27"/>
</issue>
@@ -3055,7 +1933,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="948"
+ line="938"
column="12"/>
</issue>
@@ -3066,7 +1944,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="962"
+ line="952"
column="12"/>
</issue>
@@ -3077,7 +1955,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="998"
+ line="988"
column="12"/>
</issue>
@@ -3088,7 +1966,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1009"
+ line="999"
column="43"/>
</issue>
@@ -3099,7 +1977,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1022"
+ line="1012"
column="46"/>
</issue>
@@ -3110,7 +1988,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1045"
+ line="1035"
column="19"/>
</issue>
@@ -3121,7 +1999,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1045"
+ line="1035"
column="55"/>
</issue>
@@ -3132,7 +2010,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1045"
+ line="1035"
column="72"/>
</issue>
@@ -3143,7 +2021,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1178"
+ line="1168"
column="31"/>
</issue>
@@ -3154,7 +2032,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1178"
+ line="1168"
column="47"/>
</issue>
@@ -3165,7 +2043,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1178"
+ line="1168"
column="62"/>
</issue>
@@ -3176,7 +2054,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1192"
+ line="1182"
column="43"/>
</issue>
@@ -3187,7 +2065,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1285"
+ line="1275"
column="42"/>
</issue>
@@ -3198,7 +2076,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1285"
+ line="1275"
column="58"/>
</issue>
@@ -3209,7 +2087,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1295"
+ line="1285"
column="41"/>
</issue>
@@ -3220,7 +2098,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1295"
+ line="1285"
column="55"/>
</issue>
@@ -3231,7 +2109,7 @@
errorLine2=" ~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1303"
+ line="1293"
column="38"/>
</issue>
@@ -3242,7 +2120,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1303"
+ line="1293"
column="47"/>
</issue>
@@ -3253,7 +2131,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1316"
+ line="1306"
column="39"/>
</issue>
@@ -3264,7 +2142,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1316"
+ line="1306"
column="55"/>
</issue>
@@ -3275,7 +2153,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1325"
+ line="1315"
column="38"/>
</issue>
@@ -3286,7 +2164,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1325"
+ line="1315"
column="52"/>
</issue>
@@ -3297,7 +2175,7 @@
errorLine2=" ~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1331"
+ line="1321"
column="35"/>
</issue>
@@ -3308,7 +2186,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1331"
+ line="1321"
column="44"/>
</issue>
@@ -3319,7 +2197,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1390"
+ line="1380"
column="33"/>
</issue>
@@ -3330,7 +2208,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1399"
+ line="1389"
column="33"/>
</issue>
@@ -3341,7 +2219,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1399"
+ line="1389"
column="54"/>
</issue>
@@ -3352,7 +2230,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1470"
+ line="1460"
column="36"/>
</issue>
@@ -3363,7 +2241,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1470"
+ line="1460"
column="51"/>
</issue>
@@ -3374,7 +2252,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1480"
+ line="1470"
column="36"/>
</issue>
@@ -3385,7 +2263,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1492"
+ line="1482"
column="36"/>
</issue>
@@ -3396,7 +2274,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1503"
+ line="1493"
column="39"/>
</issue>
@@ -3407,7 +2285,7 @@
errorLine2=" ~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1964"
+ line="1954"
column="23"/>
</issue>
@@ -3418,7 +2296,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1964"
+ line="1954"
column="39"/>
</issue>
@@ -3429,7 +2307,7 @@
errorLine2=" ~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1982"
+ line="1972"
column="23"/>
</issue>
@@ -3440,7 +2318,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1982"
+ line="1972"
column="39"/>
</issue>
@@ -3451,7 +2329,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1982"
+ line="1972"
column="53"/>
</issue>
@@ -3462,7 +2340,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="1999"
+ line="1989"
column="35"/>
</issue>
@@ -3473,7 +2351,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2043"
+ line="2033"
column="16"/>
</issue>
@@ -3484,7 +2362,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2051"
+ line="2041"
column="16"/>
</issue>
@@ -3495,7 +2373,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2061"
+ line="2051"
column="36"/>
</issue>
@@ -3506,7 +2384,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2071"
+ line="2061"
column="16"/>
</issue>
@@ -3517,7 +2395,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2081"
+ line="2071"
column="38"/>
</issue>
@@ -3528,7 +2406,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2091"
+ line="2081"
column="16"/>
</issue>
@@ -3539,7 +2417,7 @@
errorLine2=" ~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2113"
+ line="2103"
column="23"/>
</issue>
@@ -3550,7 +2428,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2113"
+ line="2103"
column="40"/>
</issue>
@@ -3561,7 +2439,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2168"
+ line="2158"
column="26"/>
</issue>
@@ -3572,7 +2450,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2195"
+ line="2185"
column="16"/>
</issue>
@@ -3583,7 +2461,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2207"
+ line="2197"
column="35"/>
</issue>
@@ -3594,7 +2472,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2227"
+ line="2217"
column="16"/>
</issue>
@@ -3605,7 +2483,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2247"
+ line="2237"
column="23"/>
</issue>
@@ -3616,7 +2494,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2247"
+ line="2237"
column="47"/>
</issue>
@@ -3627,7 +2505,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2269"
+ line="2259"
column="23"/>
</issue>
@@ -3638,7 +2516,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"
- line="2269"
+ line="2259"
column="57"/>
</issue>
@@ -3660,7 +2538,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="101"
+ line="102"
column="23"/>
</issue>
@@ -3671,7 +2549,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="101"
+ line="102"
column="64"/>
</issue>
@@ -3682,7 +2560,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="138"
+ line="139"
column="27"/>
</issue>
@@ -3693,7 +2571,7 @@
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="148"
+ line="149"
column="16"/>
</issue>
@@ -3704,7 +2582,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="148"
+ line="149"
column="55"/>
</issue>
@@ -3715,7 +2593,7 @@
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="157"
+ line="158"
column="16"/>
</issue>
@@ -3726,7 +2604,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="157"
+ line="158"
column="43"/>
</issue>
@@ -3737,7 +2615,7 @@
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="186"
+ line="187"
column="16"/>
</issue>
@@ -3748,7 +2626,7 @@
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="199"
+ line="200"
column="16"/>
</issue>
@@ -3759,7 +2637,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="199"
+ line="200"
column="49"/>
</issue>
@@ -3770,7 +2648,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="209"
+ line="210"
column="27"/>
</issue>
@@ -3781,7 +2659,7 @@
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="234"
+ line="235"
column="16"/>
</issue>
@@ -3792,7 +2670,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="234"
+ line="235"
column="44"/>
</issue>
@@ -3803,7 +2681,7 @@
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="302"
+ line="303"
column="16"/>
</issue>
@@ -3814,7 +2692,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="302"
+ line="303"
column="47"/>
</issue>
@@ -3825,7 +2703,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="387"
+ line="388"
column="27"/>
</issue>
@@ -3836,7 +2714,7 @@
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="401"
+ line="402"
column="16"/>
</issue>
@@ -3847,7 +2725,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="401"
+ line="402"
column="44"/>
</issue>
@@ -3858,7 +2736,7 @@
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="443"
+ line="444"
column="16"/>
</issue>
@@ -3869,7 +2747,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="443"
+ line="444"
column="47"/>
</issue>
@@ -3880,7 +2758,7 @@
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="475"
+ line="476"
column="16"/>
</issue>
@@ -3891,7 +2769,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/app/NotificationCompat.java"
- line="475"
+ line="476"
column="51"/>
</issue>
@@ -3924,7 +2802,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="616"
+ line="618"
column="31"/>
</issue>
@@ -3935,7 +2813,7 @@
errorLine2=" ~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="677"
+ line="679"
column="36"/>
</issue>
@@ -3946,7 +2824,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="739"
+ line="741"
column="12"/>
</issue>
@@ -3957,7 +2835,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="772"
+ line="774"
column="12"/>
</issue>
@@ -3968,7 +2846,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="806"
+ line="808"
column="19"/>
</issue>
@@ -3979,7 +2857,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="806"
+ line="808"
column="57"/>
</issue>
@@ -3990,7 +2868,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="851"
+ line="854"
column="12"/>
</issue>
@@ -4001,7 +2879,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="915"
+ line="918"
column="35"/>
</issue>
@@ -4012,7 +2890,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="938"
+ line="941"
column="23"/>
</issue>
@@ -4023,7 +2901,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="938"
+ line="941"
column="73"/>
</issue>
@@ -4034,7 +2912,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="967"
+ line="970"
column="16"/>
</issue>
@@ -4045,7 +2923,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="997"
+ line="1000"
column="16"/>
</issue>
@@ -4056,7 +2934,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1006"
+ line="1009"
column="16"/>
</issue>
@@ -4067,7 +2945,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1029"
+ line="1032"
column="16"/>
</issue>
@@ -4078,7 +2956,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1063"
+ line="1066"
column="28"/>
</issue>
@@ -4089,7 +2967,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1063"
+ line="1066"
column="43"/>
</issue>
@@ -4100,7 +2978,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1090"
+ line="1093"
column="20"/>
</issue>
@@ -4111,7 +2989,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1090"
+ line="1093"
column="38"/>
</issue>
@@ -4122,7 +3000,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1101"
+ line="1104"
column="20"/>
</issue>
@@ -4133,7 +3011,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1136"
+ line="1139"
column="24"/>
</issue>
@@ -4144,7 +3022,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1183"
+ line="1186"
column="16"/>
</issue>
@@ -4155,7 +3033,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1221"
+ line="1224"
column="16"/>
</issue>
@@ -4166,7 +3044,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1237"
+ line="1240"
column="16"/>
</issue>
@@ -4177,7 +3055,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1272"
+ line="1275"
column="16"/>
</issue>
@@ -4188,7 +3066,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1297"
+ line="1300"
column="16"/>
</issue>
@@ -4199,7 +3077,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1297"
+ line="1300"
column="40"/>
</issue>
@@ -4210,7 +3088,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1297"
+ line="1300"
column="55"/>
</issue>
@@ -4221,7 +3099,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1312"
+ line="1315"
column="16"/>
</issue>
@@ -4232,7 +3110,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1312"
+ line="1315"
column="40"/>
</issue>
@@ -4243,7 +3121,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1328"
+ line="1331"
column="16"/>
</issue>
@@ -4254,7 +3132,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1341"
+ line="1344"
column="16"/>
</issue>
@@ -4265,7 +3143,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1341"
+ line="1344"
column="40"/>
</issue>
@@ -4276,7 +3154,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1354"
+ line="1357"
column="16"/>
</issue>
@@ -4287,7 +3165,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1354"
+ line="1357"
column="66"/>
</issue>
@@ -4298,7 +3176,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1366"
+ line="1369"
column="16"/>
</issue>
@@ -4309,7 +3187,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1366"
+ line="1369"
column="34"/>
</issue>
@@ -4320,7 +3198,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"
- line="1374"
+ line="1377"
column="16"/>
</issue>
@@ -4331,7 +3209,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="126"
+ line="128"
column="31"/>
</issue>
@@ -4342,7 +3220,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="158"
+ line="160"
column="19"/>
</issue>
@@ -4353,7 +3231,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="179"
+ line="181"
column="19"/>
</issue>
@@ -4364,7 +3242,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="190"
+ line="192"
column="19"/>
</issue>
@@ -4375,7 +3253,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="206"
+ line="208"
column="19"/>
</issue>
@@ -4386,7 +3264,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="237"
+ line="239"
column="19"/>
</issue>
@@ -4397,7 +3275,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="332"
+ line="334"
column="19"/>
</issue>
@@ -4408,7 +3286,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="332"
+ line="334"
column="43"/>
</issue>
@@ -4419,7 +3297,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/android/support/v4/media/RatingCompat.java"
- line="375"
+ line="377"
column="12"/>
</issue>
@@ -4430,7 +3308,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/androidx/media/VolumeProviderCompat.java"
- line="188"
+ line="190"
column="29"/>
</issue>
@@ -4441,7 +3319,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/media/VolumeProviderCompat.java"
- line="200"
+ line="202"
column="12"/>
</issue>
@@ -4452,7 +3330,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/media/VolumeProviderCompat.java"
- line="236"
+ line="238"
column="46"/>
</issue>
diff --git a/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java b/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
index c9aa019..fafb972 100644
--- a/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
@@ -55,6 +55,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.media.MediaDescription;
import android.media.browse.MediaBrowser;
import android.os.BadParcelableException;
import android.os.Binder;
@@ -75,6 +76,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -496,9 +498,9 @@
return null;
}
MediaBrowser.MediaItem itemFwk = (MediaBrowser.MediaItem) itemObj;
- int flags = itemFwk.getFlags();
+ int flags = Api21Impl.getFlags(itemFwk);
MediaDescriptionCompat descriptionCompat =
- MediaDescriptionCompat.fromMediaDescription(itemFwk.getDescription());
+ MediaDescriptionCompat.fromMediaDescription(Api21Impl.getDescription(itemFwk));
return new MediaItem(descriptionCompat, flags);
}
@@ -2364,4 +2366,19 @@
}
}
}
+
+ @RequiresApi(21)
+ private static class Api21Impl {
+ private Api21Impl() {}
+
+ @DoNotInline
+ static MediaDescription getDescription(MediaBrowser.MediaItem item) {
+ return item.getDescription();
+ }
+
+ @DoNotInline
+ static int getFlags(MediaBrowser.MediaItem item) {
+ return item.getFlags();
+ }
+ }
}
diff --git a/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java b/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
index 07b3dcc..49628eb 100644
--- a/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
@@ -28,7 +28,9 @@
import android.support.v4.media.session.MediaSessionCompat;
import android.text.TextUtils;
+import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
/**
@@ -336,13 +338,13 @@
if (mDescriptionFwk != null || Build.VERSION.SDK_INT < 21) {
return mDescriptionFwk;
}
- MediaDescription.Builder bob = new MediaDescription.Builder();
- bob.setMediaId(mMediaId);
- bob.setTitle(mTitle);
- bob.setSubtitle(mSubtitle);
- bob.setDescription(mDescription);
- bob.setIconBitmap(mIcon);
- bob.setIconUri(mIconUri);
+ MediaDescription.Builder bob = Api21Impl.createBuilder();
+ Api21Impl.setMediaId(bob, mMediaId);
+ Api21Impl.setTitle(bob, mTitle);
+ Api21Impl.setSubtitle(bob, mSubtitle);
+ Api21Impl.setDescription(bob, mDescription);
+ Api21Impl.setIconBitmap(bob, mIcon);
+ Api21Impl.setIconUri(bob, mIconUri);
// Media URI was not added until API 23, so add it to the Bundle of extras to
// ensure the data is not lost - this ensures that
// fromMediaDescription(getMediaDescription(mediaDescriptionCompat)) returns
@@ -355,11 +357,11 @@
}
extras.putParcelable(DESCRIPTION_KEY_MEDIA_URI, mMediaUri);
}
- bob.setExtras(extras);
+ Api21Impl.setExtras(bob, extras);
if (Build.VERSION.SDK_INT >= 23) {
- bob.setMediaUri(mMediaUri);
+ Api23Impl.setMediaUri(bob, mMediaUri);
}
- mDescriptionFwk = bob.build();
+ mDescriptionFwk = Api21Impl.build(bob);
return mDescriptionFwk;
}
@@ -380,13 +382,13 @@
if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) {
Builder bob = new Builder();
MediaDescription description = (MediaDescription) descriptionObj;
- bob.setMediaId(description.getMediaId());
- bob.setTitle(description.getTitle());
- bob.setSubtitle(description.getSubtitle());
- bob.setDescription(description.getDescription());
- bob.setIconBitmap(description.getIconBitmap());
- bob.setIconUri(description.getIconUri());
- Bundle extras = description.getExtras();
+ bob.setMediaId(Api21Impl.getMediaId(description));
+ bob.setTitle(Api21Impl.getTitle(description));
+ bob.setSubtitle(Api21Impl.getSubtitle(description));
+ bob.setDescription(Api21Impl.getDescription(description));
+ bob.setIconBitmap(Api21Impl.getIconBitmap(description));
+ bob.setIconUri(Api21Impl.getIconUri(description));
+ Bundle extras = Api21Impl.getExtras(description);
if (extras != null) {
extras = MediaSessionCompat.unparcelWithClassLoader(extras);
}
@@ -412,7 +414,7 @@
if (mediaUri != null) {
bob.setMediaUri(mediaUri);
} else if (Build.VERSION.SDK_INT >= 23) {
- bob.setMediaUri(description.getMediaUri());
+ bob.setMediaUri(Api23Impl.getMediaUri(description));
}
MediaDescriptionCompat descriptionCompat = bob.build();
descriptionCompat.mDescriptionFwk = description;
@@ -561,4 +563,120 @@
mIconUri, mExtras, mMediaUri);
}
}
+
+ @RequiresApi(21)
+ private static class Api21Impl {
+ private Api21Impl() {}
+
+ @DoNotInline
+ static MediaDescription.Builder createBuilder() {
+ return new MediaDescription.Builder();
+ }
+
+ @DoNotInline
+ static void setMediaId(MediaDescription.Builder builder,
+ @Nullable String mediaId) {
+ builder.setMediaId(mediaId);
+ }
+
+ @DoNotInline
+ static void setTitle(MediaDescription.Builder builder,
+ @Nullable CharSequence title) {
+ builder.setTitle(title);
+ }
+
+ @DoNotInline
+ static void setSubtitle(MediaDescription.Builder builder,
+ @Nullable CharSequence subtitle) {
+ builder.setSubtitle(subtitle);
+ }
+
+ @DoNotInline
+ static void setDescription(MediaDescription.Builder builder,
+ @Nullable CharSequence description) {
+ builder.setDescription(description);
+ }
+
+ @DoNotInline
+ static void setIconBitmap(MediaDescription.Builder builder,
+ @Nullable Bitmap icon) {
+ builder.setIconBitmap(icon);
+ }
+
+ @DoNotInline
+ static void setIconUri(MediaDescription.Builder builder,
+ @Nullable Uri iconUri) {
+ builder.setIconUri(iconUri);
+ }
+
+ @DoNotInline
+ static void setExtras(MediaDescription.Builder builder,
+ @Nullable Bundle extras) {
+ builder.setExtras(extras);
+ }
+
+ @DoNotInline
+ static MediaDescription build(MediaDescription.Builder builder) {
+ return builder.build();
+ }
+
+ @DoNotInline
+ @Nullable
+ static String getMediaId(MediaDescription description) {
+ return description.getMediaId();
+ }
+
+ @DoNotInline
+ @Nullable
+ static CharSequence getTitle(MediaDescription description) {
+ return description.getTitle();
+ }
+
+ @DoNotInline
+ @Nullable
+ static CharSequence getSubtitle(MediaDescription description) {
+ return description.getSubtitle();
+ }
+
+ @DoNotInline
+ @Nullable
+ static CharSequence getDescription(MediaDescription description) {
+ return description.getDescription();
+ }
+
+ @DoNotInline
+ @Nullable
+ static Bitmap getIconBitmap(MediaDescription description) {
+ return description.getIconBitmap();
+ }
+
+ @DoNotInline
+ @Nullable
+ static Uri getIconUri(MediaDescription description) {
+ return description.getIconUri();
+ }
+
+ @DoNotInline
+ @Nullable
+ static Bundle getExtras(MediaDescription description) {
+ return description.getExtras();
+ }
+ }
+
+ @RequiresApi(23)
+ private static class Api23Impl {
+ private Api23Impl() {}
+
+ @DoNotInline
+ static void setMediaUri(MediaDescription.Builder builder,
+ @Nullable Uri mediaUri) {
+ builder.setMediaUri(mediaUri);
+ }
+
+ @DoNotInline
+ @Nullable
+ static Uri getMediaUri(MediaDescription description) {
+ return description.getMediaUri();
+ }
+ }
}
diff --git a/media/media/src/main/java/android/support/v4/media/RatingCompat.java b/media/media/src/main/java/android/support/v4/media/RatingCompat.java
index 42b6da0..6578a65 100644
--- a/media/media/src/main/java/android/support/v4/media/RatingCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/RatingCompat.java
@@ -26,7 +26,9 @@
import android.os.Parcelable;
import android.util.Log;
+import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import java.lang.annotation.Retention;
@@ -331,25 +333,25 @@
*/
public static RatingCompat fromRating(Object ratingObj) {
if (ratingObj != null && Build.VERSION.SDK_INT >= 19) {
- final int ratingStyle = ((Rating) ratingObj).getRatingStyle();
+ final int ratingStyle = Api19Impl.getRatingStyle((Rating) ratingObj);
final RatingCompat rating;
- if (((Rating) ratingObj).isRated()) {
+ if (Api19Impl.isRated((Rating) ratingObj)) {
switch (ratingStyle) {
case RATING_HEART:
- rating = newHeartRating(((Rating) ratingObj).hasHeart());
+ rating = newHeartRating(Api19Impl.hasHeart((Rating) ratingObj));
break;
case RATING_THUMB_UP_DOWN:
- rating = newThumbRating(((Rating) ratingObj).isThumbUp());
+ rating = newThumbRating(Api19Impl.isThumbUp((Rating) ratingObj));
break;
case RATING_3_STARS:
case RATING_4_STARS:
case RATING_5_STARS:
rating = newStarRating(ratingStyle,
- ((Rating) ratingObj).getStarRating());
+ Api19Impl.getStarRating((Rating) ratingObj));
break;
case RATING_PERCENTAGE:
rating = newPercentageRating(
- ((Rating) ratingObj).getPercentRating());
+ Api19Impl.getPercentRating((Rating) ratingObj));
break;
default:
return null;
@@ -377,27 +379,87 @@
if (isRated()) {
switch (mRatingStyle) {
case RATING_HEART:
- mRatingObj = Rating.newHeartRating(hasHeart());
+ mRatingObj = Api19Impl.newHeartRating(hasHeart());
break;
case RATING_THUMB_UP_DOWN:
- mRatingObj = Rating.newThumbRating(isThumbUp());
+ mRatingObj = Api19Impl.newThumbRating(isThumbUp());
break;
case RATING_3_STARS:
case RATING_4_STARS:
case RATING_5_STARS:
- mRatingObj = Rating.newStarRating(mRatingStyle,
+ mRatingObj = Api19Impl.newStarRating(mRatingStyle,
getStarRating());
break;
case RATING_PERCENTAGE:
- mRatingObj = Rating.newPercentageRating(getPercentRating());
+ mRatingObj = Api19Impl.newPercentageRating(getPercentRating());
break;
default:
return null;
}
} else {
- mRatingObj = Rating.newUnratedRating(mRatingStyle);
+ mRatingObj = Api19Impl.newUnratedRating(mRatingStyle);
}
}
return mRatingObj;
}
+
+ @RequiresApi(19)
+ private static class Api19Impl {
+ private Api19Impl() {}
+
+ @DoNotInline
+ static int getRatingStyle(Rating rating) {
+ return rating.getRatingStyle();
+ }
+
+ @DoNotInline
+ static boolean isRated(Rating rating) {
+ return rating.isRated();
+ }
+
+ @DoNotInline
+ static boolean hasHeart(Rating rating) {
+ return rating.hasHeart();
+ }
+
+ @DoNotInline
+ static boolean isThumbUp(Rating rating) {
+ return rating.isThumbUp();
+ }
+
+ @DoNotInline
+ static float getStarRating(Rating rating) {
+ return rating.getStarRating();
+ }
+
+ @DoNotInline
+ static float getPercentRating(Rating rating) {
+ return rating.getPercentRating();
+ }
+
+ @DoNotInline
+ static Rating newHeartRating(boolean hasHeart) {
+ return Rating.newHeartRating(hasHeart);
+ }
+
+ @DoNotInline
+ static Rating newThumbRating(boolean thumbIsUp) {
+ return Rating.newThumbRating(thumbIsUp);
+ }
+
+ @DoNotInline
+ static Rating newStarRating(int starRatingStyle, float starRating) {
+ return Rating.newStarRating(starRatingStyle, starRating);
+ }
+
+ @DoNotInline
+ static Rating newPercentageRating(float percent) {
+ return Rating.newPercentageRating(percent);
+ }
+
+ @DoNotInline
+ static Rating newUnratedRating(int ratingStyle) {
+ return Rating.newUnratedRating(ratingStyle);
+ }
+ }
}
diff --git a/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java b/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
index dfe730b..7f71344 100644
--- a/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -163,12 +163,7 @@
activity.getWindow().getDecorView().setTag(
R.id.media_controller_compat_view_tag, mediaController);
if (android.os.Build.VERSION.SDK_INT >= 21) {
- MediaController controllerFwk = null;
- if (mediaController != null) {
- Object sessionTokenObj = mediaController.getSessionToken().getToken();
- controllerFwk = new MediaController(activity, (MediaSession.Token) sessionTokenObj);
- }
- activity.setMediaController(controllerFwk);
+ MediaControllerImplApi21.setMediaController(activity, mediaController);
}
}
@@ -189,13 +184,7 @@
if (tag instanceof MediaControllerCompat) {
return (MediaControllerCompat) tag;
} else if (android.os.Build.VERSION.SDK_INT >= 21) {
- MediaController controllerFwk = activity.getMediaController();
- if (controllerFwk == null) {
- return null;
- }
- MediaSession.Token sessionTokenFwk = controllerFwk.getSessionToken();
- return new MediaControllerCompat(activity,
- MediaSessionCompat.Token.fromToken(sessionTokenFwk));
+ return MediaControllerImplApi21.getMediaController(activity);
}
return null;
}
@@ -238,7 +227,9 @@
}
mToken = session.getSessionToken();
- if (android.os.Build.VERSION.SDK_INT >= 21) {
+ if (Build.VERSION.SDK_INT >= 29) {
+ mImpl = new MediaControllerImplApi29(context, mToken);
+ } else if (Build.VERSION.SDK_INT >= 21) {
mImpl = new MediaControllerImplApi21(context, mToken);
} else {
mImpl = new MediaControllerImplBase(mToken);
@@ -2027,7 +2018,7 @@
private HashMap<Callback, ExtraCallback> mCallbackMap = new HashMap<>();
- private Bundle mSessionInfo;
+ protected Bundle mSessionInfo;
final MediaSessionCompat.Token mSessionToken;
@@ -2090,7 +2081,16 @@
@Override
public TransportControls getTransportControls() {
- return new TransportControlsApi21(mControllerFwk.getTransportControls());
+ MediaController.TransportControls controlsFwk = mControllerFwk.getTransportControls();
+ if (Build.VERSION.SDK_INT >= 29) {
+ return new TransportControlsApi29(controlsFwk);
+ } else if (Build.VERSION.SDK_INT >= 24) {
+ return new TransportControlsApi24(controlsFwk);
+ } else if (Build.VERSION.SDK_INT >= 23) {
+ return new TransportControlsApi23(controlsFwk);
+ } else {
+ return new TransportControlsApi21(controlsFwk);
+ }
}
@Override
@@ -2265,10 +2265,7 @@
return new Bundle(mSessionInfo);
}
- // Get the info from the connected session.
- if (Build.VERSION.SDK_INT >= 29) {
- mSessionInfo = mControllerFwk.getSessionInfo();
- } else if (mSessionToken.getExtraBinder() != null) {
+ if (mSessionToken.getExtraBinder() != null) {
try {
mSessionInfo = mSessionToken.getExtraBinder().getSessionInfo();
} catch (RemoteException e) {
@@ -2310,6 +2307,27 @@
mPendingCallbacks.clear();
}
+ static void setMediaController(@NonNull Activity activity,
+ @Nullable MediaControllerCompat mediaControllerCompat) {
+ MediaController controllerFwk = null;
+ if (mediaControllerCompat != null) {
+ Object sessionTokenObj = mediaControllerCompat.getSessionToken().getToken();
+ controllerFwk = new MediaController(activity, (MediaSession.Token) sessionTokenObj);
+ }
+ activity.setMediaController(controllerFwk);
+ }
+
+ @Nullable
+ static MediaControllerCompat getMediaController(@NonNull Activity activity) {
+ MediaController controllerFwk = activity.getMediaController();
+ if (controllerFwk == null) {
+ return null;
+ }
+ MediaSession.Token sessionTokenFwk = controllerFwk.getSessionToken();
+ return new MediaControllerCompat(activity,
+ MediaSessionCompat.Token.fromToken(sessionTokenFwk));
+ }
+
private static class ExtraBinderRequestResultReceiver extends ResultReceiver {
private WeakReference<MediaControllerImplApi21> mMediaControllerImpl;
@@ -2380,6 +2398,23 @@
}
}
+ @RequiresApi(29)
+ static class MediaControllerImplApi29 extends MediaControllerImplApi21 {
+ MediaControllerImplApi29(Context context, MediaSessionCompat.Token sessionToken) {
+ super(context, sessionToken);
+ }
+
+ @Override
+ public Bundle getSessionInfo() {
+ if (mSessionInfo != null) {
+ return new Bundle(mSessionInfo);
+ }
+ mSessionInfo = mControllerFwk.getSessionInfo();
+ mSessionInfo = MediaSessionCompat.unparcelWithClassLoader(mSessionInfo);
+ return mSessionInfo == null ? Bundle.EMPTY : new Bundle(mSessionInfo);
+ }
+ }
+
@RequiresApi(21)
static class TransportControlsApi21 extends TransportControls {
protected final MediaController.TransportControls mControlsFwk;
@@ -2390,19 +2425,11 @@
@Override
public void prepare() {
- if (Build.VERSION.SDK_INT >= 24) {
- mControlsFwk.prepare();
- return;
- }
sendCustomAction(MediaSessionCompat.ACTION_PREPARE, null);
}
@Override
public void prepareFromMediaId(String mediaId, Bundle extras) {
- if (Build.VERSION.SDK_INT >= 24) {
- mControlsFwk.prepareFromMediaId(mediaId, extras);
- return;
- }
Bundle bundle = new Bundle();
bundle.putString(MediaSessionCompat.ACTION_ARGUMENT_MEDIA_ID, mediaId);
bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras);
@@ -2411,10 +2438,6 @@
@Override
public void prepareFromSearch(String query, Bundle extras) {
- if (Build.VERSION.SDK_INT >= 24) {
- mControlsFwk.prepareFromSearch(query, extras);
- return;
- }
Bundle bundle = new Bundle();
bundle.putString(MediaSessionCompat.ACTION_ARGUMENT_QUERY, query);
bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras);
@@ -2423,10 +2446,6 @@
@Override
public void prepareFromUri(Uri uri, Bundle extras) {
- if (Build.VERSION.SDK_INT >= 24) {
- mControlsFwk.prepareFromUri(uri, extras);
- return;
- }
Bundle bundle = new Bundle();
bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_URI, uri);
bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras);
@@ -2491,10 +2510,6 @@
if (speed == 0.0f) {
throw new IllegalArgumentException("speed must not be zero");
}
- if (Build.VERSION.SDK_INT >= 29) {
- mControlsFwk.setPlaybackSpeed(speed);
- return;
- }
Bundle bundle = new Bundle();
bundle.putFloat(MediaSessionCompat.ACTION_ARGUMENT_PLAYBACK_SPEED, speed);
sendCustomAction(MediaSessionCompat.ACTION_SET_PLAYBACK_SPEED, bundle);
@@ -2533,10 +2548,6 @@
@Override
public void playFromUri(Uri uri, Bundle extras) {
- if (Build.VERSION.SDK_INT >= 23) {
- mControlsFwk.playFromUri(uri, extras);
- return;
- }
if (uri == null || Uri.EMPTY.equals(uri)) {
throw new IllegalArgumentException(
"You must specify a non-empty Uri for playFromUri.");
@@ -2564,4 +2575,58 @@
mControlsFwk.sendCustomAction(action, args);
}
}
+
+ @RequiresApi(23)
+ static class TransportControlsApi23 extends TransportControlsApi21 {
+ TransportControlsApi23(MediaController.TransportControls controlsFwk) {
+ super(controlsFwk);
+ }
+
+ @Override
+ public void playFromUri(Uri uri, Bundle extras) {
+ mControlsFwk.playFromUri(uri, extras);
+ }
+ }
+
+ @RequiresApi(24)
+ static class TransportControlsApi24 extends TransportControlsApi23 {
+ TransportControlsApi24(MediaController.TransportControls controlsFwk) {
+ super(controlsFwk);
+ }
+
+ @Override
+ public void prepare() {
+ mControlsFwk.prepare();
+ }
+
+ @Override
+ public void prepareFromMediaId(String mediaId, Bundle extras) {
+ mControlsFwk.prepareFromMediaId(mediaId, extras);
+ }
+
+ @Override
+ public void prepareFromSearch(String query, Bundle extras) {
+ mControlsFwk.prepareFromSearch(query, extras);
+ }
+
+ @Override
+ public void prepareFromUri(Uri uri, Bundle extras) {
+ mControlsFwk.prepareFromUri(uri, extras);
+ }
+ }
+
+ @RequiresApi(29)
+ static class TransportControlsApi29 extends TransportControlsApi24 {
+ TransportControlsApi29(MediaController.TransportControls controlsFwk) {
+ super(controlsFwk);
+ }
+
+ @Override
+ public void setPlaybackSpeed(float speed) {
+ if (speed == 0.0f) {
+ throw new IllegalArgumentException("speed must not be zero");
+ }
+ mControlsFwk.setPlaybackSpeed(speed);
+ }
+ }
}
diff --git a/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java b/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
index 5518d39..5f0fe6b 100644
--- a/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -65,6 +65,7 @@
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import androidx.annotation.DoNotInline;
import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -570,13 +571,14 @@
}
if (android.os.Build.VERSION.SDK_INT >= 21) {
- MediaSession sessionFwk = createFwkMediaSession(context, tag, sessionInfo);
if (android.os.Build.VERSION.SDK_INT >= 29) {
- mImpl = new MediaSessionImplApi29(sessionFwk, session2Token, sessionInfo);
+ mImpl = new MediaSessionImplApi29(context, tag, session2Token, sessionInfo);
} else if (android.os.Build.VERSION.SDK_INT >= 28) {
- mImpl = new MediaSessionImplApi28(sessionFwk, session2Token, sessionInfo);
+ mImpl = new MediaSessionImplApi28(context, tag, session2Token, sessionInfo);
+ } else if (android.os.Build.VERSION.SDK_INT >= 22) {
+ mImpl = new MediaSessionImplApi22(context, tag, session2Token, sessionInfo);
} else {
- mImpl = new MediaSessionImplApi21(sessionFwk, session2Token, sessionInfo);
+ mImpl = new MediaSessionImplApi21(context, tag, session2Token, sessionInfo);
}
// Set default callback to respond to controllers' extra binder requests.
Handler handler = new Handler(Looper.myLooper() != null
@@ -606,16 +608,6 @@
mController = new MediaControllerCompat(context, this);
}
- @RequiresApi(21)
- private MediaSession createFwkMediaSession(Context context, String tag,
- Bundle sessionInfo) {
- if (android.os.Build.VERSION.SDK_INT >= 29) {
- return new MediaSession(context, tag, sessionInfo);
- } else {
- return new MediaSession(context, tag);
- }
- }
-
/**
* Adds a callback to receive updates on for the MediaSession. This includes
* media button and volume events. The caller's thread will be used to post
@@ -2228,7 +2220,7 @@
if (mItemFwk != null || android.os.Build.VERSION.SDK_INT < 21) {
return mItemFwk;
}
- mItemFwk = new MediaSession.QueueItem(
+ mItemFwk = Api21Impl.createQueueItem(
(MediaDescription) mDescription.getMediaDescription(),
mId);
return mItemFwk;
@@ -2249,10 +2241,10 @@
return null;
}
MediaSession.QueueItem queueItemObj = (MediaSession.QueueItem) queueItem;
- Object descriptionObj = queueItemObj.getDescription();
+ Object descriptionObj = Api21Impl.getDescription(queueItemObj);
MediaDescriptionCompat description = MediaDescriptionCompat.fromMediaDescription(
descriptionObj);
- long id = queueItemObj.getQueueId();
+ long id = Api21Impl.getQueueId(queueItemObj);
return new QueueItem(queueItemObj, description, id);
}
@@ -2297,6 +2289,26 @@
"Description=" + mDescription +
", Id=" + mId + " }";
}
+
+ @RequiresApi(21)
+ private static class Api21Impl {
+ private Api21Impl() {}
+
+ @DoNotInline
+ static MediaSession.QueueItem createQueueItem(MediaDescription description, long id) {
+ return new MediaSession.QueueItem(description, id);
+ }
+
+ @DoNotInline
+ static MediaDescription getDescription(MediaSession.QueueItem queueItem) {
+ return queueItem.getDescription();
+ }
+
+ @DoNotInline
+ static long getQueueId(MediaSession.QueueItem queueItem) {
+ return queueItem.getQueueId();
+ }
+ }
}
/**
@@ -3802,9 +3814,9 @@
@GuardedBy("mLock")
RemoteUserInfo mRemoteUserInfo;
- MediaSessionImplApi21(MediaSession sessionFwk, VersionedParcelable session2Token,
+ MediaSessionImplApi21(Context context, String tag, VersionedParcelable session2Token,
Bundle sessionInfo) {
- mSessionFwk = sessionFwk;
+ mSessionFwk = createFwkMediaSession(context, tag, sessionInfo);
mToken = new Token(mSessionFwk.getSessionToken(), new ExtraSession(), session2Token);
mSessionInfo = sessionInfo;
// For backward compatibility, these flags are always set.
@@ -3823,6 +3835,10 @@
setFlags(FLAG_HANDLES_MEDIA_BUTTONS | FLAG_HANDLES_TRANSPORT_CONTROLS);
}
+ public MediaSession createFwkMediaSession(Context context, String tag, Bundle sessionInfo) {
+ return new MediaSession(context, tag);
+ }
+
@Override
public void setCallback(Callback callback, Handler handler) {
synchronized (mLock) {
@@ -3968,11 +3984,7 @@
@Override
public void setRatingType(@RatingCompat.Style int type) {
- if (android.os.Build.VERSION.SDK_INT < 22) {
- mRatingType = type;
- } else {
- mSessionFwk.setRatingType(type);
- }
+ mRatingType = type;
}
@Override
@@ -4384,11 +4396,28 @@
}
}
- @RequiresApi(28)
- static class MediaSessionImplApi28 extends MediaSessionImplApi21 {
- MediaSessionImplApi28(MediaSession sessionFwk, VersionedParcelable session2Token,
+ @RequiresApi(22)
+ static class MediaSessionImplApi22 extends MediaSessionImplApi21 {
+ MediaSessionImplApi22(Context context, String tag, VersionedParcelable session2Token,
Bundle sessionInfo) {
- super(sessionFwk, session2Token, sessionInfo);
+ super(context, tag, session2Token, sessionInfo);
+ }
+
+ MediaSessionImplApi22(Object mediaSession) {
+ super(mediaSession);
+ }
+
+ @Override
+ public void setRatingType(@RatingCompat.Style int type) {
+ mSessionFwk.setRatingType(type);
+ }
+ }
+
+ @RequiresApi(28)
+ static class MediaSessionImplApi28 extends MediaSessionImplApi22 {
+ MediaSessionImplApi28(Context context, String tag, VersionedParcelable session2Token,
+ Bundle sessionInfo) {
+ super(context, tag, session2Token, sessionInfo);
}
MediaSessionImplApi28(Object mediaSession) {
@@ -4411,14 +4440,19 @@
@RequiresApi(29)
static class MediaSessionImplApi29 extends MediaSessionImplApi28 {
- MediaSessionImplApi29(MediaSession sessionFwk, VersionedParcelable session2Token,
+ MediaSessionImplApi29(Context context, String tag, VersionedParcelable session2Token,
Bundle sessionInfo) {
- super(sessionFwk, session2Token, sessionInfo);
+ super(context, tag, session2Token, sessionInfo);
}
MediaSessionImplApi29(Object mediaSession) {
super(mediaSession);
mSessionInfo = ((MediaSession) mediaSession).getController().getSessionInfo();
}
+
+ @Override
+ public MediaSession createFwkMediaSession(Context context, String tag, Bundle sessionInfo) {
+ return new MediaSession(context, tag, sessionInfo);
+ }
}
}
diff --git a/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java b/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
index d51a003..1026438 100644
--- a/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -28,9 +28,11 @@
import android.text.TextUtils;
import android.view.KeyEvent;
+import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
import androidx.annotation.LongDef;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import java.lang.annotation.Retention;
@@ -806,7 +808,8 @@
public static PlaybackStateCompat fromPlaybackState(Object stateObj) {
if (stateObj != null && Build.VERSION.SDK_INT >= 21) {
PlaybackState stateFwk = (PlaybackState) stateObj;
- List<PlaybackState.CustomAction> customActionFwks = stateFwk.getCustomActions();
+ List<PlaybackState.CustomAction> customActionFwks =
+ Api21Impl.getCustomActions(stateFwk);
List<PlaybackStateCompat.CustomAction> customActions = null;
if (customActionFwks != null) {
customActions = new ArrayList<>(customActionFwks.size());
@@ -816,22 +819,22 @@
}
Bundle extras;
if (Build.VERSION.SDK_INT >= 22) {
- extras = stateFwk.getExtras();
+ extras = Api22Impl.getExtras(stateFwk);
MediaSessionCompat.ensureClassLoader(extras);
} else {
extras = null;
}
PlaybackStateCompat stateCompat = new PlaybackStateCompat(
- stateFwk.getState(),
- stateFwk.getPosition(),
- stateFwk.getBufferedPosition(),
- stateFwk.getPlaybackSpeed(),
- stateFwk.getActions(),
+ Api21Impl.getState(stateFwk),
+ Api21Impl.getPosition(stateFwk),
+ Api21Impl.getBufferedPosition(stateFwk),
+ Api21Impl.getPlaybackSpeed(stateFwk),
+ Api21Impl.getActions(stateFwk),
ERROR_CODE_UNKNOWN_ERROR,
- stateFwk.getErrorMessage(),
- stateFwk.getLastPositionUpdateTime(),
+ Api21Impl.getErrorMessage(stateFwk),
+ Api21Impl.getLastPositionUpdateTime(stateFwk),
customActions,
- stateFwk.getActiveQueueItemId(),
+ Api21Impl.getActiveQueueItemId(stateFwk),
extras);
stateCompat.mStateFwk = stateFwk;
return stateCompat;
@@ -850,20 +853,20 @@
*/
public Object getPlaybackState() {
if (mStateFwk == null && Build.VERSION.SDK_INT >= 21) {
- PlaybackState.Builder builder = new PlaybackState.Builder();
- builder.setState(mState, mPosition, mSpeed, mUpdateTime);
- builder.setBufferedPosition(mBufferedPosition);
- builder.setActions(mActions);
- builder.setErrorMessage(mErrorMessage);
+ PlaybackState.Builder builder = Api21Impl.createBuilder();
+ Api21Impl.setState(builder, mState, mPosition, mSpeed, mUpdateTime);
+ Api21Impl.setBufferedPosition(builder, mBufferedPosition);
+ Api21Impl.setActions(builder, mActions);
+ Api21Impl.setErrorMessage(builder, mErrorMessage);
for (PlaybackStateCompat.CustomAction customAction : mCustomActions) {
- builder.addCustomAction(
+ Api21Impl.addCustomAction(builder,
(PlaybackState.CustomAction) customAction.getCustomAction());
}
- builder.setActiveQueueItemId(mActiveItemId);
+ Api21Impl.setActiveQueueItemId(builder, mActiveItemId);
if (Build.VERSION.SDK_INT >= 22) {
- builder.setExtras(mExtras);
+ Api22Impl.setExtras(builder, mExtras);
}
- mStateFwk = builder.build();
+ mStateFwk = Api21Impl.build(builder);
}
return mStateFwk;
}
@@ -942,13 +945,13 @@
PlaybackState.CustomAction customActionFwk =
(PlaybackState.CustomAction) customActionObj;
- Bundle extras = customActionFwk.getExtras();
+ Bundle extras = Api21Impl.getExtras(customActionFwk);
MediaSessionCompat.ensureClassLoader(extras);
PlaybackStateCompat.CustomAction customActionCompat =
new PlaybackStateCompat.CustomAction(
- customActionFwk.getAction(),
- customActionFwk.getName(),
- customActionFwk.getIcon(),
+ Api21Impl.getAction(customActionFwk),
+ Api21Impl.getName(customActionFwk),
+ Api21Impl.getIcon(customActionFwk),
extras);
customActionCompat.mCustomActionFwk = customActionFwk;
return customActionCompat;
@@ -969,10 +972,10 @@
return mCustomActionFwk;
}
- PlaybackState.CustomAction.Builder builder = new PlaybackState.CustomAction.Builder(
- mAction, mName, mIcon);
- builder.setExtras(mExtras);
- return builder.build();
+ PlaybackState.CustomAction.Builder builder =
+ Api21Impl.createCustomActionBuilder(mAction, mName, mIcon);
+ Api21Impl.setExtras(builder, mExtras);
+ return Api21Impl.build(builder);
}
public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR
@@ -1377,4 +1380,147 @@
mCustomActions, mActiveItemId, mExtras);
}
}
-}
+
+ @RequiresApi(21)
+ private static class Api21Impl {
+ private Api21Impl() {}
+
+ @DoNotInline
+ static PlaybackState.Builder createBuilder() {
+ return new PlaybackState.Builder();
+ }
+
+ @DoNotInline
+ static void setState(PlaybackState.Builder builder, int state, long position,
+ float playbackSpeed, long updateTime) {
+ builder.setState(state, position, playbackSpeed, updateTime);
+ }
+
+ @DoNotInline
+ static void setBufferedPosition(PlaybackState.Builder builder, long bufferedPosition) {
+ builder.setBufferedPosition(bufferedPosition);
+ }
+
+ @DoNotInline
+ static void setActions(PlaybackState.Builder builder, long actions) {
+ builder.setActions(actions);
+ }
+
+ @DoNotInline
+ static void setErrorMessage(PlaybackState.Builder builder, CharSequence error) {
+ builder.setErrorMessage(error);
+ }
+
+ @DoNotInline
+ static void addCustomAction(PlaybackState.Builder builder,
+ PlaybackState.CustomAction customAction) {
+ builder.addCustomAction(customAction);
+ }
+
+ @DoNotInline
+ static void setActiveQueueItemId(PlaybackState.Builder builder, long id) {
+ builder.setActiveQueueItemId(id);
+ }
+
+ @DoNotInline
+ static List<PlaybackState.CustomAction> getCustomActions(PlaybackState state) {
+ return state.getCustomActions();
+ }
+
+ @DoNotInline
+ static PlaybackState build(PlaybackState.Builder builder) {
+ return builder.build();
+ }
+
+ @DoNotInline
+ static int getState(PlaybackState state) {
+ return state.getState();
+ }
+
+ @DoNotInline
+ static long getPosition(PlaybackState state) {
+ return state.getPosition();
+ }
+
+ @DoNotInline
+ static long getBufferedPosition(PlaybackState state) {
+ return state.getBufferedPosition();
+ }
+
+ @DoNotInline
+ static float getPlaybackSpeed(PlaybackState state) {
+ return state.getPlaybackSpeed();
+ }
+
+ @DoNotInline
+ static long getActions(PlaybackState state) {
+ return state.getActions();
+ }
+
+ @DoNotInline
+ static CharSequence getErrorMessage(PlaybackState state) {
+ return state.getErrorMessage();
+ }
+
+ @DoNotInline
+ static long getLastPositionUpdateTime(PlaybackState state) {
+ return state.getLastPositionUpdateTime();
+ }
+
+ @DoNotInline
+ static long getActiveQueueItemId(PlaybackState state) {
+ return state.getActiveQueueItemId();
+ }
+
+ @DoNotInline
+ static PlaybackState.CustomAction.Builder createCustomActionBuilder(String action,
+ CharSequence name, int icon) {
+ return new PlaybackState.CustomAction.Builder(action, name, icon);
+ }
+
+ @DoNotInline
+ static void setExtras(PlaybackState.CustomAction.Builder builder, Bundle extras) {
+ builder.setExtras(extras);
+ }
+
+ @DoNotInline
+ static PlaybackState.CustomAction build(PlaybackState.CustomAction.Builder builder) {
+ return builder.build();
+ }
+
+ @DoNotInline
+ static Bundle getExtras(PlaybackState.CustomAction customAction) {
+ return customAction.getExtras();
+ }
+
+ @DoNotInline
+ static String getAction(PlaybackState.CustomAction customAction) {
+ return customAction.getAction();
+ }
+
+ @DoNotInline
+ static CharSequence getName(PlaybackState.CustomAction customAction) {
+ return customAction.getName();
+ }
+
+ @DoNotInline
+ static int getIcon(PlaybackState.CustomAction customAction) {
+ return customAction.getIcon();
+ }
+ }
+
+ @RequiresApi(22)
+ private static class Api22Impl {
+ private Api22Impl() {}
+
+ @DoNotInline
+ static void setExtras(PlaybackState.Builder builder, Bundle extras) {
+ builder.setExtras(extras);
+ }
+
+ @DoNotInline
+ static Bundle getExtras(PlaybackState state) {
+ return state.getExtras();
+ }
+ }
+}
\ No newline at end of file
diff --git a/media/media/src/main/java/androidx/media/AudioAttributesImplApi21.java b/media/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
index c923287..001a470 100644
--- a/media/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
+++ b/media/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
@@ -126,6 +126,7 @@
return "AudioAttributesCompat: audioattributes=" + mAudioAttributes;
}
+ @RequiresApi(21)
static class Builder implements AudioAttributesImpl.Builder {
final AudioAttributes.Builder mFwkBuilder;
diff --git a/media/media/src/main/java/androidx/media/AudioAttributesImplApi26.java b/media/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
index 144c340..031e2d1 100644
--- a/media/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
+++ b/media/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
@@ -50,6 +50,7 @@
return mAudioAttributes.getVolumeControlStream();
}
+ @RequiresApi(26)
static class Builder extends AudioAttributesImplApi21.Builder {
Builder() {
super();
diff --git a/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java b/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
index a774664..d79fdc5 100644
--- a/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
+++ b/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
@@ -28,6 +28,7 @@
import android.os.Looper;
import android.os.Message;
+import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@@ -80,13 +81,8 @@
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- mFrameworkAudioFocusRequest =
- new AudioFocusRequest.Builder(mFocusGain)
- .setAudioAttributes(getAudioAttributes())
- .setWillPauseWhenDucked(mPauseOnDuck)
- .setOnAudioFocusChangeListener(
- mOnAudioFocusChangeListener, mFocusChangeHandler)
- .build();
+ mFrameworkAudioFocusRequest = Api26Impl.createInstance(mFocusGain, getAudioAttributes(),
+ mPauseOnDuck, mOnAudioFocusChangeListener, mFocusChangeHandler);
} else {
mFrameworkAudioFocusRequest = null;
}
@@ -433,4 +429,23 @@
return false;
}
}
+
+ @RequiresApi(26)
+ private static class Api26Impl {
+ private Api26Impl() {}
+
+ @DoNotInline
+ static AudioFocusRequest createInstance(
+ int focusGain,
+ AudioAttributes audioAttributes,
+ boolean pauseOnDuck,
+ OnAudioFocusChangeListener onAudioFocusChangeListener,
+ Handler focusChangeHandler) {
+ return new AudioFocusRequest.Builder(focusGain)
+ .setAudioAttributes(audioAttributes)
+ .setWillPauseWhenDucked(pauseOnDuck)
+ .setOnAudioFocusChangeListener(onAudioFocusChangeListener, focusChangeHandler)
+ .build();
+ }
+ }
}
diff --git a/media/media/src/main/java/androidx/media/AudioManagerCompat.java b/media/media/src/main/java/androidx/media/AudioManagerCompat.java
index a6d4ae7..8215406 100644
--- a/media/media/src/main/java/androidx/media/AudioManagerCompat.java
+++ b/media/media/src/main/java/androidx/media/AudioManagerCompat.java
@@ -16,11 +16,14 @@
package androidx.media;
+import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
+import androidx.annotation.DoNotInline;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat.StreamType;
/** Compatibility library for {@link AudioManager} with fallbacks for older platforms. */
@@ -88,7 +91,7 @@
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- return audioManager.requestAudioFocus(focusRequest.getAudioFocusRequest());
+ return Api26Impl.requestAudioFocus(audioManager, focusRequest.getAudioFocusRequest());
} else {
return audioManager.requestAudioFocus(
focusRequest.getOnAudioFocusChangeListener(),
@@ -117,7 +120,8 @@
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- return audioManager.abandonAudioFocusRequest(focusRequest.getAudioFocusRequest());
+ return Api26Impl.abandonAudioFocusRequest(audioManager,
+ focusRequest.getAudioFocusRequest());
} else {
return audioManager.abandonAudioFocus(focusRequest.getOnAudioFocusChangeListener());
}
@@ -145,11 +149,37 @@
public static int getStreamMinVolume(@NonNull AudioManager audioManager,
@StreamType int streamType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- return audioManager.getStreamMinVolume(streamType);
+ return Api28Impl.getStreamMinVolume(audioManager, streamType);
} else {
return 0;
}
}
private AudioManagerCompat() {}
+
+ @RequiresApi(26)
+ private static class Api26Impl {
+ private Api26Impl() {}
+
+ @DoNotInline
+ static int abandonAudioFocusRequest(AudioManager audioManager,
+ AudioFocusRequest focusRequest) {
+ return audioManager.abandonAudioFocusRequest(focusRequest);
+ }
+
+ @DoNotInline
+ static int requestAudioFocus(AudioManager audioManager, AudioFocusRequest focusRequest) {
+ return audioManager.requestAudioFocus(focusRequest);
+ }
+ }
+
+ @RequiresApi(28)
+ private static class Api28Impl {
+ private Api28Impl() {}
+
+ @DoNotInline
+ static int getStreamMinVolume(AudioManager audioManager, int streamType) {
+ return audioManager.getStreamMinVolume(streamType);
+ }
+ }
}
diff --git a/media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java b/media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
index 05bbf1f..ffb99dc 100644
--- a/media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
+++ b/media/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
@@ -322,21 +322,25 @@
mHandler.postOrRun(new Runnable() {
@Override
public void run() {
- if (!mRootExtrasList.isEmpty()) {
- IMediaSession extraBinder = token.getExtraBinder();
- if (extraBinder != null) {
- for (Bundle rootExtras : mRootExtrasList) {
- BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
- extraBinder.asBinder());
- }
- }
- mRootExtrasList.clear();
- }
- mServiceFwk.setSessionToken((MediaSession.Token) token.getToken());
+ setSessionTokenOnHandler(token);
}
});
}
+ void setSessionTokenOnHandler(MediaSessionCompat.Token token) {
+ if (!mRootExtrasList.isEmpty()) {
+ IMediaSession extraBinder = token.getExtraBinder();
+ if (extraBinder != null) {
+ for (Bundle rootExtras : mRootExtrasList) {
+ BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
+ extraBinder.asBinder());
+ }
+ }
+ mRootExtrasList.clear();
+ }
+ mServiceFwk.setSessionToken((MediaSession.Token) token.getToken());
+ }
+
@Override
public void notifyChildrenChanged(final String parentId, final Bundle options) {
notifyChildrenChangedForFramework(parentId, options);
@@ -490,6 +494,7 @@
return mCurConnection.browserInfo;
}
+ @RequiresApi(21)
class MediaBrowserServiceApi21 extends MediaBrowserService {
MediaBrowserServiceApi21(Context context) {
attachBaseContext(context);
diff --git a/media/media/src/main/java/androidx/media/VolumeProviderCompat.java b/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
index 201d74e..79e87f0 100644
--- a/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
+++ b/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
@@ -22,8 +22,10 @@
import android.os.Build;
import android.support.v4.media.session.MediaSessionCompat;
+import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import java.lang.annotation.Retention;
@@ -143,7 +145,7 @@
mCurrentVolume = currentVolume;
if (Build.VERSION.SDK_INT >= 21) {
VolumeProvider volumeProviderFwk = (VolumeProvider) getVolumeProvider();
- volumeProviderFwk.setCurrentVolume(currentVolume);
+ Api21Impl.setCurrentVolume(volumeProviderFwk, currentVolume);
}
if (mCallback != null) {
mCallback.onVolumeChanged(this);
@@ -235,4 +237,14 @@
public static abstract class Callback {
public abstract void onVolumeChanged(VolumeProviderCompat volumeProvider);
}
+
+ @RequiresApi(21)
+ private static class Api21Impl {
+ private Api21Impl() {}
+
+ @DoNotInline
+ static void setCurrentVolume(VolumeProvider volumeProvider, int currentVolume) {
+ volumeProvider.setCurrentVolume(currentVolume);
+ }
+ }
}
diff --git a/media/media/src/main/java/androidx/media/app/NotificationCompat.java b/media/media/src/main/java/androidx/media/app/NotificationCompat.java
index e1fe44d..bc6510e 100644
--- a/media/media/src/main/java/androidx/media/app/NotificationCompat.java
+++ b/media/media/src/main/java/androidx/media/app/NotificationCompat.java
@@ -30,6 +30,7 @@
import android.view.View;
import android.widget.RemoteViews;
+import androidx.annotation.DoNotInline;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.app.BundleCompat;
@@ -208,8 +209,8 @@
@Override
public void apply(NotificationBuilderWithBuilderAccessor builder) {
if (Build.VERSION.SDK_INT >= 21) {
- builder.getBuilder().setStyle(
- fillInMediaStyle(new Notification.MediaStyle()));
+ Api21Impl.setStyle(builder.getBuilder(),
+ fillInMediaStyle(Api21Impl.createMediaStyle()));
} else if (mShowCancelButton) {
builder.getBuilder().setOngoing(true);
}
@@ -218,10 +219,10 @@
@RequiresApi(21)
Notification.MediaStyle fillInMediaStyle(Notification.MediaStyle style) {
if (mActionsToShowInCompact != null) {
- style.setShowActionsInCompactView(mActionsToShowInCompact);
+ Api21Impl.setShowActionsInCompactView(style, mActionsToShowInCompact);
}
if (mToken != null) {
- style.setMediaSession((MediaSession.Token) mToken.getToken());
+ Api21Impl.setMediaSession(style, (MediaSession.Token) mToken.getToken());
}
return style;
}
@@ -285,7 +286,7 @@
button.setOnClickPendingIntent(R.id.action0, action.getActionIntent());
}
if (Build.VERSION.SDK_INT >= 15) {
- button.setContentDescription(R.id.action0, action.getTitle());
+ Api15Impl.setContentDescription(button, R.id.action0, action.getTitle());
}
return button;
}
@@ -386,8 +387,8 @@
@Override
public void apply(NotificationBuilderWithBuilderAccessor builder) {
if (Build.VERSION.SDK_INT >= 24) {
- builder.getBuilder().setStyle(
- fillInMediaStyle(new Notification.DecoratedMediaCustomViewStyle()));
+ Api21Impl.setStyle(builder.getBuilder(),
+ fillInMediaStyle(Api24Impl.createDecoratedMediaCustomViewStyle()));
} else {
super.apply(builder);
}
@@ -500,4 +501,50 @@
views.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor", color);
}
}
+
+ @RequiresApi(15)
+ private static class Api15Impl {
+ private Api15Impl() {}
+
+ @DoNotInline
+ static void setContentDescription(RemoteViews remoteViews, int viewId,
+ CharSequence contentDescription) {
+ remoteViews.setContentDescription(viewId, contentDescription);
+ }
+ }
+
+ @RequiresApi(21)
+ private static class Api21Impl {
+ private Api21Impl() {}
+
+ @DoNotInline
+ static void setStyle(Notification.Builder builder, Notification.Style style) {
+ builder.setStyle(style);
+ }
+
+ @DoNotInline
+ static Notification.MediaStyle createMediaStyle() {
+ return new Notification.MediaStyle();
+ }
+
+ @DoNotInline
+ static void setShowActionsInCompactView(Notification.MediaStyle style, int... actions) {
+ style.setShowActionsInCompactView(actions);
+ }
+
+ @DoNotInline
+ static void setMediaSession(Notification.MediaStyle style, MediaSession.Token token) {
+ style.setMediaSession(token);
+ }
+ }
+
+ @RequiresApi(24)
+ private static class Api24Impl {
+ private Api24Impl() {}
+
+ @DoNotInline
+ static Notification.DecoratedMediaCustomViewStyle createDecoratedMediaCustomViewStyle() {
+ return new Notification.DecoratedMediaCustomViewStyle();
+ }
+ }
}
diff --git a/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java b/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
index b6c2a3b..0d8dea6 100644
--- a/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
+++ b/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
@@ -39,6 +39,7 @@
import android.view.KeyEvent;
import androidx.annotation.RestrictTo;
+import androidx.core.content.ContextCompat;
import androidx.media.MediaBrowserServiceCompat;
import java.util.List;
@@ -114,7 +115,7 @@
getServiceComponentByAction(context, Intent.ACTION_MEDIA_BUTTON);
if (mediaButtonServiceComponentName != null) {
intent.setComponent(mediaButtonServiceComponentName);
- startForegroundService(context, intent);
+ ContextCompat.startForegroundService(context, intent);
return;
}
ComponentName mediaBrowserServiceComponentName = getServiceComponentByAction(context,
@@ -296,14 +297,6 @@
return null;
}
- private static void startForegroundService(Context context, Intent intent) {
- if (Build.VERSION.SDK_INT >= 26) {
- context.startForegroundService(intent);
- } else {
- context.startService(intent);
- }
- }
-
private static ComponentName getServiceComponentByAction(Context context, String action) {
PackageManager pm = context.getPackageManager();
Intent queryIntent = new Intent(action);
diff --git a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
index 363f158..966e42c 100644
--- a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
+++ b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_dialog.xml
@@ -21,7 +21,8 @@
<TextView android:id="@+id/mr_chooser_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:minHeight="64dp"
+ android:minHeight="52dp"
+ android:paddingTop="12dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:gravity="center_vertical"
diff --git a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_list_item.xml b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_list_item.xml
index 7fce7d5..e92def0 100644
--- a/mediarouter/mediarouter/src/main/res/layout/mr_chooser_list_item.xml
+++ b/mediarouter/mediarouter/src/main/res/layout/mr_chooser_list_item.xml
@@ -20,7 +20,8 @@
android:minHeight="32dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
- android:paddingBottom="24dp"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
android:orientation="horizontal"
android:gravity="center_vertical" >
diff --git a/navigation/navigation-dynamic-features-fragment/build.gradle b/navigation/navigation-dynamic-features-fragment/build.gradle
index 60cf4908..5448fa7 100644
--- a/navigation/navigation-dynamic-features-fragment/build.gradle
+++ b/navigation/navigation-dynamic-features-fragment/build.gradle
@@ -39,6 +39,7 @@
testImplementation(libs.testRunner)
testImplementation(libs.junit)
testImplementation(libs.mockitoCore)
+ testImplementation(libs.robolectric)
testImplementation(libs.truth)
androidTestImplementation(libs.testCore)
@@ -53,7 +54,6 @@
androidTestImplementation(project(":internal-testutils-runtime"), {
exclude group: "androidx.fragment", module: "fragment"
})
- androidTestImplementation(libs.multidex)
}
android {
diff --git a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
index 9a18178..6f6d8d7 100644
--- a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicFragmentNavigatorDestinationBuilderTest.kt
@@ -33,7 +33,7 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
public class DynamicFragmentNavigatorDestinationBuilderTest {
- @Suppress("DEPRECATION")
+
@get:Rule
public val rule: ActivityScenarioRule<TestActivity> = ActivityScenarioRule(
TestActivity::class.java
diff --git a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
index 3d0cde7..c1a8f7d 100644
--- a/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
+++ b/navigation/navigation-dynamic-features-fragment/src/androidTest/java/androidx/navigation/dynamicfeatures/fragment/DynamicNavHostFragmentTest.kt
@@ -35,9 +35,8 @@
@RunWith(AndroidJUnit4::class)
public class DynamicNavHostFragmentTest {
- @Suppress("DEPRECATION")
@get:Rule
- public val activityTestRule: ActivityScenarioRule<NavigationActivity> = ActivityScenarioRule(
+ public val rule: ActivityScenarioRule<NavigationActivity> = ActivityScenarioRule(
NavigationActivity::class.java
)
diff --git a/navigation/navigation-dynamic-features-runtime/build.gradle b/navigation/navigation-dynamic-features-runtime/build.gradle
index bc5ae9a..e1fc340 100644
--- a/navigation/navigation-dynamic-features-runtime/build.gradle
+++ b/navigation/navigation-dynamic-features-runtime/build.gradle
@@ -32,7 +32,6 @@
dependencies {
api(project(":navigation:navigation-runtime"))
api(libs.playCore)
- api(libs.kotlinStdlib)
testImplementation(project(":navigation:navigation-testing"))
testImplementation("androidx.arch.core:core-testing:2.1.0")
@@ -53,6 +52,9 @@
androidTestImplementation(libs.mockitoCore, excludes.bytebuddy)
androidTestImplementation(libs.truth)
androidTestImplementation(libs.multidex)
+ androidTestImplementation(project(":internal-testutils-runtime"), {
+ exclude group: "androidx.fragment", module: "fragment"
+ })
}
androidx {
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilderTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilderTest.kt
index 613d5b3..f8eb54b 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorDestinationBuilderTest.kt
@@ -29,7 +29,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-class DynamicActivityNavigatorDestinationBuilderTest {
+public class DynamicActivityNavigatorDestinationBuilderTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@@ -48,7 +48,7 @@
@Suppress("DEPRECATION")
@Test
- fun module() {
+ public fun module() {
val graph = navController.createGraph(startDestination = DESTINATION_ID) {
activity(DESTINATION_ID) {
moduleName = MODULE_NAME
@@ -63,7 +63,7 @@
@Suppress("DEPRECATION")
@Test
- fun noModule() {
+ public fun noModule() {
val graph = navController.createGraph(startDestination = DESTINATION_ID) {
activity(DESTINATION_ID) {
}
@@ -75,7 +75,7 @@
@Suppress("DEPRECATION")
@Test
- fun activity() {
+ public fun activity() {
val graph = navController.createGraph(startDestination = DESTINATION_ID) {
activity(DESTINATION_ID) {
moduleName = MODULE_NAME
@@ -95,7 +95,7 @@
@Suppress("DEPRECATION")
@Test
- fun noActivity() {
+ public fun noActivity() {
val graph = navController.createGraph(startDestination = DESTINATION_ID) {
activity(DESTINATION_ID) {
}
@@ -108,7 +108,7 @@
@Suppress("DEPRECATION")
@Test
- fun modulePackage() {
+ public fun modulePackage() {
val graph = navController.createGraph(startDestination = DESTINATION_ID) {
activity(DESTINATION_ID) {
moduleName = MODULE_NAME
@@ -121,7 +121,7 @@
}
@Test
- fun moduleRoute() {
+ public fun moduleRoute() {
val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
activity(DESTINATION_ROUTE) {
moduleName = MODULE_NAME
@@ -135,7 +135,7 @@
}
@Test
- fun noModuleRoute() {
+ public fun noModuleRoute() {
val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
activity(DESTINATION_ROUTE) {
}
@@ -146,7 +146,7 @@
}
@Test
- fun activityRoute() {
+ public fun activityRoute() {
val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
activity(DESTINATION_ROUTE) {
moduleName = MODULE_NAME
@@ -165,7 +165,7 @@
}
@Test
- fun noActivityRoute() {
+ public fun noActivityRoute() {
val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
activity(DESTINATION_ROUTE) {
}
@@ -177,7 +177,7 @@
}
@Test
- fun modulePackageRoute() {
+ public fun modulePackageRoute() {
val graph = navController.createGraph(startDestination = DESTINATION_ROUTE) {
activity(DESTINATION_ROUTE) {
moduleName = MODULE_NAME
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
index 06fcce8..c072251 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicActivityNavigatorTest.kt
@@ -20,8 +20,10 @@
import android.content.Intent
import androidx.navigation.NavigatorProvider
import androidx.navigation.NoOpNavigator
+import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.testutils.withActivity
import com.google.android.play.core.splitinstall.SplitInstallManager
import org.junit.Assert.assertNotNull
import org.junit.Before
@@ -31,11 +33,12 @@
import org.mockito.Mockito.mock
/* ktlint-disable no-unused-imports */ // https://github.com/pinterest/ktlint/issues/937
import org.mockito.Mockito.`when` as mockWhen
+
/* ktlint-enable unused-imports */
@SmallTest
@RunWith(AndroidJUnit4::class)
-class DynamicActivityNavigatorTest {
+public class DynamicActivityNavigatorTest {
private lateinit var navigator: DynamicActivityNavigator
private lateinit var installManager: DynamicInstallManager
@@ -46,44 +49,54 @@
@Suppress("DEPRECATION")
@get:Rule
- val activityTestRule = androidx.test.rule.ActivityTestRule(NavigationActivity::class.java)
+ public val activityRule: ActivityScenarioRule<NavigationActivity> =
+ ActivityScenarioRule(NavigationActivity::class.java)
@Before
- fun setup() {
+ public fun setup() {
splitInstallManager = mock(SplitInstallManager::class.java)
- installManager = DynamicInstallManager(activityTestRule.activity, splitInstallManager)
- navigator = DynamicActivityNavigator(activityTestRule.activity, installManager)
+ activityRule.withActivity {
+ installManager = DynamicInstallManager(
+ this,
+ splitInstallManager
+ )
+ navigator = DynamicActivityNavigator(this, installManager)
+ dynamicDestination = navigator.createDestination()
+ dynamicDestination.setIntent(
+ Intent(this, DestinationActivity::class.java)
+ )
+ }
provider = NavigatorProvider()
noOpNavigator = NoOpNavigator()
provider.addNavigator(noOpNavigator)
- dynamicDestination = navigator.createDestination()
- dynamicDestination.setIntent(
- Intent(activityTestRule.activity, DestinationActivity::class.java)
- )
}
@Test
- fun navigate_DynamicActivityDestination() {
+ public fun navigate_DynamicActivityDestination() {
navigator.navigate(dynamicDestination, null, null, null)
}
@Test(expected = IllegalStateException::class)
- fun navigate_DynamicActivityDestination_NoDynamicNavGraph() {
+ public fun navigate_DynamicActivityDestination_NoDynamicNavGraph() {
+ lateinit var activity: NavigationActivity
+ activityRule.scenario.onActivity {
+ activity = it
+ }
@Suppress("UNUSED_VARIABLE")
val destination = DynamicActivityNavigator.Destination(NavigatorProvider())
val navDestination = mock(DynamicActivityNavigator.Destination::class.java).apply {
mockWhen(moduleName).thenReturn("module")
- setIntent(Intent(activityTestRule.activity, DestinationActivity::class.java))
+ setIntent(Intent(activity, DestinationActivity::class.java))
}
navigator.navigate(navDestination, null, null, null)
}
@Test
- fun createDestination() {
+ public fun createDestination() {
assertNotNull(navigator.createDestination())
}
}
-class NavigationActivity : Activity()
+public class NavigationActivity : Activity()
-class DestinationActivity : Activity()
+public class DestinationActivity : Activity()
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeGraphNavigatorTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeGraphNavigatorTest.kt
index ab2697a..9b562d7 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeGraphNavigatorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeGraphNavigatorTest.kt
@@ -16,13 +16,16 @@
package androidx.navigation.dynamicfeatures
+import android.content.Context
import android.os.Bundle
import androidx.navigation.NavController
import androidx.navigation.NoOpNavigator
import androidx.navigation.dynamicfeatures.shared.AndroidTestDynamicInstallManager
import androidx.navigation.dynamicfeatures.test.R
+import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
+import androidx.testutils.withActivity
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertNotNull
import org.junit.Assert.fail
@@ -34,21 +37,26 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
-class DynamicIncludeGraphNavigatorTest {
+public class DynamicIncludeGraphNavigatorTest {
private lateinit var navigator: DynamicIncludeGraphNavigator
+ private lateinit var context: Context
- @Suppress("DEPRECATION")
@get:Rule
- val rule = androidx.test.rule.ActivityTestRule(NavigationActivity::class.java)
+ public val rule: ActivityScenarioRule<NavigationActivity> =
+ ActivityScenarioRule(NavigationActivity::class.java)
@Before
- fun setup() {
+ public fun setup() {
setupInternal()
}
private fun setupInternal(navGraphId: Int = R.navigation.nav_graph) {
- val context = rule.activity
+
+ rule.withActivity {
+ context = this
+ }
+
val navController = NavController(context)
val navigatorProvider = navController.navigatorProvider
val installManager = AndroidTestDynamicInstallManager(context).also {
@@ -72,13 +80,12 @@
}
@Test
- fun createDestination() {
+ public fun createDestination() {
assertNotNull(navigator.createDestination())
}
@Test
- fun testReplacePackagePlaceholder() {
- val context = rule.activity
+ public fun testReplacePackagePlaceholder() {
val packageName = context.packageName
val dynamicNavGraph = navigator.createDestination().apply {
moduleName = FEATURE_NAME
@@ -105,7 +112,7 @@
}
@Test
- fun invalidGraphId() {
+ public fun invalidGraphId() {
try {
setupInternal(R.navigation.nav_invalid_id)
fail("Inflating nav_invalid_id should fail with an IllegalStateException")
@@ -120,23 +127,23 @@
}
@Test
- fun onSaveState() {
+ public fun onSaveState() {
assertThat(navigator.onSaveState()).isEqualTo(Bundle.EMPTY)
}
@Test
- fun onRestoreState() {
+ public fun onRestoreState() {
navigator.onRestoreState(Bundle.EMPTY)
}
@Test
- fun onRestoreState_nestedInclusion() {
+ public fun onRestoreState_nestedInclusion() {
setupInternal(R.navigation.nav_graph_nested_include_dynamic)
navigator.onRestoreState(Bundle.EMPTY)
}
@Test
- fun popBackStack() {
+ public fun popBackStack() {
assertThat(navigator.popBackStack()).isTrue()
}
}
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilderTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilderTest.kt
index fe10518..d90be5f 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicIncludeNavGraphBuilderTest.kt
@@ -33,7 +33,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-class DynamicIncludeNavGraphBuilderTest {
+public class DynamicIncludeNavGraphBuilderTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val navController = NavController(context).apply {
@@ -46,7 +46,7 @@
@Suppress("DEPRECATION")
@Test
- fun includeDynamic() {
+ public fun includeDynamic() {
val graph = navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
includeDynamic(GRAPH_ID, MODULE_NAME, GRAPH_RESOURCE_NAME) {
graphPackage = GRAPH_PACKAGE
@@ -67,7 +67,8 @@
}
@Suppress("DEPRECATION")
- fun includeDynamic_emptyModuleName() {
+ @Test
+ public fun includeDynamic_emptyModuleName() {
navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
try {
includeDynamic(GRAPH_ID, "", GRAPH_RESOURCE_NAME)
@@ -80,7 +81,7 @@
@Suppress("DEPRECATION")
@Test
- fun includeDynamic_graphPackage_null() {
+ public fun includeDynamic_graphPackage_null() {
val graph = navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
includeDynamic(GRAPH_ID, MODULE_NAME, GRAPH_RESOURCE_NAME)
}
@@ -92,7 +93,7 @@
@Suppress("DEPRECATION")
@Test
- fun includeDynamic_graphPackage_empty() {
+ public fun includeDynamic_graphPackage_empty() {
navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
try {
includeDynamic(GRAPH_ID, MODULE_NAME, GRAPH_RESOURCE_NAME) {
@@ -107,7 +108,7 @@
@Suppress("DEPRECATION")
@Test
- fun includeDynamic_graphResourceName_empty() {
+ public fun includeDynamic_graphResourceName_empty() {
navController.navigatorProvider.navigation(startDestination = GRAPH_ID) {
try {
includeDynamic(GRAPH_ID, MODULE_NAME, "")
@@ -119,7 +120,7 @@
}
@Test
- fun includeDynamicRoute() {
+ public fun includeDynamicRoute() {
val graph = navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
includeDynamic(GRAPH_ROUTE, MODULE_NAME, GRAPH_RESOURCE_NAME) {
graphPackage = GRAPH_PACKAGE
@@ -140,7 +141,7 @@
.isEqualTo(GRAPH_RESOURCE_NAME)
}
- fun includeDynamic_emptyModuleNameRoute() {
+ public fun includeDynamic_emptyModuleNameRoute() {
navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
try {
includeDynamic(GRAPH_ROUTE, "", GRAPH_RESOURCE_NAME)
@@ -152,7 +153,7 @@
}
@Test
- fun includeDynamic_graphPackage_nullRoute() {
+ public fun includeDynamic_graphPackage_nullRoute() {
val graph = navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
includeDynamic(GRAPH_ROUTE, MODULE_NAME, GRAPH_RESOURCE_NAME)
}
@@ -164,7 +165,7 @@
}
@Test
- fun includeDynamic_graphPackage_emptyRoute() {
+ public fun includeDynamic_graphPackage_emptyRoute() {
navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
try {
includeDynamic(GRAPH_ROUTE, MODULE_NAME, GRAPH_RESOURCE_NAME) {
@@ -178,7 +179,7 @@
}
@Test
- fun includeDynamic_graphResourceName_emptyRoute() {
+ public fun includeDynamic_graphResourceName_emptyRoute() {
navController.navigatorProvider.navigation(startDestination = GRAPH_ROUTE) {
try {
includeDynamic(GRAPH_ROUTE, MODULE_NAME, "")
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilderTest.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilderTest.kt
index b190825..cbbcf25 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilderTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/DynamicNavGraphBuilderTest.kt
@@ -32,7 +32,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-class DynamicNavGraphBuilderTest {
+public class DynamicNavGraphBuilderTest {
private val provider = NavigatorProvider().apply {
addNavigator(
@@ -46,7 +46,7 @@
@Suppress("DEPRECATION")
@Test
- fun navigation() {
+ public fun navigation() {
val graph = provider.navigation(startDestination = DESTINATION_ID) {
moduleName = MODULE_NAME
navDestination(DESTINATION_ID) {}
@@ -61,7 +61,7 @@
}
@Suppress("DEPRECATION")
- fun navigation_emptyModuleName() {
+ public fun navigation_emptyModuleName() {
val graph = provider.navigation(startDestination = DESTINATION_ID) {
}
assertWithMessage("Without a moduleName the graph should be a NavGraph")
@@ -70,7 +70,7 @@
@Suppress("DEPRECATION")
@Test
- fun progressDestination() {
+ public fun progressDestination() {
val graph = provider.navigation(startDestination = DESTINATION_ID) {
moduleName = MODULE_NAME
progressDestination = PROGRESS_DESTINATION_ID
@@ -88,7 +88,7 @@
@Suppress("DEPRECATION")
@Test
- fun progressDestination_notSet() {
+ public fun progressDestination_notSet() {
val graph = provider.navigation(startDestination = DESTINATION_ID) {
moduleName = MODULE_NAME
} as DynamicGraphNavigator.DynamicNavGraph
@@ -99,7 +99,7 @@
}
@Test
- fun navigationRoute() {
+ public fun navigationRoute() {
val graph = provider.navigation(startDestination = DESTINATION_ROUTE) {
moduleName = MODULE_NAME
navDestination(DESTINATION_ROUTE) {}
@@ -113,7 +113,7 @@
.isEqualTo(MODULE_NAME)
}
- fun navigation_emptyModuleNameRoute() {
+ public fun navigation_emptyModuleNameRoute() {
val graph = provider.navigation(startDestination = DESTINATION_ROUTE) {
}
assertWithMessage("Without a moduleName the graph should be a NavGraph")
@@ -121,7 +121,7 @@
}
@Test
- fun progressDestinationRoute() {
+ public fun progressDestinationRoute() {
val graph = provider.navigation(startDestination = DESTINATION_ROUTE) {
moduleName = MODULE_NAME
progressDestinationRoute = PROGRESS_DESTINATION_ROUTE
@@ -138,7 +138,7 @@
}
@Test
- fun progressDestination_notSetRoute() {
+ public fun progressDestination_notSetRoute() {
val graph = provider.navigation(startDestination = DESTINATION_ROUTE) {
moduleName = MODULE_NAME
} as DynamicGraphNavigator.DynamicNavGraph
@@ -160,16 +160,16 @@
* added to a NavGraph (hence why this is not in the common-ktx library)
*/
@Suppress("DEPRECATION")
-fun DynamicNavGraphBuilder.navDestination(
+public fun DynamicNavGraphBuilder.navDestination(
@IdRes id: Int,
builder: NavDestinationBuilder<NavDestination>.() -> Unit
-) = destination(NavDestinationBuilder(provider[NoOpNavigator::class], id).apply(builder))
+): Unit = destination(NavDestinationBuilder(provider[NoOpNavigator::class], id).apply(builder))
/**
* Create a base NavDestination. Generally, only subtypes of NavDestination should be
* added to a NavGraph (hence why this is not in the common-ktx library)
*/
-fun DynamicNavGraphBuilder.navDestination(
+public fun DynamicNavGraphBuilder.navDestination(
route: String,
builder: NavDestinationBuilder<NavDestination>.() -> Unit
-) = destination(NavDestinationBuilder(provider[NoOpNavigator::class], route).apply(builder))
+): Unit = destination(NavDestinationBuilder(provider[NoOpNavigator::class], route).apply(builder))
\ No newline at end of file
diff --git a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/shared/AndroidTestDynamicInstallManager.kt b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/shared/AndroidTestDynamicInstallManager.kt
index 60580b4..c400233 100644
--- a/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/shared/AndroidTestDynamicInstallManager.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/androidTest/java/androidx/navigation/dynamicfeatures/shared/AndroidTestDynamicInstallManager.kt
@@ -24,7 +24,7 @@
/**
* A dynamic install manager used for testing.
*/
-class AndroidTestDynamicInstallManager(
+public class AndroidTestDynamicInstallManager(
context: Context,
- val splitInstallManager: SplitInstallManager = mock(SplitInstallManager::class.java)
+ public val splitInstallManager: SplitInstallManager = mock(SplitInstallManager::class.java)
) : DynamicInstallManager(context, splitInstallManager)
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicExtrasTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicExtrasTest.kt
index 74bc039..0502397 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicExtrasTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicExtrasTest.kt
@@ -23,12 +23,12 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class DynamicExtrasTest {
+public class DynamicExtrasTest {
- class TestNavigatorExtras : Navigator.Extras
+ public class TestNavigatorExtras : Navigator.Extras
@Test
- fun build_withMonitorAndExtras() {
+ public fun build_withMonitorAndExtras() {
val monitor = DynamicInstallMonitor()
val navExtras = TestNavigatorExtras()
val extras = DynamicExtras(monitor, navExtras)
@@ -37,7 +37,7 @@
}
@Test
- fun build_withoutMonitorOrExtras() {
+ public fun build_withoutMonitorOrExtras() {
val extras = DynamicExtras()
assertThat(extras.destinationExtras).isNull()
assertThat(extras.installMonitor).isNull()
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicGraphNavigatorTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicGraphNavigatorTest.kt
index f62db26..cc94ec6 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicGraphNavigatorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicGraphNavigatorTest.kt
@@ -27,7 +27,7 @@
import org.mockito.Mockito.mock
@RunWith(JUnit4::class)
-class DynamicGraphNavigatorTest {
+public class DynamicGraphNavigatorTest {
private val navigator =
DynamicGraphNavigator(
@@ -36,12 +36,12 @@
)
@Test
- fun testCreateDestination() {
+ public fun testCreateDestination() {
assertNotNull(navigator.createDestination())
}
@Test
- fun testInstallDefaultProgressDestination() {
+ public fun testInstallDefaultProgressDestination() {
val navDestination = mock(NavDestination::class.java)
navigator.installDefaultProgressDestination { navDestination }
assertEquals(navDestination, navigator.defaultProgressDestinationSupplier!!.invoke())
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
index 72ca243..d54950a 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallManagerTest.kt
@@ -30,7 +30,7 @@
import org.mockito.Mockito.spy
@RunWith(JUnit4::class)
-class DynamicInstallManagerTest {
+public class DynamicInstallManagerTest {
private val splitInstallManager = mock(SplitInstallManager::class.java)
private var manager = DynamicInstallManager(
@@ -39,13 +39,13 @@
)
@Test
- fun testNeedsInstall_InstallNeeded() {
+ public fun testNeedsInstall_InstallNeeded() {
mockWhen(splitInstallManager.installedModules).thenReturn(setOf("not-module"))
assertTrue(manager.needsInstall("module"))
}
@Test
- fun testNeedsInstall_NoInstallNeeded() {
+ public fun testNeedsInstall_NoInstallNeeded() {
mockWhen(splitInstallManager.installedModules).thenReturn(setOf("module"))
assertFalse(manager.needsInstall("module"))
}
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallMonitorTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallMonitorTest.kt
index caed9ac..b6046f5 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallMonitorTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicInstallMonitorTest.kt
@@ -26,9 +26,9 @@
import org.mockito.Mockito.verify
@RunWith(JUnit4::class)
-class DynamicInstallMonitorTest {
+public class DynamicInstallMonitorTest {
@Test
- fun testCancelInstall_sessionIdZero() {
+ public fun testCancelInstall_sessionIdZero() {
val monitor = DynamicInstallMonitor()
val manager = mock(SplitInstallManager::class.java)
@@ -39,7 +39,7 @@
}
@Test
- fun testCancelInstall_sessionIdNotZero() {
+ public fun testCancelInstall_sessionIdNotZero() {
val monitor = DynamicInstallMonitor()
val manager = mock(SplitInstallManager::class.java)
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicNavGraphTest.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicNavGraphTest.kt
index f48fdbb..4d48ad3 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicNavGraphTest.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/DynamicNavGraphTest.kt
@@ -32,10 +32,10 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class DynamicNavGraphTest {
+public class DynamicNavGraphTest {
@get:Rule
- val instantTaskExecutorRule = InstantTaskExecutorRule()
+ public val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule()
private val progressId = 1
private lateinit var provider: NavigatorProvider
@@ -46,7 +46,7 @@
private lateinit var noOpNavigator: NoOpNavigator
@Before
- fun setup() {
+ public fun setup() {
provider = NavigatorProvider()
noOpNavigator = NoOpNavigator()
navigator = DynamicGraphNavigator(
@@ -60,12 +60,12 @@
}
@Test(expected = IllegalStateException::class)
- fun testGetOrThrow_NoParent() {
+ public fun testGetOrThrow_NoParent() {
DynamicNavGraph.getOrThrow(noOpNavigator.createDestination())
}
@Test
- fun testGetOrThrow_CorrectParent() {
+ public fun testGetOrThrow_CorrectParent() {
setupProgressDestination(
noOpNavigator.createDestination().apply {
id = progressId
@@ -81,13 +81,13 @@
}
@Test(expected = IllegalStateException::class)
- fun testNavigateToProgressDestination_withoutProgressDestination() {
+ public fun testNavigateToProgressDestination_withoutProgressDestination() {
setupProgressDestination(null)
navigator.navigateToProgressDestination(dynamicNavGraph, null)
}
@Test
- fun testNavigateToProgressDestination_withProviderAndDestination() {
+ public fun testNavigateToProgressDestination_withProviderAndDestination() {
setupProgressDestination(
noOpNavigator.createDestination().apply {
id = progressId
diff --git a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/shared/TestDynamicInstallManager.kt b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/shared/TestDynamicInstallManager.kt
index e511f56..cbdf11a 100644
--- a/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/shared/TestDynamicInstallManager.kt
+++ b/navigation/navigation-dynamic-features-runtime/src/test/java/androidx/navigation/dynamicfeatures/shared/TestDynamicInstallManager.kt
@@ -24,7 +24,7 @@
/**
* A dynamic install manager used for testing.
*/
-class TestDynamicInstallManager :
+public class TestDynamicInstallManager :
DynamicInstallManager(
Mockito.spy(Context::class.java),
Mockito.mock(SplitInstallManager::class.java)
diff --git a/navigation/navigation-safe-args-gradle-plugin/build.gradle b/navigation/navigation-safe-args-gradle-plugin/build.gradle
index 49cfde0..b647eed 100644
--- a/navigation/navigation-safe-args-gradle-plugin/build.gradle
+++ b/navigation/navigation-safe-args-gradle-plugin/build.gradle
@@ -25,7 +25,7 @@
}
dependencies {
- implementation("com.android.tools.build:gradle:4.0.1")
+ implementation("com.android.tools.build:gradle:4.2.0")
implementation(libs.kotlinGradlePlugin)
api(project(":navigation:navigation-safe-args-generator"))
api(gradleApi())
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt
index a66edb1..3dbcbf5a 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt
@@ -24,11 +24,10 @@
import org.gradle.api.GradleException
import org.gradle.api.file.FileCollection
import org.gradle.api.file.ProjectLayout
+import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
-import org.gradle.api.resources.TextResource
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.work.ChangeType
@@ -44,11 +43,8 @@
@get:Input
lateinit var rFilePackage: Provider<String>
- @get:Internal
- var applicationIdResource: TextResource? = null // null on AGP 3.2.1 and below
-
- @get:Internal
- var applicationId: String? = null // null on AGP 3.3.0 and above
+ @get:Input
+ val applicationId: Property<String> = project.objects.property(String::class.java)
@get:Input
var useAndroidX: Boolean = true
@@ -66,18 +62,10 @@
@get:OutputDirectory
lateinit var incrementalFolder: File
- /**
- * Gets the app id from either the [applicationIdResource] if available or [applicationId].
- * The availability from which the app id string is retrieved from is based on the Android
- * Gradle Plugin version of the project.
- */
- @Input
- fun getApplicationIdResourceString() = applicationIdResource?.asString() ?: applicationId
-
private fun generateArgs(navFiles: Collection<File>, out: File) = navFiles.map { file ->
val output = SafeArgsGenerator(
rFilePackage = rFilePackage.get(),
- applicationId = getApplicationIdResourceString() ?: "",
+ applicationId = applicationId.get() ?: "",
navigationXml = file,
outputDir = out,
useAndroidX = useAndroidX,
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
index ef3a3e8..dc02ba6 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
@@ -16,6 +16,9 @@
package androidx.navigation.safeargs.gradle
+import com.android.build.api.extension.AndroidComponentsExtension
+import com.android.build.api.extension.ApplicationAndroidComponentsExtension
+import com.android.build.api.extension.DynamicFeatureAndroidComponentsExtension
import com.android.build.gradle.AppExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
@@ -24,6 +27,7 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
+import org.gradle.api.provider.Property
import org.gradle.api.provider.ProviderFactory
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import java.io.File
@@ -62,12 +66,29 @@
"androidx.navigation.safeargs.kotlin plugin must be used with kotlin plugin"
)
}
+ val applicationIds = mutableMapOf<String, Property<String>>()
+ val variantExtension =
+ project.extensions.findByType(AndroidComponentsExtension::class.java)
+ ?: throw GradleException("safeargs plugin must be used with android plugin")
+ variantExtension.onVariants {
+ when (it) {
+ is ApplicationAndroidComponentsExtension, is
+ DynamicFeatureAndroidComponentsExtension ->
+ applicationIds.getOrPut(it.name) {
+ project.objects.property(String::class.java)
+ }.value(it.applicationId)
+ }
+ }
forEachVariant(extension) { variant ->
val task = project.tasks.create(
"generateSafeArgs${variant.name.capitalize()}",
ArgumentsGenerationTask::class.java
) { task ->
- setApplicationId(task, variant)
+ task.applicationId.set(
+ applicationIds.getOrPut(variant.name) {
+ project.objects.property(String::class.java)
+ }.value(variant.applicationId)
+ )
task.rFilePackage = variant.rFilePackage()
task.navigationFiles = navigationFiles(variant, project)
task.outputDir = File(project.buildDir, "$GENERATED_PATH/${variant.dirName}")
@@ -81,24 +102,10 @@
}
task.generateKotlin = generateKotlin
}
- task.applicationIdResource?.let { task.dependsOn(it) }
variant.registerJavaGeneratingTask(task, task.outputDir)
}
}
- /**
- * Sets the android project application id into the task.
- */
- private fun setApplicationId(task: ArgumentsGenerationTask, variant: BaseVariant) {
- val appIdTextResource = variant.applicationIdTextResource
- if (appIdTextResource != null) {
- task.applicationIdResource = appIdTextResource
- } else {
- // getApplicationIdTextResource() returned null, fallback to getApplicationId()
- task.applicationId = variant.applicationId
- }
- }
-
private fun BaseVariant.rFilePackage() = providerFactory.provider {
val mainSourceSet = sourceSets.find { it.name == "main" }
val sourceSet = mainSourceSet ?: sourceSets[0]
diff --git a/room/compiler/src/main/kotlin/androidx/room/preconditions/Checks.kt b/room/compiler/src/main/kotlin/androidx/room/preconditions/Checks.kt
index 070e72a..271c1df 100644
--- a/room/compiler/src/main/kotlin/androidx/room/preconditions/Checks.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/preconditions/Checks.kt
@@ -16,8 +16,8 @@
package androidx.room.preconditions
-import androidx.room.log.RLog
import androidx.room.compiler.processing.XElement
+import androidx.room.log.RLog
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeVariableName
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index 7bbb587..83381bc 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -624,7 +624,13 @@
val INVALID_TABLE_NAME = "Invalid table name. Room does not allow using ` or \" in table names"
val RAW_QUERY_BAD_PARAMS = "RawQuery methods should have 1 and only 1 parameter with type" +
- " String or SupportSQLiteQuery"
+ " SupportSQLiteQuery"
+
+ fun parameterCannotBeNullable(
+ parameterName: String
+ ) = """
+ Parameter `$parameterName` cannot be nullable.
+ """.trimIndent()
val RAW_QUERY_BAD_RETURN_TYPE = "RawQuery methods must return a non-void type."
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
index 4d2a30d..39377be8 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
@@ -22,6 +22,7 @@
import androidx.room.ext.isEntityElement
import androidx.room.parser.SqlParser
import androidx.room.compiler.processing.XMethodElement
+import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.XVariableElement
import androidx.room.processor.ProcessorErrors.RAW_QUERY_STRING_PARAMETER_REMOVED
@@ -117,6 +118,17 @@
if (extractParams.size == 1 && !executableElement.isVarArgs()) {
val param = extractParams.first().asMemberOf(containing)
val processingEnv = context.processingEnv
+ if (param.nullability == XNullability.NULLABLE) {
+ context.logger.e(
+ element = extractParams.first(),
+ msg = ProcessorErrors.parameterCannotBeNullable(
+ parameterName = extractParams.first().name
+ )
+ )
+ }
+ // use nullable type to catch bad nullability. Because it is non-null by default in
+ // KSP, assignability will fail and we'll print a generic error instead of a specific
+ // one
val supportQueryType = processingEnv.requireType(SupportDbTypeNames.QUERY)
val isSupportSql = supportQueryType.isAssignableFrom(param)
if (isSupportSql) {
diff --git a/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt b/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
index 16dc991..9938afa4 100644
--- a/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
@@ -16,6 +16,7 @@
package androidx.room.writer
+import androidx.annotation.NonNull
import androidx.room.ext.AndroidTypeNames
import androidx.room.ext.CommonTypeNames
import androidx.room.ext.L
@@ -130,7 +131,7 @@
val scope = CodeGenScope(this)
return MethodSpec.methodBuilder("getRequiredAutoMigrationSpecs").apply {
addAnnotation(Override::class.java)
- addModifiers(PROTECTED)
+ addModifiers(PUBLIC)
returns(
ParameterizedTypeName.get(
CommonTypeNames.SET,
@@ -349,13 +350,27 @@
private fun getAutoMigrations(): MethodSpec {
return MethodSpec.methodBuilder("getAutoMigrations").apply {
- addModifiers(PROTECTED)
+ addModifiers(PUBLIC)
addAnnotation(Override::class.java)
+ addParameter(
+ ParameterSpec.builder(
+ ParameterizedTypeName.get(
+ CommonTypeNames.MAP,
+ ParameterizedTypeName.get(
+ ClassName.get(Class::class.java),
+ WildcardTypeName.subtypeOf(RoomTypeNames.AUTO_MIGRATION_SPEC)
+ ),
+ RoomTypeNames.AUTO_MIGRATION_SPEC
+ ),
+ "autoMigrationSpecsMap"
+ ).addAnnotation(NonNull::class.java).build()
+ )
+
returns(ParameterizedTypeName.get(CommonTypeNames.LIST, RoomTypeNames.MIGRATION))
val autoMigrationsList = database.autoMigrations.map { autoMigrationResult ->
if (autoMigrationResult.isSpecProvided) {
CodeBlock.of(
- "new $T(mAutoMigrationSpecs.get($T.class))",
+ "new $T(autoMigrationSpecsMap.get($T.class))",
autoMigrationResult.implTypeName,
autoMigrationResult.specClassName
)
diff --git a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
index 5dd84db..e9df8c9 100644
--- a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
@@ -1,5 +1,6 @@
package foo.bar;
+import androidx.annotation.NonNull;
import androidx.room.DatabaseConfiguration;
import androidx.room.InvalidationTracker;
import androidx.room.RoomOpenHelper;
@@ -188,13 +189,14 @@
}
@Override
- protected Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
+ public Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
final HashSet<Class<? extends AutoMigrationSpec>> _autoMigrationSpecsSet = new HashSet<Class<? extends AutoMigrationSpec>>();
return _autoMigrationSpecsSet;
}
@Override
- protected List<Migration> getAutoMigrations() {
+ public List<Migration> getAutoMigrations(
+ @NonNull Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> autoMigrationSpecsMap) {
return Arrays.asList();
}
diff --git a/room/compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
index 7145423..88ea374 100644
--- a/room/compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
@@ -286,6 +286,40 @@
}
@Test
+ fun badType() {
+ singleQueryMethod(
+ """
+ @RawQuery
+ abstract public int[] foo(int query);
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasErrorContaining(
+ ProcessorErrors.RAW_QUERY_BAD_PARAMS
+ )
+ }
+ }
+ }
+
+ @Test
+ fun badType_nullable() {
+ singleQueryMethod(
+ """
+ @RawQuery
+ abstract public int[] foo(@androidx.annotation.Nullable SupportSQLiteQuery query);
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasErrorContaining(
+ ProcessorErrors.parameterCannotBeNullable(
+ parameterName = "query"
+ )
+ )
+ }
+ }
+ }
+
+ @Test
fun observed_notAnEntity() {
singleQueryMethod(
"""
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
index faabb9d..8c72847 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
@@ -17,11 +17,11 @@
package androidx.room.integration.kotlintestapp.migration
import androidx.room.Room
+import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.room.testing.MigrationTestHelper
import androidx.room.util.TableInfo
import androidx.sqlite.db.SupportSQLiteDatabase
-import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import org.hamcrest.CoreMatchers.`is`
@@ -40,20 +40,21 @@
@get:Rule
var helper: MigrationTestHelper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
- MigrationDbKotlin::class.java.canonicalName,
- FrameworkSQLiteOpenHelperFactory()
+ MigrationDbKotlin::class.java
)
companion object {
val TEST_DB = "migration-test"
}
+ abstract class EmptyDb : RoomDatabase()
+
@Test
@Throws(IOException::class)
fun giveBadResource() {
val helper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
- "foo", FrameworkSQLiteOpenHelperFactory()
+ EmptyDb::class.java
)
try {
helper.createDatabase(TEST_DB, 1)
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
index fa6fa9d..3206ff9 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
@@ -16,17 +16,13 @@
package androidx.room.integration.testapp.migration;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.fail;
-
-import android.database.sqlite.SQLiteException;
import androidx.annotation.NonNull;
-import androidx.room.Room;
import androidx.room.migration.Migration;
import androidx.room.testing.MigrationTestHelper;
+import androidx.room.util.TableInfo;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
@@ -49,88 +45,42 @@
public MigrationTestHelper helper;
public AutoMigrationTest() {
- helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
- AutoMigrationDb.class.getCanonicalName());
+ helper = new MigrationTestHelper(
+ InstrumentationRegistry.getInstrumentation(),
+ AutoMigrationDb.class
+ );
}
// Run this to create the very 1st version of the db.
public void createFirstVersion() throws IOException {
- SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 2);
+ SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
+ db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
db.close();
}
- /**
- * Tests the case where a non existent auto migration is called.
- */
- @Test
- public void testBadAutoMigrationInput() throws IOException {
- try (SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1)) {
- db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
- AutoMigrationDb autoMigrationDbV2 = getLatestDb();
- helper.runMigrationsAndValidate(
- TEST_DB,
- 2,
- true,
- autoMigrationDbV2.getAutoGeneratedMigration(3, 4)
- );
- fail();
- } catch (IllegalArgumentException ex) {
- assertThat(
- ex.getMessage(),
- is("No AutoMigrations between versions 'from = 3', 'to = "
- + "4' have been provided. Annotate Database class with @AutoMigration(from = "
- + "3, to = 4) to generate this AutoMigration.")
- );
- }
- }
-
@Test
public void goFromV1ToV2() throws IOException {
- try (SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1)) {
- db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
- }
- AutoMigrationDb autoMigrationDbV2 = getLatestDb();
- helper.runMigrationsAndValidate(
+ createFirstVersion();
+ SupportSQLiteDatabase db = helper.runMigrationsAndValidate(
TEST_DB,
2,
true
);
- assertThat(autoMigrationDbV2.dao().getAllEntity1s().size(), is(1));
+ final TableInfo info = TableInfo.read(db, AutoMigrationDb.Entity1.TABLE_NAME);
+ assertThat(info.columns.size(), is(3));
}
/**
* Verifies that the user defined migration is selected over using an autoMigration.
*/
@Test
- public void goFromV1ToV2WithUserDefinedMigration() throws IOException {
- try (SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1)) {
- db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
- }
-
- try {
- AutoMigrationDb autoMigrationDbV2 = Room.databaseBuilder(
- InstrumentationRegistry.getInstrumentation().getTargetContext(),
- AutoMigrationDb.class, TEST_DB).addMigrations(MIGRATION_1_2).build();
- autoMigrationDbV2.getOpenHelper().getWritableDatabase(); // trigger open
- helper.closeWhenFinished(autoMigrationDbV2);
-
- helper.runMigrationsAndValidate(
- TEST_DB,
- 2,
- true
- );
- } catch (SQLiteException exception) {
- assertThat(exception.getMessage(), containsString("no such table: Entity0"));
- }
- }
-
- private AutoMigrationDb getLatestDb() {
- AutoMigrationDb db = Room.databaseBuilder(
- InstrumentationRegistry.getInstrumentation().getTargetContext(),
- AutoMigrationDb.class, TEST_DB).build();
- db.getOpenHelper().getWritableDatabase(); // trigger open
- helper.closeWhenFinished(db);
- return db;
+ public void testAutoMigrationsNotProcessedBeforeCustomMigrations() throws IOException {
+ helper.runMigrationsAndValidate(
+ TEST_DB,
+ 2,
+ true,
+ MIGRATION_1_2
+ );
}
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/ProvidedAutoMigrationSpecTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/ProvidedAutoMigrationSpecTest.java
index da5c158..6fdb83b 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/ProvidedAutoMigrationSpecTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/ProvidedAutoMigrationSpecTest.java
@@ -27,11 +27,11 @@
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.ProvidedAutoMigrationSpec;
-import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.AutoMigrationSpec;
import androidx.room.testing.MigrationTestHelper;
import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -41,6 +41,8 @@
import org.junit.runner.RunWith;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
/**
* Test custom database migrations.
@@ -53,11 +55,24 @@
new ProvidedAutoMigrationDb.MyProvidedAutoMigration("Hi");
@Rule
- public MigrationTestHelper helper;
+ public MigrationTestHelper helperWithoutSpec;
+ public MigrationTestHelper helperWithSpec;
public ProvidedAutoMigrationSpecTest() {
- helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
- ProvidedAutoMigrationDb.class.getCanonicalName());
+ helperWithoutSpec = new MigrationTestHelper(
+ InstrumentationRegistry.getInstrumentation(),
+ ProvidedAutoMigrationDb.class
+ );
+
+ List<AutoMigrationSpec> specs = new ArrayList<>();
+ specs.add(mProvidedSpec);
+
+ helperWithSpec = new MigrationTestHelper(
+ InstrumentationRegistry.getInstrumentation(),
+ ProvidedAutoMigrationDb.class,
+ specs,
+ new FrameworkSQLiteOpenHelperFactory()
+ );
}
@Database(
@@ -123,15 +138,15 @@
// Run this to create the very 1st version of the db.
public void createFirstVersion() throws IOException {
- SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 2);
+ SupportSQLiteDatabase db = helperWithoutSpec.createDatabase(TEST_DB, 1);
+ db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
db.close();
}
@Test
public void testOnPostMigrate() throws IOException {
- SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
- ProvidedAutoMigrationDb autoMigrationDbV2 = getLatestDb();
- helper.runMigrationsAndValidate(
+ createFirstVersion();
+ helperWithSpec.runMigrationsAndValidate(
TEST_DB,
2,
true
@@ -143,32 +158,24 @@
* Verifies that the user defined migration is selected over using an autoMigration.
*/
@Test
- public void testNoSpecProvidedInConfig() {
+ public void testNoSpecProvidedInConfig() throws IOException {
+ createFirstVersion();
try {
- ProvidedAutoMigrationDb autoMigrationDbV2 = Room.databaseBuilder(
- InstrumentationRegistry.getInstrumentation().getTargetContext(),
- ProvidedAutoMigrationDb.class, TEST_DB).build();
+ helperWithoutSpec.runMigrationsAndValidate(
+ TEST_DB,
+ 2,
+ true
+ );
} catch (IllegalArgumentException exception) {
assertThat(
exception.getMessage(),
containsString(
"A required auto migration spec (androidx.room.integration.testapp"
+ ".migration.ProvidedAutoMigrationSpecTest"
- + ".ProvidedAutoMigrationDb.MyProvidedAutoMigration) is "
- + "missing in the database configuration."
+ + ".ProvidedAutoMigrationDb.MyProvidedAutoMigration) has not "
+ + "been provided."
)
);
}
}
-
- private ProvidedAutoMigrationDb getLatestDb() {
- ProvidedAutoMigrationDb db = Room.databaseBuilder(
- InstrumentationRegistry.getInstrumentation().getTargetContext(),
- ProvidedAutoMigrationDb.class, TEST_DB)
- .addAutoMigrationSpec(mProvidedSpec)
- .build();
- db.getOpenHelper().getWritableDatabase(); // trigger open
- helper.closeWhenFinished(db);
- return db;
- }
}
diff --git a/room/runtime/api/current.txt b/room/runtime/api/current.txt
index 50f212f..c6616ad 100644
--- a/room/runtime/api/current.txt
+++ b/room/runtime/api/current.txt
@@ -52,7 +52,6 @@
method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
method @Deprecated public void endTransaction();
- method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
method public androidx.room.InvalidationTracker getInvalidationTracker();
method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
method public java.util.concurrent.Executor getQueryExecutor();
diff --git a/room/runtime/api/public_plus_experimental_current.txt b/room/runtime/api/public_plus_experimental_current.txt
index 758a9f6..bc1f3c0 100644
--- a/room/runtime/api/public_plus_experimental_current.txt
+++ b/room/runtime/api/public_plus_experimental_current.txt
@@ -42,6 +42,7 @@
public class Room {
ctor @Deprecated public Room();
method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> databaseBuilder(android.content.Context, Class<T!>, String);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <T, C> T getGeneratedImplementation(Class<C!>, String);
method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> inMemoryDatabaseBuilder(android.content.Context, Class<T!>);
field public static final String MASTER_TABLE_NAME = "room_master_table";
}
@@ -55,7 +56,6 @@
method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
method @Deprecated public void endTransaction();
- method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
method public androidx.room.InvalidationTracker getInvalidationTracker();
method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
method public java.util.concurrent.Executor getQueryExecutor();
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index 14c301a..51e2dac 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -94,7 +94,6 @@
method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
method @Deprecated public void endTransaction();
- method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
method public androidx.room.InvalidationTracker getInvalidationTracker();
method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
method public java.util.concurrent.Executor getQueryExecutor();
diff --git a/room/runtime/src/main/java/androidx/room/Room.java b/room/runtime/src/main/java/androidx/room/Room.java
index 6a570c4..d252099 100644
--- a/room/runtime/src/main/java/androidx/room/Room.java
+++ b/room/runtime/src/main/java/androidx/room/Room.java
@@ -19,6 +19,7 @@
import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
/**
* Utility class for Room.
@@ -75,7 +76,9 @@
@SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
@NonNull
- static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static <T, C> T getGeneratedImplementation(@NonNull Class<C> klass,
+ @NonNull String suffix) {
final String fullPackage = klass.getPackage().getName();
String name = klass.getCanonicalName();
final String postPackageName = fullPackage.isEmpty()
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index 3c79716..d15c9c2 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -227,7 +227,7 @@
}
}
- List<Migration> autoMigrations = getAutoMigrations();
+ List<Migration> autoMigrations = getAutoMigrations(mAutoMigrationSpecs);
for (Migration autoMigration : autoMigrations) {
boolean migrationExists = configuration.migrationContainer.getMigrations()
.containsKey(autoMigration.startVersion);
@@ -311,43 +311,16 @@
* Returns a list of {@link Migration} of a database that have been automatically generated.
*
* @return A list of migration instances each of which is a generated autoMigration
- */
- @NonNull
- protected List<Migration> getAutoMigrations() {
- return Collections.emptyList();
- }
-
- /**
- * Returns a {@link Migration} of a database that have been generated using
- * {@link AutoMigration} with the specific "from" and "to" version pair.
- * <p>
- * If a {@link Migration} with the given "from" and "to" versions cannot be found, this
- * method will throw a {@link IllegalArgumentException}.
- *
- * <p>
- * This API is intended for testing and all auto-migrations are added by default.
- *
- * @param from version of the original database schema to migrate from
- * @param to version of the new database schema to migrate to
- * @return migration instance of a generated autoMigration
+ * @param autoMigrationSpecs
*
* @hide
*/
@NonNull
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public Migration getAutoGeneratedMigration(int from, int to) {
- // TODO: (b/181985265) Support testing automigrations in MigrationTestHelper and remove
- // this method
- List<Migration> autoMigrations = getAutoMigrations();
- for (Migration autoMigration : autoMigrations) {
- if (autoMigration.startVersion == from && autoMigration.endVersion == to) {
- return autoMigration;
- }
- }
- throw new IllegalArgumentException("No AutoMigrations between versions 'from = " + from
- + "', 'to = " + to + "' have been provided. Annotate Database class with "
- + "@AutoMigration(from = " + from + ", to = " + to + ") to generate this "
- + "AutoMigration.");
+ public List<Migration> getAutoMigrations(
+ @NonNull Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> autoMigrationSpecs
+ ) {
+ return Collections.emptyList();
}
/**
@@ -427,7 +400,7 @@
*/
@NonNull
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- protected Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
+ public Set<Class<? extends AutoMigrationSpec>> getRequiredAutoMigrationSpecs() {
return Collections.emptySet();
}
diff --git a/room/testing/api/current.txt b/room/testing/api/current.txt
index 891c1b7..ea0639e 100644
--- a/room/testing/api/current.txt
+++ b/room/testing/api/current.txt
@@ -2,8 +2,11 @@
package androidx.room.testing {
public class MigrationTestHelper extends org.junit.rules.TestWatcher {
- ctor public MigrationTestHelper(android.app.Instrumentation!, String!);
- ctor public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+ ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!);
+ ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory);
method public void closeWhenFinished(androidx.sqlite.db.SupportSQLiteDatabase!);
method public void closeWhenFinished(androidx.room.RoomDatabase!);
method public androidx.sqlite.db.SupportSQLiteDatabase! createDatabase(String!, int) throws java.io.IOException;
diff --git a/room/testing/api/public_plus_experimental_current.txt b/room/testing/api/public_plus_experimental_current.txt
index 891c1b7..ea0639e 100644
--- a/room/testing/api/public_plus_experimental_current.txt
+++ b/room/testing/api/public_plus_experimental_current.txt
@@ -2,8 +2,11 @@
package androidx.room.testing {
public class MigrationTestHelper extends org.junit.rules.TestWatcher {
- ctor public MigrationTestHelper(android.app.Instrumentation!, String!);
- ctor public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+ ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!);
+ ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory);
method public void closeWhenFinished(androidx.sqlite.db.SupportSQLiteDatabase!);
method public void closeWhenFinished(androidx.room.RoomDatabase!);
method public androidx.sqlite.db.SupportSQLiteDatabase! createDatabase(String!, int) throws java.io.IOException;
diff --git a/room/testing/api/restricted_current.txt b/room/testing/api/restricted_current.txt
index 891c1b7..ea0639e 100644
--- a/room/testing/api/restricted_current.txt
+++ b/room/testing/api/restricted_current.txt
@@ -2,8 +2,11 @@
package androidx.room.testing {
public class MigrationTestHelper extends org.junit.rules.TestWatcher {
- ctor public MigrationTestHelper(android.app.Instrumentation!, String!);
- ctor public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+ ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!);
+ ctor @Deprecated public MigrationTestHelper(android.app.Instrumentation!, String!, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory!);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>);
+ ctor public MigrationTestHelper(android.app.Instrumentation, Class<? extends androidx.room.RoomDatabase>, java.util.List<androidx.room.migration.AutoMigrationSpec!>, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory);
method public void closeWhenFinished(androidx.sqlite.db.SupportSQLiteDatabase!);
method public void closeWhenFinished(androidx.room.RoomDatabase!);
method public androidx.sqlite.db.SupportSQLiteDatabase! createDatabase(String!, int) throws java.io.IOException;
diff --git a/room/testing/src/main/java/androidx/room/testing/MigrationTestHelper.java b/room/testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
index c846037..02c5192 100644
--- a/room/testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
+++ b/room/testing/src/main/java/androidx/room/testing/MigrationTestHelper.java
@@ -23,12 +23,15 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.arch.core.executor.ArchTaskExecutor;
+import androidx.room.AutoMigration;
import androidx.room.DatabaseConfiguration;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.RoomOpenHelper;
import androidx.room.RoomOpenHelper.ValidationResult;
+import androidx.room.migration.AutoMigrationSpec;
import androidx.room.migration.Migration;
import androidx.room.migration.bundle.DatabaseBundle;
import androidx.room.migration.bundle.DatabaseViewBundle;
@@ -91,14 +94,27 @@
private List<WeakReference<RoomDatabase>> mManagedRoomDatabases = new ArrayList<>();
private boolean mTestStarted;
private Instrumentation mInstrumentation;
+ @Nullable
+ private List<AutoMigrationSpec> mSpecs;
+ @Nullable
+ private Class<? extends RoomDatabase> mDatabaseClass;
/**
* Creates a new migration helper. It uses the Instrumentation context to load the schema
* (falls back to the app resources) and the target context to create the database.
*
+ * @deprecated Cannot be used to run migration tests involving {@link AutoMigration}.
+ * <p>
+ * To test {@link AutoMigration}, you must use
+ * {@link #MigrationTestHelper(Instrumentation, Class, List, SupportSQLiteOpenHelper.Factory)}
+ * for tests containing a {@link androidx.room.ProvidedAutoMigrationSpec}, or use
+ * {@link #MigrationTestHelper(Instrumentation, Class, List)}
+ * otherwise.
+ *
* @param instrumentation The instrumentation instance.
* @param assetsFolder The asset folder in the assets directory.
*/
+ @Deprecated
public MigrationTestHelper(Instrumentation instrumentation, String assetsFolder) {
this(instrumentation, assetsFolder, new FrameworkSQLiteOpenHelperFactory());
}
@@ -107,18 +123,88 @@
* Creates a new migration helper. It uses the Instrumentation context to load the schema
* (falls back to the app resources) and the target context to create the database.
*
+ * @deprecated Cannot be used to run migration tests involving {@link AutoMigration}.
+ * <p>
+ * To test {@link AutoMigration}, you must use
+ * {@link #MigrationTestHelper(Instrumentation, Class, List, SupportSQLiteOpenHelper.Factory)}
+ * for tests containing a {@link androidx.room.ProvidedAutoMigrationSpec}, or use
+ * {@link #MigrationTestHelper(Instrumentation, Class, List)}
+ * otherwise.
+ *
* @param instrumentation The instrumentation instance.
* @param assetsFolder The asset folder in the assets directory.
* @param openFactory Factory class that allows creation of {@link SupportSQLiteOpenHelper}
*/
+ @Deprecated
public MigrationTestHelper(Instrumentation instrumentation, String assetsFolder,
SupportSQLiteOpenHelper.Factory openFactory) {
mInstrumentation = instrumentation;
+ mAssetsFolder = assetsFolder;
+ mOpenFactory = openFactory;
+ mDatabaseClass = null;
+ mSpecs = new ArrayList<>();
+ }
+
+ /**
+ * Creates a new migration helper. It uses the Instrumentation context to load the schema
+ * (falls back to the app resources) and the target context to create the database.
+ *
+ * @param instrumentation The instrumentation instance.
+ * @param databaseClass The Database class to be tested.
+ */
+ public MigrationTestHelper(@NonNull Instrumentation instrumentation,
+ @NonNull Class<? extends RoomDatabase> databaseClass) {
+ this(instrumentation, databaseClass, new ArrayList<>(),
+ new FrameworkSQLiteOpenHelperFactory());
+ }
+
+ /**
+ * Creates a new migration helper. It uses the Instrumentation context to load the schema
+ * (falls back to the app resources) and the target context to create the database.
+ * <p>
+ * An instance of a class annotated with {@link androidx.room.ProvidedAutoMigrationSpec} has
+ * to be provided to Room using this constructor. MigrationTestHelper will map auto migration
+ * spec classes to their provided instances before running and validatingt the Migrations.
+ *
+ * @param instrumentation The instrumentation instance.
+ * @param databaseClass The Database class to be tested.
+ * @param specs The list of available auto migration specs that will be provided to
+ * Room at runtime.
+ */
+ public MigrationTestHelper(@NonNull Instrumentation instrumentation,
+ @NonNull Class<? extends RoomDatabase> databaseClass,
+ @NonNull List<AutoMigrationSpec> specs) {
+ this(instrumentation, databaseClass, specs, new FrameworkSQLiteOpenHelperFactory());
+ }
+
+ /**
+ * Creates a new migration helper. It uses the Instrumentation context to load the schema
+ * (falls back to the app resources) and the target context to create the database.
+ * <p>
+ * An instance of a class annotated with {@link androidx.room.ProvidedAutoMigrationSpec} has
+ * to be provided to Room using this constructor. MigrationTestHelper will map auto migration
+ * spec classes to their provided instances before running and validatingt the Migrations.
+ *
+ * @param instrumentation The instrumentation instance.
+ * @param databaseClass The Database class to be tested.
+ * @param specs The list of available auto migration specs that will be provided to
+ * Room at runtime.
+ * @param openFactory Factory class that allows creation of {@link SupportSQLiteOpenHelper}
+ */
+ public MigrationTestHelper(@NonNull Instrumentation instrumentation,
+ @NonNull Class<? extends RoomDatabase> databaseClass,
+ @NonNull List<AutoMigrationSpec> specs,
+ @NonNull SupportSQLiteOpenHelper.Factory openFactory
+ ) {
+ String assetsFolder = databaseClass.getCanonicalName();
+ mInstrumentation = instrumentation;
if (assetsFolder.endsWith("/")) {
assetsFolder = assetsFolder.substring(0, assetsFolder.length() - 1);
}
mAssetsFolder = assetsFolder;
mOpenFactory = openFactory;
+ mDatabaseClass = databaseClass;
+ mSpecs = specs;
}
@Override
@@ -144,7 +230,7 @@
if (dbPath.exists()) {
Log.d(TAG, "deleting database file " + name);
if (!dbPath.delete()) {
- throw new IllegalStateException("there is a database file and i could not delete"
+ throw new IllegalStateException("There is a database file and I could not delete"
+ " it. Make sure you don't have any open connections to that database"
+ " before calling this method.");
}
@@ -214,6 +300,7 @@
}
SchemaBundle schemaBundle = loadSchema(version);
RoomDatabase.MigrationContainer container = new RoomDatabase.MigrationContainer();
+ container.addMigrations(getAutoMigrations(mSpecs));
container.addMigrations(migrations);
DatabaseConfiguration configuration = new DatabaseConfiguration(
mInstrumentation.getTargetContext(),
@@ -244,6 +331,75 @@
return openDatabase(name, roomOpenHelper);
}
+ /**
+ * Returns a list of {@link Migration} of a database that has been generated using
+ * {@link AutoMigration}.
+ */
+ @NonNull
+ private List<Migration> getAutoMigrations(List<AutoMigrationSpec> userProvidedSpecs) {
+ if (mDatabaseClass == null) {
+ if (userProvidedSpecs.isEmpty()) {
+ // TODO: Detect that there are auto migrations to test when a deprecated
+ // constructor is used.
+ Log.e(TAG, "If you have any AutoMigrations in your implementation, you must use "
+ + "a non-deprecated MigrationTestHelper constructor to provide the "
+ + "Database class in order to test them. If you do not have any "
+ + "AutoMigrations to test, you may ignore this warning.");
+ return new ArrayList<>();
+ } else {
+ throw new IllegalStateException("You must provide the database class in the "
+ + "MigrationTestHelper constructor in order to test auto migrations.");
+ }
+ }
+
+ RoomDatabase db = Room.getGeneratedImplementation(mDatabaseClass, "_Impl");
+ Set<Class<? extends AutoMigrationSpec>> requiredAutoMigrationSpecs =
+ db.getRequiredAutoMigrationSpecs();
+ return db.getAutoMigrations(
+ createAutoMigrationSpecMap(requiredAutoMigrationSpecs, userProvidedSpecs)
+ );
+ }
+
+ /**
+ * Maps auto migration spec classes to their provided instance.
+ */
+ private Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> createAutoMigrationSpecMap(
+ Set<Class<? extends AutoMigrationSpec>> requiredAutoMigrationSpecs,
+ List<AutoMigrationSpec> userProvidedSpecs) {
+ Map<Class<? extends AutoMigrationSpec>, AutoMigrationSpec> specMap = new HashMap<>();
+ if (requiredAutoMigrationSpecs.isEmpty()) {
+ return specMap;
+ }
+
+ if (userProvidedSpecs == null) {
+ throw new IllegalStateException(
+ "You must provide all required auto migration specs in the "
+ + "MigrationTestHelper constructor."
+ );
+ }
+
+ for (Class<? extends AutoMigrationSpec> spec : requiredAutoMigrationSpecs) {
+ boolean found = false;
+ AutoMigrationSpec match = null;
+ for (AutoMigrationSpec provided : userProvidedSpecs) {
+ if (spec.isAssignableFrom(provided.getClass())) {
+ found = true;
+ match = provided;
+ break;
+ }
+ }
+ if (!found) {
+ throw new IllegalArgumentException(
+ "A required auto migration spec (" + spec.getCanonicalName() + ") has not"
+ + " been provided."
+ );
+ }
+ specMap.put(spec, match);
+ }
+ return specMap;
+ }
+
+
private SupportSQLiteDatabase openDatabase(String name, RoomOpenHelper roomOpenHelper) {
SupportSQLiteOpenHelper.Configuration config =
SupportSQLiteOpenHelper.Configuration
diff --git a/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilderTest.java b/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilderTest.java
index b45f255..83d27b6 100644
--- a/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilderTest.java
+++ b/security/security-app-authenticator-testing/src/androidTest/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilderTest.java
@@ -324,4 +324,21 @@
assertThrows(SecurityException.class, () ->
appAuthenticatorFromInputStream.enforceAppIdentity(EXPECTED_IDENTITY_PACKAGE));
}
+
+ @Test
+ public void setSigningIdentityForPackage_digestCaseMismatch_returnsMatch() throws Exception {
+ // The TestAppAuthenticatorBuilder supports specifying a signing identity for a package
+ // under test; while the AppAuthenticator will normalize the digest in the config file to
+ // match the case output by the AppAuthenticatorUtils#computeDigest, the signing identity
+ // provided to the TestAppAuthenticatorBuilder#setSigningIdentityForPackage can be
+ // specified in either case. This test ensures regardless of the case provided to this
+ // method the value is normalized and a match can be successfully verified.
+ AppAuthenticator appAuthenticator =
+ mBuilderFromResource.setSigningIdentityForPackage(EXPECTED_IDENTITY_PACKAGE,
+ "6A8B96E278E58F62CFE3584022CEC1D0527FCB85A9E5D2E1694EB0405BE5B599")
+ .build();
+
+ assertEquals(AppAuthenticator.SIGNATURE_MATCH,
+ appAuthenticator.checkAppIdentity(EXPECTED_IDENTITY_PACKAGE));
+ }
}
diff --git a/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilder.java b/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilder.java
index a5f21d6..372a166 100644
--- a/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilder.java
+++ b/security/security-app-authenticator-testing/src/main/java/androidx/security/app/authenticator/TestAppAuthenticatorBuilder.java
@@ -255,7 +255,9 @@
@NonNull String packageName,
@NonNull String certDigest) {
mTestPolicy = POLICY_CUSTOM;
- mAppSignatureVerifierBuilder.setSigningIdentityForPackage(packageName, certDigest);
+ mAppSignatureVerifierBuilder.setSigningIdentityForPackage(packageName,
+ AppAuthenticator.normalizeCertDigest(certDigest))
+ ;
return this;
}
diff --git a/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorTest.java b/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
index f7402d3..b22b902 100644
--- a/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
+++ b/security/security-app-authenticator/src/androidTest/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
@@ -322,4 +322,22 @@
"d78405f761ff6236cc9b570347a570aba0c62a129a3ac30c831c64d09ad95469"));
assertEquals("SHA-256", config.getDigestAlgorithm());
}
+
+ @Test
+ public void createConfigFromParser_upperCaseDigestInConfig_returnsMatch() throws Exception {
+ // The digest computed by the AppAuthenticatorUtils is in lower case, but the
+ // AppAuthenticator supports matching digests provided in upper case as well.
+ // This test does not directly verify the digest of a package's signing certificate
+ // but instead uses the bytes from the package name in the identity; this test ensures
+ // the AppAuthenticator properly normalizes the provided digest so that it matches the
+ // digest returned by AppAuthenticatorUtils.
+ final String packageName = "com.example.app";
+ AppAuthenticator.AppAuthenticatorConfig config = AppAuthenticator.createConfigFromParser(
+ mResources.getXml(R.xml.upper_case_digest));
+ Set<String> expectedPackageIdentities = config.getExpectedIdentities().get(packageName);
+
+ assertTrue(expectedPackageIdentities.contains(
+ AppAuthenticatorUtils.computeDigest(AppAuthenticator.DEFAULT_DIGEST_ALGORITHM,
+ packageName.getBytes())));
+ }
}
diff --git a/security/security-app-authenticator/src/androidTest/res/xml/upper_case_digest.xml b/security/security-app-authenticator/src/androidTest/res/xml/upper_case_digest.xml
new file mode 100644
index 0000000..179c964
--- /dev/null
+++ b/security/security-app-authenticator/src/androidTest/res/xml/upper_case_digest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<app-authenticator>
+ <expected-identity>
+ <package name="com.example.app">
+ <cert-digest>8A464E05BF037AF2432200C8687455FCD1E0A804D69C8A30D29DA59F584AC77F
+ </cert-digest>
+ </package>
+ </expected-identity>
+</app-authenticator>
diff --git a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticator.java b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticator.java
index c6e26df..0e9f201 100644
--- a/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticator.java
+++ b/security/security-app-authenticator/src/main/java/androidx/security/app/authenticator/AppAuthenticator.java
@@ -38,6 +38,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -591,13 +592,28 @@
+ "on line " + parser.getLineNumber() + " must have non-empty text "
+ "containing the certificate digest of the signer");
}
- allowedCertDigests.add(digest);
+ allowedCertDigests.add(normalizeCertDigest(digest));
eventType = parser.nextTag();
}
return allowedCertDigests;
}
/**
+ * Normalizes the provided {@code certDigest} to ensure it is in the proper form for {@code
+ * Collection} membership checks when comparing a package's signing certificate digest against
+ * those provided to the {@code AppAuthenticator}.
+ *
+ * @param certDigest the digest to be normalized
+ * @return a normalized form of the provided digest that can be used in subsequent {@code
+ * Collection} membership checks
+ */
+ static String normalizeCertDigest(String certDigest) {
+ // The AppAuthenticatorUtils#computeDigest method uses lower case characters to compute the
+ // digest.
+ return certDigest.toLowerCase(Locale.US);
+ }
+
+ /**
* Moves the provided {@code parser} to the next {@link XmlPullParser#START_TAG} or {@link
* XmlPullParser#END_DOCUMENT} if the end of the document is reached, returning the value of
* the event type.
diff --git a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
index 48a160f..defc993 100644
--- a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
+++ b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
@@ -27,6 +27,7 @@
import androidx.compose.testutils.assertShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.Role
@@ -310,7 +311,9 @@
.setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
CompactChip(
onClick = {},
- modifier = Modifier.testTag(chipTag).width(100.dp),
+ modifier = Modifier
+ .testTag(chipTag)
+ .width(100.dp),
icon = { CreateImage(iconTag) }
)
}
@@ -381,7 +384,7 @@
TestChipColors.Primary,
ChipStatus.Enabled,
{ MaterialTheme.colors.primary },
- { MaterialTheme.colors.onPrimary }
+ { MaterialTheme.colors.onPrimary },
)
@Test
@@ -413,7 +416,7 @@
TestChipColors.Primary,
ChipStatus.Disabled,
{ MaterialTheme.colors.primary },
- { MaterialTheme.colors.onPrimary }
+ { MaterialTheme.colors.onPrimary },
)
@Test
@@ -626,16 +629,22 @@
var expectedBackground = Color.Transparent
var expectedContent = Color.Transparent
var actualContent = Color.Transparent
- var expectedAlpha = 0.0f
+ val testBackground = Color.White
rule.setContentWithTheme {
- expectedBackground = backgroundColor()
- expectedContent = contentColor()
- expectedAlpha = ContentAlpha.disabled
+ if (status.enabled()) {
+ expectedBackground = backgroundColor()
+ expectedContent = contentColor()
+ } else {
+ expectedBackground =
+ backgroundColor().copy(alpha = ContentAlpha.disabled)
+ .compositeOver(testBackground)
+ expectedContent = contentColor().copy(alpha = ContentAlpha.disabled)
+ }
Box(
modifier = Modifier
.fillMaxSize()
- .background(expectedBackground)
+ .background(testBackground)
) {
Chip(
onClick = {},
@@ -647,17 +656,14 @@
}
}
- if (status.enabled()) {
- assertEquals(expectedContent, actualContent)
- } else {
- assertEquals(expectedContent.copy(alpha = expectedAlpha), actualContent)
- }
+ assertEquals(expectedContent, actualContent)
- if (expectedBackground != Color.Transparent) {
- rule.onNodeWithTag("test-item").onChildAt(0)
- .captureToImage()
- .assertContainsColor(expectedBackground, 50.0f)
- }
+ rule.onNodeWithTag("test-item")
+ .captureToImage()
+ .assertContainsColor(
+ if (expectedBackground != Color.Transparent) expectedBackground else testBackground,
+ 50.0f
+ )
}
private fun verifySlotColors(
@@ -676,18 +682,27 @@
var actualContent = Color.Transparent
var actualSecondaryContent = Color.Transparent
var actualIcon = Color.Transparent
- var expectedAlpha = 0.0f
+ val testBackground = Color.White
rule.setContentWithTheme {
- expectedBackground = backgroundColor()
- expectedContent = contentColor()
- expectedSecondaryContent = secondaryContentColor()
- expectedIcon = iconColor()
- expectedAlpha = ContentAlpha.disabled
+ if (status.enabled()) {
+ expectedBackground = backgroundColor()
+ expectedContent = contentColor()
+ expectedSecondaryContent = secondaryContentColor()
+ expectedIcon = iconColor()
+ } else {
+ expectedBackground =
+ backgroundColor().copy(alpha = ContentAlpha.disabled)
+ .compositeOver(testBackground)
+ expectedContent = contentColor().copy(alpha = ContentAlpha.disabled)
+ expectedSecondaryContent = secondaryContentColor()
+ .copy(alpha = ContentAlpha.disabled)
+ expectedIcon = iconColor().copy(alpha = ContentAlpha.disabled)
+ }
Box(
modifier = Modifier
.fillMaxSize()
- .background(expectedBackground)
+ .background(testBackground)
) {
if (compactChip) {
CompactChip(
@@ -712,28 +727,18 @@
}
}
- if (status.enabled()) {
- assertEquals(expectedContent, actualContent)
- if (! compactChip) {
- assertEquals(expectedSecondaryContent, actualSecondaryContent)
- }
- assertEquals(expectedIcon, actualIcon)
- } else {
- assertEquals(expectedContent.copy(alpha = expectedAlpha), actualContent)
- if (! compactChip) {
- assertEquals(
- expectedSecondaryContent.copy(alpha = expectedAlpha),
- actualSecondaryContent
- )
- }
- assertEquals(expectedIcon.copy(alpha = expectedAlpha), actualIcon)
+ assertEquals(expectedContent, actualContent)
+ if (! compactChip) {
+ assertEquals(expectedSecondaryContent, actualSecondaryContent)
}
+ assertEquals(expectedIcon, actualIcon)
- if (expectedBackground != Color.Transparent) {
- rule.onNodeWithTag("test-item").onChildAt(0)
- .captureToImage()
- .assertContainsColor(expectedBackground, 50.0f)
- }
+ rule.onNodeWithTag("test-item")
+ .captureToImage()
+ .assertContainsColor(
+ if (expectedBackground != Color.Transparent) expectedBackground else testBackground,
+ 50.0f
+ )
}
}
diff --git a/wear/compose/material/src/commonMain/kotlin/androidx/wear/compose/material/Chip.kt b/wear/compose/material/src/commonMain/kotlin/androidx/wear/compose/material/Chip.kt
index fce5d9c..75b21b3 100644
--- a/wear/compose/material/src/commonMain/kotlin/androidx/wear/compose/material/Chip.kt
+++ b/wear/compose/material/src/commonMain/kotlin/androidx/wear/compose/material/Chip.kt
@@ -15,6 +15,7 @@
*/
package androidx.wear.compose.material
+import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -29,8 +30,6 @@
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.material.ContentAlpha
-import androidx.compose.material.Surface
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -41,6 +40,7 @@
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.draw.paint
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
@@ -98,11 +98,11 @@
role: Role? = Role.Button,
content: @Composable () -> Unit,
) {
- Surface(
+ Box(
modifier = modifier
- .height(ChipDefaults.Height),
- color = Color.Transparent,
- shape = shape,
+ .height(ChipDefaults.Height)
+ .clip(shape = shape)
+ .background(color = Color.Transparent, shape = shape)
) {
// TODO: Due to b/178201337 the paint() modifier on the box doesn't make a call to draw the
// box contents. As a result we need to have stacked boxes to enable us to paint the
@@ -121,6 +121,7 @@
indication = rememberRipple(),
interactionSource = interactionSource,
)
+ .fillMaxSize()
.padding(contentPadding)
Box(
@@ -132,6 +133,7 @@
CompositionLocalProvider(
LocalContentColor provides colors.contentColor(enabled = enabled).value,
LocalTextStyle provides MaterialTheme.typography.button,
+ LocalContentAlpha provides colors.contentColor(enabled = enabled).value.alpha,
content = content
)
}
@@ -212,6 +214,8 @@
) {
CompositionLocalProvider(
LocalContentColor provides colors.iconTintColor(enabled).value,
+ LocalContentAlpha provides
+ colors.iconTintColor(enabled = enabled).value.alpha,
content = icon
)
}
@@ -221,12 +225,15 @@
CompositionLocalProvider(
LocalContentColor provides colors.contentColor(enabled).value,
LocalTextStyle provides MaterialTheme.typography.button,
+ LocalContentAlpha provides colors.contentColor(enabled = enabled).value.alpha,
content = label
)
if (secondaryLabel != null) {
CompositionLocalProvider(
LocalContentColor provides colors.secondaryContentColor(enabled).value,
LocalTextStyle provides MaterialTheme.typography.button,
+ LocalContentAlpha provides
+ colors.secondaryContentColor(enabled = enabled).value.alpha,
content = secondaryLabel
)
}
@@ -508,7 +515,7 @@
disabledContentColor: Color = contentColor.copy(alpha = ContentAlpha.disabled),
disabledSecondaryContentColor: Color =
secondaryContentColor.copy(alpha = ContentAlpha.disabled),
- disabledIconTintColor: Color = disabledContentColor,
+ disabledIconTintColor: Color = iconTintColor.copy(alpha = ContentAlpha.disabled),
): ChipColors = DefaultChipColors(
backgroundColor = backgroundColor,
contentColor = contentColor,
diff --git a/wear/tiles/tiles/api/current.txt b/wear/tiles/tiles/api/current.txt
index 4209662..1d82943 100644
--- a/wear/tiles/tiles/api/current.txt
+++ b/wear/tiles/tiles/api/current.txt
@@ -124,6 +124,32 @@
method public androidx.wear.tiles.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
}
+ public final class DeviceParametersBuilders {
+ field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+ field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+ field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+ field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+ field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class DeviceParametersBuilders.DeviceParameters {
+ method public static androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder builder();
+ method public int getDevicePlatform();
+ method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+ method public int getScreenShape();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+ }
+
+ public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters build();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+ }
+
public final class DimensionBuilders {
method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
@@ -222,6 +248,41 @@
method public androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp build();
}
+ public final class EventBuilders {
+ }
+
+ public static final class EventBuilders.TileAddEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileAddEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileAddEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileAddEvent build();
+ }
+
+ public static final class EventBuilders.TileEnterEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileEnterEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileEnterEvent build();
+ }
+
+ public static final class EventBuilders.TileLeaveEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileLeaveEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileLeaveEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileLeaveEvent build();
+ }
+
+ public static final class EventBuilders.TileRemoveEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileRemoveEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileRemoveEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileRemoveEvent build();
+ }
+
public final class LayoutElementBuilders {
field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
field public static final int ARC_ANCHOR_END = 3; // 0x3
@@ -465,28 +526,28 @@
public static class LayoutElementBuilders.FontStyles {
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
- method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
}
public static final class LayoutElementBuilders.FontWeightProp {
@@ -870,6 +931,38 @@
method public androidx.wear.tiles.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.tiles.ModifiersBuilders.Clickable.Builder);
}
+ public final class RequestBuilders {
+ }
+
+ public static final class RequestBuilders.ResourcesRequest {
+ method public static androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder builder();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+ method public java.util.List<java.lang.String!> getResourceIds();
+ method public String getVersion();
+ }
+
+ public static final class RequestBuilders.ResourcesRequest.Builder {
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest build();
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
+ }
+
+ public static final class RequestBuilders.TileRequest {
+ method public static androidx.wear.tiles.RequestBuilders.TileRequest.Builder builder();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+ method public androidx.wear.tiles.StateBuilders.State? getState();
+ }
+
+ public static final class RequestBuilders.TileRequest.Builder {
+ method public androidx.wear.tiles.RequestBuilders.TileRequest build();
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State.Builder);
+ }
+
public final class ResourceBuilders {
field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
@@ -962,12 +1055,12 @@
ctor public TileProviderService();
method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
method public android.os.IBinder? onBind(android.content.Intent);
- method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.readers.RequestReaders.ResourcesRequest);
- method @MainThread protected void onTileAddEvent(androidx.wear.tiles.readers.EventReaders.TileAddEvent);
- method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.readers.EventReaders.TileEnterEvent);
- method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.readers.EventReaders.TileLeaveEvent);
- method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.readers.EventReaders.TileRemoveEvent);
- method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.readers.RequestReaders.TileRequest);
+ method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest);
+ method @MainThread protected void onTileAddEvent(androidx.wear.tiles.EventBuilders.TileAddEvent);
+ method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.EventBuilders.TileEnterEvent);
+ method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.EventBuilders.TileLeaveEvent);
+ method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.EventBuilders.TileRemoveEvent);
+ method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest);
field public static final String ACTION_BIND_TILE_PROVIDER = "androidx.wear.tiles.action.BIND_TILE_PROVIDER";
field public static final String EXTRA_CLICKABLE_ID = "androidx.wear.tiles.extra.CLICKABLE_ID";
field public static final String METADATA_PREVIEW_KEY = "androidx.wear.tiles.PREVIEW";
@@ -1062,52 +1155,3 @@
}
-package androidx.wear.tiles.readers {
-
- public class DeviceParametersReaders {
- field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
- field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
- field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
- field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
- field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
- }
-
- public static class DeviceParametersReaders.DeviceParameters {
- method public int getDevicePlatform();
- method @FloatRange(from=0.0, fromInclusive=false) public float getScreenDensity();
- method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
- method public int getScreenShape();
- method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
- }
-
- public class EventReaders {
- }
-
- public static class EventReaders.TileAddEvent {
- }
-
- public static class EventReaders.TileEnterEvent {
- }
-
- public static class EventReaders.TileLeaveEvent {
- }
-
- public static class EventReaders.TileRemoveEvent {
- }
-
- public class RequestReaders {
- }
-
- public static class RequestReaders.ResourcesRequest {
- method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
- method public java.util.List<java.lang.String!> getResourceIds();
- method public String getVersion();
- }
-
- public static class RequestReaders.TileRequest {
- method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
- method public androidx.wear.tiles.StateBuilders.State getState();
- }
-
-}
-
diff --git a/wear/tiles/tiles/api/public_plus_experimental_current.txt b/wear/tiles/tiles/api/public_plus_experimental_current.txt
index 5688279..80d61ee 100644
--- a/wear/tiles/tiles/api/public_plus_experimental_current.txt
+++ b/wear/tiles/tiles/api/public_plus_experimental_current.txt
@@ -124,6 +124,32 @@
method public androidx.wear.tiles.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
}
+ public final class DeviceParametersBuilders {
+ field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+ field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+ field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+ field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+ field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class DeviceParametersBuilders.DeviceParameters {
+ method public static androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder builder();
+ method public int getDevicePlatform();
+ method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+ method public int getScreenShape();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+ }
+
+ public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters build();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+ }
+
public final class DimensionBuilders {
method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
@@ -222,6 +248,41 @@
method public androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp build();
}
+ public final class EventBuilders {
+ }
+
+ public static final class EventBuilders.TileAddEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileAddEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileAddEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileAddEvent build();
+ }
+
+ public static final class EventBuilders.TileEnterEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileEnterEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileEnterEvent build();
+ }
+
+ public static final class EventBuilders.TileLeaveEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileLeaveEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileLeaveEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileLeaveEvent build();
+ }
+
+ public static final class EventBuilders.TileRemoveEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileRemoveEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileRemoveEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileRemoveEvent build();
+ }
+
public final class LayoutElementBuilders {
field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
field public static final int ARC_ANCHOR_END = 3; // 0x3
@@ -469,28 +530,28 @@
public static class LayoutElementBuilders.FontStyles {
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
- method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
}
@androidx.wear.tiles.TilesExperimental public static final class LayoutElementBuilders.FontVariantProp {
@@ -887,6 +948,38 @@
method public androidx.wear.tiles.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.tiles.ModifiersBuilders.Clickable.Builder);
}
+ public final class RequestBuilders {
+ }
+
+ public static final class RequestBuilders.ResourcesRequest {
+ method public static androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder builder();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+ method public java.util.List<java.lang.String!> getResourceIds();
+ method public String getVersion();
+ }
+
+ public static final class RequestBuilders.ResourcesRequest.Builder {
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest build();
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
+ }
+
+ public static final class RequestBuilders.TileRequest {
+ method public static androidx.wear.tiles.RequestBuilders.TileRequest.Builder builder();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+ method public androidx.wear.tiles.StateBuilders.State? getState();
+ }
+
+ public static final class RequestBuilders.TileRequest.Builder {
+ method public androidx.wear.tiles.RequestBuilders.TileRequest build();
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State.Builder);
+ }
+
public final class ResourceBuilders {
field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
@@ -979,12 +1072,12 @@
ctor public TileProviderService();
method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
method public android.os.IBinder? onBind(android.content.Intent);
- method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.readers.RequestReaders.ResourcesRequest);
- method @MainThread protected void onTileAddEvent(androidx.wear.tiles.readers.EventReaders.TileAddEvent);
- method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.readers.EventReaders.TileEnterEvent);
- method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.readers.EventReaders.TileLeaveEvent);
- method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.readers.EventReaders.TileRemoveEvent);
- method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.readers.RequestReaders.TileRequest);
+ method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest);
+ method @MainThread protected void onTileAddEvent(androidx.wear.tiles.EventBuilders.TileAddEvent);
+ method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.EventBuilders.TileEnterEvent);
+ method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.EventBuilders.TileLeaveEvent);
+ method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.EventBuilders.TileRemoveEvent);
+ method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest);
field public static final String ACTION_BIND_TILE_PROVIDER = "androidx.wear.tiles.action.BIND_TILE_PROVIDER";
field public static final String EXTRA_CLICKABLE_ID = "androidx.wear.tiles.extra.CLICKABLE_ID";
field public static final String METADATA_PREVIEW_KEY = "androidx.wear.tiles.PREVIEW";
@@ -1082,52 +1175,3 @@
}
-package androidx.wear.tiles.readers {
-
- public class DeviceParametersReaders {
- field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
- field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
- field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
- field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
- field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
- }
-
- public static class DeviceParametersReaders.DeviceParameters {
- method public int getDevicePlatform();
- method @FloatRange(from=0.0, fromInclusive=false) public float getScreenDensity();
- method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
- method public int getScreenShape();
- method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
- }
-
- public class EventReaders {
- }
-
- public static class EventReaders.TileAddEvent {
- }
-
- public static class EventReaders.TileEnterEvent {
- }
-
- public static class EventReaders.TileLeaveEvent {
- }
-
- public static class EventReaders.TileRemoveEvent {
- }
-
- public class RequestReaders {
- }
-
- public static class RequestReaders.ResourcesRequest {
- method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
- method public java.util.List<java.lang.String!> getResourceIds();
- method public String getVersion();
- }
-
- public static class RequestReaders.TileRequest {
- method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
- method public androidx.wear.tiles.StateBuilders.State getState();
- }
-
-}
-
diff --git a/wear/tiles/tiles/api/restricted_current.txt b/wear/tiles/tiles/api/restricted_current.txt
index 4209662..1d82943 100644
--- a/wear/tiles/tiles/api/restricted_current.txt
+++ b/wear/tiles/tiles/api/restricted_current.txt
@@ -124,6 +124,32 @@
method public androidx.wear.tiles.ColorBuilders.ColorProp.Builder setArgb(@ColorInt int);
}
+ public final class DeviceParametersBuilders {
+ field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
+ field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
+ field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
+ field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
+ field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class DeviceParametersBuilders.DeviceParameters {
+ method public static androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder builder();
+ method public int getDevicePlatform();
+ method @FloatRange(from=0.0, fromInclusive=false, toInclusive=false) public float getScreenDensity();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
+ method public int getScreenShape();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
+ }
+
+ public static final class DeviceParametersBuilders.DeviceParameters.Builder {
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters build();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setDevicePlatform(int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenDensity(@FloatRange(from=0.0, fromInclusive=false, toInclusive=false) float);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenHeightDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenShape(int);
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder setScreenWidthDp(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+ }
+
public final class DimensionBuilders {
method public static androidx.wear.tiles.DimensionBuilders.DegreesProp degrees(float);
method public static androidx.wear.tiles.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
@@ -222,6 +248,41 @@
method public androidx.wear.tiles.DimensionBuilders.WrappedDimensionProp build();
}
+ public final class EventBuilders {
+ }
+
+ public static final class EventBuilders.TileAddEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileAddEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileAddEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileAddEvent build();
+ }
+
+ public static final class EventBuilders.TileEnterEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileEnterEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileEnterEvent build();
+ }
+
+ public static final class EventBuilders.TileLeaveEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileLeaveEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileLeaveEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileLeaveEvent build();
+ }
+
+ public static final class EventBuilders.TileRemoveEvent {
+ method public static androidx.wear.tiles.EventBuilders.TileRemoveEvent.Builder builder();
+ }
+
+ public static final class EventBuilders.TileRemoveEvent.Builder {
+ method public androidx.wear.tiles.EventBuilders.TileRemoveEvent build();
+ }
+
public final class LayoutElementBuilders {
field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
field public static final int ARC_ANCHOR_END = 3; // 0x3
@@ -465,28 +526,28 @@
public static class LayoutElementBuilders.FontStyles {
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder body2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder button(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder caption2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder display3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title1(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title2(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3();
- method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
- method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters);
+ method public static androidx.wear.tiles.LayoutElementBuilders.FontStyle.Builder title3(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method @Deprecated public static androidx.wear.tiles.LayoutElementBuilders.FontStyles withDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
}
public static final class LayoutElementBuilders.FontWeightProp {
@@ -870,6 +931,38 @@
method public androidx.wear.tiles.ModifiersBuilders.SpanModifiers.Builder setClickable(androidx.wear.tiles.ModifiersBuilders.Clickable.Builder);
}
+ public final class RequestBuilders {
+ }
+
+ public static final class RequestBuilders.ResourcesRequest {
+ method public static androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder builder();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+ method public java.util.List<java.lang.String!> getResourceIds();
+ method public String getVersion();
+ }
+
+ public static final class RequestBuilders.ResourcesRequest.Builder {
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest build();
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+ method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
+ }
+
+ public static final class RequestBuilders.TileRequest {
+ method public static androidx.wear.tiles.RequestBuilders.TileRequest.Builder builder();
+ method public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
+ method public androidx.wear.tiles.StateBuilders.State? getState();
+ }
+
+ public static final class RequestBuilders.TileRequest.Builder {
+ method public androidx.wear.tiles.RequestBuilders.TileRequest build();
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters.Builder);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State);
+ method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State.Builder);
+ }
+
public final class ResourceBuilders {
field public static final int IMAGE_FORMAT_RGB_565 = 1; // 0x1
field public static final int IMAGE_FORMAT_UNDEFINED = 0; // 0x0
@@ -962,12 +1055,12 @@
ctor public TileProviderService();
method public static androidx.wear.tiles.TileUpdateRequester getUpdater(android.content.Context);
method public android.os.IBinder? onBind(android.content.Intent);
- method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.readers.RequestReaders.ResourcesRequest);
- method @MainThread protected void onTileAddEvent(androidx.wear.tiles.readers.EventReaders.TileAddEvent);
- method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.readers.EventReaders.TileEnterEvent);
- method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.readers.EventReaders.TileLeaveEvent);
- method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.readers.EventReaders.TileRemoveEvent);
- method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.readers.RequestReaders.TileRequest);
+ method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources!> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest);
+ method @MainThread protected void onTileAddEvent(androidx.wear.tiles.EventBuilders.TileAddEvent);
+ method @MainThread protected void onTileEnterEvent(androidx.wear.tiles.EventBuilders.TileEnterEvent);
+ method @MainThread protected void onTileLeaveEvent(androidx.wear.tiles.EventBuilders.TileLeaveEvent);
+ method @MainThread protected void onTileRemoveEvent(androidx.wear.tiles.EventBuilders.TileRemoveEvent);
+ method @MainThread protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile!> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest);
field public static final String ACTION_BIND_TILE_PROVIDER = "androidx.wear.tiles.action.BIND_TILE_PROVIDER";
field public static final String EXTRA_CLICKABLE_ID = "androidx.wear.tiles.extra.CLICKABLE_ID";
field public static final String METADATA_PREVIEW_KEY = "androidx.wear.tiles.PREVIEW";
@@ -1062,52 +1155,3 @@
}
-package androidx.wear.tiles.readers {
-
- public class DeviceParametersReaders {
- field public static final int DEVICE_PLATFORM_UNDEFINED = 0; // 0x0
- field public static final int DEVICE_PLATFORM_WEAR_OS = 1; // 0x1
- field public static final int SCREEN_SHAPE_RECT = 2; // 0x2
- field public static final int SCREEN_SHAPE_ROUND = 1; // 0x1
- field public static final int SCREEN_SHAPE_UNDEFINED = 0; // 0x0
- }
-
- public static class DeviceParametersReaders.DeviceParameters {
- method public int getDevicePlatform();
- method @FloatRange(from=0.0, fromInclusive=false) public float getScreenDensity();
- method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenHeightDp();
- method public int getScreenShape();
- method @Dimension(unit=androidx.annotation.Dimension.DP) public int getScreenWidthDp();
- }
-
- public class EventReaders {
- }
-
- public static class EventReaders.TileAddEvent {
- }
-
- public static class EventReaders.TileEnterEvent {
- }
-
- public static class EventReaders.TileLeaveEvent {
- }
-
- public static class EventReaders.TileRemoveEvent {
- }
-
- public class RequestReaders {
- }
-
- public static class RequestReaders.ResourcesRequest {
- method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
- method public java.util.List<java.lang.String!> getResourceIds();
- method public String getVersion();
- }
-
- public static class RequestReaders.TileRequest {
- method public androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters getDeviceParameters();
- method public androidx.wear.tiles.StateBuilders.State getState();
- }
-
-}
-
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
index ac7dd67..1aa8fbb 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
@@ -631,9 +631,10 @@
}
/**
- * Gets the state to load the next tile with. This will be included in the TileRequest sent
- * after this action is invoked by a {@link
- * androidx.wear.tiles.ModifiersBuilders.Clickable}. Intended for testing purposes only.
+ * Gets the state to load the next tile with. This will be included in the {@link
+ * androidx.wear.tiles.RequestBuilders.TileRequest} sent after this action is invoked by a
+ * {@link androidx.wear.tiles.ModifiersBuilders.Clickable}. Intended for testing purposes
+ * only.
*/
@Nullable
public State getRequestState() {
@@ -680,9 +681,9 @@
Builder() {}
/**
- * Sets the state to load the next tile with. This will be included in the TileRequest
- * sent after this action is invoked by a {@link
- * androidx.wear.tiles.ModifiersBuilders.Clickable}.
+ * Sets the state to load the next tile with. This will be included in the {@link
+ * androidx.wear.tiles.RequestBuilders.TileRequest} sent after this action is invoked by
+ * a {@link androidx.wear.tiles.ModifiersBuilders.Clickable}.
*/
@NonNull
public Builder setRequestState(@NonNull State requestState) {
@@ -691,9 +692,9 @@
}
/**
- * Sets the state to load the next tile with. This will be included in the TileRequest
- * sent after this action is invoked by a {@link
- * androidx.wear.tiles.ModifiersBuilders.Clickable}.
+ * Sets the state to load the next tile with. This will be included in the {@link
+ * androidx.wear.tiles.RequestBuilders.TileRequest} sent after this action is invoked by
+ * a {@link androidx.wear.tiles.ModifiersBuilders.Clickable}.
*/
@NonNull
public Builder setRequestState(@NonNull State.Builder requestStateBuilder) {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DeviceParametersBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DeviceParametersBuilders.java
new file mode 100644
index 0000000..624f512
--- /dev/null
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/DeviceParametersBuilders.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import static androidx.annotation.Dimension.DP;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.tiles.proto.DeviceParametersProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Builders for request messages used to fetch tiles and resources. */
+public final class DeviceParametersBuilders {
+ private DeviceParametersBuilders() {}
+
+ /**
+ * The platform of the device requesting a tile.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @IntDef({DEVICE_PLATFORM_UNDEFINED, DEVICE_PLATFORM_WEAR_OS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DevicePlatform {}
+
+ /** Device platform is undefined. */
+ public static final int DEVICE_PLATFORM_UNDEFINED = 0;
+
+ /** Device is a Wear OS by Google device. */
+ public static final int DEVICE_PLATFORM_WEAR_OS = 1;
+
+ /**
+ * The shape of a screen.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @IntDef({SCREEN_SHAPE_UNDEFINED, SCREEN_SHAPE_ROUND, SCREEN_SHAPE_RECT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScreenShape {}
+
+ /** Screen shape is undefined. */
+ public static final int SCREEN_SHAPE_UNDEFINED = 0;
+
+ /** A round screen (typically found on most Wear devices). */
+ public static final int SCREEN_SHAPE_ROUND = 1;
+
+ /** Rectangular screens. */
+ public static final int SCREEN_SHAPE_RECT = 2;
+
+ /**
+ * Parameters describing the device requesting a tile update. This contains physical and logical
+ * characteristics about the device (e.g. screen size and density, etc).
+ */
+ public static final class DeviceParameters {
+ private final DeviceParametersProto.DeviceParameters mImpl;
+
+ private DeviceParameters(DeviceParametersProto.DeviceParameters impl) {
+ this.mImpl = impl;
+ }
+
+ /** Gets width of the device's screen in DP. */
+ @Dimension(unit = DP)
+ public int getScreenWidthDp() {
+ return mImpl.getScreenWidthDp();
+ }
+
+ /** Gets height of the device's screen in DP. */
+ @Dimension(unit = DP)
+ public int getScreenHeightDp() {
+ return mImpl.getScreenHeightDp();
+ }
+
+ /**
+ * Gets density of the display. This value is the scaling factor to get from DP to Pixels
+ * (px = dp * density).
+ */
+ @FloatRange(from = 0.0, fromInclusive = false, toInclusive = false)
+ public float getScreenDensity() {
+ return mImpl.getScreenDensity();
+ }
+
+ /** Gets the platform of the device. */
+ @DevicePlatform
+ public int getDevicePlatform() {
+ return mImpl.getDevicePlatform().getNumber();
+ }
+
+ /** Gets the shape of the device's screen. */
+ @ScreenShape
+ public int getScreenShape() {
+ return mImpl.getScreenShape().getNumber();
+ }
+
+ /** Returns a new {@link Builder}. */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static DeviceParameters fromProto(
+ @NonNull DeviceParametersProto.DeviceParameters proto) {
+ return new DeviceParameters(proto);
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public DeviceParametersProto.DeviceParameters toProto() {
+ return mImpl;
+ }
+
+ /** Builder for {@link DeviceParameters} */
+ public static final class Builder {
+ private final DeviceParametersProto.DeviceParameters.Builder mImpl =
+ DeviceParametersProto.DeviceParameters.newBuilder();
+
+ Builder() {}
+
+ /** Sets width of the device's screen in DP. */
+ @NonNull
+ public Builder setScreenWidthDp(@Dimension(unit = DP) int screenWidthDp) {
+ mImpl.setScreenWidthDp(screenWidthDp);
+ return this;
+ }
+
+ /** Sets height of the device's screen in DP. */
+ @NonNull
+ public Builder setScreenHeightDp(@Dimension(unit = DP) int screenHeightDp) {
+ mImpl.setScreenHeightDp(screenHeightDp);
+ return this;
+ }
+
+ /**
+ * Sets density of the display. This value is the scaling factor to get from DP to
+ * Pixels (px = dp * density).
+ */
+ @NonNull
+ public Builder setScreenDensity(
+ @FloatRange(from = 0.0, fromInclusive = false, toInclusive = false)
+ float screenDensity) {
+ mImpl.setScreenDensity(screenDensity);
+ return this;
+ }
+
+ /** Sets the platform of the device. */
+ @NonNull
+ public Builder setDevicePlatform(@DevicePlatform int devicePlatform) {
+ mImpl.setDevicePlatform(
+ DeviceParametersProto.DevicePlatform.forNumber(devicePlatform));
+ return this;
+ }
+
+ /** Sets the shape of the device's screen. */
+ @NonNull
+ public Builder setScreenShape(@ScreenShape int screenShape) {
+ mImpl.setScreenShape(DeviceParametersProto.ScreenShape.forNumber(screenShape));
+ return this;
+ }
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public DeviceParameters build() {
+ return DeviceParameters.fromProto(mImpl.build());
+ }
+ }
+ }
+}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
new file mode 100644
index 0000000..cfc85fd
--- /dev/null
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.tiles.proto.EventProto;
+
+/** Builders for messages used when events happen in the Tiles system. */
+public final class EventBuilders {
+ private EventBuilders() {}
+
+ /** Event fired when a tile has been added to the carousel. */
+ public static final class TileAddEvent {
+ private final EventProto.TileAddEvent mImpl;
+
+ private TileAddEvent(EventProto.TileAddEvent impl) {
+ this.mImpl = impl;
+ }
+
+ /** Returns a new {@link Builder}. */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static TileAddEvent fromProto(@NonNull EventProto.TileAddEvent proto) {
+ return new TileAddEvent(proto);
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public EventProto.TileAddEvent toProto() {
+ return mImpl;
+ }
+
+ /** Builder for {@link TileAddEvent} */
+ public static final class Builder {
+ private final EventProto.TileAddEvent.Builder mImpl =
+ EventProto.TileAddEvent.newBuilder();
+
+ Builder() {}
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public TileAddEvent build() {
+ return TileAddEvent.fromProto(mImpl.build());
+ }
+ }
+ }
+
+ /** Event fired when a tile has been removed from the carousel. */
+ public static final class TileRemoveEvent {
+ private final EventProto.TileRemoveEvent mImpl;
+
+ private TileRemoveEvent(EventProto.TileRemoveEvent impl) {
+ this.mImpl = impl;
+ }
+
+ /** Returns a new {@link Builder}. */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static TileRemoveEvent fromProto(@NonNull EventProto.TileRemoveEvent proto) {
+ return new TileRemoveEvent(proto);
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public EventProto.TileRemoveEvent toProto() {
+ return mImpl;
+ }
+
+ /** Builder for {@link TileRemoveEvent} */
+ public static final class Builder {
+ private final EventProto.TileRemoveEvent.Builder mImpl =
+ EventProto.TileRemoveEvent.newBuilder();
+
+ Builder() {}
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public TileRemoveEvent build() {
+ return TileRemoveEvent.fromProto(mImpl.build());
+ }
+ }
+ }
+
+ /** Event fired when a tile is swiped to by the user (i.e. it's visible on screen). */
+ public static final class TileEnterEvent {
+ private final EventProto.TileEnterEvent mImpl;
+
+ private TileEnterEvent(EventProto.TileEnterEvent impl) {
+ this.mImpl = impl;
+ }
+
+ /** Returns a new {@link Builder}. */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static TileEnterEvent fromProto(@NonNull EventProto.TileEnterEvent proto) {
+ return new TileEnterEvent(proto);
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public EventProto.TileEnterEvent toProto() {
+ return mImpl;
+ }
+
+ /** Builder for {@link TileEnterEvent} */
+ public static final class Builder {
+ private final EventProto.TileEnterEvent.Builder mImpl =
+ EventProto.TileEnterEvent.newBuilder();
+
+ Builder() {}
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public TileEnterEvent build() {
+ return TileEnterEvent.fromProto(mImpl.build());
+ }
+ }
+ }
+
+ /**
+ * Event fired when a tile is swiped away from by the user (i.e. it's no longer visible on
+ * screen).
+ */
+ public static final class TileLeaveEvent {
+ private final EventProto.TileLeaveEvent mImpl;
+
+ private TileLeaveEvent(EventProto.TileLeaveEvent impl) {
+ this.mImpl = impl;
+ }
+
+ /** Returns a new {@link Builder}. */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static TileLeaveEvent fromProto(@NonNull EventProto.TileLeaveEvent proto) {
+ return new TileLeaveEvent(proto);
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public EventProto.TileLeaveEvent toProto() {
+ return mImpl;
+ }
+
+ /** Builder for {@link TileLeaveEvent} */
+ public static final class Builder {
+ private final EventProto.TileLeaveEvent.Builder mImpl =
+ EventProto.TileLeaveEvent.newBuilder();
+
+ Builder() {}
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public TileLeaveEvent build() {
+ return TileLeaveEvent.fromProto(mImpl.build());
+ }
+ }
+ }
+}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
index a07f141..5d76b9d 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/LayoutElementBuilders.java
@@ -28,6 +28,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.tiles.ColorBuilders.ColorProp;
+import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
import androidx.wear.tiles.DimensionBuilders.ContainerDimension;
import androidx.wear.tiles.DimensionBuilders.DegreesProp;
import androidx.wear.tiles.DimensionBuilders.DpProp;
@@ -43,7 +44,6 @@
import androidx.wear.tiles.TypeBuilders.StringProp;
import androidx.wear.tiles.proto.LayoutElementProto;
import androidx.wear.tiles.proto.TypesProto;
-import androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java
new file mode 100644
index 0000000..f41fd416
--- /dev/null
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.tiles.StateBuilders.State;
+import androidx.wear.tiles.proto.RequestProto;
+
+import java.util.List;
+
+/** Builders for request messages used to fetch tiles and resources. */
+public final class RequestBuilders {
+ private RequestBuilders() {}
+
+ /**
+ * Parameters passed to a {@link androidx.wear.tiles.TileBuilders.Tile} provider when the
+ * renderer is requesting a new version of the tile.
+ */
+ public static final class TileRequest {
+ private final RequestProto.TileRequest mImpl;
+
+ private TileRequest(RequestProto.TileRequest impl) {
+ this.mImpl = impl;
+ }
+
+ /** Gets parameters describing the device requesting the tile update. */
+ @Nullable
+ public DeviceParameters getDeviceParameters() {
+ if (mImpl.hasDeviceParameters()) {
+ return DeviceParameters.fromProto(mImpl.getDeviceParameters());
+ } else {
+ return null;
+ }
+ }
+
+ /** Gets the state that should be used when building the tile. */
+ @Nullable
+ public State getState() {
+ if (mImpl.hasState()) {
+ return State.fromProto(mImpl.getState());
+ } else {
+ return null;
+ }
+ }
+
+ /** Returns a new {@link Builder}. */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static TileRequest fromProto(@NonNull RequestProto.TileRequest proto) {
+ return new TileRequest(proto);
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public RequestProto.TileRequest toProto() {
+ return mImpl;
+ }
+
+ /** Builder for {@link TileRequest} */
+ public static final class Builder {
+ private final RequestProto.TileRequest.Builder mImpl =
+ RequestProto.TileRequest.newBuilder();
+
+ Builder() {}
+
+ /** Sets parameters describing the device requesting the tile update. */
+ @NonNull
+ public Builder setDeviceParameters(@NonNull DeviceParameters deviceParameters) {
+ mImpl.setDeviceParameters(deviceParameters.toProto());
+ return this;
+ }
+
+ /** Sets parameters describing the device requesting the tile update. */
+ @NonNull
+ public Builder setDeviceParameters(
+ @NonNull DeviceParameters.Builder deviceParametersBuilder) {
+ mImpl.setDeviceParameters(deviceParametersBuilder.build().toProto());
+ return this;
+ }
+
+ /** Sets the state that should be used when building the tile. */
+ @NonNull
+ public Builder setState(@NonNull State state) {
+ mImpl.setState(state.toProto());
+ return this;
+ }
+
+ /** Sets the state that should be used when building the tile. */
+ @NonNull
+ public Builder setState(@NonNull State.Builder stateBuilder) {
+ mImpl.setState(stateBuilder.build().toProto());
+ return this;
+ }
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public TileRequest build() {
+ return TileRequest.fromProto(mImpl.build());
+ }
+ }
+ }
+
+ /**
+ * Parameters passed to a {@link androidx.wear.tiles.TileBuilders.Tile} provider when the
+ * renderer is requesting a specific resource version.
+ */
+ public static final class ResourcesRequest {
+ private final RequestProto.ResourcesRequest mImpl;
+
+ private ResourcesRequest(RequestProto.ResourcesRequest impl) {
+ this.mImpl = impl;
+ }
+
+ /**
+ * Gets the version of the resources being fetched. This is the same as the requested
+ * resource version, passed in {@link androidx.wear.tiles.TileBuilders.Tile}.
+ */
+ @NonNull
+ public String getVersion() {
+ return mImpl.getVersion();
+ }
+
+ /**
+ * Gets requested resource IDs. If not specified, all resources for the given version must
+ * be provided in the response.
+ */
+ @NonNull
+ public List<String> getResourceIds() {
+ return mImpl.getResourceIdsList();
+ }
+
+ /**
+ * Gets parameters describing the device requesting the resources. Intended for testing
+ * purposes only.
+ */
+ @Nullable
+ public DeviceParameters getDeviceParameters() {
+ if (mImpl.hasDeviceParameters()) {
+ return DeviceParameters.fromProto(mImpl.getDeviceParameters());
+ } else {
+ return null;
+ }
+ }
+
+ /** Returns a new {@link Builder}. */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static ResourcesRequest fromProto(@NonNull RequestProto.ResourcesRequest proto) {
+ return new ResourcesRequest(proto);
+ }
+
+ /** @hide */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public RequestProto.ResourcesRequest toProto() {
+ return mImpl;
+ }
+
+ /** Builder for {@link ResourcesRequest} */
+ public static final class Builder {
+ private final RequestProto.ResourcesRequest.Builder mImpl =
+ RequestProto.ResourcesRequest.newBuilder();
+
+ Builder() {}
+
+ /**
+ * Sets the version of the resources being fetched. This is the same as the requested
+ * resource version, passed in {@link androidx.wear.tiles.TileBuilders.Tile}.
+ */
+ @NonNull
+ public Builder setVersion(@NonNull String version) {
+ mImpl.setVersion(version);
+ return this;
+ }
+
+ /**
+ * Adds one item to requested resource IDs. If not specified, all resources for the
+ * given version must be provided in the response.
+ */
+ @NonNull
+ public Builder addResourceId(@NonNull String resourceId) {
+ mImpl.addResourceIds(resourceId);
+ return this;
+ }
+
+ /** Sets parameters describing the device requesting the resources. */
+ @NonNull
+ public Builder setDeviceParameters(@NonNull DeviceParameters deviceParameters) {
+ mImpl.setDeviceParameters(deviceParameters.toProto());
+ return this;
+ }
+
+ /** Sets parameters describing the device requesting the resources. */
+ @NonNull
+ public Builder setDeviceParameters(
+ @NonNull DeviceParameters.Builder deviceParametersBuilder) {
+ mImpl.setDeviceParameters(deviceParametersBuilder.build().toProto());
+ return this;
+ }
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public ResourcesRequest build() {
+ return ResourcesRequest.fromProto(mImpl.build());
+ }
+ }
+ }
+}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ResourceBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ResourceBuilders.java
index 3b7700e..c8b8750 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ResourceBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ResourceBuilders.java
@@ -365,8 +365,9 @@
* resources.
*
* <p>This value must match the version of the resources required by the tile for the tile
- * to render successfully, and must match the resource version specified in ResourcesRequest
- * which triggered this request. Intended for testing purposes only.
+ * to render successfully, and must match the resource version specified in {@link
+ * androidx.wear.tiles.RequestBuilders.ResourcesRequest} which triggered this request.
+ * Intended for testing purposes only.
*/
@NonNull
public String getVersion() {
@@ -422,8 +423,8 @@
* the resources.
*
* <p>This value must match the version of the resources required by the tile for the
- * tile to render successfully, and must match the resource version specified in
- * ResourcesRequest which triggered this request.
+ * tile to render successfully, and must match the resource version specified in {@link
+ * androidx.wear.tiles.RequestBuilders.ResourcesRequest} which triggered this request.
*/
@NonNull
public Builder setVersion(@NonNull String version) {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
index c1d54c8..628fc33 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/SysUiTileUpdateRequester.java
@@ -143,8 +143,7 @@
// we'll unbind, then immediately rebind. That said, this class should be
// used pretty rarely
// (and it'll be rare to have two in-flight update requests at once
- // regardless), so
- // it's probably fine.
+ // regardless), so it's probably fine.
TileUpdateRequesterService updateRequesterService =
TileUpdateRequesterService.Stub.asInterface(service);
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
index 1fcacd7..aef9ccf 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
@@ -41,9 +41,9 @@
/**
* Gets the resource version required for these tiles. This can be any developer-defined
- * string; it is only used to cache resources, and is passed in ResourcesRequest if the
- * system does not have a copy of the specified resource version. Intended for testing
- * purposes only.
+ * string; it is only used to cache resources, and is passed in {@link
+ * androidx.wear.tiles.RequestBuilders.ResourcesRequest} if the system does not have a copy
+ * of the specified resource version. Intended for testing purposes only.
*/
@NonNull
public String getResourcesVersion() {
@@ -108,8 +108,9 @@
/**
* Sets the resource version required for these tiles. This can be any developer-defined
- * string; it is only used to cache resources, and is passed in ResourcesRequest if the
- * system does not have a copy of the specified resource version.
+ * string; it is only used to cache resources, and is passed in {@link
+ * androidx.wear.tiles.RequestBuilders.ResourcesRequest} if the system does not have a
+ * copy of the specified resource version.
*/
@NonNull
public Builder setResourcesVersion(@NonNull String resourcesVersion) {
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileProviderService.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileProviderService.java
index 6029f15..9dce43d 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileProviderService.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileProviderService.java
@@ -27,16 +27,19 @@
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.wear.tiles.EventBuilders.TileAddEvent;
+import androidx.wear.tiles.EventBuilders.TileEnterEvent;
+import androidx.wear.tiles.EventBuilders.TileLeaveEvent;
+import androidx.wear.tiles.EventBuilders.TileRemoveEvent;
+import androidx.wear.tiles.RequestBuilders.ResourcesRequest;
+import androidx.wear.tiles.RequestBuilders.TileRequest;
import androidx.wear.tiles.ResourceBuilders.Resources;
import androidx.wear.tiles.TileBuilders.Tile;
import androidx.wear.tiles.TileBuilders.Version;
+import androidx.wear.tiles.proto.EventProto;
+import androidx.wear.tiles.proto.RequestProto;
import androidx.wear.tiles.proto.TileProto;
-import androidx.wear.tiles.readers.EventReaders.TileAddEvent;
-import androidx.wear.tiles.readers.EventReaders.TileEnterEvent;
-import androidx.wear.tiles.readers.EventReaders.TileLeaveEvent;
-import androidx.wear.tiles.readers.EventReaders.TileRemoveEvent;
-import androidx.wear.tiles.readers.RequestReaders.ResourcesRequest;
-import androidx.wear.tiles.readers.RequestReaders.TileRequest;
+import androidx.wear.tiles.protobuf.InvalidProtocolBufferException;
import com.google.common.util.concurrent.ListenableFuture;
@@ -208,10 +211,20 @@
return;
}
- // TODO(b/166074385): Add tileId to TileRequest
+ TileRequest tileRequest;
+
+ try {
+ tileRequest =
+ TileRequest.fromProto(
+ RequestProto.TileRequest.parseFrom(
+ requestParams.getContents()));
+ } catch (InvalidProtocolBufferException ex) {
+ Log.e(TAG, "Error deserializing TileRequest payload.", ex);
+ return;
+ }
+
ListenableFuture<Tile> tileFuture =
- tileProviderService.onTileRequest(
- TileRequest.fromParcelable(requestParams, tileId));
+ tileProviderService.onTileRequest(tileRequest);
tileFuture.addListener(
() -> {
@@ -256,10 +269,20 @@
return;
}
- // TODO(b/166074385): Add tileId to ResourcesRequest
+ ResourcesRequest req;
+
+ try {
+ req =
+ ResourcesRequest.fromProto(
+ RequestProto.ResourcesRequest.parseFrom(
+ requestParams.getContents()));
+ } catch (InvalidProtocolBufferException ex) {
+ Log.e(TAG, "Error deserializing ResourcesRequest payload.", ex);
+ return;
+ }
+
ListenableFuture<Resources> resourcesFuture =
- tileProviderService.onResourcesRequest(
- ResourcesRequest.fromParcelable(requestParams, tileId));
+ tileProviderService.onResourcesRequest(req);
resourcesFuture.addListener(
() -> {
@@ -301,7 +324,15 @@
return;
}
- tileProviderService.onTileAddEvent(TileAddEvent.fromParcelable(data));
+ try {
+ TileAddEvent evt =
+ TileAddEvent.fromProto(
+ EventProto.TileAddEvent.parseFrom(
+ data.getContents()));
+ tileProviderService.onTileAddEvent(evt);
+ } catch (InvalidProtocolBufferException ex) {
+ Log.e(TAG, "Error deserializing TileAddEvent payload.", ex);
+ }
}
});
}
@@ -321,8 +352,15 @@
return;
}
- tileProviderService.onTileRemoveEvent(
- TileRemoveEvent.fromParcelable(data));
+ try {
+ TileRemoveEvent evt =
+ TileRemoveEvent.fromProto(
+ EventProto.TileRemoveEvent.parseFrom(
+ data.getContents()));
+ tileProviderService.onTileRemoveEvent(evt);
+ } catch (InvalidProtocolBufferException ex) {
+ Log.e(TAG, "Error deserializing TileRemoveEvent payload.", ex);
+ }
}
});
}
@@ -342,8 +380,15 @@
return;
}
- tileProviderService.onTileEnterEvent(
- TileEnterEvent.fromParcelable(data));
+ try {
+ TileEnterEvent evt =
+ TileEnterEvent.fromProto(
+ EventProto.TileEnterEvent.parseFrom(
+ data.getContents()));
+ tileProviderService.onTileEnterEvent(evt);
+ } catch (InvalidProtocolBufferException ex) {
+ Log.e(TAG, "Error deserializing TileEnterEvent payload.", ex);
+ }
}
});
}
@@ -363,8 +408,15 @@
return;
}
- tileProviderService.onTileLeaveEvent(
- TileLeaveEvent.fromParcelable(data));
+ try {
+ TileLeaveEvent evt =
+ TileLeaveEvent.fromProto(
+ EventProto.TileLeaveEvent.parseFrom(
+ data.getContents()));
+ tileProviderService.onTileLeaveEvent(evt);
+ } catch (InvalidProtocolBufferException ex) {
+ Log.e(TAG, "Error deserializing TileLeaveEvent payload.", ex);
+ }
}
});
}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/DeviceParametersReaders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/DeviceParametersReaders.java
deleted file mode 100644
index 238d8e6..0000000
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/DeviceParametersReaders.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.wear.tiles.readers;
-
-import static androidx.annotation.Dimension.DP;
-
-import androidx.annotation.Dimension;
-import androidx.annotation.FloatRange;
-import androidx.annotation.IntDef;
-import androidx.annotation.RestrictTo;
-import androidx.wear.tiles.proto.DeviceParametersProto;
-import androidx.wear.tiles.readers.RequestReaders.TileRequest;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Readers for androidx.wear.tiles' device parameters structures. */
-public class DeviceParametersReaders {
- private DeviceParametersReaders() {}
-
- /**
- * The platform of the device requesting a tile.
- *
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- @IntDef({DEVICE_PLATFORM_UNDEFINED, DEVICE_PLATFORM_WEAR_OS})
- @Retention(RetentionPolicy.SOURCE)
- public @interface DevicePlatform {}
-
- /** Device platform is undefined. */
- public static final int DEVICE_PLATFORM_UNDEFINED = 0;
-
- /** Device is a Wear OS by Google device. */
- public static final int DEVICE_PLATFORM_WEAR_OS = 1;
-
- /**
- * The shape of a screen.
- *
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- @IntDef({SCREEN_SHAPE_UNDEFINED, SCREEN_SHAPE_ROUND, SCREEN_SHAPE_RECT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ScreenShape {}
-
- /** Screen shape is undefined. */
- public static final int SCREEN_SHAPE_UNDEFINED = 0;
-
- /** A round screen (typically found on most Wear devices). */
- public static final int SCREEN_SHAPE_ROUND = 1;
-
- /** Rectangular screens. */
- public static final int SCREEN_SHAPE_RECT = 2;
-
- /** Reader for the Device Parameters returned from {@link TileRequest#getDeviceParameters()}. */
- public static class DeviceParameters {
- private final DeviceParametersProto.DeviceParameters mProto;
-
- DeviceParameters(DeviceParametersProto.DeviceParameters proto) {
- this.mProto = proto;
- }
-
- /** Get the width of the screen, in DP. */
- @Dimension(unit = DP)
- public int getScreenWidthDp() {
- return mProto.getScreenWidthDp();
- }
-
- /** Get the height of the screen, in DP. */
- @Dimension(unit = DP)
- public int getScreenHeightDp() {
- return mProto.getScreenHeightDp();
- }
-
- /**
- * Get the density of the screen. This value is the scaling factor to get from DP to Pixels,
- * where PX = DP * density.
- */
- @FloatRange(from = 0.0, fromInclusive = false)
- public float getScreenDensity() {
- return mProto.getScreenDensity();
- }
-
- /** Get the platform of the device. */
- @DevicePlatform
- public int getDevicePlatform() {
- return mProto.getDevicePlatformValue();
- }
-
- /** Get the shape of the screen of the device. */
- @ScreenShape
- public int getScreenShape() {
- return mProto.getScreenShapeValue();
- }
- }
-}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/EventReaders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/EventReaders.java
deleted file mode 100644
index 8300687..0000000
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/EventReaders.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.wear.tiles.readers;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
-import androidx.wear.tiles.TileAddEventData;
-import androidx.wear.tiles.TileEnterEventData;
-import androidx.wear.tiles.TileLeaveEventData;
-import androidx.wear.tiles.TileRemoveEventData;
-import androidx.wear.tiles.proto.EventProto;
-import androidx.wear.tiles.protobuf.ExtensionRegistryLite;
-import androidx.wear.tiles.protobuf.InvalidProtocolBufferException;
-
-/** Event readers for androidx.wear.tiles' Parcelable classes. */
-public class EventReaders {
- private EventReaders() {}
-
- /** Reader for Tile add event parameters. */
- public static class TileAddEvent {
- @SuppressWarnings("unused")
- private final EventProto.TileAddEvent mProto;
-
- private TileAddEvent(@NonNull EventProto.TileAddEvent proto) {
- this.mProto = proto;
- }
-
- /**
- * Create an instance of this reader from a given {@link TileAddEventData} instance.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- @NonNull
- public static TileAddEvent fromParcelable(@NonNull TileAddEventData parcelable) {
- try {
- return new TileAddEvent(
- EventProto.TileAddEvent.parseFrom(
- parcelable.getContents(),
- ExtensionRegistryLite.getEmptyRegistry()));
- } catch (InvalidProtocolBufferException ex) {
- throw new IllegalArgumentException(
- "Passed TileAddEventData did not contain a valid proto payload", ex);
- }
- }
- }
-
- /** Reader for Tile remove event parameters. */
- public static class TileRemoveEvent {
- @SuppressWarnings("unused")
- private final EventProto.TileRemoveEvent mProto;
-
- private TileRemoveEvent(@NonNull EventProto.TileRemoveEvent proto) {
- this.mProto = proto;
- }
-
- /**
- * Create an instance of this reader from a given {@link TileRemoveEventData} instance.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- @NonNull
- public static TileRemoveEvent fromParcelable(@NonNull TileRemoveEventData parcelable) {
- try {
- return new TileRemoveEvent(
- EventProto.TileRemoveEvent.parseFrom(
- parcelable.getContents(),
- ExtensionRegistryLite.getEmptyRegistry()));
- } catch (InvalidProtocolBufferException ex) {
- throw new IllegalArgumentException(
- "Passed TileRemoveEventData did not contain a valid proto payload", ex);
- }
- }
- }
-
- /** Reader for Tile enter event parameters. */
- public static class TileEnterEvent {
- @SuppressWarnings("unused")
- private final EventProto.TileEnterEvent mProto;
-
- private TileEnterEvent(@NonNull EventProto.TileEnterEvent proto) {
- this.mProto = proto;
- }
-
- /**
- * Create an instance of this reader from a given {@link TileEnterEventData} instance.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- @NonNull
- public static TileEnterEvent fromParcelable(@NonNull TileEnterEventData parcelable) {
- try {
- return new TileEnterEvent(
- EventProto.TileEnterEvent.parseFrom(
- parcelable.getContents(),
- ExtensionRegistryLite.getEmptyRegistry()));
- } catch (InvalidProtocolBufferException ex) {
- throw new IllegalArgumentException(
- "Passed TileEnterEventData did not contain a valid proto payload", ex);
- }
- }
- }
-
- /** Reader for a Tile leave event parameters. */
- public static class TileLeaveEvent {
- @SuppressWarnings("unused")
- private final EventProto.TileLeaveEvent mProto;
-
- private TileLeaveEvent(@NonNull EventProto.TileLeaveEvent proto) {
- this.mProto = proto;
- }
-
- /**
- * Create an instance of this reader from a given {@link TileLeaveEventData} instance.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- @NonNull
- public static TileLeaveEvent fromParcelable(@NonNull TileLeaveEventData parcelable) {
- try {
- return new TileLeaveEvent(
- EventProto.TileLeaveEvent.parseFrom(
- parcelable.getContents(),
- ExtensionRegistryLite.getEmptyRegistry()));
- } catch (InvalidProtocolBufferException ex) {
- throw new IllegalArgumentException(
- "Passed TileLeaveEventData did not contain a valid proto payload", ex);
- }
- }
- }
-}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/RequestReaders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/RequestReaders.java
deleted file mode 100644
index 4462123..0000000
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/RequestReaders.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.wear.tiles.readers;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
-import androidx.wear.tiles.ResourcesRequestData;
-import androidx.wear.tiles.StateBuilders.State;
-import androidx.wear.tiles.TileRequestData;
-import androidx.wear.tiles.proto.RequestProto;
-import androidx.wear.tiles.protobuf.ExtensionRegistryLite;
-import androidx.wear.tiles.protobuf.InvalidProtocolBufferException;
-import androidx.wear.tiles.readers.DeviceParametersReaders.DeviceParameters;
-
-import java.util.List;
-
-/** Request readers for androidx.wear.tiles' Parcelable classes. */
-public class RequestReaders {
- private RequestReaders() {}
-
- /** Reader for Tile request parameters. */
- public static class TileRequest {
- private final RequestProto.TileRequest mProto;
-
- @SuppressWarnings("unused")
- private final int mTileId;
-
- private TileRequest(RequestProto.TileRequest proto, int tileId) {
- this.mProto = proto;
- this.mTileId = tileId;
- }
-
- /** Get the {@link State} that the tile should be built with. */
- @NonNull
- public State getState() {
- return State.fromProto(mProto.getState());
- }
-
- /** Get parameters describing the device requesting this tile. */
- @NonNull
- public DeviceParameters getDeviceParameters() {
- return new DeviceParameters(mProto.getDeviceParameters());
- }
-
- /** @hide */
- @RestrictTo(Scope.LIBRARY)
- @NonNull
- public static TileRequest fromParcelable(@NonNull TileRequestData parcelable, int tileId) {
- try {
- return new TileRequest(
- RequestProto.TileRequest.parseFrom(
- parcelable.getContents(), ExtensionRegistryLite.getEmptyRegistry()),
- tileId);
- } catch (InvalidProtocolBufferException ex) {
- throw new IllegalArgumentException(
- "Passed TileRequestData did not contain a valid proto payload", ex);
- }
- }
- }
-
- /** Reader for resource request parameters. */
- public static class ResourcesRequest {
- private final RequestProto.ResourcesRequest mProto;
-
- @SuppressWarnings("unused")
- private final int mTileId;
-
- private ResourcesRequest(@NonNull RequestProto.ResourcesRequest proto, int tileId) {
- this.mProto = proto;
- this.mTileId = tileId;
- }
-
- /** @hide */
- @RestrictTo(Scope.LIBRARY)
- @NonNull
- public static ResourcesRequest fromParcelable(
- @NonNull ResourcesRequestData parcelable, int tileId) {
- try {
- return new ResourcesRequest(
- RequestProto.ResourcesRequest.parseFrom(
- parcelable.getContents(), ExtensionRegistryLite.getEmptyRegistry()),
- tileId);
- } catch (InvalidProtocolBufferException ex) {
- throw new IllegalArgumentException(
- "Passed ResourcesRequestData did not contain a valid proto payload", ex);
- }
- }
-
- /** Get the requested resource version. */
- @NonNull
- public String getVersion() {
- return mProto.getVersion();
- }
-
- /**
- * Get the requested resource IDs. May be empty, in which case all resources should be
- * returned.
- */
- @NonNull
- public List<String> getResourceIds() {
- return mProto.getResourceIdsList();
- }
-
- /** Get parameters describing the device requesting these resources. */
- @NonNull
- public DeviceParameters getDeviceParameters() {
- return new DeviceParameters(mProto.getDeviceParameters());
- }
- }
-}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/package-info.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/package-info.java
deleted file mode 100644
index 48deb88..0000000
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/readers/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Contains {@link androidx.wear.tiles.readers.RequestReaders.TileRequest} and {@link
- * androidx.wear.tiles.readers.RequestReaders.ResourcesRequest}, which are passed as parameters to
- * {@link androidx.wear.tiles.TileProviderService#onTileRequest} and {@link
- * androidx.wear.tiles.TileProviderService#onResourcesRequest} respectively.
- */
-package androidx.wear.tiles.readers;
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
index 286fc1f..4f00c4f 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
@@ -24,7 +24,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.concurrent.futures.ResolvableFuture;
-import androidx.wear.tiles.readers.RequestReaders;
import com.google.common.util.concurrent.ListenableFuture;
@@ -75,7 +74,7 @@
@NonNull
@Override
protected ListenableFuture<TileBuilders.Tile> onTileRequest(
- @NonNull RequestReaders.TileRequest requestParams) {
+ @NonNull RequestBuilders.TileRequest requestParams) {
ResolvableFuture<TileBuilders.Tile> f = ResolvableFuture.create();
f.set(null);
return f;
@@ -84,7 +83,7 @@
@NonNull
@Override
protected ListenableFuture<ResourceBuilders.Resources> onResourcesRequest(
- @NonNull RequestReaders.ResourcesRequest requestParams) {
+ @NonNull RequestBuilders.ResourcesRequest requestParams) {
ResolvableFuture<ResourceBuilders.Resources> f = ResolvableFuture.create();
f.set(null);
return f;
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java
index 0a3e8f7..cdcd478 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java
@@ -25,17 +25,17 @@
import android.os.Looper;
import androidx.annotation.NonNull;
+import androidx.wear.tiles.EventBuilders.TileAddEvent;
+import androidx.wear.tiles.EventBuilders.TileEnterEvent;
+import androidx.wear.tiles.EventBuilders.TileLeaveEvent;
+import androidx.wear.tiles.EventBuilders.TileRemoveEvent;
+import androidx.wear.tiles.RequestBuilders.ResourcesRequest;
+import androidx.wear.tiles.RequestBuilders.TileRequest;
import androidx.wear.tiles.TileBuilders.Version;
import androidx.wear.tiles.proto.EventProto;
import androidx.wear.tiles.proto.RequestProto;
import androidx.wear.tiles.proto.ResourceProto.Resources;
import androidx.wear.tiles.proto.TileProto.Tile;
-import androidx.wear.tiles.readers.EventReaders.TileAddEvent;
-import androidx.wear.tiles.readers.EventReaders.TileEnterEvent;
-import androidx.wear.tiles.readers.EventReaders.TileLeaveEvent;
-import androidx.wear.tiles.readers.EventReaders.TileRemoveEvent;
-import androidx.wear.tiles.readers.RequestReaders.ResourcesRequest;
-import androidx.wear.tiles.readers.RequestReaders.TileRequest;
import com.google.common.truth.Expect;
import com.google.common.util.concurrent.Futures;
diff --git a/work/workmanager/src/androidTest/java/androidx/work/WorkDatabasePathHelperTest.kt b/work/workmanager/src/androidTest/java/androidx/work/WorkDatabasePathHelperTest.kt
index fc3f164..cd70bd7 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/WorkDatabasePathHelperTest.kt
+++ b/work/workmanager/src/androidTest/java/androidx/work/WorkDatabasePathHelperTest.kt
@@ -37,6 +37,8 @@
@RunWith(AndroidJUnit4::class)
@LargeTest
+@Suppress("DEPRECATION")
+// TODO: (b/189268580) Update this test to use the new constructors in MigrationTestHelper.
class WorkDatabasePathHelperTest {
@get:Rule
val migrationTestHelper = MigrationTestHelper(