Added Android code
[wl-app.git] / Android / app / src / main / java / com / moiseum / wolnelektury / view / player / service / PlayerAdapter.java
diff --git a/Android/app/src/main/java/com/moiseum/wolnelektury/view/player/service/PlayerAdapter.java b/Android/app/src/main/java/com/moiseum/wolnelektury/view/player/service/PlayerAdapter.java
new file mode 100755 (executable)
index 0000000..1182a11
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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;
+            }
+        }
+    }
+}