169 lines
5.6 KiB
Java
169 lines
5.6 KiB
Java
/*
|
|
* Copyright (C) 2016 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.incallui.ringtone;
|
|
|
|
import android.media.AudioManager;
|
|
import android.media.ToneGenerator;
|
|
import android.support.annotation.NonNull;
|
|
import android.support.annotation.Nullable;
|
|
import com.android.incallui.Log;
|
|
import com.android.incallui.async.PausableExecutor;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* Class responsible for playing in-call related tones in a background thread. This class only
|
|
* allows one tone to be played at a time.
|
|
*/
|
|
public class InCallTonePlayer {
|
|
|
|
public static final int TONE_CALL_WAITING = 4;
|
|
|
|
public static final int VOLUME_RELATIVE_HIGH_PRIORITY = 80;
|
|
|
|
@NonNull private final ToneGeneratorFactory toneGeneratorFactory;
|
|
@NonNull private final PausableExecutor executor;
|
|
private @Nullable CountDownLatch numPlayingTones;
|
|
|
|
/**
|
|
* Creates a new InCallTonePlayer.
|
|
*
|
|
* @param toneGeneratorFactory the {@link ToneGeneratorFactory} used to create {@link
|
|
* ToneGenerator}s.
|
|
* @param executor the {@link PausableExecutor} used to play tones in a background thread.
|
|
* @throws NullPointerException if audioModeProvider, toneGeneratorFactory, or executor are {@code
|
|
* null}.
|
|
*/
|
|
public InCallTonePlayer(
|
|
@NonNull ToneGeneratorFactory toneGeneratorFactory, @NonNull PausableExecutor executor) {
|
|
this.toneGeneratorFactory = Objects.requireNonNull(toneGeneratorFactory);
|
|
this.executor = Objects.requireNonNull(executor);
|
|
}
|
|
|
|
/** @return {@code true} if a tone is currently playing, {@code false} otherwise. */
|
|
public boolean isPlayingTone() {
|
|
return numPlayingTones != null && numPlayingTones.getCount() > 0;
|
|
}
|
|
|
|
/**
|
|
* Plays the given tone in a background thread.
|
|
*
|
|
* @param tone the tone to play.
|
|
* @throws IllegalStateException if a tone is already playing.
|
|
* @throws IllegalArgumentException if the tone is invalid.
|
|
*/
|
|
public void play(int tone) {
|
|
if (isPlayingTone()) {
|
|
throw new IllegalStateException("Tone already playing");
|
|
}
|
|
final ToneGeneratorInfo info = getToneGeneratorInfo(tone);
|
|
numPlayingTones = new CountDownLatch(1);
|
|
executor.execute(
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
playOnBackgroundThread(info);
|
|
}
|
|
});
|
|
}
|
|
|
|
private ToneGeneratorInfo getToneGeneratorInfo(int tone) {
|
|
switch (tone) {
|
|
case TONE_CALL_WAITING:
|
|
/*
|
|
* DialerCall waiting tones play until they're stopped either by the user accepting or
|
|
* declining the call so the tone length is set at what's effectively forever. The
|
|
* tone is played at a high priority volume and through STREAM_VOICE_CALL since it's
|
|
* call related and using that stream will route it through bluetooth devices
|
|
* appropriately.
|
|
*/
|
|
return new ToneGeneratorInfo(
|
|
ToneGenerator.TONE_SUP_CALL_WAITING,
|
|
VOLUME_RELATIVE_HIGH_PRIORITY,
|
|
Integer.MAX_VALUE,
|
|
AudioManager.STREAM_VOICE_CALL);
|
|
default:
|
|
throw new IllegalArgumentException("Bad tone: " + tone);
|
|
}
|
|
}
|
|
|
|
private void playOnBackgroundThread(ToneGeneratorInfo info) {
|
|
ToneGenerator toneGenerator = null;
|
|
try {
|
|
Log.v(this, "Starting tone " + info);
|
|
toneGenerator = toneGeneratorFactory.newInCallToneGenerator(info.stream, info.volume);
|
|
toneGenerator.startTone(info.tone);
|
|
/*
|
|
* During tests, this will block until the tests call mExecutor.ackMilestone. This call
|
|
* allows for synchronization to the point where the tone has started playing.
|
|
*/
|
|
executor.milestone();
|
|
if (numPlayingTones != null) {
|
|
numPlayingTones.await(info.toneLengthMillis, TimeUnit.MILLISECONDS);
|
|
// Allows for synchronization to the point where the tone has completed playing.
|
|
executor.milestone();
|
|
}
|
|
} catch (InterruptedException e) {
|
|
Log.w(this, "Interrupted while playing in-call tone.");
|
|
} finally {
|
|
if (toneGenerator != null) {
|
|
toneGenerator.release();
|
|
}
|
|
if (numPlayingTones != null) {
|
|
numPlayingTones.countDown();
|
|
}
|
|
// Allows for synchronization to the point where this background thread has cleaned up.
|
|
executor.milestone();
|
|
}
|
|
}
|
|
|
|
/** Stops playback of the current tone. */
|
|
public void stop() {
|
|
if (numPlayingTones != null) {
|
|
numPlayingTones.countDown();
|
|
}
|
|
}
|
|
|
|
private static class ToneGeneratorInfo {
|
|
|
|
public final int tone;
|
|
public final int volume;
|
|
public final int toneLengthMillis;
|
|
public final int stream;
|
|
|
|
public ToneGeneratorInfo(int toneGeneratorType, int volume, int toneLengthMillis, int stream) {
|
|
this.tone = toneGeneratorType;
|
|
this.volume = volume;
|
|
this.toneLengthMillis = toneLengthMillis;
|
|
this.stream = stream;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "ToneGeneratorInfo{"
|
|
+ "toneLengthMillis="
|
|
+ toneLengthMillis
|
|
+ ", tone="
|
|
+ tone
|
|
+ ", volume="
|
|
+ volume
|
|
+ '}';
|
|
}
|
|
}
|
|
}
|