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

Commit 35a0c676 authored by Michael Wright's avatar Michael Wright
Browse files

Add setting to control vibration intensity.

This patch adds two distinct vibration control settings: one for
notifications and ringtones, and one for haptic feedback. Since we don't
always have the exact intent of a given vibration, VibratorService will
do its best to classify each VibrationEffect into one of these two
categories and then scale the vibration accordingly based on the
intensity setting.

Bug: 64185329
Test: cts-tradefed run commandAndExit cts-dev -m CtsOsTestCases -t android.os.cts.VibratorTest
      cts-tradefed run commandAndExit cts-dev -m CtsOsTestCases -t android.os.cts.VibrationEffectTest
Change-Id: If16237f4782281aaab33e4a0f55c29f1a30ac493
parent 5e5c8d77
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1246,7 +1246,7 @@ public final class InputManager {
            int repeat;
            if (effect instanceof VibrationEffect.OneShot) {
                VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
                pattern = new long[] { 0, oneShot.getTiming() };
                pattern = new long[] { 0, oneShot.getDuration() };
                repeat = -1;
            } else if (effect instanceof VibrationEffect.Waveform) {
                VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
+173 −44
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package android.os;

import android.hardware.vibrator.V1_0.Constants.EffectStrength;
import android.hardware.vibrator.V1_1.Constants.Effect_1_1;
import android.util.MathUtils;

import java.util.Arrays;

@@ -35,6 +37,12 @@ public abstract class VibrationEffect implements Parcelable {
     */
    public static final int DEFAULT_AMPLITUDE = -1;

    /**
     * The maximum amplitude value
     * @hide
     */
    public static final int MAX_AMPLITUDE = 255;

    /**
     * A click effect.
     *
@@ -198,38 +206,75 @@ public abstract class VibrationEffect implements Parcelable {
    /** @hide */
    public abstract void validate();

    /**
     * Gets the estimated duration of the vibration in milliseconds.
     *
     * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
     * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
     * the length is device and potentially run-time dependent), this returns -1.
     *
     * @hide
     */
    public abstract long getDuration();

    /**
     * Scale the amplitude with the given constraints.
     *
     * This assumes that the previous value was in the range [0, MAX_AMPLITUDE]
     * @hide
     */
    protected static int scale(int amplitude, float gamma, int maxAmplitude) {
        float val = MathUtils.pow(amplitude / (float) MAX_AMPLITUDE, gamma);
        return (int) (val * maxAmplitude);
    }

    /** @hide */
    public static class OneShot extends VibrationEffect implements Parcelable {
        private long mTiming;
        private int mAmplitude;
        private final long mDuration;
        private final int mAmplitude;

        public OneShot(Parcel in) {
            this(in.readLong(), in.readInt());
            mDuration = in.readLong();
            mAmplitude = in.readInt();
        }

        public OneShot(long milliseconds, int amplitude) {
            mTiming = milliseconds;
            mDuration = milliseconds;
            mAmplitude = amplitude;
        }

        public long getTiming() {
            return mTiming;
        @Override
        public long getDuration() {
            return mDuration;
        }

        public int getAmplitude() {
            return mAmplitude;
        }

        /**
         * Scale the amplitude of this effect.
         *
         * @param gamma the gamma adjustment to apply
         * @param maxAmplitude the new maximum amplitude of the effect
         *
         * @return A {@link OneShot} effect with the same timing but scaled amplitude.
         */
        public VibrationEffect scale(float gamma, int maxAmplitude) {
            int newAmplitude = scale(mAmplitude, gamma, maxAmplitude);
            return new OneShot(mDuration, newAmplitude);
        }

        @Override
        public void validate() {
            if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
                throw new IllegalArgumentException(
                        "amplitude must either be DEFAULT_AMPLITUDE, " +
                        "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
                        "amplitude must either be DEFAULT_AMPLITUDE, "
                        + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
            }
            if (mTiming <= 0) {
            if (mDuration <= 0) {
                throw new IllegalArgumentException(
                        "timing must be positive (timing=" + mTiming + ")");
                        "duration must be positive (duration=" + mDuration + ")");
            }
        }

@@ -239,26 +284,26 @@ public abstract class VibrationEffect implements Parcelable {
                return false;
            }
            VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
            return other.mTiming == mTiming && other.mAmplitude == mAmplitude;
            return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
        }

        @Override
        public int hashCode() {
            int result = 17;
            result = 37 * (int) mTiming;
            result = 37 * mAmplitude;
            result += 37 * (int) mDuration;
            result += 37 * mAmplitude;
            return result;
        }

        @Override
        public String toString() {
            return "OneShot{mTiming=" + mTiming +", mAmplitude=" + mAmplitude + "}";
            return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            out.writeInt(PARCEL_TOKEN_ONE_SHOT);
            out.writeLong(mTiming);
            out.writeLong(mDuration);
            out.writeInt(mAmplitude);
        }

@@ -279,9 +324,9 @@ public abstract class VibrationEffect implements Parcelable {

    /** @hide */
    public static class Waveform extends VibrationEffect implements Parcelable {
        private long[] mTimings;
        private int[] mAmplitudes;
        private int mRepeat;
        private final long[] mTimings;
        private final int[] mAmplitudes;
        private final int mRepeat;

        public Waveform(Parcel in) {
            this(in.createLongArray(), in.createIntArray(), in.readInt());
@@ -307,35 +352,69 @@ public abstract class VibrationEffect implements Parcelable {
            return mRepeat;
        }

        @Override
        public long getDuration() {
            if (mRepeat >= 0) {
                return Long.MAX_VALUE;
            }
            long duration = 0;
            for (long d : mTimings) {
                duration += d;
            }
            return duration;
        }

        /**
         * Scale the Waveform with the given gamma and new max amplitude.
         *
         * @param gamma the gamma adjustment to apply
         * @param maxAmplitude the new maximum amplitude of the effect
         *
         * @return A {@link Waveform} effect with the same timings and repeat index
         *         but scaled amplitude.
         */
        public VibrationEffect scale(float gamma, int maxAmplitude) {
            if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
                // Just return a copy of the original if there's no scaling to be done.
                return new Waveform(mTimings, mAmplitudes, mRepeat);
            }

            int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
            for (int i = 0; i < scaledAmplitudes.length; i++) {
                scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
            }
            return new Waveform(mTimings, scaledAmplitudes, mRepeat);
        }

        @Override
        public void validate() {
            if (mTimings.length != mAmplitudes.length) {
                throw new IllegalArgumentException(
                        "timing and amplitude arrays must be of equal length" +
                        " (timings.length=" + mTimings.length +
                        ", amplitudes.length=" + mAmplitudes.length + ")");
                        "timing and amplitude arrays must be of equal length"
                        + " (timings.length=" + mTimings.length
                        + ", amplitudes.length=" + mAmplitudes.length + ")");
            }
            if (!hasNonZeroEntry(mTimings)) {
                throw new IllegalArgumentException("at least one timing must be non-zero" +
                        " (timings=" + Arrays.toString(mTimings) + ")");
                throw new IllegalArgumentException("at least one timing must be non-zero"
                        + " (timings=" + Arrays.toString(mTimings) + ")");
            }
            for (long timing : mTimings) {
                if (timing < 0) {
                    throw new IllegalArgumentException("timings must all be >= 0" +
                            " (timings=" + Arrays.toString(mTimings) + ")");
                    throw new IllegalArgumentException("timings must all be >= 0"
                            + " (timings=" + Arrays.toString(mTimings) + ")");
                }
            }
            for (int amplitude : mAmplitudes) {
                if (amplitude < -1 || amplitude > 255) {
                    throw new IllegalArgumentException(
                            "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" +
                            " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
                            "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
                            + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
                }
            }
            if (mRepeat < -1 || mRepeat >= mTimings.length) {
                throw new IllegalArgumentException(
                        "repeat index must be within the bounds of the timings array" +
                        " (timings.length=" + mTimings.length + ", index=" + mRepeat +")");
                        "repeat index must be within the bounds of the timings array"
                        + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
            }
        }

@@ -345,26 +424,26 @@ public abstract class VibrationEffect implements Parcelable {
                return false;
            }
            VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
            return Arrays.equals(mTimings, other.mTimings) &&
                Arrays.equals(mAmplitudes, other.mAmplitudes) &&
                mRepeat == other.mRepeat;
            return Arrays.equals(mTimings, other.mTimings)
                && Arrays.equals(mAmplitudes, other.mAmplitudes)
                && mRepeat == other.mRepeat;
        }

        @Override
        public int hashCode() {
            int result = 17;
            result = 37 * Arrays.hashCode(mTimings);
            result = 37 * Arrays.hashCode(mAmplitudes);
            result = 37 * mRepeat;
            result += 37 * Arrays.hashCode(mTimings);
            result += 37 * Arrays.hashCode(mAmplitudes);
            result += 37 * mRepeat;
            return result;
        }

        @Override
        public String toString() {
            return "Waveform{mTimings=" + Arrays.toString(mTimings) +
                ", mAmplitudes=" + Arrays.toString(mAmplitudes) +
                ", mRepeat=" + mRepeat +
                "}";
            return "Waveform{mTimings=" + Arrays.toString(mTimings)
                + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
                + ", mRepeat=" + mRepeat
                + "}";
        }

        @Override
@@ -402,16 +481,20 @@ public abstract class VibrationEffect implements Parcelable {

    /** @hide */
    public static class Prebaked extends VibrationEffect implements Parcelable {
        private int mEffectId;
        private boolean mFallback;
        private final int mEffectId;
        private final boolean mFallback;

        private int mEffectStrength;

        public Prebaked(Parcel in) {
            this(in.readInt(), in.readByte() != 0);
            mEffectStrength = in.readInt();
        }

        public Prebaked(int effectId, boolean fallback) {
            mEffectId = effectId;
            mFallback = fallback;
            mEffectStrength = EffectStrength.MEDIUM;
        }

        public int getId() {
@@ -426,6 +509,39 @@ public abstract class VibrationEffect implements Parcelable {
            return mFallback;
        }

        @Override
        public long getDuration() {
            return -1;
        }

        /**
         * Set the effect strength of the prebaked effect.
         */
        public void setEffectStrength(int strength) {
            if (!isValidEffectStrength(strength)) {
                throw new IllegalArgumentException("Invalid effect strength: " + strength);
            }
            mEffectStrength = strength;
        }

        /**
         * Set the effect strength.
         */
        public int getEffectStrength() {
            return mEffectStrength;
        }

        private static boolean isValidEffectStrength(int strength) {
            switch (strength) {
                case EffectStrength.LIGHT:
                case EffectStrength.MEDIUM:
                case EffectStrength.STRONG:
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public void validate() {
            switch (mEffectId) {
@@ -437,6 +553,10 @@ public abstract class VibrationEffect implements Parcelable {
                    throw new IllegalArgumentException(
                            "Unknown prebaked effect type (value=" + mEffectId + ")");
            }
            if (!isValidEffectStrength(mEffectStrength)) {
                throw new IllegalArgumentException(
                        "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
            }
        }

        @Override
@@ -445,17 +565,25 @@ public abstract class VibrationEffect implements Parcelable {
                return false;
            }
            VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
            return mEffectId == other.mEffectId && mFallback == other.mFallback;
            return mEffectId == other.mEffectId
                && mFallback == other.mFallback
                && mEffectStrength == other.mEffectStrength;
        }

        @Override
        public int hashCode() {
            return mEffectId;
            int result = 17;
            result += 37 * mEffectId;
            result += 37 * mEffectStrength;
            return result;
        }

        @Override
        public String toString() {
            return "Prebaked{mEffectId=" + mEffectId + ", mFallback=" + mFallback + "}";
            return "Prebaked{mEffectId=" + mEffectId
                + ", mEffectStrength=" + mEffectStrength
                + ", mFallback=" + mFallback
                + "}";
        }


@@ -464,6 +592,7 @@ public abstract class VibrationEffect implements Parcelable {
            out.writeInt(PARCEL_TOKEN_EFFECT);
            out.writeInt(mEffectId);
            out.writeByte((byte) (mFallback ? 1 : 0));
            out.writeInt(mEffectStrength);
        }

        public static final Parcelable.Creator<Prebaked> CREATOR =
+54 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.os;

import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.app.ActivityThread;
@@ -23,6 +24,9 @@ import android.content.Context;
import android.media.AudioAttributes;
import android.util.Log;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Class that operates the vibrator on the device.
 * <p>
@@ -33,6 +37,40 @@ import android.util.Log;
public abstract class Vibrator {
    private static final String TAG = "Vibrator";

    /**
     * Vibration intensity: no vibrations.
     * @hide
     */
    public static final int VIBRATION_INTENSITY_OFF = 0;

    /**
     * Vibration intensity: low.
     * @hide
     */
    public static final int VIBRATION_INTENSITY_LOW = 1;

    /**
     * Vibration intensity: medium.
     * @hide
     */
    public static final int VIBRATION_INTENSITY_MEDIUM = 2;

    /**
     * Vibration intensity: high.
     * @hide
     */
    public static final int VIBRATION_INTENSITY_HIGH = 3;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "VIBRATION_INTENSITY_" }, value = {
        VIBRATION_INTENSITY_OFF,
        VIBRATION_INTENSITY_LOW,
        VIBRATION_INTENSITY_MEDIUM,
        VIBRATION_INTENSITY_HIGH
    })
    public @interface VibrationIntensity{}

    private final String mPackageName;

    /**
@@ -49,6 +87,22 @@ public abstract class Vibrator {
        mPackageName = context.getOpPackageName();
    }

    /**
     * Get the default vibration intensity for haptic feedback.
     * @hide
     */
    public int getDefaultHapticFeedbackIntensity() {
        return VIBRATION_INTENSITY_MEDIUM;
    }

    /**
     * Get the default vibration intensity for notifications and ringtones.
     * @hide
     */
    public int getDefaultNotificationVibrationIntensity() {
        return VIBRATION_INTENSITY_HIGH;
    }

    /**
     * Check whether the hardware has a vibrator.
     *
+42 −1
Original line number Diff line number Diff line
@@ -3174,6 +3174,43 @@ public final class Settings {

        private static final Validator VIBRATE_INPUT_DEVICES_VALIDATOR = BOOLEAN_VALIDATOR;

        /**
         * The intensity of notification vibrations, if configurable.
         *
         * Not all devices are capable of changing their vibration intensity; on these devices
         * there will likely be no difference between the various vibration intensities except for
         * intensity 0 (off) and the rest.
         *
         * <b>Values:</b><br/>
         * 0 - Vibration is disabled<br/>
         * 1 - Weak vibrations<br/>
         * 2 - Medium vibrations<br/>
         * 3 - Strong vibrations
         * @hide
         */
        public static final String NOTIFICATION_VIBRATION_INTENSITY =
                "notification_vibration_intensity";

        /**
         * The intensity of haptic feedback vibrations, if configurable.
         *
         * Not all devices are capable of changing their feedback intensity; on these devices
         * there will likely be no difference between the various vibration intensities except for
         * intensity 0 (off) and the rest.
         *
         * <b>Values:</b><br/>
         * 0 - Vibration is disabled<br/>
         * 1 - Weak vibrations<br/>
         * 2 - Medium vibrations<br/>
         * 3 - Strong vibrations
         * @hide
         */
        public static final String HAPTIC_FEEDBACK_INTENSITY =
                "haptic_feedback_intensity";

        private static final Validator VIBRATION_INTENSITY_VALIDATOR =
                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);

        /**
         * Ringer volume. This is used internally, changing this value will not
         * change the volume. See AudioManager.
@@ -3995,7 +4032,9 @@ public final class Settings {
            LOCK_TO_APP_ENABLED,
            NOTIFICATION_SOUND,
            ACCELEROMETER_ROTATION,
            SHOW_BATTERY_PERCENT
            SHOW_BATTERY_PERCENT,
            NOTIFICATION_VIBRATION_INTENSITY,
            HAPTIC_FEEDBACK_INTENSITY,
        };

        /**
@@ -4136,6 +4175,8 @@ public final class Settings {
            VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR);
            VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR);
            VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR);
            VALIDATORS.put(NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
            VALIDATORS.put(HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
            VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR);
            VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR);
            VALIDATORS.put(ALARM_ALERT, ALARM_ALERT_VALIDATOR);
+261 −100

File changed.

Preview size limit exceeded, changes collapsed.