1 /* 2 * Copyright (C) 2016 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.audio; 17 18 import android.content.BroadcastReceiver; 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.database.ContentObserver; 24 import android.media.AudioManager; 25 import android.net.Uri; 26 import android.os.Handler; 27 import androidx.annotation.Nullable; 28 import com.google.android.exoplayer2.util.Assertions; 29 import com.google.android.exoplayer2.util.Util; 30 31 /** 32 * Receives broadcast events indicating changes to the device's audio capabilities, notifying a 33 * {@link Listener} when audio capability changes occur. 34 */ 35 public final class AudioCapabilitiesReceiver { 36 37 /** 38 * Listener notified when audio capabilities change. 39 */ 40 public interface Listener { 41 42 /** 43 * Called when the audio capabilities change. 44 * 45 * @param audioCapabilities The current audio capabilities for the device. 46 */ onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities)47 void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities); 48 49 } 50 51 private final Context context; 52 private final Listener listener; 53 private final Handler handler; 54 @Nullable private final BroadcastReceiver receiver; 55 @Nullable private final ExternalSurroundSoundSettingObserver externalSurroundSoundSettingObserver; 56 57 @Nullable /* package */ AudioCapabilities audioCapabilities; 58 private boolean registered; 59 60 /** 61 * @param context A context for registering the receiver. 62 * @param listener The listener to notify when audio capabilities change. 63 */ AudioCapabilitiesReceiver(Context context, Listener listener)64 public AudioCapabilitiesReceiver(Context context, Listener listener) { 65 context = context.getApplicationContext(); 66 this.context = context; 67 this.listener = Assertions.checkNotNull(listener); 68 handler = new Handler(Util.getLooper()); 69 receiver = Util.SDK_INT >= 21 ? new HdmiAudioPlugBroadcastReceiver() : null; 70 Uri externalSurroundSoundUri = AudioCapabilities.getExternalSurroundSoundGlobalSettingUri(); 71 externalSurroundSoundSettingObserver = 72 externalSurroundSoundUri != null 73 ? new ExternalSurroundSoundSettingObserver( 74 handler, context.getContentResolver(), externalSurroundSoundUri) 75 : null; 76 } 77 78 /** 79 * Registers the receiver, meaning it will notify the listener when audio capability changes 80 * occur. The current audio capabilities will be returned. It is important to call 81 * {@link #unregister} when the receiver is no longer required. 82 * 83 * @return The current audio capabilities for the device. 84 */ 85 @SuppressWarnings("InlinedApi") register()86 public AudioCapabilities register() { 87 if (registered) { 88 return Assertions.checkNotNull(audioCapabilities); 89 } 90 registered = true; 91 if (externalSurroundSoundSettingObserver != null) { 92 externalSurroundSoundSettingObserver.register(); 93 } 94 Intent stickyIntent = null; 95 if (receiver != null) { 96 IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG); 97 stickyIntent = 98 context.registerReceiver( 99 receiver, intentFilter, /* broadcastPermission= */ null, handler); 100 } 101 audioCapabilities = AudioCapabilities.getCapabilities(context, stickyIntent); 102 return audioCapabilities; 103 } 104 105 /** 106 * Unregisters the receiver, meaning it will no longer notify the listener when audio capability 107 * changes occur. 108 */ unregister()109 public void unregister() { 110 if (!registered) { 111 return; 112 } 113 audioCapabilities = null; 114 if (receiver != null) { 115 context.unregisterReceiver(receiver); 116 } 117 if (externalSurroundSoundSettingObserver != null) { 118 externalSurroundSoundSettingObserver.unregister(); 119 } 120 registered = false; 121 } 122 onNewAudioCapabilities(AudioCapabilities newAudioCapabilities)123 private void onNewAudioCapabilities(AudioCapabilities newAudioCapabilities) { 124 if (registered && !newAudioCapabilities.equals(audioCapabilities)) { 125 audioCapabilities = newAudioCapabilities; 126 listener.onAudioCapabilitiesChanged(newAudioCapabilities); 127 } 128 } 129 130 private final class HdmiAudioPlugBroadcastReceiver extends BroadcastReceiver { 131 132 @Override onReceive(Context context, Intent intent)133 public void onReceive(Context context, Intent intent) { 134 if (!isInitialStickyBroadcast()) { 135 onNewAudioCapabilities(AudioCapabilities.getCapabilities(context, intent)); 136 } 137 } 138 } 139 140 private final class ExternalSurroundSoundSettingObserver extends ContentObserver { 141 142 private final ContentResolver resolver; 143 private final Uri settingUri; 144 ExternalSurroundSoundSettingObserver( Handler handler, ContentResolver resolver, Uri settingUri)145 public ExternalSurroundSoundSettingObserver( 146 Handler handler, ContentResolver resolver, Uri settingUri) { 147 super(handler); 148 this.resolver = resolver; 149 this.settingUri = settingUri; 150 } 151 register()152 public void register() { 153 resolver.registerContentObserver(settingUri, /* notifyForDescendants= */ false, this); 154 } 155 unregister()156 public void unregister() { 157 resolver.unregisterContentObserver(this); 158 } 159 160 @Override onChange(boolean selfChange)161 public void onChange(boolean selfChange) { 162 onNewAudioCapabilities(AudioCapabilities.getCapabilities(context)); 163 } 164 } 165 166 } 167