Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static android.os.Build.VERSION_CODES.O;
      4 
      5 import android.annotation.TargetApi;
      6 import android.media.AudioAttributes;
      7 import android.media.AudioManager;
      8 import android.media.AudioPlaybackConfiguration;
      9 import android.os.Build.VERSION_CODES;
     10 import android.os.Parcel;
     11 import java.util.ArrayList;
     12 import java.util.Collections;
     13 import java.util.HashMap;
     14 import java.util.List;
     15 import java.util.Map;
     16 import org.robolectric.annotation.Implementation;
     17 import org.robolectric.annotation.Implements;
     18 import org.robolectric.util.ReflectionHelpers;
     19 
     20 @SuppressWarnings({"UnusedDeclaration"})
     21 @Implements(AudioManager.class)
     22 public class ShadowAudioManager {
     23   public static final int MAX_VOLUME_MUSIC_DTMF = 15;
     24   public static final int DEFAULT_MAX_VOLUME = 7;
     25   public static final int DEFAULT_VOLUME = 7;
     26   public static final int INVALID_VOLUME = 0;
     27   public static final int FLAG_NO_ACTION = 0;
     28   public static final int[] ALL_STREAMS = {
     29       AudioManager.STREAM_MUSIC,
     30       AudioManager.STREAM_ALARM,
     31       AudioManager.STREAM_NOTIFICATION,
     32       AudioManager.STREAM_RING,
     33       AudioManager.STREAM_SYSTEM,
     34       AudioManager.STREAM_VOICE_CALL,
     35       AudioManager.STREAM_DTMF
     36   };
     37 
     38   private AudioFocusRequest lastAudioFocusRequest;
     39   private int nextResponseValue = AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
     40   private AudioManager.OnAudioFocusChangeListener lastAbandonedAudioFocusListener;
     41   private android.media.AudioFocusRequest lastAbandonedAudioFocusRequest;
     42   private HashMap<Integer, AudioStream> streamStatus = new HashMap<>();
     43   private List<AudioPlaybackConfiguration> activePlaybackConfigurations = Collections.emptyList();
     44   private int ringerMode = AudioManager.RINGER_MODE_NORMAL;
     45   private int mode = AudioManager.MODE_NORMAL;
     46   private boolean bluetoothA2dpOn;
     47   private boolean isBluetoothScoOn;
     48   private boolean isSpeakerphoneOn;
     49   private boolean isMicrophoneMuted = false;
     50   private boolean isMusicActive;
     51   private boolean wiredHeadsetOn;
     52 
     53   public ShadowAudioManager() {
     54     for (int stream : ALL_STREAMS) {
     55       streamStatus.put(stream, new AudioStream(DEFAULT_VOLUME, DEFAULT_MAX_VOLUME, FLAG_NO_ACTION));
     56     }
     57     streamStatus.get(AudioManager.STREAM_MUSIC).setMaxVolume(MAX_VOLUME_MUSIC_DTMF);
     58     streamStatus.get(AudioManager.STREAM_DTMF).setMaxVolume(MAX_VOLUME_MUSIC_DTMF);
     59   }
     60 
     61   @Implementation
     62   protected int getStreamMaxVolume(int streamType) {
     63     AudioStream stream = streamStatus.get(streamType);
     64     return (stream != null) ? stream.getMaxVolume() : INVALID_VOLUME;
     65   }
     66 
     67   @Implementation
     68   protected int getStreamVolume(int streamType) {
     69     AudioStream stream = streamStatus.get(streamType);
     70     return (stream != null) ? stream.getCurrentVolume() : INVALID_VOLUME;
     71   }
     72 
     73   @Implementation
     74   protected void setStreamVolume(int streamType, int index, int flags) {
     75     AudioStream stream = streamStatus.get(streamType);
     76     if (stream != null) {
     77       stream.setCurrentVolume(index);
     78       stream.setFlag(flags);
     79     }
     80   }
     81 
     82   @Implementation
     83   protected int requestAudioFocus(
     84       android.media.AudioManager.OnAudioFocusChangeListener l, int streamType, int durationHint) {
     85     lastAudioFocusRequest = new AudioFocusRequest(l, streamType, durationHint);
     86     return nextResponseValue;
     87   }
     88 
     89   /**
     90    * Provides a mock like interface for the requestAudioFocus method by storing the request
     91    * object for later inspection and returning the value specified in setNextFocusRequestResponse.
     92    */
     93   @Implementation(minSdk = O)
     94   protected int requestAudioFocus(android.media.AudioFocusRequest audioFocusRequest) {
     95     lastAudioFocusRequest = new AudioFocusRequest(audioFocusRequest);
     96     return nextResponseValue;
     97   }
     98 
     99   @Implementation
    100   protected int abandonAudioFocus(AudioManager.OnAudioFocusChangeListener l) {
    101     lastAbandonedAudioFocusListener = l;
    102     return nextResponseValue;
    103   }
    104 
    105 
    106   /**
    107    * Provides a mock like interface for the abandonAudioFocusRequest method by storing the request
    108    * object for later inspection and returning the value specified in setNextFocusRequestResponse.
    109    */
    110   @Implementation(minSdk = O)
    111   protected int abandonAudioFocusRequest(android.media.AudioFocusRequest audioFocusRequest) {
    112     lastAbandonedAudioFocusRequest = audioFocusRequest;
    113     return nextResponseValue;
    114   }
    115 
    116   @Implementation
    117   protected int getRingerMode() {
    118     return ringerMode;
    119   }
    120 
    121   @Implementation
    122   protected void setRingerMode(int ringerMode) {
    123     if (!AudioManager.isValidRingerMode(ringerMode)) {
    124       return;
    125     }
    126     this.ringerMode = ringerMode;
    127   }
    128 
    129   public static boolean isValidRingerMode(int ringerMode) {
    130     if (ringerMode < 0 || ringerMode > (int)ReflectionHelpers.getStaticField(AudioManager.class, "RINGER_MODE_MAX")) {
    131       return false;
    132     }
    133     return true;
    134   }
    135 
    136   @Implementation
    137   protected void setMode(int mode) {
    138     this.mode = mode;
    139   }
    140 
    141   @Implementation
    142   protected int getMode() {
    143     return this.mode;
    144   }
    145 
    146   public void setStreamMaxVolume(int streamMaxVolume) {
    147     for (Map.Entry<Integer, AudioStream> entry : streamStatus.entrySet()) {
    148       entry.getValue().setMaxVolume(streamMaxVolume);
    149     }
    150   }
    151 
    152   public void setStreamVolume(int streamVolume) {
    153     for (Map.Entry<Integer, AudioStream> entry : streamStatus.entrySet()) {
    154       entry.getValue().setCurrentVolume(streamVolume);
    155     }
    156   }
    157 
    158   @Implementation
    159   protected void setWiredHeadsetOn(boolean on) {
    160     wiredHeadsetOn = on;
    161   }
    162 
    163   @Implementation
    164   protected boolean isWiredHeadsetOn() {
    165     return wiredHeadsetOn;
    166   }
    167 
    168   @Implementation
    169   protected void setBluetoothA2dpOn(boolean on) {
    170     bluetoothA2dpOn = on;
    171   }
    172 
    173   @Implementation
    174   protected boolean isBluetoothA2dpOn() {
    175     return bluetoothA2dpOn;
    176   }
    177 
    178   @Implementation
    179   protected void setSpeakerphoneOn(boolean on) {
    180     isSpeakerphoneOn = on;
    181   }
    182 
    183   @Implementation
    184   protected boolean isSpeakerphoneOn() {
    185     return isSpeakerphoneOn;
    186   }
    187 
    188   @Implementation
    189   protected void setMicrophoneMute(boolean on) {
    190     isMicrophoneMuted = on;
    191   }
    192 
    193   @Implementation
    194   protected boolean isMicrophoneMute() {
    195     return isMicrophoneMuted;
    196   }
    197 
    198   @Implementation
    199   protected boolean isBluetoothScoOn() {
    200     return isBluetoothScoOn;
    201   }
    202 
    203   @Implementation
    204   protected void setBluetoothScoOn(boolean isBluetoothScoOn) {
    205     this.isBluetoothScoOn = isBluetoothScoOn;
    206   }
    207 
    208   @Implementation
    209   protected boolean isMusicActive() {
    210     return isMusicActive;
    211   }
    212 
    213   @Implementation(minSdk = O)
    214   protected List<AudioPlaybackConfiguration> getActivePlaybackConfigurations() {
    215     return new ArrayList<>(activePlaybackConfigurations);
    216   }
    217 
    218   /**
    219    * Sets active playback configurations that will be served by {@link
    220    * AudioManager#getActivePlaybackConfigurations}.
    221    *
    222    * <p>Note that there is no public {@link AudioPlaybackConfiguration} constructor, so the
    223    * configurations returned are specified by their audio attributes only.
    224    */
    225   @TargetApi(VERSION_CODES.O)
    226   public void setActivePlaybackConfigurationsFor(List<AudioAttributes> audioAttributes) {
    227     activePlaybackConfigurations = new ArrayList<>(audioAttributes.size());
    228     for (AudioAttributes audioAttribute : audioAttributes) {
    229       Parcel p = Parcel.obtain();
    230       p.writeInt(0); // mPlayerIId
    231       p.writeInt(0); // mPlayerType
    232       p.writeInt(0); // mClientUid
    233       p.writeInt(0); // mClientPid
    234       p.writeInt(AudioPlaybackConfiguration.PLAYER_STATE_STARTED); // mPlayerState
    235       audioAttribute.writeToParcel(p, 0);
    236       p.writeStrongInterface(null);
    237       byte[] bytes = p.marshall();
    238       p.recycle();
    239       p = Parcel.obtain();
    240       p.unmarshall(bytes, 0, bytes.length);
    241       AudioPlaybackConfiguration configuration =
    242           AudioPlaybackConfiguration.CREATOR.createFromParcel(p);
    243       p.recycle();
    244       activePlaybackConfigurations.add(configuration);
    245     }
    246   }
    247 
    248   public void setIsMusicActive(boolean isMusicActive) {
    249     this.isMusicActive = isMusicActive;
    250   }
    251 
    252   public AudioFocusRequest getLastAudioFocusRequest() {
    253     return lastAudioFocusRequest;
    254   }
    255 
    256   public void setNextFocusRequestResponse(int nextResponseValue) {
    257     this.nextResponseValue = nextResponseValue;
    258   }
    259 
    260   public AudioManager.OnAudioFocusChangeListener getLastAbandonedAudioFocusListener() {
    261     return lastAbandonedAudioFocusListener;
    262   }
    263 
    264   public android.media.AudioFocusRequest getLastAbandonedAudioFocusRequest() {
    265     return lastAbandonedAudioFocusRequest;
    266   }
    267 
    268   public static class AudioFocusRequest {
    269     public final AudioManager.OnAudioFocusChangeListener listener;
    270     public final int streamType;
    271     public final int durationHint;
    272     public final android.media.AudioFocusRequest audioFocusRequest;
    273 
    274     private AudioFocusRequest(AudioManager.OnAudioFocusChangeListener listener, int streamType, int durationHint) {
    275       this.listener = listener;
    276       this.streamType = streamType;
    277       this.durationHint = durationHint;
    278       this.audioFocusRequest = null;
    279     }
    280 
    281     private AudioFocusRequest(android.media.AudioFocusRequest audioFocusRequest) {
    282       this.listener = null;
    283       this.streamType = this.durationHint = -1;
    284       this.audioFocusRequest = audioFocusRequest;
    285     }
    286   }
    287 
    288   private static class AudioStream {
    289     private int currentVolume;
    290     private int maxVolume;
    291     private int flag;
    292 
    293     public AudioStream(int currVol, int maxVol, int flag) {
    294       setCurrentVolume(currVol);
    295       setMaxVolume(maxVol);
    296       setFlag(flag);
    297     }
    298 
    299     public int getCurrentVolume() {
    300       return currentVolume;
    301     }
    302 
    303     public int getMaxVolume() {
    304       return maxVolume;
    305     }
    306 
    307     public int getFlag() {
    308       return flag;
    309     }
    310 
    311     public void setCurrentVolume(int vol) {
    312       if (vol > maxVolume) {
    313         vol = maxVolume;
    314       } else if (vol < 0) {
    315         vol = 0;
    316       }
    317       currentVolume = vol;
    318     }
    319 
    320     public void setMaxVolume(int vol) {
    321       maxVolume = vol;
    322     }
    323 
    324     public void setFlag(int flag) {
    325       this.flag = flag;
    326     }
    327   }
    328 }
    329