Merge "Bump versions for Beta 1-synced releases" into androidx-platform-dev
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index 602c351..cede9fd 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -317,6 +317,10 @@
android:theme="@style/Theme.CustomInflater"/>
<activity
+ android:name="androidx.appcompat.app.NoThemeAppCompatActivity"
+ android:theme="@android:style/Theme.Holo.NoActionBar"/>
+
+ <activity
android:name="androidx.appcompat.widget.ToolbarTestActivity"
android:theme="@style/Theme.AppCompat.NoActionBar"/>
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NoThemeAppCompatActivity.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NoThemeAppCompatActivity.kt
new file mode 100644
index 0000000..e0a6fb8
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NoThemeAppCompatActivity.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.app
+
+import android.os.Bundle
+
+import androidx.appcompat.test.R
+
+class NoThemeAppCompatActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ // We don't have an AppCompat theme by default, but this
+ // shouldn't create the subDecor - it should just no-op
+ invalidateOptionsMenu()
+
+ super.onCreate(savedInstanceState)
+
+ // Now set the theme to whatever AppCompat theme we have in tests
+ setTheme(R.style.Theme_TextColors)
+
+ // And call setContentView(), which will internally create the subDecor
+ setContentView(R.layout.layout_actv)
+ }
+}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NoThemeAppCompatTest.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NoThemeAppCompatTest.kt
new file mode 100644
index 0000000..5614e37
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/NoThemeAppCompatTest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.app
+
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class NoThemeAppCompatTest {
+
+ @Test
+ fun initializeNoThemeAppCompatActivity() {
+ try {
+ ActivityScenario.launch(NoThemeAppCompatActivity::class.java)
+ } catch (e: IllegalStateException) {
+ fail("launching AppCompatActivity with no theme should not throw error")
+ }
+ }
+}
\ No newline at end of file
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 4e85bcb..bcdc36b 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -1237,8 +1237,7 @@
@Override
public void invalidateOptionsMenu() {
- final ActionBar ab = getSupportActionBar();
- if (ab != null && ab.invalidateOptionsMenu()) return;
+ if (peekSupportActionBar() == null || getSupportActionBar().invalidateOptionsMenu()) return;
invalidatePanelMenu(FEATURE_OPTIONS_PANEL);
}
diff --git a/appsearch/appsearch-local-storage/build.gradle b/appsearch/appsearch-local-storage/build.gradle
index 35f841a..d723e00 100644
--- a/appsearch/appsearch-local-storage/build.gradle
+++ b/appsearch/appsearch-local-storage/build.gradle
@@ -44,12 +44,11 @@
defaultConfig {
externalNativeBuild {
cmake {
- cppFlags "-std=c++17"
arguments "-DCMAKE_VERBOSE_MAKEFILE=ON"
targets "icing"
}
}
- multiDexEnabled true
+ multiDexEnabled true
}
externalNativeBuild {
cmake {
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java
index de551e6..8dc560a 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java
@@ -160,7 +160,7 @@
/**
* Maps speed units in car service to speed units in {@link CarUnit}.
*/
- public static @CarUnit.CarSpeedUnit int covertSpeedUnit(int vehicleUnit) {
+ public static @CarUnit.CarSpeedUnit int convertSpeedUnit(int vehicleUnit) {
switch (vehicleUnit) {
case VEHICLE_UNIT_METER_PER_SEC:
return CarUnit.METERS_PER_SEC;
@@ -176,7 +176,7 @@
/**
* Maps distance units in car service to distance units in {@link CarUnit}.
*/
- public static @CarUnit.CarDistanceUnit int covertDistanceUnit(int vehicleUnit) {
+ public static @CarUnit.CarDistanceUnit int convertDistanceUnit(int vehicleUnit) {
switch (vehicleUnit) {
case VEHICLE_UNIT_METER:
return CarUnit.METER;
@@ -196,7 +196,7 @@
*/
// TODO(b/202303614): Remove this annotation once FuelVolumeDisplayUnit is ready.
@OptIn(markerClass = ExperimentalCarApi.class)
- public static @CarUnit.CarVolumeUnit int covertVolumeUnit(int vehicleUnit) {
+ public static @CarUnit.CarVolumeUnit int convertVolumeUnit(int vehicleUnit) {
switch (vehicleUnit) {
case VEHICLE_UNIT_MILLILITER:
return MILLILITER;
@@ -214,7 +214,7 @@
/**
* Maps EV connector types in car service to types in {@link EnergyProfile}.
*/
- public static @EnergyProfile.EvConnectorType int covertEvConnectorType(
+ public static @EnergyProfile.EvConnectorType int convertEvConnectorType(
int vehicleEvConnectorType) {
switch (vehicleEvConnectorType) {
case 1: // IEC_TYPE_1_AC
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
index 9340089..9485209 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
@@ -349,7 +349,7 @@
List<Integer> evConnectorsInCarValue = new ArrayList<>();
for (Integer connectorType : evConnectorsInVehicle) {
evConnectorsInCarValue.add(
- PropertyUtils.covertEvConnectorType(connectorType));
+ PropertyUtils.convertEvConnectorType(connectorType));
}
evConnector = getCarValue(value, evConnectorsInCarValue);
}
@@ -465,7 +465,7 @@
case SPEED_DISPLAY_UNIT_ID:
Integer speedUnit = null;
if (response.getValue() != null) {
- speedUnit = PropertyUtils.covertSpeedUnit(
+ speedUnit = PropertyUtils.convertSpeedUnit(
(Integer) response.getValue());
}
displayUnitValue = getCarValue(response, speedUnit);
@@ -588,7 +588,7 @@
case DISTANCE_DISPLAY_UNITS:
Integer displayUnit = null;
if (response.getValue() != null) {
- displayUnit = PropertyUtils.covertDistanceUnit(
+ displayUnit = PropertyUtils.convertDistanceUnit(
(Integer) response.getValue());
}
distanceDisplayUnitValue = new CarValue<>(displayUnit,
@@ -689,7 +689,7 @@
case DISTANCE_DISPLAY_UNITS:
Integer displayUnit = null;
if (response.getValue() != null) {
- displayUnit = PropertyUtils.covertDistanceUnit(
+ displayUnit = PropertyUtils.convertDistanceUnit(
(Integer) response.getValue());
}
distanceDisplayUnitValue = new CarValue<>(displayUnit,
@@ -698,7 +698,7 @@
case FUEL_VOLUME_DISPLAY_UNITS:
Integer volumeUnit = null;
if (response.getValue() != null) {
- volumeUnit = PropertyUtils.covertVolumeUnit(
+ volumeUnit = PropertyUtils.convertVolumeUnit(
(Integer) response.getValue());
}
fuelVolumeDisplayUnitValue =
diff --git a/car/app/app-samples/helloworld/automotive/github_build.gradle b/car/app/app-samples/helloworld/automotive/github_build.gradle
index 06afd50..2e30e16 100644
--- a/car/app/app-samples/helloworld/automotive/github_build.gradle
+++ b/car/app/app-samples/helloworld/automotive/github_build.gradle
@@ -44,6 +44,6 @@
}
dependencies {
- implementation "androidx.car.app:app-automotive:1.2.0-alpha02"
+ implementation "androidx.car.app:app-automotive:1.2.0-rc01"
implementation project(":helloworld:common")
}
diff --git a/car/app/app-samples/helloworld/common/github_build.gradle b/car/app/app-samples/helloworld/common/github_build.gradle
index c897e85..8eb82ea 100644
--- a/car/app/app-samples/helloworld/common/github_build.gradle
+++ b/car/app/app-samples/helloworld/common/github_build.gradle
@@ -43,7 +43,7 @@
}
dependencies {
- implementation "androidx.car.app:app:1.2.0-alpha02"
+ implementation "androidx.car.app:app:1.2.0-rc01"
}
diff --git a/car/app/app-samples/helloworld/mobile/github_build.gradle b/car/app/app-samples/helloworld/mobile/github_build.gradle
index 4997f292..ea2d696 100644
--- a/car/app/app-samples/helloworld/mobile/github_build.gradle
+++ b/car/app/app-samples/helloworld/mobile/github_build.gradle
@@ -44,7 +44,7 @@
}
dependencies {
- implementation "androidx.car.app:app-projected:1.2.0-alpha02"
+ implementation "androidx.car.app:app-projected:1.2.0-rc01"
implementation project(":helloworld:common")
}
diff --git a/car/app/app-samples/navigation/automotive/github_build.gradle b/car/app/app-samples/navigation/automotive/github_build.gradle
index 253704b..8583f42 100644
--- a/car/app/app-samples/navigation/automotive/github_build.gradle
+++ b/car/app/app-samples/navigation/automotive/github_build.gradle
@@ -23,8 +23,10 @@
applicationId "androidx.car.app.sample.navigation"
minSdkVersion 29
targetSdkVersion 31
- versionCode 101 // Increment this to generate signed builds for uploading to Playstore
- versionName "101"
+ // Increment this to generate signed builds for uploading to Playstore
+ // Make sure this is different from the navigation-mobile version
+ versionCode 109
+ versionName "109"
}
buildTypes {
@@ -44,6 +46,6 @@
}
dependencies {
- implementation "androidx.car.app:app-automotive:1.2.0-alpha02"
+ implementation "androidx.car.app:app-automotive:1.2.0-rc01"
implementation project(":navigation:common")
}
diff --git a/car/app/app-samples/navigation/common/github_build.gradle b/car/app/app-samples/navigation/common/github_build.gradle
index e05f3bc..0b13b40 100644
--- a/car/app/app-samples/navigation/common/github_build.gradle
+++ b/car/app/app-samples/navigation/common/github_build.gradle
@@ -35,6 +35,6 @@
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.core:core:1.5.0-alpha01"
- implementation "androidx.car.app:app:1.2.0-alpha02"
+ implementation "androidx.car.app:app:1.2.0-rc01"
implementation "androidx.annotation:annotation-experimental:1.0.0"
}
diff --git a/car/app/app-samples/navigation/mobile/github_build.gradle b/car/app/app-samples/navigation/mobile/github_build.gradle
index 3fb6ea8..78ea1f8 100644
--- a/car/app/app-samples/navigation/mobile/github_build.gradle
+++ b/car/app/app-samples/navigation/mobile/github_build.gradle
@@ -23,8 +23,10 @@
applicationId "androidx.car.app.sample.navigation"
minSdkVersion 23
targetSdkVersion 31
- versionCode 101 // Increment this to generate signed builds for uploading to Playstore
- versionName "101"
+ // Increment this to generate signed builds for uploading to Playstore
+ // Make sure this is different from the navigation-automotive version
+ versionCode 108
+ versionName "108"
}
buildTypes {
@@ -44,6 +46,6 @@
}
dependencies {
- implementation "androidx.car.app:app-projected:1.2.0-alpha02"
+ implementation "androidx.car.app:app-projected:1.2.0-rc01"
implementation project(":navigation:common")
}
diff --git a/car/app/app-samples/places/automotive/github_build.gradle b/car/app/app-samples/places/automotive/github_build.gradle
index 319064a..f9b2078 100644
--- a/car/app/app-samples/places/automotive/github_build.gradle
+++ b/car/app/app-samples/places/automotive/github_build.gradle
@@ -43,6 +43,6 @@
}
dependencies {
- implementation "androidx.car.app:app-automotive:1.2.0-alpha02"
+ implementation "androidx.car.app:app-automotive:1.2.0-rc01"
implementation project(":places:common")
}
\ No newline at end of file
diff --git a/car/app/app-samples/places/common/github_build.gradle b/car/app/app-samples/places/common/github_build.gradle
index 45c1d1a..e51f92f 100644
--- a/car/app/app-samples/places/common/github_build.gradle
+++ b/car/app/app-samples/places/common/github_build.gradle
@@ -31,7 +31,7 @@
}
dependencies {
- implementation "androidx.car.app:app:1.2.0-alpha02"
+ implementation "androidx.car.app:app:1.2.0-rc01"
implementation "androidx.core:core:1.5.0-alpha01"
implementation 'com.google.guava:guava:28.1-jre'
}
\ No newline at end of file
diff --git a/car/app/app-samples/places/mobile/github_build.gradle b/car/app/app-samples/places/mobile/github_build.gradle
index c737ee1..cf37c23 100644
--- a/car/app/app-samples/places/mobile/github_build.gradle
+++ b/car/app/app-samples/places/mobile/github_build.gradle
@@ -43,6 +43,6 @@
}
dependencies {
- implementation "androidx.car.app:app-projected:1.2.0-alpha02"
+ implementation "androidx.car.app:app-projected:1.2.0-rc01"
implementation project(":places:common")
}
\ No newline at end of file
diff --git a/car/app/app-samples/showcase/automotive/github_build.gradle b/car/app/app-samples/showcase/automotive/github_build.gradle
index d41d257..046c9ae 100644
--- a/car/app/app-samples/showcase/automotive/github_build.gradle
+++ b/car/app/app-samples/showcase/automotive/github_build.gradle
@@ -25,8 +25,8 @@
targetSdkVersion 31
// Increment this to generate signed builds for uploading to Playstore
// Make sure this is different from the showcase-mobile version
- versionCode 107
- versionName "107"
+ versionCode 109
+ versionName "109"
}
buildTypes {
@@ -48,6 +48,6 @@
}
dependencies {
- implementation "androidx.car.app:app-automotive:1.2.0-alpha02"
+ implementation "androidx.car.app:app-automotive:1.2.0-rc01"
implementation project(":showcase:common")
}
diff --git a/car/app/app-samples/showcase/common/github_build.gradle b/car/app/app-samples/showcase/common/github_build.gradle
index 04be794..33a90bb 100644
--- a/car/app/app-samples/showcase/common/github_build.gradle
+++ b/car/app/app-samples/showcase/common/github_build.gradle
@@ -34,6 +34,6 @@
dependencies {
implementation "androidx.core:core:1.6.0-alpha01"
- implementation "androidx.car.app:app:1.2.0-alpha02"
+ implementation "androidx.car.app:app:1.2.0-rc01"
implementation "androidx.annotation:annotation-experimental:1.0.0"
}
diff --git a/car/app/app-samples/showcase/mobile/github_build.gradle b/car/app/app-samples/showcase/mobile/github_build.gradle
index 83498fb..a95c043 100644
--- a/car/app/app-samples/showcase/mobile/github_build.gradle
+++ b/car/app/app-samples/showcase/mobile/github_build.gradle
@@ -25,8 +25,8 @@
targetSdkVersion 31
// Increment this to generate signed builds for uploading to Playstore
// Make sure this is different from the showcase-automotive version
- versionCode 106
- versionName "106"
+ versionCode 108
+ versionName "108"
}
buildTypes {
@@ -48,6 +48,6 @@
}
dependencies {
- implementation "androidx.car.app:app-projected:1.2.0-alpha02"
+ implementation "androidx.car.app:app-projected:1.2.0-rc01"
implementation project(":showcase:common")
}
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 7af4b5c..83fef5e 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1010,10 +1010,15 @@
method public static <T> T? getSystemService(android.content.Context, Class<T!>);
method public static String? getSystemServiceName(android.content.Context, Class<?>);
method public static boolean isDeviceProtectedStorage(android.content.Context);
+ method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, int);
+ method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, String?, android.os.Handler?, int);
method public static boolean startActivities(android.content.Context, android.content.Intent![]);
method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
method public static void startForegroundService(android.content.Context, android.content.Intent);
+ field public static final int RECEIVER_EXPORTED = 2; // 0x2
+ field public static final int RECEIVER_NOT_EXPORTED = 4; // 0x4
+ field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
}
public class FileProvider extends android.content.ContentProvider {
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index f0fc4cf..70ec372 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -1010,10 +1010,15 @@
method public static <T> T? getSystemService(android.content.Context, Class<T!>);
method public static String? getSystemServiceName(android.content.Context, Class<?>);
method public static boolean isDeviceProtectedStorage(android.content.Context);
+ method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, int);
+ method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, String?, android.os.Handler?, int);
method public static boolean startActivities(android.content.Context, android.content.Intent![]);
method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
method public static void startForegroundService(android.content.Context, android.content.Intent);
+ field public static final int RECEIVER_EXPORTED = 2; // 0x2
+ field public static final int RECEIVER_NOT_EXPORTED = 4; // 0x4
+ field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
}
public class FileProvider extends android.content.ContentProvider {
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 6cc603e..12ad90c 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1118,10 +1118,15 @@
method public static <T> T? getSystemService(android.content.Context, Class<T!>);
method public static String? getSystemServiceName(android.content.Context, Class<?>);
method public static boolean isDeviceProtectedStorage(android.content.Context);
+ method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, int);
+ method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, String?, android.os.Handler?, int);
method public static boolean startActivities(android.content.Context, android.content.Intent![]);
method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
method public static void startForegroundService(android.content.Context, android.content.Intent);
+ field public static final int RECEIVER_EXPORTED = 2; // 0x2
+ field public static final int RECEIVER_NOT_EXPORTED = 4; // 0x4
+ field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
}
public class FileProvider extends android.content.ContentProvider {
diff --git a/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
index 3c082f1..afb8249 100644
--- a/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
@@ -70,7 +70,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import android.Manifest;
import android.accounts.AccountManager;
@@ -88,9 +93,12 @@
import android.app.usage.UsageStatsManager;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.RestrictionsManager;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
@@ -150,7 +158,14 @@
@LargeTest
public class ContextCompatTest extends BaseInstrumentationTestCase<ThemedYellowActivity> {
private Context mContext;
+ private IntentFilter mTestFilter = new IntentFilter();
+ private String mPermission;
+ private BroadcastReceiver mTestReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ }
+ };
public ContextCompatTest() {
super(ThemedYellowActivity.class);
}
@@ -158,6 +173,7 @@
@Before
public void setup() {
mContext = mActivityTestRule.getActivity();
+ mPermission = mContext.getPackageName() + ".DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION";
}
@Test
@@ -449,6 +465,71 @@
return ((size * tdensity) + (sdensity >> 1)) / sdensity;
}
+ @Test
+ public void testRegisterReceiver_noExportStateFlagThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> ContextCompat.registerReceiver(mContext,
+ mTestReceiver, mTestFilter, 0));
+
+ assertThrows(IllegalArgumentException.class, () -> ContextCompat.registerReceiver(mContext,
+ mTestReceiver, mTestFilter, Context.RECEIVER_VISIBLE_TO_INSTANT_APPS));
+ }
+
+ @Test
+ public void testRegisterReceiver_specifyBothExportStateFlagsThrowsException() {
+ assertThrows(IllegalArgumentException.class,
+ () -> ContextCompat.registerReceiver(mContext,
+ mTestReceiver, mTestFilter,
+ ContextCompat.RECEIVER_EXPORTED | ContextCompat.RECEIVER_NOT_EXPORTED));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 33)
+ public void testRegisterReceiverApi33() {
+ Context spyContext = spy(mContext);
+
+ ContextCompat.registerReceiver(spyContext, mTestReceiver, mTestFilter,
+ ContextCompat.RECEIVER_NOT_EXPORTED);
+ verify(spyContext).registerReceiver(eq(mTestReceiver), eq(mTestFilter), eq(null),
+ any(), eq(ContextCompat.RECEIVER_NOT_EXPORTED));
+
+ ContextCompat.registerReceiver(spyContext, mTestReceiver, mTestFilter,
+ ContextCompat.RECEIVER_EXPORTED);
+ verify(spyContext).registerReceiver(eq(mTestReceiver), eq(mTestFilter), eq(null), any(),
+ eq(ContextCompat.RECEIVER_EXPORTED));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 26, maxSdkVersion = 32)
+ public void testRegisterReceiverApi26() {
+ Context spyContext = spy(mContext);
+
+ ContextCompat.registerReceiver(spyContext, mTestReceiver, mTestFilter,
+ ContextCompat.RECEIVER_NOT_EXPORTED);
+ verify(spyContext).registerReceiver(eq(mTestReceiver), eq(mTestFilter),
+ eq(mPermission), any());
+
+ ContextCompat.registerReceiver(spyContext, mTestReceiver, mTestFilter,
+ ContextCompat.RECEIVER_EXPORTED);
+ verify(spyContext).registerReceiver(eq(mTestReceiver), eq(mTestFilter), eq(null), any(),
+ eq(0));
+
+ }
+
+ @Test
+ @SdkSuppress(maxSdkVersion = 25)
+ public void testRegisterReceiver() {
+ Context spyContext = spy(mContext);
+
+ ContextCompat.registerReceiver(spyContext, mTestReceiver, mTestFilter,
+ ContextCompat.RECEIVER_NOT_EXPORTED);
+ verify(spyContext).registerReceiver(eq(mTestReceiver), eq(mTestFilter), eq(mPermission),
+ any());
+
+ ContextCompat.registerReceiver(spyContext, mTestReceiver, mTestFilter,
+ ContextCompat.RECEIVER_EXPORTED);
+ verify(spyContext).registerReceiver(eq(mTestReceiver), eq(mTestFilter), eq(null), any());
+ }
+
@Test(expected = NullPointerException.class)
public void testCheckSelfPermissionNull() {
ContextCompat.checkSelfPermission(mContext, null);
diff --git a/core/core/src/main/AndroidManifest.xml b/core/core/src/main/AndroidManifest.xml
index 8481017..b050c7e 100644
--- a/core/core/src/main/AndroidManifest.xml
+++ b/core/core/src/main/AndroidManifest.xml
@@ -16,4 +16,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:appComponentFactory="androidx.core.app.CoreComponentFactory" />
+ <permission android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"/>
+ <uses-permission android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"/>
</manifest>
diff --git a/core/core/src/main/java/androidx/core/content/ContextCompat.java b/core/core/src/main/java/androidx/core/content/ContextCompat.java
index bcf29ab..2ac62dc 100644
--- a/core/core/src/main/java/androidx/core/content/ContextCompat.java
+++ b/core/core/src/main/java/androidx/core/content/ContextCompat.java
@@ -84,10 +84,12 @@
import android.app.usage.UsageStatsManager;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.RestrictionsManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
@@ -140,10 +142,12 @@
import androidx.annotation.ColorRes;
import androidx.annotation.DoNotInline;
import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.res.ResourcesCompat;
@@ -153,6 +157,8 @@
import androidx.core.util.ObjectsCompat;
import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.concurrent.Executor;
@@ -194,6 +200,35 @@
return null;
}
+
+ private static final String DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION_SUFFIX =
+ ".DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION";
+
+
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @IntDef(flag = true, value = {
+ RECEIVER_VISIBLE_TO_INSTANT_APPS, RECEIVER_EXPORTED, RECEIVER_NOT_EXPORTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RegisterReceiverFlags {}
+ /**
+ * Flag for {@link #registerReceiver}: The receiver can receive broadcasts from Instant Apps.
+ */
+ public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 0x1;
+
+ /**
+ * Flag for {@link #registerReceiver}: The receiver can receive broadcasts from other Apps.
+ * Has the same behavior as marking a statically registered receiver with "exported=true"
+ */
+ public static final int RECEIVER_EXPORTED = 0x2;
+
+ /**
+ * Flag for {@link #registerReceiver}: The receiver cannot receive broadcasts from other Apps.
+ * Has the same behavior as marking a statically registered receiver with "exported=false"
+ */
+ public static final int RECEIVER_NOT_EXPORTED = 0x4;
+
/**
* Start a set of activities as a synthesized task stack, if able.
*
@@ -738,6 +773,80 @@
}
/**
+ * Register a broadcast receiver.
+ *
+ * @param context Context to retrieve service from.
+ * @param receiver The BroadcastReceiver to handle the broadcast.
+ * @param filter Selects the Intent broadcasts to be received.
+ * @param flags Specify one of {@link #RECEIVER_EXPORTED}, if you wish for your receiver
+ * to be able to receiver broadcasts from other applications, or
+ * {@link #RECEIVER_NOT_EXPORTED} if you only want your receiver to be able
+ * to receive broadcasts from the system or your own app.
+ * @return The first sticky intent found that matches <var>filter</var>,
+ * or null if there are none.
+ * @see Context#registerReceiver(BroadcastReceiver, IntentFilter, int)
+ */
+ @Nullable
+ public static Intent registerReceiver(@NonNull Context context,
+ @Nullable BroadcastReceiver receiver, @NonNull IntentFilter filter,
+ @RegisterReceiverFlags int flags) {
+ return registerReceiver(context, receiver, filter, null, null, flags);
+ }
+
+ /**
+ * Register a broadcast receiver.
+ *
+ * @param context Context to retrieve service from.
+ * @param receiver The BroadcastReceiver to handle the broadcast.
+ * @param filter Selects the Intent broadcasts to be received.
+ * @param broadcastPermission String naming a permission that a broadcaster must hold in
+ * order to send and Intent to you. If null, no permission is
+ * required.
+ * @param scheduler Handler identifying the thread will receive the Intent. If
+ * null, the main thread of the process will be used.
+ * @param flags Specify one of {@link #RECEIVER_EXPORTED}, if you wish for your
+ * receiver to be able to receiver broadcasts from other
+ * applications, or {@link #RECEIVER_NOT_EXPORTED} if you only want
+ * your receiver to be able to receive broadcasts from the system
+ * or your own app.
+ * @return The first sticky intent found that matches <var>filter</var>,
+ * or null if there are none.
+ * @see Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler, int)
+ */
+ @Nullable
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ public static Intent registerReceiver(@NonNull Context context,
+ @Nullable BroadcastReceiver receiver, @NonNull IntentFilter filter,
+ @Nullable String broadcastPermission,
+ @Nullable Handler scheduler, @RegisterReceiverFlags int flags) {
+ if (((flags & RECEIVER_EXPORTED) == 0) && ((flags & RECEIVER_NOT_EXPORTED) == 0)) {
+ throw new IllegalArgumentException("One of either RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED is required");
+ }
+
+ if (((flags & RECEIVER_EXPORTED) != 0) && ((flags & RECEIVER_NOT_EXPORTED) != 0)) {
+ throw new IllegalArgumentException("Cannot specify both RECEIVER_EXPORTED and "
+ + "RECEIVER_NOT_EXPORTED");
+ }
+
+ if (BuildCompat.isAtLeastT()) {
+ return Api33Impl.registerReceiver(context, receiver, filter, broadcastPermission,
+ scheduler, flags);
+ }
+ if (Build.VERSION.SDK_INT >= 26) {
+ return Api26Impl.registerReceiver(context, receiver, filter, broadcastPermission,
+ scheduler, flags);
+ }
+ if (((flags & RECEIVER_NOT_EXPORTED) != 0) && (broadcastPermission == null)) {
+ String permission =
+ context.getPackageName() + DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION_SUFFIX;
+ return context.registerReceiver(receiver, filter, permission, scheduler /* handler */);
+ }
+ return context.registerReceiver(receiver, filter, broadcastPermission,
+ scheduler);
+ }
+
+ /**
* Gets the name of the system-level service that is represented by the specified class.
*
* @param context Context to retrieve service name from.
@@ -936,6 +1045,19 @@
// This class is not instantiable.
}
+ @DoNotInline
+ static Intent registerReceiver(Context obj, @Nullable BroadcastReceiver receiver,
+ IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+ if ((flags & RECEIVER_NOT_EXPORTED) != 0 && broadcastPermission == null) {
+ String permission =
+ obj.getPackageName() + DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION_SUFFIX;
+ // receivers that are not exported should also not be visible to instant apps
+ return obj.registerReceiver(receiver, filter, permission, scheduler);
+ }
+ flags &= Context.RECEIVER_VISIBLE_TO_INSTANT_APPS;
+ return obj.registerReceiver(receiver, filter, broadcastPermission, scheduler, flags);
+ }
+
@SuppressWarnings("UnusedReturnValue")
@DoNotInline
static ComponentName startForegroundService(Context obj, Intent service) {
@@ -966,4 +1088,17 @@
return obj.getAttributionTag();
}
}
+
+ @RequiresApi(33)
+ static class Api33Impl {
+ private Api33Impl() {
+ // This class is not instantiable
+ }
+
+ @DoNotInline
+ static Intent registerReceiver(Context obj, @Nullable BroadcastReceiver receiver,
+ IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+ return obj.registerReceiver(receiver, filter, broadcastPermission, scheduler, flags);
+ }
+ }
}
diff --git a/datastore/datastore-compose-samples/build.gradle b/datastore/datastore-compose-samples/build.gradle
index d7c2c16..15480ae 100644
--- a/datastore/datastore-compose-samples/build.gradle
+++ b/datastore/datastore-compose-samples/build.gradle
@@ -53,9 +53,6 @@
buildFeatures {
compose true
}
- composeOptions {
- kotlinCompilerExtensionVersion '1.1.0-rc02'
- }
}
protobuf {
diff --git a/savedstate/savedstate/api/current.txt b/savedstate/savedstate/api/current.txt
index 2d04ca6..7c561cb 100644
--- a/savedstate/savedstate/api/current.txt
+++ b/savedstate/savedstate/api/current.txt
@@ -19,11 +19,17 @@
}
public final class SavedStateRegistryController {
- method public static androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner);
+ method public static androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner owner);
method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
method @MainThread public void performAttach();
- method @MainThread public void performRestore(android.os.Bundle?);
- method @MainThread public void performSave(android.os.Bundle);
+ method @MainThread public void performRestore(android.os.Bundle? savedState);
+ method @MainThread public void performSave(android.os.Bundle outBundle);
+ property public final androidx.savedstate.SavedStateRegistry savedStateRegistry;
+ field public static final androidx.savedstate.SavedStateRegistryController.Companion Companion;
+ }
+
+ public static final class SavedStateRegistryController.Companion {
+ method public androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner owner);
}
public interface SavedStateRegistryOwner extends androidx.lifecycle.LifecycleOwner {
diff --git a/savedstate/savedstate/api/public_plus_experimental_current.txt b/savedstate/savedstate/api/public_plus_experimental_current.txt
index 2d04ca6..7c561cb 100644
--- a/savedstate/savedstate/api/public_plus_experimental_current.txt
+++ b/savedstate/savedstate/api/public_plus_experimental_current.txt
@@ -19,11 +19,17 @@
}
public final class SavedStateRegistryController {
- method public static androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner);
+ method public static androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner owner);
method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
method @MainThread public void performAttach();
- method @MainThread public void performRestore(android.os.Bundle?);
- method @MainThread public void performSave(android.os.Bundle);
+ method @MainThread public void performRestore(android.os.Bundle? savedState);
+ method @MainThread public void performSave(android.os.Bundle outBundle);
+ property public final androidx.savedstate.SavedStateRegistry savedStateRegistry;
+ field public static final androidx.savedstate.SavedStateRegistryController.Companion Companion;
+ }
+
+ public static final class SavedStateRegistryController.Companion {
+ method public androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner owner);
}
public interface SavedStateRegistryOwner extends androidx.lifecycle.LifecycleOwner {
diff --git a/savedstate/savedstate/api/restricted_current.txt b/savedstate/savedstate/api/restricted_current.txt
index 2d04ca6..7c561cb 100644
--- a/savedstate/savedstate/api/restricted_current.txt
+++ b/savedstate/savedstate/api/restricted_current.txt
@@ -19,11 +19,17 @@
}
public final class SavedStateRegistryController {
- method public static androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner);
+ method public static androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner owner);
method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
method @MainThread public void performAttach();
- method @MainThread public void performRestore(android.os.Bundle?);
- method @MainThread public void performSave(android.os.Bundle);
+ method @MainThread public void performRestore(android.os.Bundle? savedState);
+ method @MainThread public void performSave(android.os.Bundle outBundle);
+ property public final androidx.savedstate.SavedStateRegistry savedStateRegistry;
+ field public static final androidx.savedstate.SavedStateRegistryController.Companion Companion;
+ }
+
+ public static final class SavedStateRegistryController.Companion {
+ method public androidx.savedstate.SavedStateRegistryController create(androidx.savedstate.SavedStateRegistryOwner owner);
}
public interface SavedStateRegistryOwner extends androidx.lifecycle.LifecycleOwner {
diff --git a/savedstate/savedstate/build.gradle b/savedstate/savedstate/build.gradle
index f5735d0..ae8b16d 100644
--- a/savedstate/savedstate/build.gradle
+++ b/savedstate/savedstate/build.gradle
@@ -15,6 +15,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
+ api("androidx.core:core-ktx:1.2.0")
implementation("androidx.arch.core:core-common:2.1.0")
implementation("androidx.lifecycle:lifecycle-common:2.4.0")
diff --git a/savedstate/savedstate/src/main/java/androidx/savedstate/Recreator.java b/savedstate/savedstate/src/main/java/androidx/savedstate/Recreator.java
deleted file mode 100644
index 33647d3..0000000
--- a/savedstate/savedstate/src/main/java/androidx/savedstate/Recreator.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.savedstate;
-
-
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleEventObserver;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.savedstate.SavedStateRegistry.AutoRecreated;
-
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-final class Recreator implements LifecycleEventObserver {
-
- static final String CLASSES_KEY = "classes_to_restore";
- static final String COMPONENT_KEY = "androidx.savedstate.Restarter";
-
- private final SavedStateRegistryOwner mOwner;
-
- Recreator(SavedStateRegistryOwner owner) {
- mOwner = owner;
- }
-
- @Override
- public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
- if (event != Lifecycle.Event.ON_CREATE) {
- throw new AssertionError("Next event must be ON_CREATE");
- }
- source.getLifecycle().removeObserver(this);
- Bundle bundle = mOwner.getSavedStateRegistry()
- .consumeRestoredStateForKey(COMPONENT_KEY);
- if (bundle == null) {
- return;
- }
- ArrayList<String> classes = bundle.getStringArrayList(CLASSES_KEY);
- if (classes == null) {
- throw new IllegalStateException("Bundle with restored state for the component \""
- + COMPONENT_KEY + "\" must contain list of strings by the key \""
- + CLASSES_KEY + "\"");
- }
- for (String className : classes) {
- reflectiveNew(className);
- }
- }
-
- private void reflectiveNew(String className) {
- Class<? extends AutoRecreated> clazz;
- try {
- clazz = Class.forName(className, false,
- Recreator.class.getClassLoader()).asSubclass(AutoRecreated.class);
- } catch (ClassNotFoundException e) {
- throw new RuntimeException("Class " + className + " wasn't found", e);
- }
-
- Constructor<? extends AutoRecreated> constructor;
- try {
- constructor = clazz.getDeclaredConstructor();
- } catch (NoSuchMethodException e) {
- throw new IllegalStateException("Class" + clazz.getSimpleName() + " must have "
- + "default constructor in order to be automatically recreated", e);
- }
- constructor.setAccessible(true);
-
- AutoRecreated newInstance;
- try {
- newInstance = constructor.newInstance();
- } catch (Exception e) {
- throw new RuntimeException("Failed to instantiate " + className, e);
- }
- newInstance.onRecreated(mOwner);
- }
-
- static final class SavedStateProvider implements SavedStateRegistry.SavedStateProvider {
- @SuppressWarnings("WeakerAccess") // synthetic access
- final Set<String> mClasses = new HashSet<>();
-
- SavedStateProvider(final SavedStateRegistry registry) {
- registry.registerSavedStateProvider(COMPONENT_KEY, this);
- }
-
- @NonNull
- @Override
- public Bundle saveState() {
- Bundle bundle = new Bundle();
- bundle.putStringArrayList(CLASSES_KEY, new ArrayList<>(mClasses));
- return bundle;
- }
-
- void add(String className) {
- mClasses.add(className);
- }
- }
-}
diff --git a/savedstate/savedstate/src/main/java/androidx/savedstate/Recreator.kt b/savedstate/savedstate/src/main/java/androidx/savedstate/Recreator.kt
new file mode 100644
index 0000000..315ab92
--- /dev/null
+++ b/savedstate/savedstate/src/main/java/androidx/savedstate/Recreator.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.savedstate
+
+import android.os.Bundle
+import androidx.core.os.bundleOf
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.savedstate.SavedStateRegistry.AutoRecreated
+
+internal class Recreator(
+ private val owner: SavedStateRegistryOwner
+) : LifecycleEventObserver {
+
+ override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+ if (event != Lifecycle.Event.ON_CREATE) {
+ throw AssertionError("Next event must be ON_CREATE")
+ }
+ source.lifecycle.removeObserver(this)
+ val bundle: Bundle = owner.savedStateRegistry
+ .consumeRestoredStateForKey(COMPONENT_KEY) ?: return
+ val classes: MutableList<String> = bundle.getStringArrayList(CLASSES_KEY)
+ ?: throw IllegalStateException(
+ "Bundle with restored state for the component " +
+ "\"$COMPONENT_KEY\" must contain list of strings by the key " +
+ "\"$CLASSES_KEY\""
+ )
+ for (className: String in classes) {
+ reflectiveNew(className)
+ }
+ }
+
+ private fun reflectiveNew(className: String) {
+ val clazz: Class<out AutoRecreated> =
+ try {
+ Class.forName(className, false, Recreator::class.java.classLoader)
+ .asSubclass(AutoRecreated::class.java)
+ } catch (e: ClassNotFoundException) {
+ throw RuntimeException("Class $className wasn't found", e)
+ }
+ val constructor =
+ try {
+ clazz.getDeclaredConstructor()
+ } catch (e: NoSuchMethodException) {
+ throw IllegalStateException(
+ "Class ${clazz.simpleName} must have " +
+ "default constructor in order to be automatically recreated", e
+ )
+ }
+ constructor.isAccessible = true
+ val newInstance: AutoRecreated =
+ try {
+ constructor.newInstance()
+ } catch (e: Exception) {
+ throw RuntimeException("Failed to instantiate $className", e)
+ }
+ newInstance.onRecreated(owner)
+ }
+
+ internal class SavedStateProvider(registry: SavedStateRegistry) :
+ SavedStateRegistry.SavedStateProvider {
+
+ private val classes: MutableSet<String> = mutableSetOf()
+
+ init {
+ registry.registerSavedStateProvider(COMPONENT_KEY, this)
+ }
+
+ override fun saveState(): Bundle {
+ return bundleOf(CLASSES_KEY to ArrayList(classes))
+ }
+
+ fun add(className: String) {
+ classes.add(className)
+ }
+ }
+
+ companion object {
+ const val CLASSES_KEY = "classes_to_restore"
+ const val COMPONENT_KEY = "androidx.savedstate.Restarter"
+ }
+}
\ No newline at end of file
diff --git a/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.java b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.java
deleted file mode 100644
index 98fe68f..0000000
--- a/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.savedstate;
-
-import android.os.Bundle;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.Lifecycle;
-
-/**
- * An API for {@link SavedStateRegistryOwner} implementations to control {@link SavedStateRegistry}.
- * <p>
- * {@code SavedStateRegistryOwner} should call {@link #performRestore(Bundle)} to restore state of
- * {@link SavedStateRegistry} and {@link #performSave(Bundle)} to gather SavedState from it.
- */
-public final class SavedStateRegistryController {
- private final SavedStateRegistryOwner mOwner;
- private final SavedStateRegistry mRegistry;
-
- private boolean mAttached = false;
-
- private SavedStateRegistryController(SavedStateRegistryOwner owner) {
- mOwner = owner;
- mRegistry = new SavedStateRegistry();
- }
-
- /**
- * Returns controlled {@link SavedStateRegistry}
- */
- @NonNull
- public SavedStateRegistry getSavedStateRegistry() {
- return mRegistry;
- }
-
- /**
- * Perform the initial, one time attachment necessary to configure this
- * {@link SavedStateRegistry}. This must be called when the owner's {@link Lifecycle} is
- * {@link Lifecycle.State#INITIALIZED} and before you call
- * {@link #performRestore(Bundle)}.
- */
- @MainThread
- public void performAttach() {
- Lifecycle lifecycle = mOwner.getLifecycle();
- if (lifecycle.getCurrentState() != Lifecycle.State.INITIALIZED) {
- throw new IllegalStateException("Restarter must be created only during "
- + "owner's initialization stage");
- }
- lifecycle.addObserver(new Recreator(mOwner));
- mRegistry.performAttach(lifecycle);
-
- mAttached = true;
- }
-
- /**
- * An interface for an owner of this {@link SavedStateRegistry} to restore saved state.
- *
- * @param savedState restored state
- */
- @MainThread
- public void performRestore(@Nullable Bundle savedState) {
- // To support backward compatibility with libraries that do not explicitly
- // call performAttach(), we make sure that work is done here
- if (!mAttached) {
- performAttach();
- }
- Lifecycle lifecycle = mOwner.getLifecycle();
- if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
- throw new IllegalStateException("performRestore cannot be called when owner "
- + " is " + lifecycle.getCurrentState());
- }
- mRegistry.performRestore(savedState);
- }
-
- /**
- * An interface for an owner of this {@link SavedStateRegistry}
- * to perform state saving, it will call all registered providers and
- * merge with unconsumed state.
- *
- * @param outBundle Bundle in which to place a saved state
- */
- @MainThread
- public void performSave(@NonNull Bundle outBundle) {
- mRegistry.performSave(outBundle);
- }
-
- /**
- * Creates a {@link SavedStateRegistryController}.
- * <p>
- * It should be called during construction time of {@link SavedStateRegistryOwner}
- */
- @NonNull
- public static SavedStateRegistryController create(@NonNull SavedStateRegistryOwner owner) {
- return new SavedStateRegistryController(owner);
- }
-}
diff --git a/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.kt b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.kt
new file mode 100644
index 0000000..8a477ed
--- /dev/null
+++ b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.savedstate
+
+import android.os.Bundle
+import androidx.annotation.MainThread
+import androidx.lifecycle.Lifecycle
+
+/**
+ * An API for [SavedStateRegistryOwner] implementations to control [SavedStateRegistry].
+ *
+ * `SavedStateRegistryOwner` should call [performRestore] to restore state of
+ * [SavedStateRegistry] and [performSave] to gather SavedState from it.
+ */
+class SavedStateRegistryController private constructor(private val owner: SavedStateRegistryOwner) {
+
+ val savedStateRegistry: SavedStateRegistry = SavedStateRegistry()
+
+ private var attached = false
+
+ /**
+ * Perform the initial, one time attachment necessary to configure this
+ * [SavedStateRegistry]. This must be called when the owner's [Lifecycle] is
+ * [Lifecycle.State.INITIALIZED] and before you call [performRestore].
+ */
+ @MainThread
+ fun performAttach() {
+ val lifecycle = owner.lifecycle
+ check(lifecycle.currentState == Lifecycle.State.INITIALIZED) {
+ ("Restarter must be created only during owner's initialization stage")
+ }
+ lifecycle.addObserver(Recreator(owner))
+ savedStateRegistry.performAttach(lifecycle)
+ attached = true
+ }
+
+ /**
+ * An interface for an owner of this [SavedStateRegistry] to restore saved state.
+ *
+ * @param savedState restored state
+ */
+ @MainThread
+ fun performRestore(savedState: Bundle?) {
+ // To support backward compatibility with libraries that do not explicitly
+ // call performAttach(), we make sure that work is done here
+ if (!attached) {
+ performAttach()
+ }
+ val lifecycle = owner.lifecycle
+ check(!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
+ ("performRestore cannot be called when owner is ${lifecycle.currentState}")
+ }
+ savedStateRegistry.performRestore(savedState)
+ }
+
+ /**
+ * An interface for an owner of this [SavedStateRegistry]
+ * to perform state saving, it will call all registered providers and
+ * merge with unconsumed state.
+ *
+ * @param outBundle Bundle in which to place a saved state
+ */
+ @MainThread
+ fun performSave(outBundle: Bundle) {
+ savedStateRegistry.performSave(outBundle)
+ }
+
+ companion object {
+ /**
+ * Creates a [SavedStateRegistryController].
+ *
+ * It should be called during construction time of [SavedStateRegistryOwner]
+ */
+ @JvmStatic
+ fun create(owner: SavedStateRegistryOwner): SavedStateRegistryController {
+ return SavedStateRegistryController(owner)
+ }
+ }
+}
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index dc4347d..b4508e3 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -102,6 +102,7 @@
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -1437,6 +1438,7 @@
assertTrue(service.hasComplicationDataCache())
}
+ @Ignore // b/225230182
@RequiresApi(Build.VERSION_CODES.O_MR1)
@Test
fun interactiveAndHeadlessOpenGlWatchFaceInstances() {