diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java index b3272cd566..34c187efeb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.components.voice; import android.app.Notification; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -11,19 +10,14 @@ import android.os.Bundle; import android.os.Process; import android.os.RemoteException; import android.support.v4.media.MediaBrowserCompat; -import android.support.v4.media.MediaDescriptionCompat; -import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; import androidx.media.MediaBrowserServiceCompat; -import androidx.media.session.MediaButtonReceiver; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; @@ -38,18 +32,10 @@ import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.ui.PlayerNotificationManager; -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.contacts.avatars.ContactColors; -import org.thoughtcrime.securesms.conversation.ConversationActivity; -import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.recipients.RecipientId; import java.util.Collections; import java.util.List; -import java.util.Objects; /** * Android Service responsible for playback of voice notes. @@ -74,6 +60,7 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat { private VoiceNoteNotificationManager voiceNoteNotificationManager; private VoiceNoteQueueDataAdapter queueDataAdapter; private VoiceNotePlaybackPreparer voiceNotePlaybackPreparer; + private VoiceNoteProximityManager voiceNoteProximityManager; private boolean isForegroundService; private final LoadControl loadControl = new DefaultLoadControl.Builder() @@ -102,6 +89,7 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat { VoiceNoteMediaSourceFactory mediaSourceFactory = new VoiceNoteMediaSourceFactory(this); voiceNotePlaybackPreparer = new VoiceNotePlaybackPreparer(this, player, queueDataAdapter, mediaSourceFactory); + voiceNoteProximityManager = new VoiceNoteProximityManager(this, player); mediaSession.setPlaybackState(stateBuilder.build()); @@ -155,6 +143,7 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat { switch (playbackState) { case Player.STATE_BUFFERING: case Player.STATE_READY: + voiceNoteProximityManager.onPlayerReady(); voiceNoteNotificationManager.showNotification(player); if (!playWhenReady) { @@ -165,6 +154,7 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat { } break; default: + voiceNoteProximityManager.onPlayerEnded(); becomingNoisyReceiver.unregister(); voiceNoteNotificationManager.hideNotification(); } @@ -184,6 +174,7 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat { @Override public void onPlayerError(ExoPlaybackException error) { Log.w(TAG, "ExoPlayer error occurred:", error); + voiceNoteProximityManager.onPlayerError(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteProximityManager.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteProximityManager.java new file mode 100644 index 0000000000..b8881e57c2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteProximityManager.java @@ -0,0 +1,119 @@ +package org.thoughtcrime.securesms.components.voice; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.media.AudioManager; +import android.os.Build; +import android.os.PowerManager; + +import androidx.annotation.NonNull; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.audio.AudioAttributes; +import com.google.android.exoplayer2.util.Util; + +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.ServiceUtil; + +import java.util.concurrent.TimeUnit; + +class VoiceNoteProximityManager implements SensorEventListener { + + private static final String TAG = Log.tag(VoiceNoteProximityManager.class); + + private static final float PROXIMITY_THRESHOLD = 5f; + + private final SimpleExoPlayer player; + private final AudioManager audioManager; + private final SensorManager sensorManager; + private final Sensor proximitySensor; + private final PowerManager.WakeLock wakeLock; + + private long startTime; + + VoiceNoteProximityManager(@NonNull Context context, @NonNull SimpleExoPlayer player) { + this.player = player; + this.audioManager = ServiceUtil.getAudioManager(context); + this.sensorManager = ServiceUtil.getSensorManager(context); + this.proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + + if (Build.VERSION.SDK_INT >= 21) { + this.wakeLock = ServiceUtil.getPowerManager(context).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG); + } else { + this.wakeLock = null; + } + } + + void onPlayerReady() { + startTime = System.currentTimeMillis(); + sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); + } + + void onPlayerEnded() { + sensorManager.unregisterListener(this); + + if (wakeLock != null && wakeLock.isHeld() && Build.VERSION.SDK_INT >= 21) { + wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); + } + } + + void onPlayerError() { + onPlayerEnded(); + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() != Sensor.TYPE_PROXIMITY || player.getPlaybackState() != Player.STATE_READY) { + return; + } + + final int desiredStreamType; + if (event.values[0] < PROXIMITY_THRESHOLD && event.values[0] != proximitySensor.getMaximumRange()) { + desiredStreamType = AudioManager.STREAM_VOICE_CALL; + } else { + desiredStreamType = AudioManager.STREAM_MUSIC; + } + + final int currentStreamType = Util.getStreamTypeForAudioUsage(player.getAudioAttributes().usage); + + if (desiredStreamType == AudioManager.STREAM_VOICE_CALL && + desiredStreamType != currentStreamType && + !audioManager.isWiredHeadsetOn()) + { + if (wakeLock != null) { + wakeLock.acquire(TimeUnit.MINUTES.toMillis(30)); + } + + player.setPlayWhenReady(false); + player.setAudioAttributes(new AudioAttributes.Builder() + .setContentType(C.CONTENT_TYPE_SPEECH) + .setUsage(C.USAGE_VOICE_COMMUNICATION) + .build()); + player.setPlayWhenReady(true); + + startTime = System.currentTimeMillis(); + } else if (desiredStreamType == AudioManager.STREAM_MUSIC && + desiredStreamType != currentStreamType && + System.currentTimeMillis() - startTime > 500) + { + if (wakeLock != null) { + wakeLock.release(); + player.setPlayWhenReady(false); + player.setAudioAttributes(new AudioAttributes.Builder() + .setContentType(C.CONTENT_TYPE_MUSIC) + .setUsage(C.USAGE_MEDIA) + .build(), + true); + } + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java index aed3c4482a..9094063412 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java @@ -7,6 +7,7 @@ import android.app.NotificationManager; import android.app.job.JobScheduler; import android.content.ClipboardManager; import android.content.Context; +import android.hardware.SensorManager; import android.hardware.display.DisplayManager; import android.location.LocationManager; import android.media.AudioManager; @@ -49,6 +50,10 @@ public class ServiceUtil { return (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); } + public static SensorManager getSensorManager(Context context) { + return (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + } + public static PowerManager getPowerManager(Context context) { return (PowerManager)context.getSystemService(Context.POWER_SERVICE); }