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

Commit e9273c48 authored by Lais Andrade's avatar Lais Andrade Committed by Android (Google) Code Review
Browse files

Merge "Update VibrationEffect haptic feedback heuristic"

parents 1ddbc0e6 24e96615
Loading
Loading
Loading
Loading
+0 −34
Original line number Diff line number Diff line
@@ -1831,11 +1831,6 @@ package android.os {
    method public int getAudioUsage();
  }

  public static final class VibrationAttributes.Builder {
    ctor public VibrationAttributes.Builder(@NonNull android.media.AudioAttributes, @NonNull android.os.VibrationEffect);
    ctor public VibrationAttributes.Builder(@NonNull android.os.VibrationAttributes, @NonNull android.os.VibrationEffect);
  }

  public abstract class VibrationEffect implements android.os.Parcelable {
    method public static android.os.VibrationEffect get(int);
    method public static android.os.VibrationEffect get(int, boolean);
@@ -1852,13 +1847,9 @@ package android.os {
  }

  public static final class VibrationEffect.Composed extends android.os.VibrationEffect {
    method @NonNull public android.os.VibrationEffect.Composed applyEffectStrength(int);
    method public long getDuration();
    method public int getRepeatIndex();
    method @NonNull public java.util.List<android.os.vibrator.VibrationEffectSegment> getSegments();
    method @NonNull public android.os.VibrationEffect.Composed resolve(int);
    method @NonNull public android.os.VibrationEffect.Composed scale(float);
    method public void validate();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Composed> CREATOR;
  }
@@ -2006,72 +1997,47 @@ package android.os.strictmode {
package android.os.vibrator {

  public final class PrebakedSegment extends android.os.vibrator.VibrationEffectSegment {
    method @NonNull public android.os.vibrator.PrebakedSegment applyEffectStrength(int);
    method public int describeContents();
    method public long getDuration();
    method public int getEffectId();
    method public int getEffectStrength();
    method public boolean hasNonZeroAmplitude();
    method @NonNull public android.os.vibrator.PrebakedSegment resolve(int);
    method @NonNull public android.os.vibrator.PrebakedSegment scale(float);
    method public boolean shouldFallback();
    method public void validate();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.PrebakedSegment> CREATOR;
  }

  public final class PrimitiveSegment extends android.os.vibrator.VibrationEffectSegment {
    method @NonNull public android.os.vibrator.PrimitiveSegment applyEffectStrength(int);
    method public int describeContents();
    method public int getDelay();
    method public long getDuration();
    method public int getPrimitiveId();
    method public float getScale();
    method public boolean hasNonZeroAmplitude();
    method @NonNull public android.os.vibrator.PrimitiveSegment resolve(int);
    method @NonNull public android.os.vibrator.PrimitiveSegment scale(float);
    method public void validate();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.PrimitiveSegment> CREATOR;
  }

  public final class RampSegment extends android.os.vibrator.VibrationEffectSegment {
    method @NonNull public android.os.vibrator.RampSegment applyEffectStrength(int);
    method public int describeContents();
    method public long getDuration();
    method public float getEndAmplitude();
    method public float getEndFrequency();
    method public float getStartAmplitude();
    method public float getStartFrequency();
    method public boolean hasNonZeroAmplitude();
    method @NonNull public android.os.vibrator.RampSegment resolve(int);
    method @NonNull public android.os.vibrator.RampSegment scale(float);
    method public void validate();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.RampSegment> CREATOR;
  }

  public final class StepSegment extends android.os.vibrator.VibrationEffectSegment {
    method @NonNull public android.os.vibrator.StepSegment applyEffectStrength(int);
    method public int describeContents();
    method public float getAmplitude();
    method public long getDuration();
    method public float getFrequency();
    method public boolean hasNonZeroAmplitude();
    method @NonNull public android.os.vibrator.StepSegment resolve(int);
    method @NonNull public android.os.vibrator.StepSegment scale(float);
    method public void validate();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.StepSegment> CREATOR;
  }

  public abstract class VibrationEffectSegment implements android.os.Parcelable {
    method @NonNull public abstract <T extends android.os.vibrator.VibrationEffectSegment> T applyEffectStrength(int);
    method public abstract long getDuration();
    method public abstract boolean hasNonZeroAmplitude();
    method @NonNull public abstract <T extends android.os.vibrator.VibrationEffectSegment> T resolve(int);
    method @NonNull public abstract <T extends android.os.vibrator.VibrationEffectSegment> T scale(float);
    method public abstract void validate();
    field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.VibrationEffectSegment> CREATOR;
  }

+49 −0
Original line number Diff line number Diff line
@@ -110,6 +110,20 @@ public abstract class CombinedVibration implements Parcelable {
    @TestApi
    public abstract long getDuration();

    /**
     * Returns true if this effect could represent a touch haptic feedback.
     *
     * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
     * for each vibration, with the correct usage. When a vibration is played with usage UNKNOWN,
     * then this method will be used to classify the most common use case and make sure they are
     * covered by the user settings for "Touch feedback".
     *
     * @hide
     */
    public boolean isHapticFeedbackCandidate() {
        return false;
    }

    /** @hide */
    public abstract void validate();

@@ -312,6 +326,12 @@ public abstract class CombinedVibration implements Parcelable {
            return mEffect.getDuration();
        }

        /** @hide */
        @Override
        public boolean isHapticFeedbackCandidate() {
            return mEffect.isHapticFeedbackCandidate();
        }

        /** @hide */
        @Override
        public void validate() {
@@ -429,6 +449,17 @@ public abstract class CombinedVibration implements Parcelable {
            return maxDuration;
        }

        /** @hide */
        @Override
        public boolean isHapticFeedbackCandidate() {
            for (int i = 0; i < mEffects.size(); i++) {
                if (!mEffects.valueAt(i).isHapticFeedbackCandidate()) {
                    return false;
                }
            }
            return true;
        }

        /** @hide */
        @Override
        public void validate() {
@@ -513,6 +544,9 @@ public abstract class CombinedVibration implements Parcelable {
     */
    @TestApi
    public static final class Sequential extends CombinedVibration {
        // If a vibration is playing more than 3 effects, it's probably not haptic feedback
        private static final long MAX_HAPTIC_FEEDBACK_SEQUENCE_SIZE = 3;

        private final List<CombinedVibration> mEffects;
        private final List<Integer> mDelays;

@@ -573,6 +607,21 @@ public abstract class CombinedVibration implements Parcelable {
            return durations + delays;
        }

        /** @hide */
        @Override
        public boolean isHapticFeedbackCandidate() {
            final int effectCount = mEffects.size();
            if (effectCount > MAX_HAPTIC_FEEDBACK_SEQUENCE_SIZE) {
                return false;
            }
            for (int i = 0; i < effectCount; i++) {
                if (!mEffects.get(i).isHapticFeedbackCandidate()) {
                    return false;
                }
            }
            return true;
        }

        /** @hide */
        @Override
        public void validate() {
+0 −3
Original line number Diff line number Diff line
@@ -196,9 +196,6 @@ public class SystemVibrator extends Vibrator {
            return;
        }
        CombinedVibration combinedEffect = CombinedVibration.createParallel(effect);
        // TODO(b/185351540): move this into VibratorManagerService once the touch vibration
        // heuristics is fixed and works for CombinedVibration. Make sure it's always applied.
        attributes = new VibrationAttributes.Builder(attributes, effect).build();
        mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes);
    }

+0 −3
Original line number Diff line number Diff line
@@ -223,9 +223,6 @@ public class SystemVibratorManager extends VibratorManager {
            CombinedVibration combined = CombinedVibration.startParallel()
                    .addVibrator(mVibratorInfo.getId(), vibe)
                    .combine();
            // TODO(b/185351540): move this into VibratorManagerService once the touch vibration
            // heuristics is fixed and works for CombinedVibration. Make sure it's always applied.
            attributes = new VibrationAttributes.Builder(attributes, vibe).build();
            SystemVibratorManager.this.vibrate(uid, opPkg, combined, reason, attributes);
        }

+0 −67
Original line number Diff line number Diff line
@@ -21,9 +21,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.media.AudioAttributes;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.Slog;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -156,9 +153,6 @@ public final class VibrationAttributes implements Parcelable {
     */
    public static final int FLAG_ALL_SUPPORTED = FLAG_BYPASS_INTERRUPTION_POLICY;

    // If a vibration is playing for longer than 5s, it's probably not haptic feedback
    private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;

    /** Creates a new {@link VibrationAttributes} instance with given usage. */
    public static @NonNull VibrationAttributes createForUsage(int usage) {
        return new VibrationAttributes.Builder().setUsage(usage).build();
@@ -359,67 +353,6 @@ public final class VibrationAttributes implements Parcelable {
            setFlags(audio);
        }

        /**
         * Constructs a new Builder from AudioAttributes and a VibrationEffect to infer usage.
         * @hide
         */
        @TestApi
        public Builder(@NonNull AudioAttributes audio, @NonNull VibrationEffect effect) {
            this(audio);
            applyHapticFeedbackHeuristics(effect);
        }

        /**
         * Constructs a new Builder from VibrationAttributes and a VibrationEffect to infer usage.
         * @hide
         */
        @TestApi
        public Builder(@NonNull VibrationAttributes vib, @NonNull VibrationEffect effect) {
            this(vib);
            applyHapticFeedbackHeuristics(effect);
        }

        private void applyHapticFeedbackHeuristics(@Nullable VibrationEffect effect) {
            if (effect != null) {
                PrebakedSegment prebaked = extractPrebakedSegment(effect);
                if (mUsage == USAGE_UNKNOWN && prebaked != null) {
                    switch (prebaked.getEffectId()) {
                        case VibrationEffect.EFFECT_CLICK:
                        case VibrationEffect.EFFECT_DOUBLE_CLICK:
                        case VibrationEffect.EFFECT_HEAVY_CLICK:
                        case VibrationEffect.EFFECT_TEXTURE_TICK:
                        case VibrationEffect.EFFECT_TICK:
                        case VibrationEffect.EFFECT_POP:
                        case VibrationEffect.EFFECT_THUD:
                            mUsage = USAGE_TOUCH;
                            break;
                        default:
                            Slog.w(TAG, "Unknown prebaked vibration effect, assuming it isn't "
                                    + "haptic feedback");
                    }
                }
                final long duration = effect.getDuration();
                if (mUsage == USAGE_UNKNOWN && duration >= 0
                        && duration < MAX_HAPTIC_FEEDBACK_DURATION) {
                    mUsage = USAGE_TOUCH;
                }
            }
        }

        @Nullable
        private PrebakedSegment extractPrebakedSegment(VibrationEffect effect) {
            if (effect instanceof VibrationEffect.Composed) {
                VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
                if (composed.getSegments().size() == 1) {
                    VibrationEffectSegment segment = composed.getSegments().get(0);
                    if (segment instanceof PrebakedSegment) {
                        return (PrebakedSegment) segment;
                    }
                }
            }
            return null;
        }

        private void setUsage(@NonNull AudioAttributes audio) {
            mOriginalAudioUsage = audio.getUsage();
            switch (audio.getUsage()) {
Loading