• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package com.android.car.settings.sound;
18 
19 import android.content.Context;
20 import android.media.AudioAttributes;
21 import android.media.Ringtone;
22 import android.media.RingtoneManager;
23 import android.net.Uri;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.provider.Settings;
27 
28 import androidx.annotation.Nullable;
29 import androidx.annotation.VisibleForTesting;
30 
31 import com.android.car.settings.R;
32 import com.android.car.settings.common.Logger;
33 
34 import java.util.HashMap;
35 import java.util.Map;
36 
37 /** Manges the audio played by the {@link VolumeSettingsPreferenceController}. */
38 public class VolumeSettingsRingtoneManager {
39 
40     private static final Logger LOG = new Logger(VolumeSettingsRingtoneManager.class);
41 
42     private static final int AUDIO_FEEDBACK_DURATION_MS = 1000;
43 
44     private final Context mContext;
45     private final Handler mUiHandler;
46     private final Map<Integer, Ringtone> mGroupToRingtoneMap = new HashMap<>();
47     private final Map<Integer, Uri> mAttributeUsageToRingtoneUriMap = new HashMap<>();
48 
49     @Nullable
50     private Ringtone mCurrentRingtone;
51 
VolumeSettingsRingtoneManager(Context context)52     public VolumeSettingsRingtoneManager(Context context) {
53         mContext = context;
54         mUiHandler = new Handler(Looper.getMainLooper());
55         populateAttributeUsageToRingtoneUriMap();
56     }
57 
populateAttributeUsageToRingtoneUriMap()58     private void populateAttributeUsageToRingtoneUriMap() {
59         int[] audioAttributeUsages = mContext
60             .getResources()
61             .getIntArray(R.array.config_ringtone_audio_attribute_usages_map_key);
62         String[] ringtoneStrings = mContext
63             .getResources()
64             .getStringArray(R.array.config_ringtone_uri_map_value);
65         if (audioAttributeUsages.length != ringtoneStrings.length) {
66             throw new IllegalArgumentException(
67                 "config_ringtone_audio_attribute_usages_map_key and config_ringtone_uri_map_value "
68                 + "must have the same number of items");
69         }
70 
71         for (int i = 0; i < audioAttributeUsages.length; i++) {
72             @AudioAttributes.AttributeUsage int usage = audioAttributeUsages[i];
73             if (AudioAttributes.usageToString(usage).contains("unknown usage")) {
74                 throw new IllegalArgumentException(
75                     "Invalid usage in config_ringtone_audio_attribute_usages_map_key");
76             }
77             Uri ringtoneUri = Uri.parse(ringtoneStrings[i]);
78             LOG.d("Usage " + usage + " mapped to ringtone " + ringtoneUri);
79             mAttributeUsageToRingtoneUriMap.put(usage, ringtoneUri);
80         }
81     }
82 
83     /**
84      * Play the audio defined by the current group and usage. Stop the current ringtone if it is a
85      * different ringtone than what is currently playing.
86      */
playAudioFeedback(int group, int usage)87     public void playAudioFeedback(int group, int usage) {
88         Ringtone nextRingtone = lazyLoadRingtone(group, usage);
89         if (mCurrentRingtone != null && mCurrentRingtone != nextRingtone
90                 && mCurrentRingtone.isPlaying()) {
91             mCurrentRingtone.stop();
92         }
93 
94         mUiHandler.removeCallbacksAndMessages(null);
95         mCurrentRingtone = nextRingtone;
96         mCurrentRingtone.play();
97         mUiHandler.postDelayed(() -> {
98             if (mCurrentRingtone.isPlaying()) {
99                 mCurrentRingtone.stop();
100                 mCurrentRingtone = null;
101             }
102         }, AUDIO_FEEDBACK_DURATION_MS);
103     }
104 
105     /** Stop playing the current ringtone. */
stopCurrentRingtone()106     public void stopCurrentRingtone() {
107         if (mCurrentRingtone != null) {
108             mCurrentRingtone.stop();
109         }
110     }
111 
112     /** If we have already seen this ringtone, use it. Otherwise load when requested. */
lazyLoadRingtone(int group, int usage)113     private Ringtone lazyLoadRingtone(int group, int usage) {
114         if (!mGroupToRingtoneMap.containsKey(group)) {
115             Ringtone ringtone = RingtoneManager.getRingtone(mContext, getRingtoneUri(usage));
116             ringtone.setAudioAttributes(new AudioAttributes.Builder().setUsage(usage).build());
117             mGroupToRingtoneMap.put(group, ringtone);
118         }
119         return mGroupToRingtoneMap.get(group);
120     }
121 
122     // TODO: bundle car-specific audio sample assets in res/raw by usage
123     @VisibleForTesting
getRingtoneUri(@udioAttributes.AttributeUsage int usage)124     Uri getRingtoneUri(@AudioAttributes.AttributeUsage int usage) {
125         return mAttributeUsageToRingtoneUriMap.getOrDefault(
126             usage, Settings.System.DEFAULT_RINGTONE_URI);
127     }
128 }
129