Loading res/layout/remove_account_button.xml +1 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ android:layout_height="wrap_content" android:orientation="vertical"> <Button <com.android.settings.widget.RestrictedButton android:id="@+id/button" android:text="@string/remove_account_label" android:layout_width="wrap_content" Loading src/com/android/settings/accounts/RemoveAccountPreferenceController.java +12 −15 Original line number Diff line number Diff line Loading @@ -34,19 +34,17 @@ import android.os.UserManager; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settings.widget.RestrictedButton; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.LayoutPreference; Loading @@ -64,6 +62,7 @@ public class RemoveAccountPreferenceController extends AbstractPreferenceControl private Fragment mParentFragment; private UserHandle mUserHandle; private LayoutPreference mRemoveAccountPreference; private RestrictedButton mRemoveAccountButton; public RemoveAccountPreferenceController(Context context, Fragment parent) { super(context); Loading @@ -75,8 +74,14 @@ public class RemoveAccountPreferenceController extends AbstractPreferenceControl public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mRemoveAccountPreference = screen.findPreference(KEY_REMOVE_ACCOUNT); final Button removeAccountButton = mRemoveAccountPreference.findViewById(R.id.button); removeAccountButton.setOnClickListener(this); mRemoveAccountButton = mRemoveAccountPreference.findViewById(R.id.button); mRemoveAccountButton.setOnClickListener(this); } @Override public void updateState(Preference preference) { super.updateState(preference); mRemoveAccountButton.updateState(); } @Override Loading @@ -93,21 +98,13 @@ public class RemoveAccountPreferenceController extends AbstractPreferenceControl public void onClick(View v) { mMetricsFeatureProvider.logClickedPreference(mRemoveAccountPreference, mMetricsFeatureProvider.getMetricsCategory(mParentFragment)); if (mUserHandle != null) { final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( mContext, UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle.getIdentifier()); if (admin != null) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin); return; } } ConfirmRemoveAccountDialog.show(mParentFragment, mAccount, mUserHandle); } public void init(Account account, UserHandle userHandle) { mAccount = account; mUserHandle = userHandle; mRemoveAccountButton.init(mUserHandle, UserManager.DISALLOW_MODIFY_ACCOUNTS); } /** Loading src/com/android/settings/widget/RestrictedButton.java 0 → 100644 +86 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.settings.widget; import android.content.Context; import android.os.UserHandle; import android.util.AttributeSet; import android.widget.Button; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; /** * A preference with a plus button on the side representing an "add" action. The plus button will * only be visible when a non-null click listener is registered. */ public class RestrictedButton extends Button { private UserHandle mUserHandle; private String mUserRestriction; public RestrictedButton(Context context) { super(context); } public RestrictedButton(Context context, AttributeSet attrs) { super(context, attrs); } public RestrictedButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public RestrictedButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean performClick() { EnforcedAdmin admin = getEnforcedAdmin(); if (admin != null) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin); return false; } return super.performClick(); } /** Initialize the button with {@link UserHandle} and a restriction */ public void init(UserHandle userHandle, String restriction) { setAllowClickWhenDisabled(true); mUserHandle = userHandle; mUserRestriction = restriction; } /** Update the restriction state */ public void updateState() { setEnabled(getEnforcedAdmin() == null); } private EnforcedAdmin getEnforcedAdmin() { if (mUserHandle != null) { EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( mContext, mUserRestriction, mUserHandle.getIdentifier()); if (admin != null) { return admin; } } return null; } } tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java +2 −33 Original line number Diff line number Diff line Loading @@ -35,13 +35,10 @@ import android.accounts.AuthenticatorDescription; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.widget.Button; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; Loading @@ -57,11 +54,11 @@ import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.utils.ActivityControllerWrapper; import com.android.settings.widget.RestrictedButton; import com.android.settingslib.widget.LayoutPreference; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; Loading @@ -74,8 +71,6 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; import java.io.IOException; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) @Config(shadows = { Loading Loading @@ -129,7 +124,7 @@ public class RemoveAccountPreferenceControllerTest { @Test public void displayPreference_shouldAddClickListener() { when(mScreen.findPreference(KEY_REMOVE_ACCOUNT)).thenReturn(mPreference); final Button button = mock(Button.class); final RestrictedButton button = mock(RestrictedButton.class); when(mPreference.findViewById(R.id.button)).thenReturn(button); mController.displayPreference(mScreen); Loading @@ -147,32 +142,6 @@ public class RemoveAccountPreferenceControllerTest { eq(TAG_REMOVE_ACCOUNT_DIALOG)); } @Ignore @Test public void onClick_modifyAccountsIsDisallowed_shouldNotStartConfirmDialog() { when(mFragment.isAdded()).thenReturn(true); final int userId = UserHandle.myUserId(); mController.init(new Account("test", "test"), UserHandle.of(userId)); List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>(); enforcingUsers.add(new UserManager.EnforcingUser(userId, UserManager.RESTRICTION_SOURCE_DEVICE_OWNER)); ComponentName componentName = new ComponentName("test", "test"); // Ensure that RestrictedLockUtils.checkIfRestrictionEnforced doesn't return null. ShadowUserManager.getShadow().setUserRestrictionSources( UserManager.DISALLOW_MODIFY_ACCOUNTS, UserHandle.of(userId), enforcingUsers); ShadowDevicePolicyManager.getShadow().setDeviceOwnerComponentOnAnyUser(componentName); mController.onClick(null); verify(mFragmentTransaction, never()).add( any(RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.class), eq(TAG_REMOVE_ACCOUNT_DIALOG)); } @Test @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class, ShadowFragment.class}) Loading tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java +12 −2 Original line number Diff line number Diff line Loading @@ -2,18 +2,22 @@ package com.android.settings.testutils.shadow; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static android.os.Build.VERSION_CODES.O; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager.DeviceOwnerType; import android.app.admin.IDevicePolicyManager; import android.app.admin.ManagedSubscriptionsPolicy; import android.app.admin.PasswordMetrics; import android.app.admin.PasswordPolicy; import android.content.ComponentName; import android.content.Context; import androidx.test.core.app.ApplicationProvider; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadow.api.Shadow; Loading @@ -39,6 +43,11 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev private List<String> mPermittedAccessibilityServices = null; @Implementation(minSdk = O) protected void __constructor__(Context context, IDevicePolicyManager service) { super.__constructor__(ApplicationProvider.getApplicationContext(), service); } public void setShortSupportMessageForUser(ComponentName admin, int userHandle, String message) { mSupportMessagesMap.put(Objects.hash(admin, userHandle), message); } Loading Loading @@ -137,6 +146,7 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev public static ShadowDevicePolicyManager getShadow() { return (ShadowDevicePolicyManager) Shadow.extract( RuntimeEnvironment.application.getSystemService(DevicePolicyManager.class)); ApplicationProvider.getApplicationContext() .getSystemService(DevicePolicyManager.class)); } } Loading
res/layout/remove_account_button.xml +1 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ android:layout_height="wrap_content" android:orientation="vertical"> <Button <com.android.settings.widget.RestrictedButton android:id="@+id/button" android:text="@string/remove_account_label" android:layout_width="wrap_content" Loading
src/com/android/settings/accounts/RemoveAccountPreferenceController.java +12 −15 Original line number Diff line number Diff line Loading @@ -34,19 +34,17 @@ import android.os.UserManager; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settings.widget.RestrictedButton; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.LayoutPreference; Loading @@ -64,6 +62,7 @@ public class RemoveAccountPreferenceController extends AbstractPreferenceControl private Fragment mParentFragment; private UserHandle mUserHandle; private LayoutPreference mRemoveAccountPreference; private RestrictedButton mRemoveAccountButton; public RemoveAccountPreferenceController(Context context, Fragment parent) { super(context); Loading @@ -75,8 +74,14 @@ public class RemoveAccountPreferenceController extends AbstractPreferenceControl public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mRemoveAccountPreference = screen.findPreference(KEY_REMOVE_ACCOUNT); final Button removeAccountButton = mRemoveAccountPreference.findViewById(R.id.button); removeAccountButton.setOnClickListener(this); mRemoveAccountButton = mRemoveAccountPreference.findViewById(R.id.button); mRemoveAccountButton.setOnClickListener(this); } @Override public void updateState(Preference preference) { super.updateState(preference); mRemoveAccountButton.updateState(); } @Override Loading @@ -93,21 +98,13 @@ public class RemoveAccountPreferenceController extends AbstractPreferenceControl public void onClick(View v) { mMetricsFeatureProvider.logClickedPreference(mRemoveAccountPreference, mMetricsFeatureProvider.getMetricsCategory(mParentFragment)); if (mUserHandle != null) { final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( mContext, UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle.getIdentifier()); if (admin != null) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin); return; } } ConfirmRemoveAccountDialog.show(mParentFragment, mAccount, mUserHandle); } public void init(Account account, UserHandle userHandle) { mAccount = account; mUserHandle = userHandle; mRemoveAccountButton.init(mUserHandle, UserManager.DISALLOW_MODIFY_ACCOUNTS); } /** Loading
src/com/android/settings/widget/RestrictedButton.java 0 → 100644 +86 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.settings.widget; import android.content.Context; import android.os.UserHandle; import android.util.AttributeSet; import android.widget.Button; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; /** * A preference with a plus button on the side representing an "add" action. The plus button will * only be visible when a non-null click listener is registered. */ public class RestrictedButton extends Button { private UserHandle mUserHandle; private String mUserRestriction; public RestrictedButton(Context context) { super(context); } public RestrictedButton(Context context, AttributeSet attrs) { super(context, attrs); } public RestrictedButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public RestrictedButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean performClick() { EnforcedAdmin admin = getEnforcedAdmin(); if (admin != null) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, admin); return false; } return super.performClick(); } /** Initialize the button with {@link UserHandle} and a restriction */ public void init(UserHandle userHandle, String restriction) { setAllowClickWhenDisabled(true); mUserHandle = userHandle; mUserRestriction = restriction; } /** Update the restriction state */ public void updateState() { setEnabled(getEnforcedAdmin() == null); } private EnforcedAdmin getEnforcedAdmin() { if (mUserHandle != null) { EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( mContext, mUserRestriction, mUserHandle.getIdentifier()); if (admin != null) { return admin; } } return null; } }
tests/robotests/src/com/android/settings/accounts/RemoveAccountPreferenceControllerTest.java +2 −33 Original line number Diff line number Diff line Loading @@ -35,13 +35,10 @@ import android.accounts.AuthenticatorDescription; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.widget.Button; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; Loading @@ -57,11 +54,11 @@ import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.utils.ActivityControllerWrapper; import com.android.settings.widget.RestrictedButton; import com.android.settingslib.widget.LayoutPreference; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; Loading @@ -74,8 +71,6 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; import java.io.IOException; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) @Config(shadows = { Loading Loading @@ -129,7 +124,7 @@ public class RemoveAccountPreferenceControllerTest { @Test public void displayPreference_shouldAddClickListener() { when(mScreen.findPreference(KEY_REMOVE_ACCOUNT)).thenReturn(mPreference); final Button button = mock(Button.class); final RestrictedButton button = mock(RestrictedButton.class); when(mPreference.findViewById(R.id.button)).thenReturn(button); mController.displayPreference(mScreen); Loading @@ -147,32 +142,6 @@ public class RemoveAccountPreferenceControllerTest { eq(TAG_REMOVE_ACCOUNT_DIALOG)); } @Ignore @Test public void onClick_modifyAccountsIsDisallowed_shouldNotStartConfirmDialog() { when(mFragment.isAdded()).thenReturn(true); final int userId = UserHandle.myUserId(); mController.init(new Account("test", "test"), UserHandle.of(userId)); List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>(); enforcingUsers.add(new UserManager.EnforcingUser(userId, UserManager.RESTRICTION_SOURCE_DEVICE_OWNER)); ComponentName componentName = new ComponentName("test", "test"); // Ensure that RestrictedLockUtils.checkIfRestrictionEnforced doesn't return null. ShadowUserManager.getShadow().setUserRestrictionSources( UserManager.DISALLOW_MODIFY_ACCOUNTS, UserHandle.of(userId), enforcingUsers); ShadowDevicePolicyManager.getShadow().setDeviceOwnerComponentOnAnyUser(componentName); mController.onClick(null); verify(mFragmentTransaction, never()).add( any(RemoveAccountPreferenceController.ConfirmRemoveAccountDialog.class), eq(TAG_REMOVE_ACCOUNT_DIALOG)); } @Test @Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class, ShadowFragment.class}) Loading
tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java +12 −2 Original line number Diff line number Diff line Loading @@ -2,18 +2,22 @@ package com.android.settings.testutils.shadow; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import static android.os.Build.VERSION_CODES.O; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager.DeviceOwnerType; import android.app.admin.IDevicePolicyManager; import android.app.admin.ManagedSubscriptionsPolicy; import android.app.admin.PasswordMetrics; import android.app.admin.PasswordPolicy; import android.content.ComponentName; import android.content.Context; import androidx.test.core.app.ApplicationProvider; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadow.api.Shadow; Loading @@ -39,6 +43,11 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev private List<String> mPermittedAccessibilityServices = null; @Implementation(minSdk = O) protected void __constructor__(Context context, IDevicePolicyManager service) { super.__constructor__(ApplicationProvider.getApplicationContext(), service); } public void setShortSupportMessageForUser(ComponentName admin, int userHandle, String message) { mSupportMessagesMap.put(Objects.hash(admin, userHandle), message); } Loading Loading @@ -137,6 +146,7 @@ public class ShadowDevicePolicyManager extends org.robolectric.shadows.ShadowDev public static ShadowDevicePolicyManager getShadow() { return (ShadowDevicePolicyManager) Shadow.extract( RuntimeEnvironment.application.getSystemService(DevicePolicyManager.class)); ApplicationProvider.getApplicationContext() .getSystemService(DevicePolicyManager.class)); } }