Merge "Make work profile sounds section as its own entry" into sc-dev

This commit is contained in:
Yanting Yang
2021-03-31 08:08:41 +00:00
committed by Android (Google) Code Review
12 changed files with 991 additions and 50 deletions

View File

@@ -8449,7 +8449,7 @@
<string name="work_use_personal_sounds_title">Use personal profile sounds</string>
<!-- Work Sound: Summary for the switch that enables syncing of personal ringtones to work profile. [CHAR LIMIT=160] -->
<string name="work_use_personal_sounds_summary">Sounds are the same for work and personal profiles</string>
<string name="work_use_personal_sounds_summary">Use the same sounds as your personal profile</string>
<!-- Work Sounds: Title for the option defining the work phone ringtone. [CHAR LIMIT=60] -->
<string name="work_ringtone_title">Work phone ringtone</string>
@@ -8464,13 +8464,13 @@
<string name="work_sound_same_as_personal">Same as personal profile</string>
<!-- Work Sound: Title for dialog shown when enabling sync with personal sounds. [CHAR LIMIT=60] -->
<string name="work_sync_dialog_title">Replace sounds?</string>
<string name="work_sync_dialog_title">Use personal profile sounds?</string>
<!-- Work Sound: Confirm action text for dialog shown when overriding work notification sounds with personal sounds. [CHAR LIMIT=30] -->
<string name="work_sync_dialog_yes">Replace</string>
<string name="work_sync_dialog_yes">Confirm</string>
<!-- Work Sound: Message for dialog shown when using the same sounds for work events as for personal events (notifications / ringtones / alarms). [CHAR LIMIT=none] -->
<string name="work_sync_dialog_message">Your personal profile sounds will be used for your work profile</string>
<string name="work_sync_dialog_message">Your work profile will use the same sounds as your personal profile</string>
<!-- Sound installation: Title for the dialog to confirm that a new sound will be installed to the Ringtones, Notifications, or Alarms folder. [CHAR LIMIT=50] -->
<string name="ringtones_install_custom_sound_title">Add custom sound?</string>

View File

@@ -212,43 +212,10 @@
android:summary="%s"
android:order="-10"/>
<!-- TODO(b/174964721): make this category its own entry -->
<com.android.settings.widget.WorkOnlyCategory
android:key="sound_work_settings_section"
<Preference
android:key="sound_work_settings"
android:title="@string/sound_work_settings"
android:order="100">
<!-- Use the same sounds of the work profile -->
<SwitchPreference
android:key="work_use_personal_sounds"
android:title="@string/work_use_personal_sounds_title"
android:summary="@string/work_use_personal_sounds_summary"
android:disableDependentsState="true"/>
<!-- Work phone ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_ringtone"
android:title="@string/work_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:ringtoneType="ringtone"
android:dependency="work_use_personal_sounds"/>
<!-- Default work notification ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_notification_ringtone"
android:title="@string/work_notification_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:ringtoneType="notification"
android:dependency="work_use_personal_sounds"/>
<!-- Default work alarm ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_alarm_ringtone"
android:title="@string/work_alarm_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:persistent="false"
android:ringtoneType="alarm"
android:dependency="work_use_personal_sounds"/>
</com.android.settings.widget.WorkOnlyCategory>
android:fragment="com.android.settings.notification.SoundWorkSettings"
android:order="100"
settings:controller="com.android.settings.notification.WorkSoundsPreferenceController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/sound_work_settings">
<!-- Use the same sounds of the work profile -->
<SwitchPreference
android:key="work_use_personal_sounds"
android:title="@string/work_use_personal_sounds_title"
android:summary="@string/work_use_personal_sounds_summary"
android:disableDependentsState="true"/>
<!-- Work phone ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_ringtone"
android:title="@string/work_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:ringtoneType="ringtone"
android:dependency="work_use_personal_sounds"/>
<!-- Default work notification ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_notification_ringtone"
android:title="@string/work_notification_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:ringtoneType="notification"
android:dependency="work_use_personal_sounds"/>
<!-- Default work alarm ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_alarm_ringtone"
android:title="@string/work_alarm_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:persistent="false"
android:ringtoneType="alarm"
android:dependency="work_use_personal_sounds"/>
</PreferenceScreen>

View File

@@ -25,6 +25,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.preference.SeekBarVolumizer;
import android.provider.SearchIndexableResource;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
@@ -250,8 +251,11 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult
controllers.add(new AlarmRingtonePreferenceController(context));
controllers.add(new NotificationRingtonePreferenceController(context));
// === Work Sound Settings ===
controllers.add(new WorkSoundPreferenceController(context, fragment, lifecycle));
if (!FeatureFlagUtils.isEnabled(context, FeatureFlags.SILKY_HOME)) {
// TODO(b/174964721): This should be removed when the flag is deprecated.
// === Work Sound Settings ===
controllers.add(new WorkSoundPreferenceController(context, fragment, lifecycle));
}
// === Other Sound Settings ===
final DialPadTonePreferenceController dialPadTonePreferenceController =
@@ -308,15 +312,27 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult
return buildPreferenceControllers(context, null /* fragment */,
null /* lifecycle */);
}
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = FeatureFlagUtils.isEnabled(context, FeatureFlags.SILKY_HOME)
? R.xml.sound_settings_v2 : R.xml.sound_settings;
return Arrays.asList(sir);
}
};
// === Work Sound Settings ===
void enableWorkSync() {
final WorkSoundPreferenceController workSoundController =
use(WorkSoundPreferenceController.class);
if (workSoundController != null) {
workSoundController.enableWorkSync();
// TODO(b/174964721): This should be refined when the flag is deprecated.
if (!FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.SILKY_HOME)) {
final WorkSoundPreferenceController workSoundController =
use(WorkSoundPreferenceController.class);
if (workSoundController != null) {
workSoundController.enableWorkSync();
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2021 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.notification;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.OnActivityResultListener;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
/** Sounds settings for work profile. */
@SearchIndexable
public class SoundWorkSettings extends DashboardFragment implements OnActivityResultListener {
private static final String TAG = "SoundWorkSettings";
private static final int REQUEST_CODE = 200;
private static final String SELECTED_PREFERENCE_KEY = "selected_preference";
private RingtonePreference mRequestPreference;
@Override
public int getMetricsCategory() {
return SettingsEnums.WORK_PROFILE_SOUNDS;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String selectedPreference = savedInstanceState.getString(
SELECTED_PREFERENCE_KEY, /* defaultValue= */ null);
if (!TextUtils.isEmpty(selectedPreference)) {
mRequestPreference = findPreference(selectedPreference);
}
}
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference instanceof RingtonePreference) {
writePreferenceClickMetric(preference);
mRequestPreference = (RingtonePreference) preference;
mRequestPreference.onPrepareRingtonePickerIntent(mRequestPreference.getIntent());
getActivity().startActivityForResultAsUser(
mRequestPreference.getIntent(),
REQUEST_CODE,
/* options= */ null,
UserHandle.of(mRequestPreference.getUserId()));
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mRequestPreference != null) {
mRequestPreference.onActivityResult(requestCode, resultCode, data);
mRequestPreference = null;
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mRequestPreference != null) {
outState.putString(SELECTED_PREFERENCE_KEY, mRequestPreference.getKey());
}
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, /* fragment= */ this, getSettingsLifecycle());
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.sound_work_settings;
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
SoundWorkSettings fragment, Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new SoundWorkSettingsController(context, fragment, lifecycle));
return controllers;
}
static final boolean isSupportWorkProfileSound(Context context) {
// TODO(b/174964721): Feature flag should be removed when silky home launched.
final boolean isSilkyEnabled = FeatureFlagUtils.isEnabled(context,
FeatureFlags.SILKY_HOME);
final AudioHelper audioHelper = new AudioHelper(context);
final boolean hasWorkProfile = audioHelper.getManagedProfileId(
UserManager.get(context)) != UserHandle.USER_NULL;
final boolean shouldShowRingtoneSettings = !audioHelper.isSingleVolume();
return isSilkyEnabled && hasWorkProfile && shouldShowRingtoneSettings;
}
void enableWorkSync() {
final SoundWorkSettingsController soundWorkSettingsController =
use(SoundWorkSettingsController.class);
if (soundWorkSettingsController != null) {
soundWorkSettingsController.enableWorkSync();
}
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.sound_work_settings) {
@Override
protected boolean isPageSearchEnabled(Context context) {
return isSupportWorkProfileSound(context);
}
};
}

View File

@@ -0,0 +1,358 @@
/*
* Copyright (C) 2021 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.notification;
import android.annotation.UserIdInt;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.DefaultRingtonePreference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
/** Controller that manages the Sounds settings relevant preferences for work profile. */
public class SoundWorkSettingsController extends AbstractPreferenceController
implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
private static final String TAG = "SoundWorkSettingsController";
private static final String KEY_WORK_USE_PERSONAL_SOUNDS = "work_use_personal_sounds";
private static final String KEY_WORK_PHONE_RINGTONE = "work_ringtone";
private static final String KEY_WORK_NOTIFICATION_RINGTONE = "work_notification_ringtone";
private static final String KEY_WORK_ALARM_RINGTONE = "work_alarm_ringtone";
private final boolean mVoiceCapable;
private final UserManager mUserManager;
private final SoundWorkSettings mParent;
private final AudioHelper mHelper;
private TwoStatePreference mWorkUsePersonalSounds;
private Preference mWorkPhoneRingtonePreference;
private Preference mWorkNotificationRingtonePreference;
private Preference mWorkAlarmRingtonePreference;
private PreferenceScreen mScreen;
@UserIdInt
private int mManagedProfileId;
private final BroadcastReceiver mManagedProfileReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int userId = ((UserHandle) intent.getExtra(Intent.EXTRA_USER)).getIdentifier();
switch (intent.getAction()) {
case Intent.ACTION_MANAGED_PROFILE_ADDED: {
onManagedProfileAdded(userId);
return;
}
case Intent.ACTION_MANAGED_PROFILE_REMOVED: {
onManagedProfileRemoved(userId);
return;
}
}
}
};
public SoundWorkSettingsController(Context context, SoundWorkSettings parent,
Lifecycle lifecycle) {
this(context, parent, lifecycle, new AudioHelper(context));
}
@VisibleForTesting
SoundWorkSettingsController(Context context, SoundWorkSettings parent, Lifecycle lifecycle,
AudioHelper helper) {
super(context);
mUserManager = UserManager.get(context);
mVoiceCapable = Utils.isVoiceCapable(mContext);
mParent = parent;
mHelper = helper;
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mScreen = screen;
}
@Override
public void onResume() {
IntentFilter managedProfileFilter = new IntentFilter();
managedProfileFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
managedProfileFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mContext.registerReceiver(mManagedProfileReceiver, managedProfileFilter);
mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
updateWorkPreferences();
}
@Override
public void onPause() {
mContext.unregisterReceiver(mManagedProfileReceiver);
}
@Override
public boolean isAvailable() {
return mHelper.getManagedProfileId(mUserManager) != UserHandle.USER_NULL
&& shouldShowRingtoneSettings();
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
return false;
}
@Override
public String getPreferenceKey() {
return null;
}
/**
* Updates the summary of work preferences
*
* This controller listens to changes on the work ringtone preferences, identified by keys
* "work_ringtone", "work_notification_ringtone" and "work_alarm_ringtone".
*/
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
int ringtoneType;
if (KEY_WORK_PHONE_RINGTONE.equals(preference.getKey())) {
ringtoneType = RingtoneManager.TYPE_RINGTONE;
} else if (KEY_WORK_NOTIFICATION_RINGTONE.equals(preference.getKey())) {
ringtoneType = RingtoneManager.TYPE_NOTIFICATION;
} else if (KEY_WORK_ALARM_RINGTONE.equals(preference.getKey())) {
ringtoneType = RingtoneManager.TYPE_ALARM;
} else {
return true;
}
preference.setSummary(updateRingtoneName(getManagedProfileContext(), ringtoneType));
return true;
}
private boolean shouldShowRingtoneSettings() {
return !mHelper.isSingleVolume();
}
private CharSequence updateRingtoneName(Context context, int type) {
if (context == null || !mHelper.isUserUnlocked(mUserManager, context.getUserId())) {
return mContext.getString(R.string.managed_profile_not_available_label);
}
Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
return Ringtone.getTitle(context, ringtoneUri, false /* followSettingsUri */,
/* allowRemote= */ true);
}
private Context getManagedProfileContext() {
if (mManagedProfileId == UserHandle.USER_NULL) {
return null;
}
return mHelper.createPackageContextAsUser(mManagedProfileId);
}
private DefaultRingtonePreference initWorkPreference(PreferenceGroup root, String key) {
final DefaultRingtonePreference pref = root.findPreference(key);
pref.setOnPreferenceChangeListener(this);
// Required so that RingtonePickerActivity lists the work profile ringtones
pref.setUserId(mManagedProfileId);
return pref;
}
private void updateWorkPreferences() {
if (!isAvailable()) {
return;
}
if (mWorkUsePersonalSounds == null) {
mWorkUsePersonalSounds = mScreen.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS);
mWorkUsePersonalSounds.setOnPreferenceChangeListener((Preference p, Object value) -> {
if ((boolean) value) {
SoundWorkSettingsController.UnifyWorkDialogFragment.show(mParent);
return false;
} else {
disableWorkSync();
return true;
}
});
}
if (mWorkPhoneRingtonePreference == null) {
mWorkPhoneRingtonePreference = initWorkPreference(mScreen,
KEY_WORK_PHONE_RINGTONE);
}
if (mWorkNotificationRingtonePreference == null) {
mWorkNotificationRingtonePreference = initWorkPreference(mScreen,
KEY_WORK_NOTIFICATION_RINGTONE);
}
if (mWorkAlarmRingtonePreference == null) {
mWorkAlarmRingtonePreference = initWorkPreference(mScreen,
KEY_WORK_ALARM_RINGTONE);
}
if (!mVoiceCapable) {
mWorkPhoneRingtonePreference.setVisible(false);
mWorkPhoneRingtonePreference = null;
}
final Context managedProfileContext = getManagedProfileContext();
if (Settings.Secure.getIntForUser(managedProfileContext.getContentResolver(),
Settings.Secure.SYNC_PARENT_SOUNDS, /* def= */ 0, mManagedProfileId) == 1) {
enableWorkSyncSettings();
} else {
disableWorkSyncSettings();
}
}
void enableWorkSync() {
RingtoneManager.enableSyncFromParent(getManagedProfileContext());
enableWorkSyncSettings();
}
private void enableWorkSyncSettings() {
mWorkUsePersonalSounds.setChecked(true);
if (mWorkPhoneRingtonePreference != null) {
mWorkPhoneRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
}
mWorkNotificationRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
mWorkAlarmRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
}
private void disableWorkSync() {
RingtoneManager.disableSyncFromParent(getManagedProfileContext());
disableWorkSyncSettings();
}
private void disableWorkSyncSettings() {
if (mWorkPhoneRingtonePreference != null) {
mWorkPhoneRingtonePreference.setEnabled(true);
}
mWorkNotificationRingtonePreference.setEnabled(true);
mWorkAlarmRingtonePreference.setEnabled(true);
updateWorkRingtoneSummaries();
}
private void updateWorkRingtoneSummaries() {
Context managedProfileContext = getManagedProfileContext();
if (mWorkPhoneRingtonePreference != null) {
mWorkPhoneRingtonePreference.setSummary(
updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_RINGTONE));
}
mWorkNotificationRingtonePreference.setSummary(
updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_NOTIFICATION));
mWorkAlarmRingtonePreference.setSummary(
updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_ALARM));
}
/**
* Update work preferences if work profile added.
* @param profileId the profile identifier.
*/
public void onManagedProfileAdded(@UserIdInt int profileId) {
if (mManagedProfileId == UserHandle.USER_NULL) {
mManagedProfileId = profileId;
updateWorkPreferences();
}
}
/**
* Update work preferences if work profile removed.
* @param profileId the profile identifier.
*/
public void onManagedProfileRemoved(@UserIdInt int profileId) {
if (mManagedProfileId == profileId) {
mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
updateWorkPreferences();
}
}
/**
* Dialog to confirm with the user if it's ok to use the personal profile sounds as the work
* profile sounds.
*/
public static class UnifyWorkDialogFragment extends InstrumentedDialogFragment
implements DialogInterface.OnClickListener {
private static final String TAG = "UnifyWorkDialogFragment";
private static final int REQUEST_CODE = 200;
/**
* Show dialog that allows to use the personal profile sounds as the work profile sounds.
* @param parent SoundWorkSettings fragment.
*/
public static void show(SoundWorkSettings parent) {
FragmentManager fm = parent.getFragmentManager();
if (fm.findFragmentByTag(TAG) == null) {
UnifyWorkDialogFragment fragment = new UnifyWorkDialogFragment();
fragment.setTargetFragment(parent, REQUEST_CODE);
fragment.show(fm, TAG);
}
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_UNIFY_SOUND_SETTINGS;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.work_sync_dialog_title)
.setMessage(R.string.work_sync_dialog_message)
.setPositiveButton(R.string.work_sync_dialog_yes,
SoundWorkSettingsController.UnifyWorkDialogFragment.this)
.setNegativeButton(android.R.string.no, /* listener= */ null)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
SoundWorkSettings soundWorkSettings = (SoundWorkSettings) getTargetFragment();
if (soundWorkSettings.isAdded()) {
soundWorkSettings.enableWorkSync();
}
}
}
}

View File

@@ -54,6 +54,9 @@ import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.List;
/**
* TODO(b/183670633): Remove this file when silky flag deprecated.
*/
public class WorkSoundPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, OnPreferenceChangeListener, LifecycleObserver,
OnResume, OnPause {

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2021 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.notification;
import android.content.Context;
import com.android.settings.core.BasePreferenceController;
/** This controller manages the work profile sounds preference. */
public class WorkSoundsPreferenceController extends BasePreferenceController {
public WorkSoundsPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return SoundWorkSettings.isSupportWorkProfileSound(mContext) ? AVAILABLE
: DISABLED_FOR_USER;
}
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright (C) 2021 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.notification;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.TelephonyManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.DefaultRingtonePreference;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
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;
@RunWith(RobolectricTestRunner.class)
public class SoundWorkSettingsControllerTest {
private static final String KEY_WORK_USE_PERSONAL_SOUNDS = "work_use_personal_sounds";
private static final String KEY_WORK_PHONE_RINGTONE = "work_ringtone";
private static final String KEY_WORK_NOTIFICATION_RINGTONE = "work_notification_ringtone";
private static final String KEY_WORK_ALARM_RINGTONE = "work_alarm_ringtone";
@Mock
private Context mContext;
@Mock
private PreferenceScreen mScreen;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private AudioHelper mAudioHelper;
@Mock
private SoundWorkSettings mFragment;
private SoundWorkSettingsController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
when(mScreen.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS))
.thenReturn(mock(TwoStatePreference.class));
when(mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
.thenReturn(mock(DefaultRingtonePreference.class));
when(mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
.thenReturn(mock(DefaultRingtonePreference.class));
when(mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
.thenReturn(mock(DefaultRingtonePreference.class));
mController = new SoundWorkSettingsController(mContext, mFragment, null, mAudioHelper);
}
@Test
public void isAvailable_managedProfileAndNotSingleVolume_shouldReturnTrue() {
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.myUserId());
when(mAudioHelper.isSingleVolume()).thenReturn(false);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_noManagedProfile_shouldReturnFalse() {
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.USER_NULL);
when(mAudioHelper.isSingleVolume()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_singleVolume_shouldReturnFalse() {
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.myUserId());
when(mAudioHelper.isSingleVolume()).thenReturn(true);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void onPreferenceChange_shouldUpdateSummary() {
final Preference preference = mock(Preference.class);
when(preference.getKey()).thenReturn(KEY_WORK_PHONE_RINGTONE);
mController.onPreferenceChange(preference, "hello");
verify(preference).setSummary(nullable(String.class));
}
@Test
public void onResume_noVoiceCapability_shouldHidePhoneRingtone() {
when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
mController = new SoundWorkSettingsController(mContext, mFragment, null, mAudioHelper);
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.myUserId());
when(mAudioHelper.isUserUnlocked(nullable(UserManager.class), anyInt())).thenReturn(true);
when(mAudioHelper.isSingleVolume()).thenReturn(false);
when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
// Precondition: work profile is available.
assertThat(mController.isAvailable()).isTrue();
mController.displayPreference(mScreen);
mController.onResume();
verify((Preference) mScreen.findPreference(KEY_WORK_PHONE_RINGTONE)).setVisible(false);
}
@Test
public void onResume_availableButLocked_shouldRedactPreferences() {
final String notAvailable = "(not available)";
when(mContext.getString(R.string.managed_profile_not_available_label))
.thenReturn(notAvailable);
// Given a device with a managed profile:
when(mAudioHelper.isSingleVolume()).thenReturn(false);
when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.myUserId());
when(mAudioHelper.isUserUnlocked(nullable(UserManager.class), anyInt())).thenReturn(false);
// When resumed:
mController.displayPreference(mScreen);
mController.onResume();
// Sound preferences should explain that the profile isn't available yet.
verify((Preference) mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
.setSummary(eq(notAvailable));
verify((Preference) mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
.setSummary(eq(notAvailable));
verify((Preference) mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
.setSummary(eq(notAvailable));
}
@Test
public void onResume_shouldSetUserIdToPreference() {
final int managedProfileUserId = 10;
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(managedProfileUserId);
when(mAudioHelper.isUserUnlocked(nullable(UserManager.class), anyInt())).thenReturn(true);
when(mAudioHelper.isSingleVolume()).thenReturn(false);
when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
mController.displayPreference(mScreen);
mController.onResume();
verify((RingtonePreference) mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
.setUserId(managedProfileUserId);
verify((RingtonePreference) mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
.setUserId(managedProfileUserId);
verify((RingtonePreference) mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
.setUserId(managedProfileUserId);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2021 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.notification;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.UserHandle;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
import com.android.settings.DefaultRingtonePreference;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
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 org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class SoundWorkSettingsTest {
@Mock
private FragmentActivity mActivity;
@Mock
private MetricsFeatureProvider mMetricsFeatureProvider;
private FakeFeatureFactory mFeatureFactory;
private SoundWorkSettings mFragment;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFragment = spy(new SoundWorkSettings());
when(mFragment.getActivity()).thenReturn(mActivity);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mMetricsFeatureProvider = mFeatureFactory.getMetricsFeatureProvider();
ReflectionHelpers.setField(mFragment, "mMetricsFeatureProvider", mMetricsFeatureProvider);
}
@Test
public void onPreferenceTreeClick_isRingtonePreference_shouldStartActivity() {
final DefaultRingtonePreference ringtonePreference = mock(DefaultRingtonePreference.class);
when(mMetricsFeatureProvider.logClickedPreference(any(Preference.class),
anyInt())).thenReturn(true);
mFragment.onPreferenceTreeClick(ringtonePreference);
verify(mActivity).startActivityForResultAsUser(any(), anyInt(), any(),
any(UserHandle.class));
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2021 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.notification;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.UserHandle;
import android.util.FeatureFlagUtils;
import com.android.settings.core.FeatureFlags;
import com.android.settings.testutils.shadow.ShadowAudioHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@Config(shadows = ShadowAudioHelper.class)
@RunWith(RobolectricTestRunner.class)
public class WorkSoundsPreferenceControllerTest {
private Context mContext;
private WorkSoundsPreferenceController mController;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mController = new WorkSoundsPreferenceController(mContext, "test_key");
}
@After
public void tearDown() {
ShadowAudioHelper.reset();
}
@Test
public void getAvailabilityStatus_supportWorkProfileSound_shouldReturnAvailable() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.SILKY_HOME, true);
ShadowAudioHelper.setIsSingleVolume(false);
ShadowAudioHelper.setManagedProfileId(UserHandle.USER_CURRENT);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_notSupportWorkProfileSound_shouldReturnDisabled() {
ShadowAudioHelper.setIsSingleVolume(true);
ShadowAudioHelper.setManagedProfileId(UserHandle.USER_NULL);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_USER);
}
}

View File

@@ -23,17 +23,35 @@ import com.android.settings.notification.AudioHelper;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(AudioHelper.class)
public class ShadowAudioHelper {
private static boolean sIsSingleVolume = true;
private static int sManagedProfileId = UserHandle.USER_CURRENT;
@Resetter
public static void reset() {
sIsSingleVolume = true;
sManagedProfileId = UserHandle.USER_CURRENT;
}
public static void setIsSingleVolume(boolean isSingleVolume) {
sIsSingleVolume = isSingleVolume;
}
public static void setManagedProfileId(int managedProfileId) {
sManagedProfileId = managedProfileId;
}
@Implementation
protected boolean isSingleVolume() {
return true;
return sIsSingleVolume;
}
@Implementation
protected int getManagedProfileId(UserManager um) {
return UserHandle.USER_CURRENT;
return sManagedProfileId;
}
}