Skip to content

Commit 1ee96b8

Browse files
committed
Add audio focus control.
Change-Id: I17697bd557f4f399ae4a557a369a8bd280313b3a
1 parent 4d67d83 commit 1ee96b8

File tree

8 files changed

+147
-1
lines changed

8 files changed

+147
-1
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@ Another example with UAMP is to perform a search with the term "jazz?" one would
5858
Verification
5959
============
6060

61+
Audio Focus
62+
-----------
63+
64+
This app allows for testing how media apps respond to audio focus changes.
65+
66+
The app allows requesting and abandoning the following types of audio focus:
67+
68+
* AUDIOFOCUS_GAIN
69+
* AUDIOFOCUS_GAIN_TRANSIENT
70+
* AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
71+
72+
For more information on audio focus please see
73+
[Managing Audio Focus](https://developer.android.com/guide/topics/media-apps/audio-focus.html).
74+
75+
Supported Actions
76+
-----------------
77+
6178
This tool displays the supported actions as reported by the MediaSession in the call to
6279
[MediaSessionCompat.setPlaybackState()](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.html#setPlaybackState(android.support.v4.media.session.PlaybackStateCompat))
6380
as a list of prepare and play actions on the main screen. For actions that are not declared as

mediacontroller/src/main/java/com/example/android/mediacontroller/MediaAppControllerActivity.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import android.graphics.Color;
2626
import android.graphics.drawable.BitmapDrawable;
2727
import android.graphics.drawable.Drawable;
28+
import android.media.AudioManager;
2829
import android.net.Uri;
2930
import android.os.Bundle;
3031
import android.os.RemoteException;
@@ -49,12 +50,14 @@
4950
import android.util.SparseArray;
5051
import android.view.View;
5152
import android.view.ViewGroup;
53+
import android.widget.AdapterView;
5254
import android.widget.EditText;
5355
import android.widget.ImageButton;
5456
import android.widget.ImageView;
5557
import android.widget.Spinner;
5658
import android.widget.TextView;
5759
import android.widget.Toast;
60+
import android.widget.ToggleButton;
5861

5962
import java.util.ArrayList;
6063
import java.util.Collections;
@@ -112,6 +115,7 @@ public class MediaAppControllerActivity extends AppCompatActivity {
112115
private MediaAppDetails mMediaAppDetails;
113116
private MediaControllerCompat mController;
114117
private MediaBrowserCompat mBrowser;
118+
private AudioFocusHelper mAudioFocusHelper;
115119

116120
private View mRootView;
117121
private Spinner mInputTypeView;
@@ -332,6 +336,10 @@ private void setupButtons() {
332336
findViewById(R.id.action_prepare).setOnClickListener(preparePlayHandler);
333337
findViewById(R.id.action_play).setOnClickListener(preparePlayHandler);
334338

339+
mAudioFocusHelper = new AudioFocusHelper(this,
340+
(ToggleButton) findViewById(R.id.audio_focus_button),
341+
(Spinner) findViewById(R.id.audio_focus_type));
342+
335343
mActionButtonMap.clear();
336344
final List<Action> mediaActions = Action.createActions(this);
337345
for (final Action action : mediaActions) {
@@ -638,6 +646,85 @@ private int getTint(@PlaybackStateCompat.Actions long actions,
638646
}
639647
}
640648

649+
/**
650+
* Helper class to manage audio focus requests and the UI surrounding this feature.
651+
*/
652+
private static class AudioFocusHelper
653+
implements View.OnClickListener,
654+
AudioManager.OnAudioFocusChangeListener,
655+
AdapterView.OnItemSelectedListener {
656+
657+
/**
658+
* This list MUST match the order of the string-array
659+
* {@see R.array.audio_focus_types}.
660+
*/
661+
private static final int[] FOCUS_TYPES = {
662+
AudioManager.AUDIOFOCUS_GAIN,
663+
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
664+
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
665+
};
666+
667+
private final AudioManager mAudioManager;
668+
private final ToggleButton mToggleButton;
669+
private final Spinner mFocusTypeSpinner;
670+
671+
private AudioFocusHelper(@NonNull Context context,
672+
@NonNull ToggleButton focusToggleButton,
673+
@NonNull Spinner focusTypeSpinner) {
674+
675+
mAudioManager = (AudioManager) context.getSystemService(AUDIO_SERVICE);
676+
mToggleButton = focusToggleButton;
677+
mFocusTypeSpinner = focusTypeSpinner;
678+
679+
mToggleButton.setOnClickListener(this);
680+
mFocusTypeSpinner.setOnItemSelectedListener(this);
681+
}
682+
683+
@Override
684+
public void onClick(View v) {
685+
if (mToggleButton.isChecked()) {
686+
requestAudioFocus(getSelectedFocusType());
687+
} else {
688+
mAudioManager.abandonAudioFocus(this);
689+
}
690+
}
691+
692+
@Override
693+
public void onAudioFocusChange(int focusChange) {
694+
switch (focusChange) {
695+
case AudioManager.AUDIOFOCUS_GAIN:
696+
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
697+
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
698+
mToggleButton.setChecked(true);
699+
break;
700+
default:
701+
mToggleButton.setChecked(false);
702+
}
703+
}
704+
705+
private int getSelectedFocusType() {
706+
return FOCUS_TYPES[mFocusTypeSpinner.getSelectedItemPosition()];
707+
}
708+
709+
private void requestAudioFocus(final int hint) {
710+
mAudioManager.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, hint);
711+
}
712+
713+
@Override
714+
public void onItemSelected(AdapterView parent, View view, int position, long id) {
715+
// If we're holding audio focus and the type should change, automatically
716+
// request the new type of focus.
717+
if (mToggleButton.isChecked()) {
718+
requestAudioFocus(getSelectedFocusType());
719+
}
720+
}
721+
722+
@Override
723+
public void onNothingSelected(AdapterView parent) {
724+
// Nothing to do.
725+
}
726+
}
727+
641728
private static class KeyComparator implements Comparator<String> {
642729
private final Set<String> mCapKeys = new HashSet<>();
643730

mediacontroller/src/main/res/layout/media_info.xml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,35 @@
8080

8181
<TextView
8282
android:id="@+id/media_info"
83+
android:layout_width="match_parent"
84+
android:layout_height="0dp"
85+
android:layout_weight="1"
86+
android:gravity="top|start"
87+
android:text="@string/media_info_default" />
88+
89+
<TextView
90+
style="@style/TextAppearance.AppCompat.Large"
8391
android:layout_width="wrap_content"
8492
android:layout_height="wrap_content"
85-
android:text="@string/media_info_default" />
93+
android:text="@string/audio_focus_title" />
94+
95+
<LinearLayout
96+
android:layout_width="match_parent"
97+
android:layout_height="wrap_content"
98+
android:layout_marginBottom="32dp">
99+
100+
<ToggleButton
101+
android:id="@+id/audio_focus_button"
102+
android:layout_width="wrap_content"
103+
android:layout_height="wrap_content"
104+
android:textOff="@string/audio_focus_gain_focus"
105+
android:textOn="@string/audio_focus_abandon_focus" />
106+
107+
<Spinner
108+
android:id="@+id/audio_focus_type"
109+
android:layout_width="match_parent"
110+
android:layout_height="match_parent"
111+
android:entries="@array/audio_focus_types" />
112+
113+
LinearLayout>
86114
LinearLayout>

mediacontroller/src/main/res/values/strings.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,18 @@
7474

7575
<string name="action_skip_30s_forward">Skip 30sstring>
7676
<string name="action_skip_30s_backward">Skip back 30sstring>
77+
78+
<string name="audio_focus_title">Audio Focusstring>
79+
<string name="audio_focus_gain_focus">Gainstring>
80+
<string name="audio_focus_abandon_focus">Abandonstring>
81+
82+
86+
<string-array name="audio_focus_types">
87+
<item>GAINitem>
88+
<item>GAIN_TRANSIENTitem>
89+
<item>GAIN_TRANSIENT_MAY_DUCKitem>
90+
string-array>
7791
resources>

screenshots/choose.png

440 KB
Loading

screenshots/control.png

1.72 MB
Loading

screenshots/screenshots.png

24 KB
Loading

screenshots/search.png

465 KB
Loading

0 commit comments

Comments
 (0)