Loading res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -9571,6 +9571,8 @@ <!-- [CHAR_LIMIT=NONE] Developer Settings: Label for the option that turns off all overlays in a given category. --> <string name="overlay_option_device_default">Device default</string> <!-- [CHAR_LIMIT=NONE] Developer Settings: Toast displayed to the user when an overlay fails to apply. --> <string name="overlay_toast_failed_to_apply">Failed to apply overlay</string> <!-- [CHAR_LIMIT=60] Label for special access screen --> <string name="special_access">Special app access</string> src/com/android/settings/development/OverlayCategoryPreferenceController.java +36 −17 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading @@ -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"; Loading Loading @@ -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)) { Loading @@ -113,18 +117,33 @@ public class OverlayCategoryPreferenceController extends DeveloperOptionsPrefere return true; } final boolean result; new AsyncTask<Void, Void, Boolean>() { @Override protected Boolean doInBackground(Void... params) { try { if (PACKAGE_DEVICE_DEFAULT.equals(packageName)) { result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); return mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); } else { result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); return mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); } } catch (RemoteException re) { throw re.rethrowFromSystemServer(); Log.w(TAG, "Error enabling overlay.", re); return false; } } @Override protected void onPostExecute(Boolean success) { updateState(mPreference); return result; 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 Loading tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java +47 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading Loading
res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -9571,6 +9571,8 @@ <!-- [CHAR_LIMIT=NONE] Developer Settings: Label for the option that turns off all overlays in a given category. --> <string name="overlay_option_device_default">Device default</string> <!-- [CHAR_LIMIT=NONE] Developer Settings: Toast displayed to the user when an overlay fails to apply. --> <string name="overlay_toast_failed_to_apply">Failed to apply overlay</string> <!-- [CHAR_LIMIT=60] Label for special access screen --> <string name="special_access">Special app access</string>
src/com/android/settings/development/OverlayCategoryPreferenceController.java +36 −17 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading @@ -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"; Loading Loading @@ -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)) { Loading @@ -113,18 +117,33 @@ public class OverlayCategoryPreferenceController extends DeveloperOptionsPrefere return true; } final boolean result; new AsyncTask<Void, Void, Boolean>() { @Override protected Boolean doInBackground(Void... params) { try { if (PACKAGE_DEVICE_DEFAULT.equals(packageName)) { result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); return mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); } else { result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); return mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); } } catch (RemoteException re) { throw re.rethrowFromSystemServer(); Log.w(TAG, "Error enabling overlay.", re); return false; } } @Override protected void onPostExecute(Boolean success) { updateState(mPreference); return result; 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 Loading
tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java +47 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading