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

Commit 56ebc519 authored by Lais Andrade's avatar Lais Andrade
Browse files

Update default vibration to use OGG haptic file

Add a new ringtone to RingtoneFactory that has only haptic channels
unmutted, to be used by the Ringer to play attentional haptics on
RINGER_MODE_VIBRATE.

The existing default vibration pattern is still applied on devices
withouht audio-coupled haptics support.

Fix: 182157749
Test: RingerTest
Change-Id: I97b1dd00a0f05fa1400e28bcebda547b9cee55bb
parent 74458c58
Loading
Loading
Loading
Loading

.gitignore

0 → 100644
+2 −0
Original line number Diff line number Diff line
.idea
*.iml
+50 −41
Original line number Diff line number Diff line
@@ -77,8 +77,10 @@ public class AsyncRingtonePlayer {
     *         {@code False} indicates that a haptic track is NOT present on the ringtone;
     *         in this case the default vibration in {@link Ringer} should be trigger if needed.
     */
    public @NonNull CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall,
            @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isVibrationEnabled) {
    public @NonNull
    CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall,
            @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isRingerAudible,
            boolean isVibrationEnabled) {
        Log.d(this, "Posting play.");
        if (mHapticsFuture == null) {
            mHapticsFuture = new CompletableFuture<>();
@@ -88,7 +90,8 @@ public class AsyncRingtonePlayer {
        args.arg2 = incomingCall;
        args.arg3 = volumeShaperConfig;
        args.arg4 = isVibrationEnabled;
        args.arg5 = Log.createSubsession();
        args.arg5 = isRingerAudible;
        args.arg6 = Log.createSubsession();
        postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
        return mHapticsFuture;
    }
@@ -152,31 +155,34 @@ public class AsyncRingtonePlayer {
        Call incomingCall = (Call) args.arg2;
        VolumeShaper.Configuration volumeShaperConfig = (VolumeShaper.Configuration) args.arg3;
        boolean isVibrationEnabled = (boolean) args.arg4;
        Session session = (Session) args.arg5;
        boolean isRingerAudible = (boolean) args.arg5;
        Session session = (Session) args.arg6;
        args.recycle();

        Log.continueSession(session, "ARP.hP");
        try {
            // don't bother with any of this if there is an EVENT_STOP waiting.
            if (mHandler.hasMessages(EVENT_STOP)) {
                if (mHapticsFuture != null) {
                    mHapticsFuture.complete(false /* ringtoneHasHaptics */);
                    mHapticsFuture = null;
                }
                completeHapticFuture(false /* ringtoneHasHaptics */);
                return;
            }

            // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected. Do not play
            // anything.
            if (Uri.EMPTY.equals(incomingCall.getRingtone())) {
                mRingtone = null;
                if (mHapticsFuture != null) {
                    mHapticsFuture.complete(false /* ringtoneHasHaptics */);
                    mHapticsFuture = null;
            // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected.
            // If ringer is not audible for this call, then the phone is in "Vibrate" mode.
            // Use haptic-only ringtone or do not play anything.
            if (!isRingerAudible || Uri.EMPTY.equals(incomingCall.getRingtone())) {
                if (isVibrationEnabled) {
                    mRingtone = factory.getHapticOnlyRingtone();
                    if (mRingtone == null) {
                        completeHapticFuture(false /* ringtoneHasHaptics */);
                        return;
                    }

                } else {
                    mRingtone = null;
                    completeHapticFuture(false /* ringtoneHasHaptics */);
                    return;
                }
            }

            ThreadUtil.checkNotOnMainThread();
            Log.i(this, "handlePlay: Play ringtone.");
@@ -189,16 +195,14 @@ public class AsyncRingtonePlayer {
                            ringtoneUri.toSafeString();
                    Log.addEvent(null, LogUtils.Events.ERROR_LOG, "Failed to get ringtone from " +
                            "factory. Skipping ringing. Uri was: " + ringtoneUriString);
                    if (mHapticsFuture != null) {
                        mHapticsFuture.complete(false /* ringtoneHasHaptics */);
                        mHapticsFuture = null;
                    }
                    completeHapticFuture(false /* ringtoneHasHaptics */);
                    return;
                }
            }

            // With the ringtone to play now known, we can determine if it has haptic channels or
                // not; we will complete the haptics future so the default vibration code in Ringer
                // can know whether to trigger the vibrator.
            // not; we will complete the haptics future so the default vibration code in Ringer can
            // know whether to trigger the vibrator.
            if (mHapticsFuture != null && !mHapticsFuture.isDone()) {
                boolean hasHaptics = factory.hasHapticChannels(mRingtone);
                Log.i(this, "handlePlay: hasHaptics=%b, isVibrationEnabled=%b", hasHaptics,
@@ -214,9 +218,7 @@ public class AsyncRingtonePlayer {
                                    .setHapticChannelsMuted(!isVibrationEnabled)
                                    .build());
                }
                    mHapticsFuture.complete(hasHaptics);
                    mHapticsFuture = null;
                }
                completeHapticFuture(hasHaptics);
            }

            mRingtone.setLooping(true);
@@ -259,4 +261,11 @@ public class AsyncRingtonePlayer {
    public boolean isPlaying() {
        return mRingtone != null;
    }

    private void completeHapticFuture(boolean ringtoneHasHaptics) {
        if (mHapticsFuture != null) {
            mHapticsFuture.complete(ringtoneHasHaptics);
            mHapticsFuture = null;
        }
    }
}
+16 −8
Original line number Diff line number Diff line
@@ -282,23 +282,31 @@ public class Ringer {
                    float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION)
                            / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION);
                    mVolumeShaperConfig = new VolumeShaper.Configuration.Builder()
                        .setDuration(RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION)
                            .setDuration(
                                    RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION)
                            .setCurve(new float[]{0.f, silencePoint + EPSILON /*keep monotonicity*/,
                                    1.f}, new float[]{0.f, 0.f, 1.f})
                        .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
                            .setInterpolatorType(
                                    VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
                            .build();
                }
                hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall,
                        mVolumeShaperConfig, isVibratorEnabled);
                        mVolumeShaperConfig, attributes.isRingerAudible(), isVibratorEnabled);
            } else {
                // Ramping ringtone is not enabled.
                hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null,
                        isVibratorEnabled);
                        attributes.isRingerAudible(), isVibratorEnabled);
                effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
            }
        } else {
            Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: "
                    + attributes.getInaudibleReason());
            if (isVibratorEnabled && mIsHapticPlaybackSupportedByDevice) {
                // Attempt to run the attentional haptic ringtone first and fallback to the default
                // vibration effect if hapticFuture is completed with false.
                hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null,
                        attributes.isRingerAudible(), isVibratorEnabled);
            }
            effect = mDefaultVibrationEffect;
        }

+21 −4
Original line number Diff line number Diff line
@@ -103,6 +103,27 @@ public class RingtoneFactory {
                Log.e(this, npe, "getRingtone: NPE while getting ringtone.");
            }
        }
        return setRingtoneAudioAttributes(ringtone);
    }

    public Ringtone getRingtone(Call incomingCall) {
        return getRingtone(incomingCall, null);
    }

    /** Returns a ringtone to be used when ringer is not audible for the incoming call. */
    @Nullable
    public Ringtone getHapticOnlyRingtone() {
        Uri ringtoneUri = Uri.parse("file://" + mContext.getString(
                com.android.internal.R.string.config_defaultRingtoneVibrationSound));
        Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri, null);
        if (ringtone != null) {
            // Make sure the sound is muted.
            ringtone.setVolume(0);
        }
        return setRingtoneAudioAttributes(ringtone);
    }

    private Ringtone setRingtoneAudioAttributes(Ringtone ringtone) {
        if (ringtone != null) {
            ringtone.setAudioAttributes(new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
@@ -112,10 +133,6 @@ public class RingtoneFactory {
        return ringtone;
    }

    public Ringtone getRingtone(Call incomingCall) {
        return getRingtone(incomingCall, null);
    }

    private Context getWorkProfileContextForUser(UserHandle userHandle) {
        // UserManager.getEnabledProfiles returns the enabled profiles along with the user's handle
        // itself (so we must filter out the user).
+46 −20
Original line number Diff line number Diff line
@@ -149,7 +149,8 @@ public class RingerTest extends TelecomTestCase {
        when(notificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
        when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false);
        when(mockRingtonePlayer.play(any(RingtoneFactory.class), any(Call.class),
                nullable(VolumeShaper.Configuration.class), anyBoolean())).thenReturn(mFuture);
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()))
                .thenReturn(mFuture);
        mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
                mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
                mockInCallController);
@@ -174,7 +175,7 @@ public class RingerTest extends TelecomTestCase {
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                eq(null), eq(false));
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }
@@ -192,7 +193,7 @@ public class RingerTest extends TelecomTestCase {
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }
@@ -208,7 +209,7 @@ public class RingerTest extends TelecomTestCase {
        mRingCompletionFuture.get();
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }
@@ -225,7 +226,7 @@ public class RingerTest extends TelecomTestCase {
        mRingCompletionFuture.get();
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }
@@ -241,7 +242,7 @@ public class RingerTest extends TelecomTestCase {
        assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }
@@ -260,7 +261,7 @@ public class RingerTest extends TelecomTestCase {
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }
@@ -276,8 +277,8 @@ public class RingerTest extends TelecomTestCase {
        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
        mRingCompletionFuture.get();
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer).play(
                any(RingtoneFactory.class), any(Call.class), isNull(), eq(true));
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(),
                eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
        verify(mockVibrator, never()).vibrate(any(VibrationEffect.class),
                any(AudioAttributes.class));
    }
@@ -293,8 +294,10 @@ public class RingerTest extends TelecomTestCase {
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        mRingCompletionFuture.get();
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
        // Try to play a silent haptics ringtone
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(),
                eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
        // Play default vibration when future completes with no audio coupled haptics
        verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
                any(AudioAttributes.class));
    }
@@ -305,19 +308,42 @@ public class RingerTest extends TelecomTestCase {
        mRingerUnderTest.startCallWaiting(mockCall1);
        Ringtone mockRingtone = mock(Ringtone.class);
        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
        mFuture.complete(false); // not using audio coupled haptics
        enableVibrationWhenRinging();
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        mRingCompletionFuture.get();
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
        // Try to play a silent haptics ringtone
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(),
                eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
        // Play default vibration when future completes with no audio coupled haptics
        verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
                any(AudioAttributes.class));
    }

    @SmallTest
    @Test
    public void testAudioCoupledHapticsForSilentRingtone() throws Exception {
        mRingerUnderTest.startCallWaiting(mockCall1);
        Ringtone mockRingtone = mock(Ringtone.class);
        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
        mFuture.complete(true); // using audio coupled haptics
        enableVibrationWhenRinging();
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        mRingCompletionFuture.get();
        verify(mockTonePlayer).stopTone();
        // Try to play a silent haptics ringtone
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(),
                eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
        // Skip vibration for audio coupled haptics
        verify(mockVibrator, never()).vibrate(any(VibrationEffect.class),
                any(AudioAttributes.class));
    }

    @SmallTest
    @Test
    public void testStopRingingBeforeHapticsLookupComplete() throws Exception {
@@ -329,7 +355,7 @@ public class RingerTest extends TelecomTestCase {
        mRingerUnderTest.startRinging(mockCall1, false);
        // Make sure we haven't started the vibrator yet, but have started ringing.
        verify(mockRingtonePlayer).play(nullable(RingtoneFactory.class), nullable(Call.class),
                nullable(VolumeShaper.Configuration.class), anyBoolean());
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never()).vibrate(nullable(VibrationEffect.class),
                nullable(AudioAttributes.class));
        // Simulate something stopping the ringer
@@ -357,7 +383,7 @@ public class RingerTest extends TelecomTestCase {
        mRingCompletionFuture.get();
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null),
                eq(true));
                eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
        verify(mockVibrator).vibrate(eq(spyVibrationEffectProxy.get(FAKE_RINGTONE_URI, mContext)),
                any(AudioAttributes.class));
    }
@@ -373,7 +399,7 @@ public class RingerTest extends TelecomTestCase {
        mRingCompletionFuture.get();
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null),
                eq(false));
                eq(true) /* isRingerAudible */, eq(false) /* isVibrationEnabled */);
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }
@@ -391,7 +417,7 @@ public class RingerTest extends TelecomTestCase {
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer).play(
            any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class),
                eq(true));
                eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */);
    }

    @SmallTest
@@ -408,7 +434,7 @@ public class RingerTest extends TelecomTestCase {
        mRingCompletionFuture.get();
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }
@@ -426,7 +452,7 @@ public class RingerTest extends TelecomTestCase {
        mRingCompletionFuture.get();
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class),
                any(VolumeShaper.Configuration.class), anyBoolean());
                nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean());
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }