diff --git a/res/values/strings.xml b/res/values/strings.xml index add226ab8f8..9ce5a389ee2 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5877,12 +5877,8 @@ Add account Work profile isn\u2019t available yet - - Work profile - - Managed by your organization - - Apps and notifications are off + + Work apps Remove work profile @@ -9783,6 +9779,8 @@ Cross-profile calendar Show work events on your personal calendar + + When work apps are off, they’re paused and can’t be accessed or send you notifications Manage storage diff --git a/res/xml/managed_profile_settings.xml b/res/xml/managed_profile_settings.xml index 20f6d3dc3f3..ddfb86987ca 100644 --- a/res/xml/managed_profile_settings.xml +++ b/res/xml/managed_profile_settings.xml @@ -19,10 +19,9 @@ android:key="managed_profile_settings_screen" android:title="@string/managed_profile_settings_title"> - + + \ No newline at end of file diff --git a/src/com/android/settings/accounts/ContactSearchPreferenceController.java b/src/com/android/settings/accounts/ContactSearchPreferenceController.java index 5e4ef48d7c2..87dabd8d9a4 100644 --- a/src/com/android/settings/accounts/ContactSearchPreferenceController.java +++ b/src/com/android/settings/accounts/ContactSearchPreferenceController.java @@ -20,37 +20,38 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settings.Utils; -import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.TogglePreferenceController; import com.android.settings.slices.SliceData; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedSwitchPreference; -public class ContactSearchPreferenceController extends BasePreferenceController implements - Preference.OnPreferenceChangeListener { +import org.jetbrains.annotations.NotNull; - private UserHandle mManagedUser; +public class ContactSearchPreferenceController extends TogglePreferenceController implements + Preference.OnPreferenceChangeListener, DefaultLifecycleObserver, + ManagedProfileQuietModeEnabler.QuietModeChangeListener { + + private final ManagedProfileQuietModeEnabler mQuietModeEnabler; + private final UserHandle mManagedUser; + private Preference mPreference; public ContactSearchPreferenceController(Context context, String key) { super(context, key); - // Set default managed profile for the current user, otherwise isAvailable will be false and - // the setting won't be searchable. - UserManager userManager = context.getSystemService(UserManager.class); - mManagedUser = Utils.getManagedProfile(userManager); - } - - @VisibleForTesting - void setManagedUser(UserHandle managedUser) { - mManagedUser = managedUser; + mManagedUser = Utils.getManagedProfile(context.getSystemService(UserManager.class)); + mQuietModeEnabler = new ManagedProfileQuietModeEnabler(context, this); } @Override public int getAvailabilityStatus() { - return (mManagedUser != null) ? AVAILABLE : DISABLED_FOR_USER; + return mQuietModeEnabler.isAvailable() ? AVAILABLE : DISABLED_FOR_USER; } @Override @@ -59,6 +60,7 @@ public class ContactSearchPreferenceController extends BasePreferenceController if (preference instanceof RestrictedSwitchPreference) { final RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference; pref.setChecked(isChecked()); + pref.setEnabled(!mQuietModeEnabler.isQuietModeEnabled()); if (mManagedUser != null) { final RestrictedLockUtils.EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal.checkIfRemoteContactSearchDisallowed( @@ -68,26 +70,48 @@ public class ContactSearchPreferenceController extends BasePreferenceController } } - private boolean isChecked() { - if (mManagedUser == null) { + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + updateState(mPreference); + } + + @Override + public void onStart(@NotNull LifecycleOwner lifecycleOwner) { + lifecycleOwner.getLifecycle().addObserver(mQuietModeEnabler); + } + + @Override + public void onStop(@NotNull LifecycleOwner lifecycleOwner) { + lifecycleOwner.getLifecycle().removeObserver(mQuietModeEnabler); + } + + @Override + public boolean isChecked() { + if (mManagedUser == null || mQuietModeEnabler.isQuietModeEnabled()) { return false; } return 0 != Settings.Secure.getIntForUser(mContext.getContentResolver(), MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, mManagedUser.getIdentifier()); } - private boolean setChecked(boolean isChecked) { - if (mManagedUser != null) { - final int value = isChecked ? 1 : 0; - Settings.Secure.putIntForUser(mContext.getContentResolver(), - MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, value, mManagedUser.getIdentifier()); + @Override + public boolean setChecked(boolean isChecked) { + if (mManagedUser == null || mQuietModeEnabler.isQuietModeEnabled()) { + return false; } + final int value = isChecked ? 1 : 0; + Settings.Secure.putIntForUser(mContext.getContentResolver(), + MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, value, mManagedUser.getIdentifier()); return true; } @Override - public final boolean onPreferenceChange(Preference preference, Object newValue) { - return setChecked((boolean) newValue); + public void onQuietModeChanged() { + if (mPreference != null) { + updateState(mPreference); + } } @Override @@ -95,4 +119,9 @@ public class ContactSearchPreferenceController extends BasePreferenceController public int getSliceType() { return SliceData.SliceType.SWITCH; } -} \ No newline at end of file + + @Override + public int getSliceHighlightMenuRes() { + return R.string.menu_key_accounts; + } +} diff --git a/src/com/android/settings/accounts/ManagedProfileQuietModeEnabler.java b/src/com/android/settings/accounts/ManagedProfileQuietModeEnabler.java new file mode 100644 index 00000000000..989be09565c --- /dev/null +++ b/src/com/android/settings/accounts/ManagedProfileQuietModeEnabler.java @@ -0,0 +1,122 @@ +/* + * 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.accounts; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; + +import com.android.settings.Utils; + +import javax.annotation.Nullable; + +/** + * A class that controls the managed profile's quiet mode, listens to quiet mode changes and + * modifies managed profile settings. The user facing term for quiet mode is "work apps". + */ +final class ManagedProfileQuietModeEnabler implements DefaultLifecycleObserver { + + private static final String TAG = "QuietModeEnabler"; + private final Context mContext; + private final QuietModeChangeListener mListener; + @Nullable private final UserHandle mManagedProfile; + private final UserManager mUserManager; + + public interface QuietModeChangeListener { + /** Called when quiet mode has changed. */ + void onQuietModeChanged(); + } + + ManagedProfileQuietModeEnabler(Context context, QuietModeChangeListener listener) { + mContext = context; + mListener = listener; + mUserManager = context.getSystemService(UserManager.class); + mManagedProfile = Utils.getManagedProfile(mUserManager); + } + + public void setQuietModeEnabled(boolean enabled) { + if (mManagedProfile != null) { + mUserManager.requestQuietModeEnabled(enabled, mManagedProfile); + } + } + + public boolean isQuietModeEnabled() { + return mManagedProfile != null && mUserManager.isQuietModeEnabled(mManagedProfile); + } + + @Override + public void onStart(@NonNull LifecycleOwner owner) { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); + intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); + mContext.registerReceiver(mReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED); + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) { + mContext.unregisterReceiver(mReceiver); + } + + public boolean isAvailable() { + return (mManagedProfile != null); + } + + private void refreshQuietMode() { + if (mListener != null) { + mListener.onQuietModeChanged(); + } + } + + /** + * Receiver that listens to {@link Intent#ACTION_MANAGED_PROFILE_AVAILABLE} and + * {@link Intent#ACTION_MANAGED_PROFILE_UNAVAILABLE}, and updates the work mode + */ + @VisibleForTesting + final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) { + return; + } + String action = intent.getAction(); + Log.v(TAG, "Received broadcast: " + action); + + if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) + || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { + int intentUserIdentifier = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, + UserHandle.USER_NULL); + if (intentUserIdentifier == mManagedProfile.getIdentifier()) { + refreshQuietMode(); + } else { + Log.w(TAG, "Managed profile broadcast ID: " + intentUserIdentifier + + " does not match managed user: " + mManagedProfile); + } + } else { + Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction()); + } + } + }; +} diff --git a/src/com/android/settings/accounts/WorkModePreferenceController.java b/src/com/android/settings/accounts/WorkModePreferenceController.java index 1941de45c4d..a261afccaa6 100644 --- a/src/com/android/settings/accounts/WorkModePreferenceController.java +++ b/src/com/android/settings/accounts/WorkModePreferenceController.java @@ -1,165 +1,90 @@ /* * Copyright (C) 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 + * 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. + * 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.accounts; -import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_OFF_SUMMARY; -import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_ON_SUMMARY; - -import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; -import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; -import androidx.preference.TwoStatePreference; +import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; -import com.android.settings.Utils; -import com.android.settings.core.BasePreferenceController; import com.android.settings.slices.SliceData; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnStart; -import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settings.widget.SettingsMainSwitchPreferenceController; +import com.android.settingslib.widget.MainSwitchPreference; -public class WorkModePreferenceController extends BasePreferenceController implements - Preference.OnPreferenceChangeListener, LifecycleObserver, OnStart, OnStop { +import org.jetbrains.annotations.NotNull; - private static final String TAG = "WorkModeController"; - private UserManager mUserManager; - private UserHandle mManagedUser; - private DevicePolicyManager mDevicePolicyManager; +public class WorkModePreferenceController extends SettingsMainSwitchPreferenceController + implements Preference.OnPreferenceChangeListener, DefaultLifecycleObserver, + ManagedProfileQuietModeEnabler.QuietModeChangeListener { - private Preference mPreference; - private IntentFilter mIntentFilter; + private final ManagedProfileQuietModeEnabler mQuietModeEnabler; public WorkModePreferenceController(Context context, String key) { super(context, key); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); - mIntentFilter = new IntentFilter(); - mIntentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); - mIntentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); - // Set default managed profile for the current user, otherwise isAvailable will be false and - // the setting won't be searchable. - mManagedUser = Utils.getManagedProfile(mUserManager); - } - - @VisibleForTesting - void setManagedUser(UserHandle managedUser) { - mManagedUser = managedUser; - } - - @Override - public void onStart() { - mContext.registerReceiver(mReceiver, mIntentFilter, - Context.RECEIVER_EXPORTED_UNAUDITED); - } - - @Override - public void onStop() { - mContext.unregisterReceiver(mReceiver); + mQuietModeEnabler = new ManagedProfileQuietModeEnabler(context, this); } @Override public int getAvailabilityStatus() { - return (mManagedUser != null) ? AVAILABLE : DISABLED_FOR_USER; + return (mQuietModeEnabler.isAvailable()) ? AVAILABLE : DISABLED_FOR_USER; } @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - mPreference = screen.findPreference(getPreferenceKey()); + public void onStart(@NotNull LifecycleOwner lifecycleOwner) { + lifecycleOwner.getLifecycle().addObserver(mQuietModeEnabler); } @Override - public CharSequence getSummary() { - if (isChecked()) { - return mDevicePolicyManager.getResources().getString( - WORK_PROFILE_SETTING_ON_SUMMARY, - () -> mContext.getString(R.string.work_mode_on_summary)); - } - - return mDevicePolicyManager.getResources().getString( - WORK_PROFILE_SETTING_OFF_SUMMARY, - () -> mContext.getString(R.string.work_mode_off_summary)); + public void onStop(@NotNull LifecycleOwner lifecycleOwner) { + lifecycleOwner.getLifecycle().removeObserver(mQuietModeEnabler); } - private boolean isChecked() { - boolean isWorkModeOn = false; - if (mUserManager != null && mManagedUser != null) { - isWorkModeOn = !mUserManager.isQuietModeEnabled(mManagedUser); - } - return isWorkModeOn; + @Override + public boolean isChecked() { + return !mQuietModeEnabler.isQuietModeEnabled(); } - private boolean setChecked(boolean isChecked) { - if (mUserManager != null && mManagedUser != null) { - final boolean quietModeEnabled = !isChecked; - mUserManager.requestQuietModeEnabled(quietModeEnabled, mManagedUser); - } + @Override + public boolean setChecked(boolean isChecked) { + mQuietModeEnabler.setQuietModeEnabled(!isChecked); return true; } @Override - public void updateState(Preference preference) { - super.updateState(preference); - if (preference instanceof TwoStatePreference) { - ((TwoStatePreference) preference).setChecked(isChecked()); - } + public void onQuietModeChanged() { + updateState(mSwitchPreference); } - @Override - public final boolean onPreferenceChange(Preference preference, Object newValue) { - return setChecked((boolean) newValue); - } - - /** - * Receiver that listens to {@link Intent#ACTION_MANAGED_PROFILE_AVAILABLE} and - * {@link Intent#ACTION_MANAGED_PROFILE_UNAVAILABLE}, and updates the work mode - */ - @VisibleForTesting - final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent == null) { - return; - } - final String action = intent.getAction(); - Log.v(TAG, "Received broadcast: " + action); - - if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) - || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { - if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, - UserHandle.USER_NULL) == mManagedUser.getIdentifier()) { - updateState(mPreference); - } - return; - } - Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction()); - } - }; - @Override @SliceData.SliceType public int getSliceType() { return SliceData.SliceType.SWITCH; } -} \ No newline at end of file + + @Override + public int getSliceHighlightMenuRes() { + return R.string.menu_key_accounts; + } + + @VisibleForTesting + void setPreference(MainSwitchPreference preference) { + mSwitchPreference = preference; + } +} diff --git a/tests/robotests/src/com/android/settings/accounts/ContactSearchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/ContactSearchPreferenceControllerTest.java index c8606e134a5..bc65563650f 100644 --- a/tests/robotests/src/com/android/settings/accounts/ContactSearchPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accounts/ContactSearchPreferenceControllerTest.java @@ -20,13 +20,19 @@ import static android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SE import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; +import android.content.pm.UserInfo; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; +import androidx.test.core.app.ApplicationProvider; + import com.android.settingslib.RestrictedSwitchPreference; import org.junit.Before; @@ -35,39 +41,51 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; + +import java.util.Collections; @RunWith(RobolectricTestRunner.class) public class ContactSearchPreferenceControllerTest { private static final String PREF_KEY = "contacts_search"; - - @Mock - private UserHandle mManagedUser; + private static final int MANAGED_USER_ID = 10; private Context mContext; private ContactSearchPreferenceController mController; private RestrictedSwitchPreference mPreference; + @Mock + private UserHandle mManagedUser; + @Mock + private UserManager mUserManager; + @Mock + private UserInfo mUserInfo; + @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mController = new ContactSearchPreferenceController(mContext, PREF_KEY); - mController.setManagedUser(mManagedUser); + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); mPreference = spy(new RestrictedSwitchPreference(mContext)); + when(mUserInfo.isManagedProfile()).thenReturn(true); + when(mUserManager.getUserInfo(anyInt())).thenReturn(mUserInfo); + when(mUserManager.getProcessUserId()).thenReturn(0); + when(mUserManager.getUserProfiles()).thenReturn(Collections.singletonList(mManagedUser)); + when(mManagedUser.getIdentifier()).thenReturn(MANAGED_USER_ID); + mController = new ContactSearchPreferenceController(mContext, PREF_KEY); } @Test public void getAvailabilityStatus_noManagedUser_DISABLED() { - mController.setManagedUser(null); + when(mUserManager.getProcessUserId()).thenReturn(MANAGED_USER_ID); + mController = new ContactSearchPreferenceController(mContext, PREF_KEY); + assertThat(mController.getAvailabilityStatus()) .isNotEqualTo(ContactSearchPreferenceController.AVAILABLE); } @Test public void getAvailabilityStatus_hasManagedUser_AVAILABLE() { - mController.setManagedUser(mManagedUser); assertThat(mController.getAvailabilityStatus()) .isEqualTo(ContactSearchPreferenceController.AVAILABLE); } @@ -75,32 +93,96 @@ public class ContactSearchPreferenceControllerTest { @Test public void updateState_shouldRefreshContent() { Settings.Secure.putIntForUser(mContext.getContentResolver(), - MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, mManagedUser.getIdentifier()); + MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, MANAGED_USER_ID); + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isFalse(); Settings.Secure.putIntForUser(mContext.getContentResolver(), - MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, mManagedUser.getIdentifier()); + MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, MANAGED_USER_ID); + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isTrue(); } @Test public void updateState_preferenceShouldBeDisabled() { mController.updateState(mPreference); + verify(mPreference).setDisabledByAdmin(any()); } @Test public void onPreferenceChange_shouldUpdateProviderValue() { mController.onPreferenceChange(mPreference, false); + assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(), - MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, mManagedUser.getIdentifier())) + MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, MANAGED_USER_ID)) .isEqualTo(0); mController.onPreferenceChange(mPreference, true); + assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(), - MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, mManagedUser.getIdentifier())) + MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, MANAGED_USER_ID)) .isEqualTo(1); } -} \ No newline at end of file + + @Test + public void onQuietModeDisabled_preferenceEnabled() { + when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isTrue(); + } + + @Test + public void onQuietModeEnabled_preferenceDisabledAndUnchecked() { + when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isFalse(); + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void afterQuietModeTurnedOnAndOffWhenPreferenceChecked_toggleCheckedAndEnabled() { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 1, MANAGED_USER_ID); + when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isFalse(); + assertThat(mPreference.isChecked()).isFalse(); + + when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isTrue(); + assertThat(mPreference.isChecked()).isTrue(); + } + + @Test + public void afterQuietModeTurnedOnAndOffWhenPreferenceUnchecked_toggleUncheckedAndEnabled() { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0, MANAGED_USER_ID); + when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isFalse(); + assertThat(mPreference.isChecked()).isFalse(); + + when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isTrue(); + assertThat(mPreference.isChecked()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/accounts/ManagedProfileQuietModeEnablerTest.java b/tests/robotests/src/com/android/settings/accounts/ManagedProfileQuietModeEnablerTest.java new file mode 100644 index 00000000000..2698efa4bd8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accounts/ManagedProfileQuietModeEnablerTest.java @@ -0,0 +1,126 @@ +/* + * 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.accounts; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.UserInfo; +import android.os.UserHandle; +import android.os.UserManager; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +import java.util.Collections; + + +@RunWith(RobolectricTestRunner.class) +public class ManagedProfileQuietModeEnablerTest { + private static final int MANAGED_USER_ID = 10; + private Context mContext; + private ManagedProfileQuietModeEnabler mQuietModeEnabler; + private LifecycleOwner mLifecycleOwner = new LifecycleOwner() { + public LifecycleRegistry registry = new LifecycleRegistry(this); + + @Override + public Lifecycle getLifecycle() { + return registry; + } + }; + + @Mock + private ManagedProfileQuietModeEnabler.QuietModeChangeListener mOnQuietModeChangeListener; + @Mock + private UserManager mUserManager; + @Mock + private UserHandle mManagedUser; + @Mock + private UserInfo mUserInfo; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserInfo.isManagedProfile()).thenReturn(true); + when(mUserManager.getUserInfo(anyInt())).thenReturn(mUserInfo); + when(mUserManager.getProcessUserId()).thenReturn(0); + when(mManagedUser.getIdentifier()).thenReturn(MANAGED_USER_ID); + when(mUserManager.getUserProfiles()).thenReturn(Collections.singletonList(mManagedUser)); + mQuietModeEnabler = new ManagedProfileQuietModeEnabler(mContext, + mOnQuietModeChangeListener); + } + + @Test + public void onSetQuietMode_shouldRequestQuietModeEnabled() { + mQuietModeEnabler.setQuietModeEnabled(false); + verify(mUserManager).requestQuietModeEnabled(false, mManagedUser); + mQuietModeEnabler.setQuietModeEnabled(true); + verify(mUserManager).requestQuietModeEnabled(true, mManagedUser); + } + + @Test + public void onIsQuietModeEnabled_shouldCallIsQuietModeEnabled() { + assertThat(mQuietModeEnabler.isQuietModeEnabled()).isEqualTo( + verify(mUserManager).isQuietModeEnabled(any())); + } + + @Test + public void onQuietModeChanged_listenerNotified() { + mQuietModeEnabler.onStart(mLifecycleOwner); + mContext.sendBroadcast(new Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE).putExtra( + Intent.EXTRA_USER_HANDLE, MANAGED_USER_ID)); + mContext.sendBroadcast(new Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE).putExtra( + Intent.EXTRA_USER_HANDLE, MANAGED_USER_ID)); + verify(mOnQuietModeChangeListener, times(2)).onQuietModeChanged(); + } + + @Test + public void onStart_shouldRegisterReceiver() { + mQuietModeEnabler.onStart(mLifecycleOwner); + verify(mContext).registerReceiver(eq(mQuietModeEnabler.mReceiver), any(), anyInt()); + } + + @Test + public void onStop_shouldUnregisterReceiver() { + // register it first + mContext.registerReceiver(mQuietModeEnabler.mReceiver, null, + Context.RECEIVER_EXPORTED/*UNAUDITED*/); + + mQuietModeEnabler.onStop(mLifecycleOwner); + verify(mContext).unregisterReceiver(mQuietModeEnabler.mReceiver); + } +} diff --git a/tests/robotests/src/com/android/settings/accounts/WorkModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/WorkModePreferenceControllerTest.java index 2a283181b1c..e862d108c7c 100644 --- a/tests/robotests/src/com/android/settings/accounts/WorkModePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accounts/WorkModePreferenceControllerTest.java @@ -19,18 +19,18 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; -import androidx.preference.SwitchPreference; +import androidx.test.core.app.ApplicationProvider; -import com.android.settings.R; +import com.android.settingslib.widget.MainSwitchPreference; import org.junit.Before; import org.junit.Test; @@ -38,43 +38,51 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; + +import java.util.Collections; @RunWith(RobolectricTestRunner.class) public class WorkModePreferenceControllerTest { private static final String PREF_KEY = "work_mode"; + private static final int MANAGED_USER_ID = 10; + + private Context mContext; + private WorkModePreferenceController mController; + private MainSwitchPreference mPreference; @Mock private UserManager mUserManager; @Mock private UserHandle mManagedUser; - - private Context mContext; - private WorkModePreferenceController mController; - private SwitchPreference mPreference; + @Mock + private UserInfo mUserInfo; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = spy(RuntimeEnvironment.application); - when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); - + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); + mPreference = new MainSwitchPreference(mContext); + when(mUserInfo.isManagedProfile()).thenReturn(true); + when(mUserManager.getUserInfo(anyInt())).thenReturn(mUserInfo); + when(mUserManager.getProcessUserId()).thenReturn(0); + when(mUserManager.getUserProfiles()).thenReturn(Collections.singletonList(mManagedUser)); + when(mManagedUser.getIdentifier()).thenReturn(MANAGED_USER_ID); mController = new WorkModePreferenceController(mContext, PREF_KEY); - mController.setManagedUser(mManagedUser); - mPreference = new SwitchPreference(mContext); } @Test public void getAvailabilityStatus_noManagedUser_DISABLED() { - mController.setManagedUser(null); + when(mUserManager.getProcessUserId()).thenReturn(MANAGED_USER_ID); + mController = new WorkModePreferenceController(mContext, PREF_KEY); + assertThat(mController.getAvailabilityStatus()) .isNotEqualTo(WorkModePreferenceController.AVAILABLE); } @Test public void getAvailabilityStatus_hasManagedUser_AVAILABLE() { - mController.setManagedUser(mManagedUser); assertThat(mController.getAvailabilityStatus()) .isEqualTo(WorkModePreferenceController.AVAILABLE); } @@ -83,41 +91,29 @@ public class WorkModePreferenceControllerTest { public void updateState_shouldRefreshContent() { when(mUserManager.isQuietModeEnabled(any(UserHandle.class))) .thenReturn(false); + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isTrue(); - assertThat(mPreference.getSummary()) - .isEqualTo(mContext.getText(R.string.work_mode_on_summary)); when(mUserManager.isQuietModeEnabled(any(UserHandle.class))) .thenReturn(true); + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isFalse(); - assertThat(mPreference.getSummary()) - .isEqualTo(mContext.getText(R.string.work_mode_off_summary)); } @Test public void onPreferenceChange_shouldRequestQuietModeEnabled() { + mController.setPreference(mPreference); + mController.onPreferenceChange(mPreference, true); + verify(mUserManager).requestQuietModeEnabled(false, mManagedUser); mController.onPreferenceChange(mPreference, false); + verify(mUserManager).requestQuietModeEnabled(true, mManagedUser); } - - @Test - public void onStart_shouldRegisterReceiver() { - mController.onStart(); - verify(mContext).registerReceiver(eq(mController.mReceiver), any(), anyInt()); - } - - @Test - public void onStop_shouldUnregisterReceiver() { - // register it first - mContext.registerReceiver(mController.mReceiver, null, - Context.RECEIVER_EXPORTED/*UNAUDITED*/); - - mController.onStop(); - verify(mContext).unregisterReceiver(mController.mReceiver); - } } \ No newline at end of file