• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 package com.google.android.exoplayer2;
17 
18 import android.content.BroadcastReceiver;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import android.media.AudioManager;
23 import android.os.Handler;
24 import com.google.android.exoplayer2.util.Assertions;
25 import com.google.android.exoplayer2.util.Util;
26 
27 /** A manager that wraps {@link AudioManager} to control/listen audio stream volume. */
28 /* package */ final class StreamVolumeManager {
29 
30   /** A listener for changes in the manager. */
31   public interface Listener {
32 
33     /** Called when the audio stream type is changed. */
onStreamTypeChanged(@.StreamType int streamType)34     void onStreamTypeChanged(@C.StreamType int streamType);
35 
36     /** Called when the audio stream volume or mute state is changed. */
onStreamVolumeChanged(int streamVolume, boolean streamMuted)37     void onStreamVolumeChanged(int streamVolume, boolean streamMuted);
38   }
39 
40   // TODO(b/151280453): Replace the hidden intent action with an official one.
41   // Copied from AudioManager#VOLUME_CHANGED_ACTION
42   private static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
43 
44   // TODO(b/153317944): Allow users to override these flags.
45   private static final int VOLUME_FLAGS = AudioManager.FLAG_SHOW_UI;
46 
47   private final Context applicationContext;
48   private final Handler eventHandler;
49   private final Listener listener;
50   private final AudioManager audioManager;
51   private final VolumeChangeReceiver receiver;
52 
53   @C.StreamType private int streamType;
54   private int volume;
55   private boolean muted;
56   private boolean released;
57 
58   /** Creates a manager. */
StreamVolumeManager(Context context, Handler eventHandler, Listener listener)59   public StreamVolumeManager(Context context, Handler eventHandler, Listener listener) {
60     applicationContext = context.getApplicationContext();
61     this.eventHandler = eventHandler;
62     this.listener = listener;
63     audioManager =
64         Assertions.checkStateNotNull(
65             (AudioManager) applicationContext.getSystemService(Context.AUDIO_SERVICE));
66 
67     streamType = C.STREAM_TYPE_DEFAULT;
68     volume = getVolumeFromManager(audioManager, streamType);
69     muted = getMutedFromManager(audioManager, streamType);
70 
71     receiver = new VolumeChangeReceiver();
72     IntentFilter filter = new IntentFilter(VOLUME_CHANGED_ACTION);
73     applicationContext.registerReceiver(receiver, filter);
74   }
75 
76   /** Sets the audio stream type. */
setStreamType(@.StreamType int streamType)77   public void setStreamType(@C.StreamType int streamType) {
78     if (this.streamType == streamType) {
79       return;
80     }
81     this.streamType = streamType;
82 
83     updateVolumeAndNotifyIfChanged();
84     listener.onStreamTypeChanged(streamType);
85   }
86 
87   /**
88    * Gets the minimum volume for the current audio stream. It can be changed if {@link
89    * #setStreamType(int)} is called.
90    */
getMinVolume()91   public int getMinVolume() {
92     return Util.SDK_INT >= 28 ? audioManager.getStreamMinVolume(streamType) : 0;
93   }
94 
95   /**
96    * Gets the maximum volume for the current audio stream. It can be changed if {@link
97    * #setStreamType(int)} is called.
98    */
getMaxVolume()99   public int getMaxVolume() {
100     return audioManager.getStreamMaxVolume(streamType);
101   }
102 
103   /** Gets the current volume for the current audio stream. */
getVolume()104   public int getVolume() {
105     return volume;
106   }
107 
108   /** Gets whether the current audio stream is muted or not. */
isMuted()109   public boolean isMuted() {
110     return muted;
111   }
112 
113   /**
114    * Sets the volume with the given value for the current audio stream. The value should be between
115    * {@link #getMinVolume()} and {@link #getMaxVolume()}, otherwise it will be ignored.
116    */
setVolume(int volume)117   public void setVolume(int volume) {
118     if (volume < getMinVolume() || volume > getMaxVolume()) {
119       return;
120     }
121     audioManager.setStreamVolume(streamType, volume, VOLUME_FLAGS);
122     updateVolumeAndNotifyIfChanged();
123   }
124 
125   /**
126    * Increases the volume by one for the current audio stream. It will be ignored if the current
127    * volume is equal to {@link #getMaxVolume()}.
128    */
increaseVolume()129   public void increaseVolume() {
130     if (volume >= getMaxVolume()) {
131       return;
132     }
133     audioManager.adjustStreamVolume(streamType, AudioManager.ADJUST_RAISE, VOLUME_FLAGS);
134     updateVolumeAndNotifyIfChanged();
135   }
136 
137   /**
138    * Decreases the volume by one for the current audio stream. It will be ignored if the current
139    * volume is equal to {@link #getMinVolume()}.
140    */
decreaseVolume()141   public void decreaseVolume() {
142     if (volume <= getMinVolume()) {
143       return;
144     }
145     audioManager.adjustStreamVolume(streamType, AudioManager.ADJUST_LOWER, VOLUME_FLAGS);
146     updateVolumeAndNotifyIfChanged();
147   }
148 
149   /** Sets the mute state of the current audio stream. */
setMuted(boolean muted)150   public void setMuted(boolean muted) {
151     if (Util.SDK_INT >= 23) {
152       audioManager.adjustStreamVolume(
153           streamType, muted ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, VOLUME_FLAGS);
154     } else {
155       audioManager.setStreamMute(streamType, muted);
156     }
157     updateVolumeAndNotifyIfChanged();
158   }
159 
160   /** Releases the manager. It must be called when the manager is no longer required. */
release()161   public void release() {
162     if (released) {
163       return;
164     }
165     applicationContext.unregisterReceiver(receiver);
166     released = true;
167   }
168 
updateVolumeAndNotifyIfChanged()169   private void updateVolumeAndNotifyIfChanged() {
170     int newVolume = getVolumeFromManager(audioManager, streamType);
171     boolean newMuted = getMutedFromManager(audioManager, streamType);
172     if (volume != newVolume || muted != newMuted) {
173       volume = newVolume;
174       muted = newMuted;
175       listener.onStreamVolumeChanged(newVolume, newMuted);
176     }
177   }
178 
getVolumeFromManager(AudioManager audioManager, @C.StreamType int streamType)179   private static int getVolumeFromManager(AudioManager audioManager, @C.StreamType int streamType) {
180     return audioManager.getStreamVolume(streamType);
181   }
182 
getMutedFromManager( AudioManager audioManager, @C.StreamType int streamType)183   private static boolean getMutedFromManager(
184       AudioManager audioManager, @C.StreamType int streamType) {
185     if (Util.SDK_INT >= 23) {
186       return audioManager.isStreamMute(streamType);
187     } else {
188       return audioManager.getStreamVolume(streamType) == 0;
189     }
190   }
191 
192   private final class VolumeChangeReceiver extends BroadcastReceiver {
193 
194     @Override
onReceive(Context context, Intent intent)195     public void onReceive(Context context, Intent intent) {
196       eventHandler.post(StreamVolumeManager.this::updateVolumeAndNotifyIfChanged);
197     }
198   }
199 }
200