--- /dev/null
+/*
+ * Copyright 2017 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.moiseum.wolnelektury.view.player.service;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.support.annotation.NonNull;
+import android.support.v4.media.MediaMetadataCompat;
+
+/**
+ * Abstract player implementation that handles playing music with proper handling of headphones
+ * and audio focus.
+ */
+public abstract class PlayerAdapter {
+
+ private static final float MEDIA_VOLUME_DEFAULT = 1.0f;
+ private static final float MEDIA_VOLUME_DUCK = 0.2f;
+
+ private static final IntentFilter AUDIO_NOISY_INTENT_FILTER =
+ new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+
+ private boolean mAudioNoisyReceiverRegistered = false;
+ private final BroadcastReceiver mAudioNoisyReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
+ if (isPlaying()) {
+ pause();
+ }
+ }
+ }
+ };
+
+ private final Context mApplicationContext;
+ private final AudioManager mAudioManager;
+ private final AudioFocusHelper mAudioFocusHelper;
+
+ private boolean mPlayOnAudioFocus = false;
+
+ public PlayerAdapter(@NonNull Context context) {
+ mApplicationContext = context.getApplicationContext();
+ mAudioManager = (AudioManager) mApplicationContext.getSystemService(Context.AUDIO_SERVICE);
+ mAudioFocusHelper = new AudioFocusHelper();
+ }
+
+ public abstract void playFromMedia(MediaMetadataCompat metadata);
+
+ public abstract MediaMetadataCompat getCurrentMedia();
+
+ public abstract boolean isPlaying();
+
+ public final void play() {
+ if (mAudioFocusHelper.requestAudioFocus()) {
+ registerAudioNoisyReceiver();
+ onPlay();
+ }
+ }
+
+ /**
+ * Called when media is ready to be played and indicates the app has audio focus.
+ */
+ protected abstract void onPlay();
+
+ public final void pause() {
+ if (!mPlayOnAudioFocus) {
+ mAudioFocusHelper.abandonAudioFocus();
+ }
+
+ unregisterAudioNoisyReceiver();
+ onPause();
+ }
+
+ /**
+ * Called when media must be paused.
+ */
+ protected abstract void onPause();
+
+ public final void stop() {
+ mAudioFocusHelper.abandonAudioFocus();
+ unregisterAudioNoisyReceiver();
+ onStop();
+ }
+
+ /**
+ * Called when the media must be stopped. The player should clean up resources at this
+ * point.
+ */
+ protected abstract void onStop();
+
+ public abstract void seekTo(long position);
+
+ public abstract void fastForward();
+
+ public abstract void rewind();
+
+ public abstract void setVolume(float volume);
+
+ private void registerAudioNoisyReceiver() {
+ if (!mAudioNoisyReceiverRegistered) {
+ mApplicationContext.registerReceiver(mAudioNoisyReceiver, AUDIO_NOISY_INTENT_FILTER);
+ mAudioNoisyReceiverRegistered = true;
+ }
+ }
+
+ private void unregisterAudioNoisyReceiver() {
+ if (mAudioNoisyReceiverRegistered) {
+ mApplicationContext.unregisterReceiver(mAudioNoisyReceiver);
+ mAudioNoisyReceiverRegistered = false;
+ }
+ }
+
+ /**
+ * Helper class for managing audio focus related tasks.
+ */
+ private final class AudioFocusHelper
+ implements AudioManager.OnAudioFocusChangeListener {
+
+ private boolean requestAudioFocus() {
+ final int result = mAudioManager.requestAudioFocus(this,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ }
+
+ private void abandonAudioFocus() {
+ mAudioManager.abandonAudioFocus(this);
+ }
+
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ switch (focusChange) {
+ case AudioManager.AUDIOFOCUS_GAIN:
+ if (mPlayOnAudioFocus && !isPlaying()) {
+ play();
+ } else if (isPlaying()) {
+ setVolume(MEDIA_VOLUME_DEFAULT);
+ }
+ mPlayOnAudioFocus = false;
+ break;
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
+ setVolume(MEDIA_VOLUME_DUCK);
+ break;
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
+ if (isPlaying()) {
+ mPlayOnAudioFocus = true;
+ pause();
+ }
+ break;
+ case AudioManager.AUDIOFOCUS_LOSS:
+ mAudioManager.abandonAudioFocus(this);
+ mPlayOnAudioFocus = false;
+ stop();
+ break;
+ }
+ }
+ }
+}