[Android] AudioFocus 알아보기(2) - 커스텀과 확장

개요

지난 포스트 에서 작성한 코드인 AudioFocusHelper.java 에 약간의 기능을 덧붙이고, Build.VERSION_CODES.O 미만인 단말에서도 동작할 수 있도록 수정해 볼 것이다. 말은 거창하지만 그리 많이 수정할 것은 아니다.


무엇을 수정할 것인가

우선, AudioFocusBuild.VERSION_CODES.O 미만인 단말에서도 잘 동작해야 하기 때문에 이 부분의 코드를 고쳐보려 한다. 그전에, 지난 포스팅에서 작성한 코드에 TODO를 먼저 붙여 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
private static final String TAG = AudioFocusHelper.class.getSimpleName();
private static AudioManager audioManager;
private static AudioFocusRequest audioFocusRequest;

public AudioFocusHelper(@Nonnull Context context) {
Log.d(TAG, "Create AudioFocusHelper");
// TODO: 1. Build.VERSION_CODES.O 미만인 단말에서의 생성자 세팅 추가
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioAttributes mAudioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(mAudioAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(this)
.setWillPauseWhenDucked(true)
.build();
}
}

public void requestAudioFocus() {
Log.d(TAG, "AudioFocus >> called requestAudioFocus() / Build.VERSION: " + Build.VERSION.SDK_INT);
// TODO: 2. Build.VERSION_CODES.O 미만 단말에서의 AudioFocus Request 기능 추가
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Log.d(TAG, "AudioFocus >> requestAudioFocus");
audioManager.requestAudioFocus(audioFocusRequest);
}
}

// TODO: 3. abandonAudioFocus() function 추가

@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// 이제부터 AudioFocus는 우리 앱의 소유!
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN");
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
// 일시적으로 AudioFocus를 가져온다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN_TRANSIENT");
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
// 15초 이상 점유하면 안 된다. 앱의 소리가 나지만, Background App의 사운드가 작게 들릴 수 있다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK");
break;
case AudioManager.AUDIOFOCUS_LOSS:
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS");
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// 일시적인 LOSS로, 잠깐 사용한 App이 점유를 끝내면 가장 마지막에 GAIN한 App이 AudioFocus를 점유한다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS_TRANSIENT");
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// 볼륨을 낮추는 것을 권장한다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
break;
case AudioManager.AUDIOFOCUS_REQUEST_FAILED:
// 요청 실패할 경우의 로직을 추가한다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_REQUEST_FAILED");
break;
}
}
}

1번부터 3번까지의 TODO를 추가해 보았다. 1번부터 천천히 수정해 보자.


1. Build.VERSION_CODES.O 미만인 단말에서의 생성자 세팅 추가

이 코드는 아주 간단하다. Build.VERSION_CODES.O 미만인 단말에서는 AudioManager 생성만 해 주면 된다. 그래서 별도의 else 처리 없이, if 문 안에 있던 AudioManager 를 밖으로 빼 준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public AudioFocusHelper(@Nonnull Context context) {
Log.d(TAG, "Create AudioFocusHelper");
// 이렇게 위로 빼 주면 됨
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioAttributes mAudioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(mAudioAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(this)
.setWillPauseWhenDucked(true)
.build();
}
}

2. Build.VERSION_CODES.O 미만 단말에서의 AudioFocus Request 기능 추가

Oreo, API level 26보다 낮은 단말에서 AudioFocus 를 얻고 싶다면 다음의 코드를 사용하자.

1
2
3
4
5
6
7
8
9
public void requestAudioFocus() {
Log.d(TAG, "AudioFocus >> called requestAudioFocus() / Build.VERSION: " + Build.VERSION.SDK_INT);
// TODO: 2. Build.VERSION_CODES.O 미만 단말에서의 AudioFocus Request 기능 추가
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioManager.requestAudioFocus(audioFocusRequest);
} else {
audioManager.requestAudioFocus(this, AudioManager.USE_DEFAULT_STREAM_TYPE, AudioManager.AUDIOFOCUS_GAIN);
}
}

requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) 형태를 따라 적절한 값을 넣어 주면 된다.


3. abandonAudioFocus() function 추가

앞서 우리는 AudioFocus 를 요청만 했지 반납하지 않았다. 서비스가 종료되거나, 더이상의 Audio가 필요하지 않을 땐 AudioFocus 를 반납하는 것이 인지상정! 이 코드도 굉장히 짧다.

1
2
3
4
5
6
7
8
public void abandonAudioFocus() {
Log.d(TAG, "AudioFocus >> called abandonAudioFocus()");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioManager.abandonAudioFocusRequest(audioFocusRequest);
} else {
audioManager.abandonAudioFocus(this);
}
}

너무 쉽죠?


전체 코드 보기

Oreo 미만 단말에서 AudioFocus 를 요청할 수 있고, 반납 기능이 추가된 AudioFocusHelper.java 코드 전체는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
private static final String TAG = AudioFocusHelper.class.getSimpleName();
private static AudioManager audioManager;
private static AudioFocusRequest audioFocusRequest;

public AudioFocusHelper(@Nonnull Context context) {
Log.d(TAG, "Create AudioFocusHelper");
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AudioAttributes mAudioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(mAudioAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(this)
.setWillPauseWhenDucked(true)
.build();
}
}

public void requestAudioFocus() {
Log.d(TAG, "AudioFocus >> called requestAudioFocus() / Build.VERSION: " + Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioManager.requestAudioFocus(audioFocusRequest);
} else {
audioManager.requestAudioFocus(this, AudioManager.USE_DEFAULT_STREAM_TYPE, AudioManager.AUDIOFOCUS_GAIN);
}
}

public void abandonAudioFocus() {
Log.d(TAG, "AudioFocus >> called abandonAudioFocus()");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioManager.abandonAudioFocusRequest(audioFocusRequest);
} else {
audioManager.abandonAudioFocus(this);
}
}

@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// 이제부터 AudioFocus는 우리 앱의 소유!
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN");
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
// 일시적으로 AudioFocus를 가져온다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN_TRANSIENT");
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
// 15초 이상 점유하면 안 된다. 앱의 소리가 나지만, Background App의 사운드가 작게 들릴 수 있다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK");
break;
case AudioManager.AUDIOFOCUS_LOSS:
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS");
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// 일시적인 LOSS로, 잠깐 사용한 App이 점유를 끝내면 가장 마지막에 GAIN한 App이 AudioFocus를 점유한다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS_TRANSIENT");
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// 볼륨을 낮추는 것을 권장한다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
break;
case AudioManager.AUDIOFOCUS_REQUEST_FAILED:
// 요청 실패할 경우의 로직을 추가한다.
Log.d(TAG, "AudioFocus >> AUDIOFOCUS_REQUEST_FAILED");
break;
}
}
}

위의 코드에서 나는 OnAudioFocusChangeListenerimplement 하여 구현하였지만, class 내에서 직접 new 로 선언해 주어도 문제가 되지 않는다. 이전 코드는 그런 형식으로 되어 있었는데, implement 하는 방식이 더 깔끔하게 구현될 것 같아서 약간의 리팩토링 과정을 거쳐 수정하였다. 아무튼, 이대로 싱겁게 이번 포스팅 끝!


Share