Merge "Split vibration effect adapters into separate files" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b2198d3a3d
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.os.VibratorInfo;
|
||||||
|
import android.os.vibrator.RampSegment;
|
||||||
|
import android.os.vibrator.StepSegment;
|
||||||
|
import android.os.vibrator.VibrationEffectSegment;
|
||||||
|
import android.util.MathUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRange()} and
|
||||||
|
* amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
|
||||||
|
*
|
||||||
|
* <p>Devices with no frequency control will collapse all frequencies to zero and leave
|
||||||
|
* amplitudes unchanged.
|
||||||
|
*
|
||||||
|
* <p>The frequency value returned in segments will be absolute, converted with
|
||||||
|
* {@link VibratorInfo#getAbsoluteFrequency(float)}.
|
||||||
|
*/
|
||||||
|
final class ClippingAmplitudeAndFrequencyAdapter
|
||||||
|
implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info) {
|
||||||
|
int segmentCount = segments.size();
|
||||||
|
for (int i = 0; i < segmentCount; i++) {
|
||||||
|
VibrationEffectSegment segment = segments.get(i);
|
||||||
|
if (segment instanceof StepSegment) {
|
||||||
|
segments.set(i, apply((StepSegment) segment, info));
|
||||||
|
} else if (segment instanceof RampSegment) {
|
||||||
|
segments.set(i, apply((RampSegment) segment, info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repeatIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StepSegment apply(StepSegment segment, VibratorInfo info) {
|
||||||
|
float clampedFrequency = clampFrequency(info, segment.getFrequency());
|
||||||
|
return new StepSegment(
|
||||||
|
clampAmplitude(info, clampedFrequency, segment.getAmplitude()),
|
||||||
|
info.getAbsoluteFrequency(clampedFrequency),
|
||||||
|
(int) segment.getDuration());
|
||||||
|
}
|
||||||
|
|
||||||
|
private RampSegment apply(RampSegment segment, VibratorInfo info) {
|
||||||
|
float clampedStartFrequency = clampFrequency(info, segment.getStartFrequency());
|
||||||
|
float clampedEndFrequency = clampFrequency(info, segment.getEndFrequency());
|
||||||
|
return new RampSegment(
|
||||||
|
clampAmplitude(info, clampedStartFrequency, segment.getStartAmplitude()),
|
||||||
|
clampAmplitude(info, clampedEndFrequency, segment.getEndAmplitude()),
|
||||||
|
info.getAbsoluteFrequency(clampedStartFrequency),
|
||||||
|
info.getAbsoluteFrequency(clampedEndFrequency),
|
||||||
|
(int) segment.getDuration());
|
||||||
|
}
|
||||||
|
|
||||||
|
private float clampFrequency(VibratorInfo info, float frequency) {
|
||||||
|
return info.getFrequencyRange().clamp(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float clampAmplitude(VibratorInfo info, float frequency, float amplitude) {
|
||||||
|
return MathUtils.min(amplitude, info.getMaxAmplitude(frequency));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,231 +16,33 @@
|
|||||||
|
|
||||||
package com.android.server.vibrator;
|
package com.android.server.vibrator;
|
||||||
|
|
||||||
import android.hardware.vibrator.IVibrator;
|
|
||||||
import android.os.VibrationEffect;
|
import android.os.VibrationEffect;
|
||||||
import android.os.VibratorInfo;
|
import android.os.VibratorInfo;
|
||||||
import android.os.vibrator.RampSegment;
|
|
||||||
import android.os.vibrator.StepSegment;
|
|
||||||
import android.os.vibrator.VibrationEffectSegment;
|
|
||||||
import android.util.MathUtils;
|
|
||||||
import android.util.Range;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Adapts a {@link VibrationEffect} to a specific device, taking into account its capabilities. */
|
/** Adapts a {@link VibrationEffect} to a specific device, taking into account its capabilities. */
|
||||||
final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<VibratorInfo> {
|
final class DeviceVibrationEffectAdapter
|
||||||
|
implements VibrationEffectAdapters.EffectAdapter<VibratorInfo> {
|
||||||
|
|
||||||
|
/** Duration of each step created to simulate a ramp segment. */
|
||||||
private static final int RAMP_STEP_DURATION_MILLIS = 5;
|
private static final int RAMP_STEP_DURATION_MILLIS = 5;
|
||||||
|
|
||||||
/** Adapts a sequence of {@link VibrationEffectSegment} to device's capabilities. */
|
private final List<VibrationEffectAdapters.SegmentsAdapter<VibratorInfo>> mSegmentAdapters;
|
||||||
interface SegmentsAdapter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifies the given segments list by adding/removing segments to it based on the
|
|
||||||
* device capabilities specified by given {@link VibratorInfo}.
|
|
||||||
*
|
|
||||||
* @param segments List of {@link VibrationEffectSegment} to be modified.
|
|
||||||
* @param repeatIndex Repeat index of the vibration with given segment list.
|
|
||||||
* @param info The device vibrator info that the segments must be adapted to.
|
|
||||||
* @return The new repeat index to be used for the modified list.
|
|
||||||
*/
|
|
||||||
int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final SegmentsAdapter mAmplitudeFrequencyAdapter;
|
|
||||||
private final SegmentsAdapter mStepToRampAdapter;
|
|
||||||
private final SegmentsAdapter mRampToStepsAdapter;
|
|
||||||
|
|
||||||
DeviceVibrationEffectAdapter() {
|
DeviceVibrationEffectAdapter() {
|
||||||
this(new ClippingAmplitudeFrequencyAdapter());
|
mSegmentAdapters = Arrays.asList(
|
||||||
}
|
// TODO(b/167947076): add filter that removes unsupported primitives
|
||||||
|
// TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
|
||||||
DeviceVibrationEffectAdapter(SegmentsAdapter amplitudeFrequencyAdapter) {
|
new RampToStepAdapter(RAMP_STEP_DURATION_MILLIS),
|
||||||
mAmplitudeFrequencyAdapter = amplitudeFrequencyAdapter;
|
new StepToRampAdapter(),
|
||||||
mStepToRampAdapter = new StepToRampAdapter();
|
new ClippingAmplitudeAndFrequencyAdapter()
|
||||||
mRampToStepsAdapter = new RampToStepsAdapter(RAMP_STEP_DURATION_MILLIS);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VibrationEffect apply(VibrationEffect effect, VibratorInfo info) {
|
public VibrationEffect apply(VibrationEffect effect, VibratorInfo info) {
|
||||||
if (!(effect instanceof VibrationEffect.Composed)) {
|
return VibrationEffectAdapters.apply(effect, mSegmentAdapters, info);
|
||||||
return effect;
|
|
||||||
}
|
|
||||||
|
|
||||||
VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
|
|
||||||
List<VibrationEffectSegment> newSegments = new ArrayList<>(composed.getSegments());
|
|
||||||
int newRepeatIndex = composed.getRepeatIndex();
|
|
||||||
|
|
||||||
// Replace ramps with a sequence of fixed steps, or no-op if PWLE capability present.
|
|
||||||
newRepeatIndex = mRampToStepsAdapter.apply(newSegments, newRepeatIndex, info);
|
|
||||||
|
|
||||||
// Replace steps that should be handled by PWLE to ramps, or no-op if capability missing.
|
|
||||||
// This should be done before frequency is converted from relative to absolute values.
|
|
||||||
newRepeatIndex = mStepToRampAdapter.apply(newSegments, newRepeatIndex, info);
|
|
||||||
|
|
||||||
// Adapt amplitude and frequency values to device supported ones, converting frequency
|
|
||||||
// to absolute values in Hertz.
|
|
||||||
newRepeatIndex = mAmplitudeFrequencyAdapter.apply(newSegments, newRepeatIndex, info);
|
|
||||||
|
|
||||||
// TODO(b/167947076): add filter that removes unsupported primitives
|
|
||||||
// TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
|
|
||||||
|
|
||||||
return new VibrationEffect.Composed(newSegments, newRepeatIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter that converts step segments that should be handled as PWLEs to ramp segments.
|
|
||||||
*
|
|
||||||
* <p>This leaves the list unchanged if the device do not have compose PWLE capability.
|
|
||||||
*/
|
|
||||||
private static final class StepToRampAdapter implements SegmentsAdapter {
|
|
||||||
@Override
|
|
||||||
public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
|
|
||||||
VibratorInfo info) {
|
|
||||||
if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
|
|
||||||
// The vibrator do not have PWLE capability, so keep the segments unchanged.
|
|
||||||
return repeatIndex;
|
|
||||||
}
|
|
||||||
int segmentCount = segments.size();
|
|
||||||
// Convert steps that require frequency control to ramps.
|
|
||||||
for (int i = 0; i < segmentCount; i++) {
|
|
||||||
VibrationEffectSegment segment = segments.get(i);
|
|
||||||
if ((segment instanceof StepSegment)
|
|
||||||
&& ((StepSegment) segment).getFrequency() != 0) {
|
|
||||||
segments.set(i, apply((StepSegment) segment));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Convert steps that are next to ramps to also become ramps, so they can be composed
|
|
||||||
// together in the same PWLE waveform.
|
|
||||||
for (int i = 1; i < segmentCount; i++) {
|
|
||||||
if (segments.get(i) instanceof RampSegment) {
|
|
||||||
for (int j = i - 1; j >= 0 && (segments.get(j) instanceof StepSegment); j--) {
|
|
||||||
segments.set(j, apply((StepSegment) segments.get(j)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return repeatIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RampSegment apply(StepSegment segment) {
|
|
||||||
return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
|
|
||||||
segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter that converts ramp segments that to a sequence of fixed step segments.
|
|
||||||
*
|
|
||||||
* <p>This leaves the list unchanged if the device have compose PWLE capability.
|
|
||||||
*/
|
|
||||||
private static final class RampToStepsAdapter implements SegmentsAdapter {
|
|
||||||
private final int mStepDuration;
|
|
||||||
|
|
||||||
RampToStepsAdapter(int stepDuration) {
|
|
||||||
mStepDuration = stepDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
|
|
||||||
VibratorInfo info) {
|
|
||||||
if (info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
|
|
||||||
// The vibrator have PWLE capability, so keep the segments unchanged.
|
|
||||||
return repeatIndex;
|
|
||||||
}
|
|
||||||
int segmentCount = segments.size();
|
|
||||||
for (int i = 0; i < segmentCount; i++) {
|
|
||||||
VibrationEffectSegment segment = segments.get(i);
|
|
||||||
if (!(segment instanceof RampSegment)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
List<StepSegment> steps = apply((RampSegment) segment);
|
|
||||||
segments.remove(i);
|
|
||||||
segments.addAll(i, steps);
|
|
||||||
int addedSegments = steps.size() - 1;
|
|
||||||
if (repeatIndex > i) {
|
|
||||||
repeatIndex += addedSegments;
|
|
||||||
}
|
|
||||||
i += addedSegments;
|
|
||||||
segmentCount += addedSegments;
|
|
||||||
}
|
|
||||||
return repeatIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<StepSegment> apply(RampSegment ramp) {
|
|
||||||
if (Float.compare(ramp.getStartAmplitude(), ramp.getEndAmplitude()) == 0) {
|
|
||||||
// Amplitude is the same, so return a single step to simulate this ramp.
|
|
||||||
return Arrays.asList(
|
|
||||||
new StepSegment(ramp.getStartAmplitude(), ramp.getStartFrequency(),
|
|
||||||
(int) ramp.getDuration()));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<StepSegment> steps = new ArrayList<>();
|
|
||||||
int stepCount = (int) (ramp.getDuration() + mStepDuration - 1) / mStepDuration;
|
|
||||||
for (int i = 0; i < stepCount - 1; i++) {
|
|
||||||
float pos = (float) i / stepCount;
|
|
||||||
steps.add(new StepSegment(
|
|
||||||
interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
|
|
||||||
interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), pos),
|
|
||||||
mStepDuration));
|
|
||||||
}
|
|
||||||
int duration = (int) ramp.getDuration() - mStepDuration * (stepCount - 1);
|
|
||||||
steps.add(new StepSegment(ramp.getEndAmplitude(), ramp.getEndFrequency(), duration));
|
|
||||||
return steps;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float interpolate(float start, float end, float position) {
|
|
||||||
return start + position * (end - start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRange()} and
|
|
||||||
* amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
|
|
||||||
*
|
|
||||||
* <p>Devices with no frequency control will collapse all frequencies to zero and leave
|
|
||||||
* amplitudes unchanged.
|
|
||||||
*
|
|
||||||
* <p>The frequency value returned in segments will be absolute, conveted with
|
|
||||||
* {@link VibratorInfo#getAbsoluteFrequency(float)}.
|
|
||||||
*/
|
|
||||||
private static final class ClippingAmplitudeFrequencyAdapter implements SegmentsAdapter {
|
|
||||||
@Override
|
|
||||||
public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
|
|
||||||
VibratorInfo info) {
|
|
||||||
int segmentCount = segments.size();
|
|
||||||
for (int i = 0; i < segmentCount; i++) {
|
|
||||||
VibrationEffectSegment segment = segments.get(i);
|
|
||||||
if (segment instanceof StepSegment) {
|
|
||||||
segments.set(i, apply((StepSegment) segment, info));
|
|
||||||
} else if (segment instanceof RampSegment) {
|
|
||||||
segments.set(i, apply((RampSegment) segment, info));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return repeatIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private StepSegment apply(StepSegment segment, VibratorInfo info) {
|
|
||||||
float clampedFrequency = info.getFrequencyRange().clamp(segment.getFrequency());
|
|
||||||
return new StepSegment(
|
|
||||||
MathUtils.min(segment.getAmplitude(), info.getMaxAmplitude(clampedFrequency)),
|
|
||||||
info.getAbsoluteFrequency(clampedFrequency),
|
|
||||||
(int) segment.getDuration());
|
|
||||||
}
|
|
||||||
|
|
||||||
private RampSegment apply(RampSegment segment, VibratorInfo info) {
|
|
||||||
Range<Float> frequencyRange = info.getFrequencyRange();
|
|
||||||
float clampedStartFrequency = frequencyRange.clamp(segment.getStartFrequency());
|
|
||||||
float clampedEndFrequency = frequencyRange.clamp(segment.getEndFrequency());
|
|
||||||
return new RampSegment(
|
|
||||||
MathUtils.min(segment.getStartAmplitude(),
|
|
||||||
info.getMaxAmplitude(clampedStartFrequency)),
|
|
||||||
MathUtils.min(segment.getEndAmplitude(),
|
|
||||||
info.getMaxAmplitude(clampedEndFrequency)),
|
|
||||||
info.getAbsoluteFrequency(clampedStartFrequency),
|
|
||||||
info.getAbsoluteFrequency(clampedEndFrequency),
|
|
||||||
(int) segment.getDuration());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.hardware.vibrator.IVibrator;
|
||||||
|
import android.os.VibratorInfo;
|
||||||
|
import android.os.vibrator.RampSegment;
|
||||||
|
import android.os.vibrator.StepSegment;
|
||||||
|
import android.os.vibrator.VibrationEffectSegment;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that converts ramp segments that to a sequence of fixed step segments.
|
||||||
|
*
|
||||||
|
* <p>This leaves the list unchanged if the device have compose PWLE capability.
|
||||||
|
*/
|
||||||
|
final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
|
||||||
|
|
||||||
|
private final int mStepDuration;
|
||||||
|
|
||||||
|
RampToStepAdapter(int stepDuration) {
|
||||||
|
mStepDuration = stepDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
|
||||||
|
VibratorInfo info) {
|
||||||
|
if (info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
|
||||||
|
// The vibrator have PWLE capability, so keep the segments unchanged.
|
||||||
|
return repeatIndex;
|
||||||
|
}
|
||||||
|
int segmentCount = segments.size();
|
||||||
|
for (int i = 0; i < segmentCount; i++) {
|
||||||
|
VibrationEffectSegment segment = segments.get(i);
|
||||||
|
if (!(segment instanceof RampSegment)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<StepSegment> steps = apply((RampSegment) segment);
|
||||||
|
segments.remove(i);
|
||||||
|
segments.addAll(i, steps);
|
||||||
|
int addedSegments = steps.size() - 1;
|
||||||
|
if (repeatIndex > i) {
|
||||||
|
repeatIndex += addedSegments;
|
||||||
|
}
|
||||||
|
i += addedSegments;
|
||||||
|
segmentCount += addedSegments;
|
||||||
|
}
|
||||||
|
return repeatIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<StepSegment> apply(RampSegment ramp) {
|
||||||
|
if (Float.compare(ramp.getStartAmplitude(), ramp.getEndAmplitude()) == 0) {
|
||||||
|
// Amplitude is the same, so return a single step to simulate this ramp.
|
||||||
|
return Arrays.asList(
|
||||||
|
new StepSegment(ramp.getStartAmplitude(), ramp.getStartFrequency(),
|
||||||
|
(int) ramp.getDuration()));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<StepSegment> steps = new ArrayList<>();
|
||||||
|
int stepCount = (int) (ramp.getDuration() + mStepDuration - 1) / mStepDuration;
|
||||||
|
for (int i = 0; i < stepCount - 1; i++) {
|
||||||
|
float pos = (float) i / stepCount;
|
||||||
|
steps.add(new StepSegment(
|
||||||
|
interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
|
||||||
|
interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), pos),
|
||||||
|
mStepDuration));
|
||||||
|
}
|
||||||
|
int duration = (int) ramp.getDuration() - mStepDuration * (stepCount - 1);
|
||||||
|
steps.add(new StepSegment(ramp.getEndAmplitude(), ramp.getEndFrequency(), duration));
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float interpolate(float start, float end, float position) {
|
||||||
|
return start + position * (end - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.hardware.vibrator.IVibrator;
|
||||||
|
import android.os.VibratorInfo;
|
||||||
|
import android.os.vibrator.RampSegment;
|
||||||
|
import android.os.vibrator.StepSegment;
|
||||||
|
import android.os.vibrator.VibrationEffectSegment;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that converts step segments that should be handled as PWLEs to ramp segments.
|
||||||
|
*
|
||||||
|
* <p>This leaves the list unchanged if the device do not have compose PWLE capability.
|
||||||
|
*/
|
||||||
|
final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
|
||||||
|
@Override
|
||||||
|
public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
|
||||||
|
VibratorInfo info) {
|
||||||
|
if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
|
||||||
|
// The vibrator do not have PWLE capability, so keep the segments unchanged.
|
||||||
|
return repeatIndex;
|
||||||
|
}
|
||||||
|
int segmentCount = segments.size();
|
||||||
|
// Convert steps that require frequency control to ramps.
|
||||||
|
for (int i = 0; i < segmentCount; i++) {
|
||||||
|
VibrationEffectSegment segment = segments.get(i);
|
||||||
|
if ((segment instanceof StepSegment)
|
||||||
|
&& ((StepSegment) segment).getFrequency() != 0) {
|
||||||
|
segments.set(i, apply((StepSegment) segment));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Convert steps that are next to ramps to also become ramps, so they can be composed
|
||||||
|
// together in the same PWLE waveform.
|
||||||
|
for (int i = 1; i < segmentCount; i++) {
|
||||||
|
if (segments.get(i) instanceof RampSegment) {
|
||||||
|
for (int j = i - 1; j >= 0 && (segments.get(j) instanceof StepSegment); j--) {
|
||||||
|
segments.set(j, apply((StepSegment) segments.get(j)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repeatIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RampSegment apply(StepSegment segment) {
|
||||||
|
return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
|
||||||
|
segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.os.VibrationEffect;
|
||||||
|
import android.os.vibrator.VibrationEffectSegment;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers to adapt a {@link VibrationEffect} to generic modifiers (e.g. device capabilities,
|
||||||
|
* user settings, etc).
|
||||||
|
*/
|
||||||
|
public final class VibrationEffectAdapters {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that applies a generic modifier to a sequence of {@link VibrationEffectSegment}.
|
||||||
|
*
|
||||||
|
* @param <T> The type of modifiers this adapter accepts.
|
||||||
|
*/
|
||||||
|
public interface SegmentsAdapter<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add and/or remove segments to the given {@link VibrationEffectSegment} list based on the
|
||||||
|
* given modifier.
|
||||||
|
*
|
||||||
|
* <p>This returns the new {@code repeatIndex} to be used together with the updated list to
|
||||||
|
* specify an equivalent {@link VibrationEffect}.
|
||||||
|
*
|
||||||
|
* @param segments List of {@link VibrationEffectSegment} to be modified.
|
||||||
|
* @param repeatIndex Repeat index on the current segment list.
|
||||||
|
* @param modifier The modifier to be applied to the sequence of segments.
|
||||||
|
* @return The new repeat index on the modifies list.
|
||||||
|
*/
|
||||||
|
int apply(List<VibrationEffectSegment> segments, int repeatIndex, T modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that applies a generic modifier to a {@link VibrationEffect}.
|
||||||
|
*
|
||||||
|
* @param <T> The type of modifiers this adapter accepts.
|
||||||
|
*/
|
||||||
|
public interface EffectAdapter<T> {
|
||||||
|
|
||||||
|
/** Applies the modifier to given {@link VibrationEffect}, returning the new effect. */
|
||||||
|
VibrationEffect apply(VibrationEffect effect, T modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a sequence of {@link SegmentsAdapter} to the segments of a given
|
||||||
|
* {@link VibrationEffect}, in order.
|
||||||
|
*
|
||||||
|
* @param effect The effect to be adapted to given modifier.
|
||||||
|
* @param adapters The sequence of adapters to be applied to given {@link VibrationEffect}.
|
||||||
|
* @param modifier The modifier to be passed to each adapter that describes the conditions the
|
||||||
|
* {@link VibrationEffect} needs to be adapted to (e.g. device capabilities,
|
||||||
|
* user settings, etc).
|
||||||
|
*/
|
||||||
|
public static <T> VibrationEffect apply(VibrationEffect effect,
|
||||||
|
List<SegmentsAdapter<T>> adapters, T modifier) {
|
||||||
|
if (!(effect instanceof VibrationEffect.Composed)) {
|
||||||
|
// Segments adapters can only be applied to Composed effects.
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
|
||||||
|
List<VibrationEffectSegment> newSegments = new ArrayList<>(composed.getSegments());
|
||||||
|
int newRepeatIndex = composed.getRepeatIndex();
|
||||||
|
|
||||||
|
int adapterCount = adapters.size();
|
||||||
|
for (int i = 0; i < adapterCount; i++) {
|
||||||
|
newRepeatIndex = adapters.get(i).apply(newSegments, newRepeatIndex, modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VibrationEffect.Composed(newSegments, newRepeatIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 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.os.VibrationEffect;
|
|
||||||
|
|
||||||
/** Function that applies a generic modifier to a {@link VibrationEffect}. */
|
|
||||||
interface VibrationEffectModifier<T> {
|
|
||||||
|
|
||||||
/** Applies the modifier to given {@link VibrationEffect}. */
|
|
||||||
VibrationEffect apply(VibrationEffect effect, T modifier);
|
|
||||||
}
|
|
||||||
@@ -95,7 +95,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
private final WorkSource mWorkSource = new WorkSource();
|
private final WorkSource mWorkSource = new WorkSource();
|
||||||
private final PowerManager.WakeLock mWakeLock;
|
private final PowerManager.WakeLock mWakeLock;
|
||||||
private final IBatteryStats mBatteryStatsService;
|
private final IBatteryStats mBatteryStatsService;
|
||||||
private final VibrationEffectModifier<VibratorInfo> mDeviceEffectAdapter =
|
private final DeviceVibrationEffectAdapter mDeviceEffectAdapter =
|
||||||
new DeviceVibrationEffectAdapter();
|
new DeviceVibrationEffectAdapter();
|
||||||
private final Vibration mVibration;
|
private final Vibration mVibration;
|
||||||
private final VibrationCallbacks mCallbacks;
|
private final VibrationCallbacks mCallbacks;
|
||||||
|
|||||||
Reference in New Issue
Block a user