Merge "Move Haptic Feedback Vibration Determination Logic to Helper Class" into main

This commit is contained in:
Yeabkal Wubshit
2023-07-19 18:34:09 +00:00
committed by Android (Google) Code Review
3 changed files with 222 additions and 148 deletions

View File

@@ -208,7 +208,6 @@ import com.android.internal.policy.LogDecelerateInterpolator;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.AccessibilityManagerInternal;
@@ -228,6 +227,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vibrator.HapticFeedbackVibrationProvider;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -371,12 +371,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final String TALKBACK_LABEL = "TalkBack";
private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
/**
* Keyguard stuff
@@ -450,6 +444,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
PackageManager mPackageManager;
SideFpsEventHandler mSideFpsEventHandler;
LockPatternUtils mLockPatternUtils;
private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -458,9 +453,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Assigned on main thread, accessed on UI thread
volatile VrManagerInternal mVrManagerInternal;
// Vibrator pattern for haptic feedback during boot when safe mode is enabled.
long[] mSafeModeEnabledVibePattern;
/** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
boolean mEnableShiftMenuBugReports = false;
@@ -558,7 +550,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mPowerVolUpBehavior;
boolean mStylusButtonsEnabled = true;
boolean mHasSoftInput = false;
boolean mHapticTextHandleEnabled;
boolean mUseTvRouting;
boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
MetricsLogger mLogger;
@@ -2251,9 +2242,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
mHapticTextHandleEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
@@ -2294,8 +2282,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mContext.registerReceiver(mMultiuserReceiver, filter);
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_safeModeEnabledVibePattern);
mHapticFeedbackVibrationProvider =
new HapticFeedbackVibrationProvider(mContext.getResources(), mVibrator);
mGlobalKeyManager = new GlobalKeyManager(mContext);
@@ -5499,10 +5487,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
static long[] getLongIntArray(Resources r, int resid) {
return ArrayUtils.convertToLongArray(r.getIntArray(resid));
}
private void bindKeyguard() {
synchronized (mLock) {
if (mKeyguardBound) {
@@ -5989,138 +5973,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (!mVibrator.hasVibrator()) {
return false;
}
VibrationEffect effect = getVibrationEffect(effectId);
VibrationEffect effect =
mHapticFeedbackVibrationProvider.getVibrationForHapticFeedback(effectId);
if (effect == null) {
return false;
}
VibrationAttributes attrs = getVibrationAttributes(effectId);
if (always) {
attrs = new VibrationAttributes.Builder(attrs)
.setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
.build();
}
VibrationAttributes attrs =
mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
effectId, /* bypassVibrationIntensitySetting= */ always);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
}
private VibrationEffect getVibrationEffect(int effectId) {
long[] pattern;
switch (effectId) {
case HapticFeedbackConstants.CONTEXT_CLICK:
case HapticFeedbackConstants.GESTURE_END:
case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
case HapticFeedbackConstants.SCROLL_TICK:
case HapticFeedbackConstants.SEGMENT_TICK:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
if (!mHapticTextHandleEnabled) {
return null;
}
// fallthrough
case HapticFeedbackConstants.CLOCK_TICK:
case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
case HapticFeedbackConstants.KEYBOARD_RELEASE:
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
case HapticFeedbackConstants.ENTRY_BUMP:
case HapticFeedbackConstants.DRAG_CROSSING:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
case HapticFeedbackConstants.VIRTUAL_KEY:
case HapticFeedbackConstants.EDGE_RELEASE:
case HapticFeedbackConstants.CALENDAR_DATE:
case HapticFeedbackConstants.CONFIRM:
case HapticFeedbackConstants.GESTURE_START:
case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
case HapticFeedbackConstants.SCROLL_LIMIT:
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.LONG_PRESS:
case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
case HapticFeedbackConstants.DRAG_START:
case HapticFeedbackConstants.EDGE_SQUEEZE:
return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
case HapticFeedbackConstants.REJECT:
return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
case HapticFeedbackConstants.SAFE_MODE_ENABLED:
pattern = mSafeModeEnabledVibePattern;
break;
case HapticFeedbackConstants.ASSISTANT_BUTTON:
if (mVibrator.areAllPrimitivesSupported(
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
VibrationEffect.Composition.PRIMITIVE_TICK)) {
// quiet ramp, short pause, then sharp tick
return VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
.compose();
}
// fallback for devices without composition support
return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
return getScaledPrimitiveOrElseEffect(
VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
VibrationEffect.EFFECT_TEXTURE_TICK);
case HapticFeedbackConstants.TOGGLE_ON:
return getScaledPrimitiveOrElseEffect(
VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
VibrationEffect.EFFECT_TICK);
case HapticFeedbackConstants.TOGGLE_OFF:
return getScaledPrimitiveOrElseEffect(
VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
VibrationEffect.EFFECT_TEXTURE_TICK);
case HapticFeedbackConstants.NO_HAPTICS:
default:
return null;
}
if (pattern.length == 0) {
// No vibration
return null;
} else if (pattern.length == 1) {
// One-shot vibration
return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
} else {
// Pattern vibration
return VibrationEffect.createWaveform(pattern, -1);
}
}
private VibrationEffect getScaledPrimitiveOrElseEffect(int primitiveId, float scale,
int elseEffectId) {
if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
return VibrationEffect.startComposition()
.addPrimitive(primitiveId, scale)
.compose();
} else {
return VibrationEffect.get(elseEffectId);
}
}
private VibrationAttributes getVibrationAttributes(int effectId) {
switch (effectId) {
case HapticFeedbackConstants.EDGE_SQUEEZE:
case HapticFeedbackConstants.EDGE_RELEASE:
return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
case HapticFeedbackConstants.ASSISTANT_BUTTON:
case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
case HapticFeedbackConstants.SCROLL_TICK:
case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
case HapticFeedbackConstants.SCROLL_LIMIT:
return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
default:
return TOUCH_VIBRATION_ATTRIBUTES;
}
}
@Override
public void keepScreenOnStartedLw() {
@@ -6258,8 +6122,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
pw.print(prefix);
pw.print("mHasSoftInput="); pw.print(mHasSoftInput);
pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
pw.print("mHasSoftInput="); pw.println(mHasSoftInput);
pw.print(prefix);
pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed);
pw.print(" mIncallPowerBehavior=");
@@ -6284,6 +6147,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
mHapticFeedbackVibrationProvider.dump(prefix, pw);
mGlobalKeyManager.dump(prefix, pw);
mKeyCombinationManager.dump(prefix, pw);
mSingleKeyGestureDetector.dump(prefix, pw);

View File

@@ -0,0 +1,194 @@
/*
* Copyright 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.server.vibrator;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.view.HapticFeedbackConstants;
import java.io.PrintWriter;
/**
* Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback.
*
* @hide
*/
public final class HapticFeedbackVibrationProvider {
private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
private final Vibrator mVibrator;
private final boolean mHapticTextHandleEnabled;
// Vibrator effect for haptic feedback during boot when safe mode is enabled.
private final VibrationEffect mSafeModeEnabledVibrationEffect;
/** @hide */
public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
mVibrator = vibrator;
mHapticTextHandleEnabled = res.getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
mSafeModeEnabledVibrationEffect =
VibrationSettings.createEffectFromResource(
res, com.android.internal.R.array.config_safeModeEnabledVibePattern);
}
/**
* Provides the {@link VibrationEffect} for a given haptic feedback effect ID (provided in
* {@link HapticFeedbackConstants}).
*
* @param effectId the haptic feedback effect ID whose respective vibration we want to get.
* @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if
* the provided effect ID is not supported.
*/
@Nullable public VibrationEffect getVibrationForHapticFeedback(int effectId) {
switch (effectId) {
case HapticFeedbackConstants.CONTEXT_CLICK:
case HapticFeedbackConstants.GESTURE_END:
case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
case HapticFeedbackConstants.SCROLL_TICK:
case HapticFeedbackConstants.SEGMENT_TICK:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
if (!mHapticTextHandleEnabled) {
return null;
}
// fallthrough
case HapticFeedbackConstants.CLOCK_TICK:
case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
case HapticFeedbackConstants.KEYBOARD_RELEASE:
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
case HapticFeedbackConstants.ENTRY_BUMP:
case HapticFeedbackConstants.DRAG_CROSSING:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
case HapticFeedbackConstants.VIRTUAL_KEY:
case HapticFeedbackConstants.EDGE_RELEASE:
case HapticFeedbackConstants.CALENDAR_DATE:
case HapticFeedbackConstants.CONFIRM:
case HapticFeedbackConstants.GESTURE_START:
case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
case HapticFeedbackConstants.SCROLL_LIMIT:
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.LONG_PRESS:
case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
case HapticFeedbackConstants.DRAG_START:
case HapticFeedbackConstants.EDGE_SQUEEZE:
return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
case HapticFeedbackConstants.REJECT:
return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
case HapticFeedbackConstants.SAFE_MODE_ENABLED:
return mSafeModeEnabledVibrationEffect;
case HapticFeedbackConstants.ASSISTANT_BUTTON:
if (mVibrator.areAllPrimitivesSupported(
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
VibrationEffect.Composition.PRIMITIVE_TICK)) {
// quiet ramp, short pause, then sharp tick
return VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
.compose();
}
// fallback for devices without composition support
return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
return getScaledPrimitiveOrElseEffect(
VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
VibrationEffect.EFFECT_TEXTURE_TICK);
case HapticFeedbackConstants.TOGGLE_ON:
return getScaledPrimitiveOrElseEffect(
VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
VibrationEffect.EFFECT_TICK);
case HapticFeedbackConstants.TOGGLE_OFF:
return getScaledPrimitiveOrElseEffect(
VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
VibrationEffect.EFFECT_TEXTURE_TICK);
case HapticFeedbackConstants.NO_HAPTICS:
default:
return null;
}
}
/**
* Provides the {@link VibrationAttributes} that should be used for a haptic feedback.
*
* @param effectId the haptic feedback effect ID whose respective vibration attributes we want
* to get.
* @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass
* vibration intensity settings. {@code false} otherwise.
* @return the {@link VibrationAttributes} that should be used for the provided haptic feedback.
*/
public VibrationAttributes getVibrationAttributesForHapticFeedback(
int effectId, boolean bypassVibrationIntensitySetting) {
VibrationAttributes attrs;
switch (effectId) {
case HapticFeedbackConstants.EDGE_SQUEEZE:
case HapticFeedbackConstants.EDGE_RELEASE:
attrs = PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
break;
case HapticFeedbackConstants.ASSISTANT_BUTTON:
case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
case HapticFeedbackConstants.SCROLL_TICK:
case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
case HapticFeedbackConstants.SCROLL_LIMIT:
attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
break;
default:
attrs = TOUCH_VIBRATION_ATTRIBUTES;
}
if (bypassVibrationIntensitySetting) {
attrs = new VibrationAttributes.Builder(attrs)
.setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
.build();
}
return attrs;
}
/** Dumps relevant state. */
public void dump(String prefix, PrintWriter pw) {
pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
}
private VibrationEffect getScaledPrimitiveOrElseEffect(
int primitiveId, float scale, int elseEffectId) {
if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
return VibrationEffect.startComposition()
.addPrimitive(primitiveId, scale)
.compose();
} else {
return VibrationEffect.get(elseEffectId);
}
}
}

View File

@@ -698,7 +698,23 @@ final class VibrationSettings {
@Nullable
private VibrationEffect createEffectFromResource(int resId) {
long[] timings = getLongIntArray(mContext.getResources(), resId);
return createEffectFromResource(mContext.getResources(), resId);
}
/**
* Provides a {@link VibrationEffect} from a timings-array provided as an int-array resource..
*
* <p>If the timings array is {@code null} or empty, it returns {@code null}.
*
* <p>If the timings array has a size of one, it returns a one-shot vibration with duration that
* is equal to the single value in the array.
*
* <p>If the timings array has more than one values, it returns a non-repeating wave-form
* vibration with off-on timings as per the provided timings array.
*/
@Nullable
static VibrationEffect createEffectFromResource(Resources res, int resId) {
long[] timings = getLongIntArray(res, resId);
return createEffectFromTimings(timings);
}