Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 49200de8 authored by Yeabkal Wubshit's avatar Yeabkal Wubshit Committed by Android (Google) Code Review
Browse files

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

parents a25fb65a d95c41af
Loading
Loading
Loading
Loading
+11 −147
Original line number Diff line number Diff line
@@ -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);
+194 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+17 −1
Original line number Diff line number Diff line
@@ -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);
    }