Added Android code
[wl-app.git] / Android / app / src / main / java / com / moiseum / wolnelektury / view / player / service / PlayerAdapter.java
1 /*
2  * Copyright 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.moiseum.wolnelektury.view.player.service;
18
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.media.AudioManager;
24 import android.support.annotation.NonNull;
25 import android.support.v4.media.MediaMetadataCompat;
26
27 /**
28  * Abstract player implementation that handles playing music with proper handling of headphones
29  * and audio focus.
30  */
31 public abstract class PlayerAdapter {
32
33     private static final float MEDIA_VOLUME_DEFAULT = 1.0f;
34     private static final float MEDIA_VOLUME_DUCK = 0.2f;
35
36     private static final IntentFilter AUDIO_NOISY_INTENT_FILTER =
37             new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
38
39     private boolean mAudioNoisyReceiverRegistered = false;
40     private final BroadcastReceiver mAudioNoisyReceiver =
41             new BroadcastReceiver() {
42                 @Override
43                 public void onReceive(Context context, Intent intent) {
44                     if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
45                         if (isPlaying()) {
46                             pause();
47                         }
48                     }
49                 }
50             };
51
52     private final Context mApplicationContext;
53     private final AudioManager mAudioManager;
54     private final AudioFocusHelper mAudioFocusHelper;
55
56     private boolean mPlayOnAudioFocus = false;
57
58     public PlayerAdapter(@NonNull Context context) {
59         mApplicationContext = context.getApplicationContext();
60         mAudioManager = (AudioManager) mApplicationContext.getSystemService(Context.AUDIO_SERVICE);
61         mAudioFocusHelper = new AudioFocusHelper();
62     }
63
64     public abstract void playFromMedia(MediaMetadataCompat metadata);
65
66     public abstract MediaMetadataCompat getCurrentMedia();
67
68     public abstract boolean isPlaying();
69
70     public final void play() {
71         if (mAudioFocusHelper.requestAudioFocus()) {
72             registerAudioNoisyReceiver();
73             onPlay();
74         }
75     }
76
77     /**
78      * Called when media is ready to be played and indicates the app has audio focus.
79      */
80     protected abstract void onPlay();
81
82     public final void pause() {
83         if (!mPlayOnAudioFocus) {
84             mAudioFocusHelper.abandonAudioFocus();
85         }
86
87         unregisterAudioNoisyReceiver();
88         onPause();
89     }
90
91     /**
92      * Called when media must be paused.
93      */
94     protected abstract void onPause();
95
96     public final void stop() {
97         mAudioFocusHelper.abandonAudioFocus();
98         unregisterAudioNoisyReceiver();
99         onStop();
100     }
101
102     /**
103      * Called when the media must be stopped. The player should clean up resources at this
104      * point.
105      */
106     protected abstract void onStop();
107
108     public abstract void seekTo(long position);
109
110     public abstract void fastForward();
111
112     public abstract void rewind();
113
114     public abstract void setVolume(float volume);
115
116     private void registerAudioNoisyReceiver() {
117         if (!mAudioNoisyReceiverRegistered) {
118             mApplicationContext.registerReceiver(mAudioNoisyReceiver, AUDIO_NOISY_INTENT_FILTER);
119             mAudioNoisyReceiverRegistered = true;
120         }
121     }
122
123     private void unregisterAudioNoisyReceiver() {
124         if (mAudioNoisyReceiverRegistered) {
125             mApplicationContext.unregisterReceiver(mAudioNoisyReceiver);
126             mAudioNoisyReceiverRegistered = false;
127         }
128     }
129
130     /**
131      * Helper class for managing audio focus related tasks.
132      */
133     private final class AudioFocusHelper
134             implements AudioManager.OnAudioFocusChangeListener {
135
136         private boolean requestAudioFocus() {
137             final int result = mAudioManager.requestAudioFocus(this,
138                     AudioManager.STREAM_MUSIC,
139                     AudioManager.AUDIOFOCUS_GAIN);
140             return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
141         }
142
143         private void abandonAudioFocus() {
144             mAudioManager.abandonAudioFocus(this);
145         }
146
147         @Override
148         public void onAudioFocusChange(int focusChange) {
149             switch (focusChange) {
150                 case AudioManager.AUDIOFOCUS_GAIN:
151                     if (mPlayOnAudioFocus && !isPlaying()) {
152                         play();
153                     } else if (isPlaying()) {
154                         setVolume(MEDIA_VOLUME_DEFAULT);
155                     }
156                     mPlayOnAudioFocus = false;
157                     break;
158                 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
159                     setVolume(MEDIA_VOLUME_DUCK);
160                     break;
161                 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
162                     if (isPlaying()) {
163                         mPlayOnAudioFocus = true;
164                         pause();
165                     }
166                     break;
167                 case AudioManager.AUDIOFOCUS_LOSS:
168                     mAudioManager.abandonAudioFocus(this);
169                     mPlayOnAudioFocus = false;
170                     stop();
171                     break;
172             }
173         }
174     }
175 }