public final class

AudioCapabilitiesReceiver

extends java.lang.Object

 java.lang.Object

↳androidx.media3.exoplayer.audio.AudioCapabilitiesReceiver

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-exoplayer:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Receives broadcast events indicating changes to the device's audio capabilities, notifying a AudioCapabilitiesReceiver.Listener when audio capability changes occur.

Summary

Constructors
publicAudioCapabilitiesReceiver(Context context, AudioCapabilitiesReceiver.Listener listener)

publicAudioCapabilitiesReceiver(Context context, AudioCapabilitiesReceiver.Listener listener, AudioAttributes audioAttributes, AudioDeviceInfo routedDevice)

Methods
public AudioCapabilitiesregister()

Registers the receiver, meaning it will notify the listener when audio capability changes occur.

public voidsetAudioAttributes(AudioAttributes audioAttributes)

Updates the AudioAttributes used by this instance.

public voidsetRoutedDevice(AudioDeviceInfo routedDevice)

Updates the audio will be routed to.

public voidunregister()

Unregisters the receiver, meaning it will no longer notify the listener when audio capability changes occur.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public AudioCapabilitiesReceiver(Context context, AudioCapabilitiesReceiver.Listener listener)

Deprecated: Use AudioCapabilitiesReceiver.AudioCapabilitiesReceiver(Context, AudioCapabilitiesReceiver.Listener, AudioAttributes, AudioDeviceInfo) instead.

public AudioCapabilitiesReceiver(Context context, AudioCapabilitiesReceiver.Listener listener, AudioAttributes audioAttributes, AudioDeviceInfo routedDevice)

Parameters:

context: A context for registering the receiver.
listener: The listener to notify when audio capabilities change.
audioAttributes: The AudioAttributes.
routedDevice: The audio will be routed to if known, or null to assume the default route.

Methods

public void setAudioAttributes(AudioAttributes audioAttributes)

Updates the AudioAttributes used by this instance.

Parameters:

audioAttributes: The AudioAttributes.

public void setRoutedDevice(AudioDeviceInfo routedDevice)

Updates the audio will be routed to.

Parameters:

routedDevice: The audio will be routed to if known, or null to assume the default route.

public AudioCapabilities register()

Registers the receiver, meaning it will notify the listener when audio capability changes occur. The current audio capabilities will be returned. It is important to call AudioCapabilitiesReceiver.unregister() when the receiver is no longer required.

Returns:

The current audio capabilities for the device.

public void unregister()

Unregisters the receiver, meaning it will no longer notify the listener when audio capability changes occur.

Source

/*
 * Copyright (C) 2016 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 androidx.media3.exoplayer.audio;

import static androidx.media3.common.util.Assertions.checkNotNull;

import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;

/**
 * Receives broadcast events indicating changes to the device's audio capabilities, notifying a
 * {@link Listener} when audio capability changes occur.
 */
@UnstableApi
public final class AudioCapabilitiesReceiver {

  /** Listener notified when audio capabilities change. */
  public interface Listener {

    /**
     * Called when the audio capabilities change.
     *
     * @param audioCapabilities The current audio capabilities for the device.
     */
    void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities);
  }

  private final Context context;
  private final Listener listener;
  private final Handler handler;
  @Nullable private final AudioDeviceCallbackV23 audioDeviceCallback;
  private final BroadcastReceiver hdmiAudioPlugBroadcastReceiver;
  @Nullable private final ExternalSurroundSoundSettingObserver externalSurroundSoundSettingObserver;

  @Nullable private AudioCapabilities audioCapabilities;
  @Nullable private AudioDeviceInfoApi23 routedDevice;
  private AudioAttributes audioAttributes;
  private boolean registered;

  /**
   * @deprecated Use {@link #AudioCapabilitiesReceiver(Context, Listener, AudioAttributes,
   *     AudioDeviceInfo)} instead.
   */
  @Deprecated
  public AudioCapabilitiesReceiver(Context context, Listener listener) {
    this(context, listener, AudioAttributes.DEFAULT, /* routedDevice= */ (AudioDeviceInfo) null);
  }

  /**
   * @param context A context for registering the receiver.
   * @param listener The listener to notify when audio capabilities change.
   * @param audioAttributes The {@link AudioAttributes}.
   * @param routedDevice The {@link AudioDeviceInfo} audio will be routed to if known, or null to
   *     assume the default route.
   */
  public AudioCapabilitiesReceiver(
      Context context,
      Listener listener,
      AudioAttributes audioAttributes,
      @Nullable AudioDeviceInfo routedDevice) {
    this(
        context,
        listener,
        audioAttributes,
        Util.SDK_INT >= 23 && routedDevice != null ? new AudioDeviceInfoApi23(routedDevice) : null);
  }

  /* package */ AudioCapabilitiesReceiver(
      Context context,
      Listener listener,
      AudioAttributes audioAttributes,
      @Nullable AudioDeviceInfoApi23 routedDevice) {
    context = context.getApplicationContext();
    this.context = context;
    this.listener = checkNotNull(listener);
    this.audioAttributes = audioAttributes;
    this.routedDevice = routedDevice;
    handler = Util.createHandlerForCurrentOrMainLooper();
    audioDeviceCallback = Util.SDK_INT >= 23 ? new AudioDeviceCallbackV23() : null;
    hdmiAudioPlugBroadcastReceiver = new HdmiAudioPlugBroadcastReceiver();
    Uri externalSurroundSoundUri = AudioCapabilities.getExternalSurroundSoundGlobalSettingUri();
    externalSurroundSoundSettingObserver =
        externalSurroundSoundUri != null
            ? new ExternalSurroundSoundSettingObserver(
                handler, context.getContentResolver(), externalSurroundSoundUri)
            : null;
  }

  /**
   * Updates the {@link AudioAttributes} used by this instance.
   *
   * @param audioAttributes The {@link AudioAttributes}.
   */
  public void setAudioAttributes(AudioAttributes audioAttributes) {
    this.audioAttributes = audioAttributes;
    onNewAudioCapabilities(
        AudioCapabilities.getCapabilitiesInternal(context, audioAttributes, routedDevice));
  }

  /**
   * Updates the {@link AudioDeviceInfo} audio will be routed to.
   *
   * @param routedDevice The {@link AudioDeviceInfo} audio will be routed to if known, or null to
   *     assume the default route.
   */
  @RequiresApi(23)
  public void setRoutedDevice(@Nullable AudioDeviceInfo routedDevice) {
    if (Util.areEqual(
        routedDevice, this.routedDevice == null ? null : this.routedDevice.audioDeviceInfo)) {
      return;
    }
    this.routedDevice = routedDevice != null ? new AudioDeviceInfoApi23(routedDevice) : null;
    onNewAudioCapabilities(
        AudioCapabilities.getCapabilitiesInternal(context, audioAttributes, this.routedDevice));
  }

  /**
   * Registers the receiver, meaning it will notify the listener when audio capability changes
   * occur. The current audio capabilities will be returned. It is important to call {@link
   * #unregister} when the receiver is no longer required.
   *
   * @return The current audio capabilities for the device.
   */
  @SuppressWarnings("InlinedApi")
  public AudioCapabilities register() {
    if (registered) {
      return checkNotNull(audioCapabilities);
    }
    registered = true;
    if (externalSurroundSoundSettingObserver != null) {
      externalSurroundSoundSettingObserver.register();
    }
    if (Util.SDK_INT >= 23 && audioDeviceCallback != null) {
      Api23.registerAudioDeviceCallback(context, audioDeviceCallback, handler);
    }
    Intent stickyIntent =
        context.registerReceiver(
            hdmiAudioPlugBroadcastReceiver,
            new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG),
            /* broadcastPermission= */ null,
            handler);
    audioCapabilities =
        AudioCapabilities.getCapabilitiesInternal(
            context, stickyIntent, audioAttributes, routedDevice);
    return audioCapabilities;
  }

  /**
   * Unregisters the receiver, meaning it will no longer notify the listener when audio capability
   * changes occur.
   */
  public void unregister() {
    if (!registered) {
      return;
    }
    audioCapabilities = null;
    if (Util.SDK_INT >= 23 && audioDeviceCallback != null) {
      Api23.unregisterAudioDeviceCallback(context, audioDeviceCallback);
    }
    context.unregisterReceiver(hdmiAudioPlugBroadcastReceiver);
    if (externalSurroundSoundSettingObserver != null) {
      externalSurroundSoundSettingObserver.unregister();
    }
    registered = false;
  }

  private void onNewAudioCapabilities(AudioCapabilities newAudioCapabilities) {
    if (registered && !newAudioCapabilities.equals(audioCapabilities)) {
      audioCapabilities = newAudioCapabilities;
      listener.onAudioCapabilitiesChanged(newAudioCapabilities);
    }
  }

  private final class HdmiAudioPlugBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
      if (!isInitialStickyBroadcast()) {
        onNewAudioCapabilities(
            AudioCapabilities.getCapabilitiesInternal(
                context, intent, audioAttributes, routedDevice));
      }
    }
  }

  private final class ExternalSurroundSoundSettingObserver extends ContentObserver {

    private final ContentResolver resolver;
    private final Uri settingUri;

    public ExternalSurroundSoundSettingObserver(
        Handler handler, ContentResolver resolver, Uri settingUri) {
      super(handler);
      this.resolver = resolver;
      this.settingUri = settingUri;
    }

    public void register() {
      resolver.registerContentObserver(settingUri, /* notifyForDescendants= */ false, this);
    }

    public void unregister() {
      resolver.unregisterContentObserver(this);
    }

    @Override
    public void onChange(boolean selfChange) {
      onNewAudioCapabilities(
          AudioCapabilities.getCapabilitiesInternal(context, audioAttributes, routedDevice));
    }
  }

  @RequiresApi(23)
  private final class AudioDeviceCallbackV23 extends AudioDeviceCallback {
    @Override
    public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
      onNewAudioCapabilities(
          AudioCapabilities.getCapabilitiesInternal(context, audioAttributes, routedDevice));
    }

    @Override
    public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
      if (Util.contains(removedDevices, routedDevice)) {
        routedDevice = null;
      }
      onNewAudioCapabilities(
          AudioCapabilities.getCapabilitiesInternal(context, audioAttributes, routedDevice));
    }
  }

  @RequiresApi(23)
  private static final class Api23 {

    @DoNotInline
    public static void registerAudioDeviceCallback(
        Context context, AudioDeviceCallback callback, Handler handler) {
      AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
      checkNotNull(audioManager).registerAudioDeviceCallback(callback, handler);
    }

    @DoNotInline
    public static void unregisterAudioDeviceCallback(
        Context context, AudioDeviceCallback callback) {
      AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
      checkNotNull(audioManager).unregisterAudioDeviceCallback(callback);
    }

    private Api23() {}
  }
}