1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.accessibility;
18 
19 import android.content.Context;
20 import android.database.ContentObserver;
21 import android.media.AudioManager;
22 import android.net.Uri;
23 import android.os.Handler;
24 import android.os.Vibrator;
25 import android.provider.DeviceConfig;
26 import android.provider.Settings;
27 
28 import androidx.preference.Preference;
29 import androidx.preference.PreferenceScreen;
30 
31 import com.android.settings.R;
32 import com.android.settings.Utils;
33 import com.android.settings.core.TogglePreferenceController;
34 import com.android.settingslib.core.lifecycle.LifecycleObserver;
35 import com.android.settingslib.core.lifecycle.events.OnStart;
36 import com.android.settingslib.core.lifecycle.events.OnStop;
37 
38 /**
39  * Preference controller for the ramping ringer setting key, controlled via {@link AudioManager}.
40  *
41  * <p>This preference depends on the {@link Settings.System#RING_VIBRATION_INTENSITY}, and it will
42  * be disabled and display the unchecked state when the ring intensity is set to OFF. The actual
43  * ramping ringer setting will not be overwritten when the ring intensity is turned off, so the
44  * user original value will be naturally restored when the ring intensity is enabled again.
45  */
46 public class VibrationRampingRingerTogglePreferenceController
47         extends TogglePreferenceController implements LifecycleObserver, OnStart, OnStop {
48 
49     /** Wrapper around static {@link DeviceConfig} accessor for testing. */
50     protected static class DeviceConfigProvider {
isRampingRingerEnabledOnTelephonyConfig()51         public boolean isRampingRingerEnabledOnTelephonyConfig() {
52             return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TELEPHONY,
53                     "ramping_ringer_enabled", false);
54         }
55     }
56 
57     private final DeviceConfigProvider mDeviceConfigProvider;
58     private final ContentObserver mSettingObserver;
59     private final AudioManager mAudioManager;
60     private final VibrationPreferenceConfig mRingVibrationPreferenceConfig;
61     private final VibrationPreferenceConfig.SettingObserver mRingSettingObserver;
62 
63     private Preference mPreference;
64 
VibrationRampingRingerTogglePreferenceController(Context context, String preferenceKey)65     public VibrationRampingRingerTogglePreferenceController(Context context, String preferenceKey) {
66         this(context, preferenceKey, new DeviceConfigProvider());
67     }
68 
VibrationRampingRingerTogglePreferenceController(Context context, String preferenceKey, DeviceConfigProvider deviceConfigProvider)69     protected VibrationRampingRingerTogglePreferenceController(Context context,
70             String preferenceKey, DeviceConfigProvider deviceConfigProvider) {
71         super(context, preferenceKey);
72         mDeviceConfigProvider = deviceConfigProvider;
73         mAudioManager = context.getSystemService(AudioManager.class);
74         mRingVibrationPreferenceConfig = new RingVibrationPreferenceConfig(context);
75         mRingSettingObserver = new VibrationPreferenceConfig.SettingObserver(
76                 mRingVibrationPreferenceConfig);
77         mSettingObserver = new ContentObserver(new Handler(/* async= */ true)) {
78             @Override
79             public void onChange(boolean selfChange, Uri uri) {
80                 updateState(mPreference);
81             }
82         };
83     }
84 
85     @Override
getAvailabilityStatus()86     public int getAvailabilityStatus() {
87         final boolean rampingRingerEnabledOnTelephonyConfig =
88                 mDeviceConfigProvider.isRampingRingerEnabledOnTelephonyConfig();
89         return (Utils.isVoiceCapable(mContext) && !rampingRingerEnabledOnTelephonyConfig)
90                 ? AVAILABLE
91                 : UNSUPPORTED_ON_DEVICE;
92     }
93 
94     @Override
onStart()95     public void onStart() {
96         mRingSettingObserver.register(mContext);
97         mContext.getContentResolver().registerContentObserver(
98                 Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER),
99                 /* notifyForDescendants= */ false,
100                 mSettingObserver);
101     }
102 
103     @Override
onStop()104     public void onStop() {
105         mRingSettingObserver.unregister(mContext);
106         mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
107     }
108 
109     @Override
displayPreference(PreferenceScreen screen)110     public void displayPreference(PreferenceScreen screen) {
111         super.displayPreference(screen);
112         mPreference = screen.findPreference(getPreferenceKey());
113         mRingSettingObserver.onDisplayPreference(this, mPreference);
114         mPreference.setEnabled(isRingVibrationEnabled());
115     }
116 
117     @Override
isChecked()118     public boolean isChecked() {
119         return isRingVibrationEnabled() && mAudioManager.isRampingRingerEnabled();
120     }
121 
122     @Override
setChecked(boolean isChecked)123     public boolean setChecked(boolean isChecked) {
124         if (isRingVibrationEnabled()) {
125             // Don't update ramping ringer setting value if ring vibration is disabled.
126             mAudioManager.setRampingRingerEnabled(isChecked);
127 
128             if (isChecked) {
129                 // Vibrate when toggle is enabled for consistency with all the other toggle/slides
130                 // in the same screen.
131                 mRingVibrationPreferenceConfig.playVibrationPreview();
132             }
133         }
134         return true;
135     }
136 
137     @Override
getSliceHighlightMenuRes()138     public int getSliceHighlightMenuRes() {
139         return R.string.menu_key_accessibility;
140     }
141 
142     @Override
updateState(Preference preference)143     public void updateState(Preference preference) {
144         super.updateState(preference);
145         if (preference != null) {
146             preference.setEnabled(isRingVibrationEnabled());
147         }
148     }
149 
isRingVibrationEnabled()150     private boolean isRingVibrationEnabled() {
151         return mRingVibrationPreferenceConfig.isPreferenceEnabled()
152                 && (mRingVibrationPreferenceConfig.readIntensity()
153                 != Vibrator.VIBRATION_INTENSITY_OFF);
154     }
155 }
156