diff --git a/res/values/strings.xml b/res/values/strings.xml
index 35c89234eee..81528f64f2d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9557,6 +9557,8 @@
Device default
+
+ Failed to apply overlay
Special app access
diff --git a/src/com/android/settings/development/OverlayCategoryPreferenceController.java b/src/com/android/settings/development/OverlayCategoryPreferenceController.java
index 9db8a2fc39b..0ba9d799651 100644
--- a/src/com/android/settings/development/OverlayCategoryPreferenceController.java
+++ b/src/com/android/settings/development/OverlayCategoryPreferenceController.java
@@ -22,9 +22,12 @@ import android.content.Context;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
import android.content.pm.PackageManager;
+import android.os.AsyncTask;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
@@ -38,6 +41,7 @@ import com.android.settingslib.development.DeveloperOptionsPreferenceController;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+import java.util.Optional;
/**
* Preference controller to allow users to choose an overlay from a list for a given category.
@@ -46,6 +50,7 @@ import java.util.List;
*/
public class OverlayCategoryPreferenceController extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+ private static final String TAG = "OverlayCategoryPC";
@VisibleForTesting
static final String PACKAGE_DEVICE_DEFAULT = "package_device_default";
private static final String OVERLAY_TARGET_PACKAGE = "android";
@@ -100,12 +105,11 @@ public class OverlayCategoryPreferenceController extends DeveloperOptionsPrefere
}
private boolean setOverlay(String packageName) {
- String currentPackageName = null;
- for (OverlayInfo o : getOverlayInfos()) {
- if (o.isEnabled()) {
- currentPackageName = o.packageName;
- }
- }
+ final String currentPackageName = getOverlayInfos().stream()
+ .filter(info -> info.isEnabled())
+ .map(info -> info.packageName)
+ .findFirst()
+ .orElse(null);
if (PACKAGE_DEVICE_DEFAULT.equals(packageName) && TextUtils.isEmpty(currentPackageName)
|| TextUtils.equals(packageName, currentPackageName)) {
@@ -113,18 +117,33 @@ public class OverlayCategoryPreferenceController extends DeveloperOptionsPrefere
return true;
}
- final boolean result;
- try {
- if (PACKAGE_DEVICE_DEFAULT.equals(packageName)) {
- result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM);
- } else {
- result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM);
+ new AsyncTask() {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ try {
+ if (PACKAGE_DEVICE_DEFAULT.equals(packageName)) {
+ return mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM);
+ } else {
+ return mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM);
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Error enabling overlay.", re);
+ return false;
+ }
}
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- updateState(mPreference);
- return result;
+
+ @Override
+ protected void onPostExecute(Boolean success) {
+ updateState(mPreference);
+ if (!success) {
+ Toast.makeText(
+ mContext, R.string.overlay_toast_failed_to_apply, Toast.LENGTH_LONG)
+ .show();
+ }
+ }
+ }.execute();
+
+ return true; // Assume success; toast on failure.
}
@Override
diff --git a/tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java
index 3938b8993d7..8a90b205fa2 100644
--- a/tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java
@@ -31,6 +31,8 @@ import android.content.om.OverlayInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
+import com.android.settings.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,6 +40,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowToast;
import java.util.ArrayList;
import java.util.Arrays;
@@ -105,21 +109,64 @@ public class OverlayCategoryPreferenceControllerTest {
mockCurrentOverlays(ONE_DISABLED, TWO_DISABLED);
mController.onPreferenceChange(null, TWO_DISABLED.packageName);
+ ShadowApplication.runBackgroundTasks();
verify(mOverlayManager)
.setEnabledExclusiveInCategory(eq(TWO_DISABLED.packageName), anyInt());
}
+ @Test
+ public void onPreferenceChange_enable_fails() throws Exception {
+ mockCurrentOverlays(ONE_DISABLED, TWO_DISABLED);
+ when(mOverlayManager.setEnabledExclusiveInCategory(eq(TWO_DISABLED.packageName), anyInt()))
+ .thenReturn(false);
+
+ mController.onPreferenceChange(null, TWO_DISABLED.packageName);
+ ShadowApplication.runBackgroundTasks();
+
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ RuntimeEnvironment.application.getString(R.string.overlay_toast_failed_to_apply));
+ }
+
@Test
public void onPreferenceChange_disable() throws Exception {
mockCurrentOverlays(ONE_DISABLED, TWO_ENABLED);
mController.onPreferenceChange(
null, OverlayCategoryPreferenceController.PACKAGE_DEVICE_DEFAULT);
+ ShadowApplication.runBackgroundTasks();
verify(mOverlayManager).setEnabled(eq(TWO_ENABLED.packageName), eq(false), anyInt());
}
+ @Test
+ public void onPreferenceChange_disable_fails() throws Exception {
+ mockCurrentOverlays(ONE_DISABLED, TWO_ENABLED);
+ when(mOverlayManager.setEnabled(eq(TWO_ENABLED.packageName), eq(false), anyInt()))
+ .thenReturn(false);
+
+ mController.onPreferenceChange(
+ null, OverlayCategoryPreferenceController.PACKAGE_DEVICE_DEFAULT);
+ ShadowApplication.runBackgroundTasks();
+
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ RuntimeEnvironment.application.getString(R.string.overlay_toast_failed_to_apply));
+ }
+
+ @Test
+ public void onPreferenceChange_disable_throws() throws Exception {
+ mockCurrentOverlays(ONE_DISABLED, TWO_ENABLED);
+ when(mOverlayManager.setEnabled(eq(TWO_ENABLED.packageName), eq(false), anyInt()))
+ .thenThrow(new RemoteException());
+
+ mController.onPreferenceChange(
+ null, OverlayCategoryPreferenceController.PACKAGE_DEVICE_DEFAULT);
+ ShadowApplication.runBackgroundTasks();
+
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ RuntimeEnvironment.application.getString(R.string.overlay_toast_failed_to_apply));
+ }
+
@Test
public void updateState_enabled() {
mockCurrentOverlays(ONE_DISABLED, TWO_ENABLED);