Files
app_Settings/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
Chun-Ku Lin d077ca6dd9 Pass null looper if the looper is not prepared in the calling thread
**Root cause**
The PreferenceController can be constructed by the
SettingsSearchIndexablesProvider where the looper of the thread is not
prepared. This result in not able to construct the PreferenceController
that will be used as part of the SettingsSearch.

Currently, if the SettingsSearchIndexablesProvider is not able to
construct the PreferenceController defined in xml, it would just
silently failed.

Test: atest SettingsUnitTests
Test: atest SettingsRoboTests

Flag: EXEMPT low risk bugfix
Bug: 352622249
Change-Id: I72a4ce24ec6842b9efe067e3cb7d1c73cd98a566
2024-08-29 23:02:34 +00:00

167 lines
6.0 KiB
Java

/*
* 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.accessibility;
import static android.provider.Settings.System.KEYBOARD_VIBRATION_ENABLED;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/**
* A preference controller to turn on/off keyboard vibration state with a single toggle.
*/
public class KeyboardVibrationTogglePreferenceController extends TogglePreferenceController
implements DefaultLifecycleObserver {
private static final String TAG = "KeyboardVibrateControl";
private static final Uri MAIN_VIBRATION_SWITCH_URI =
Settings.System.getUriFor(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY);
private final ContentObserver mContentObserver;
private final Vibrator mVibrator;
@Nullable
private TwoStatePreference mPreference;
private MetricsFeatureProvider mMetricsFeatureProvider;
public KeyboardVibrationTogglePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mVibrator = context.getSystemService(Vibrator.class);
Handler handler = Looper.myLooper() != null ? new Handler(/* async= */ true) : null;
mContentObserver = new ContentObserver(handler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (uri.equals(MAIN_VIBRATION_SWITCH_URI)) {
updateState(mPreference);
} else {
Log.w(TAG, "Unexpected uri change:" + uri);
}
}
};
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
mContext.getContentResolver().registerContentObserver(MAIN_VIBRATION_SWITCH_URI,
/* notifyForDescendants= */ false, mContentObserver);
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public void updateState(@Nullable Preference preference) {
if (preference != null) {
super.updateState(preference);
preference.setEnabled(isPreferenceEnabled());
}
}
@Override
public int getAvailabilityStatus() {
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_keyboardVibrationSettingsSupported)) {
return AVAILABLE;
}
return UNSUPPORTED_ON_DEVICE;
}
@Override
public boolean isChecked() {
// Always unchecked if the preference disabled
return isPreferenceEnabled() && isKeyboardVibrationSwitchEnabled();
}
@Override
public boolean setChecked(boolean isChecked) {
final boolean success = updateKeyboardVibrationSetting(isChecked);
mMetricsFeatureProvider.action(mContext,
SettingsEnums.ACTION_KEYBOARD_VIBRATION_CHANGED, isChecked);
if (success && isChecked) {
// Play the preview vibration effect for the IME feedback when the toggle is on.
VibrationPreferenceConfig.playVibrationPreview(
mVibrator, VibrationAttributes.USAGE_IME_FEEDBACK);
}
return true;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
private boolean isPreferenceEnabled() {
return VibrationPreferenceConfig.isMainVibrationSwitchEnabled(
mContext.getContentResolver());
}
private boolean isKeyboardVibrationSwitchEnabled() {
return Settings.System.getInt(
mContext.getContentResolver(), KEYBOARD_VIBRATION_ENABLED, ON) == ON;
}
private boolean updateKeyboardVibrationSetting(boolean enable) {
final ContentResolver contentResolver = mContext.getContentResolver();
final boolean success = Settings.System.putInt(contentResolver,
KEYBOARD_VIBRATION_ENABLED, enable ? ON : OFF);
contentResolver.notifyChange(Settings.System.getUriFor(KEYBOARD_VIBRATION_ENABLED),
null /* observer */, ContentResolver.NOTIFY_NO_DELAY);
if (!success) {
Log.w(TAG, "Update settings database error!");
}
return success;
}
}