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);