1/* 2 * Copyright (C) 2006 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 17package android.media; 18 19import android.content.ContentResolver; 20import android.content.Context; 21import android.database.Cursor; 22import android.net.Uri; 23import android.os.Binder; 24import android.os.RemoteException; 25import android.provider.DrmStore; 26import android.provider.MediaStore; 27import android.provider.Settings; 28import android.util.Log; 29 30import java.io.IOException; 31 32/** 33 * Ringtone provides a quick method for playing a ringtone, notification, or 34 * other similar types of sounds. 35 * <p> 36 * For ways of retrieving {@link Ringtone} objects or to show a ringtone 37 * picker, see {@link RingtoneManager}. 38 * 39 * @see RingtoneManager 40 */ 41public class Ringtone { 42 private static final String TAG = "Ringtone"; 43 private static final boolean LOGD = true; 44 45 private static final String[] MEDIA_COLUMNS = new String[] { 46 MediaStore.Audio.Media._ID, 47 MediaStore.Audio.Media.DATA, 48 MediaStore.Audio.Media.TITLE 49 }; 50 51 private static final String[] DRM_COLUMNS = new String[] { 52 DrmStore.Audio._ID, 53 DrmStore.Audio.DATA, 54 DrmStore.Audio.TITLE 55 }; 56 57 private final Context mContext; 58 private final AudioManager mAudioManager; 59 private final boolean mAllowRemote; 60 private final IRingtonePlayer mRemotePlayer; 61 private final Binder mRemoteToken; 62 63 private MediaPlayer mLocalPlayer; 64 65 private Uri mUri; 66 private String mTitle; 67 68 private int mStreamType = AudioManager.STREAM_RING; 69 70 /** {@hide} */ 71 public Ringtone(Context context, boolean allowRemote) { 72 mContext = context; 73 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 74 mAllowRemote = allowRemote; 75 mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null; 76 mRemoteToken = allowRemote ? new Binder() : null; 77 } 78 79 /** 80 * Sets the stream type where this ringtone will be played. 81 * 82 * @param streamType The stream, see {@link AudioManager}. 83 */ 84 public void setStreamType(int streamType) { 85 mStreamType = streamType; 86 87 // The stream type has to be set before the media player is prepared. 88 // Re-initialize it. 89 setUri(mUri); 90 } 91 92 /** 93 * Gets the stream type where this ringtone will be played. 94 * 95 * @return The stream type, see {@link AudioManager}. 96 */ 97 public int getStreamType() { 98 return mStreamType; 99 } 100 101 /** 102 * Returns a human-presentable title for ringtone. Looks in media and DRM 103 * content providers. If not in either, uses the filename 104 * 105 * @param context A context used for querying. 106 */ 107 public String getTitle(Context context) { 108 if (mTitle != null) return mTitle; 109 return mTitle = getTitle(context, mUri, true); 110 } 111 112 private static String getTitle(Context context, Uri uri, boolean followSettingsUri) { 113 Cursor cursor = null; 114 ContentResolver res = context.getContentResolver(); 115 116 String title = null; 117 118 if (uri != null) { 119 String authority = uri.getAuthority(); 120 121 if (Settings.AUTHORITY.equals(authority)) { 122 if (followSettingsUri) { 123 Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, 124 RingtoneManager.getDefaultType(uri)); 125 String actualTitle = getTitle(context, actualUri, false); 126 title = context 127 .getString(com.android.internal.R.string.ringtone_default_with_actual, 128 actualTitle); 129 } 130 } else { 131 try { 132 if (DrmStore.AUTHORITY.equals(authority)) { 133 cursor = res.query(uri, DRM_COLUMNS, null, null, null); 134 } else if (MediaStore.AUTHORITY.equals(authority)) { 135 cursor = res.query(uri, MEDIA_COLUMNS, null, null, null); 136 } 137 } catch (SecurityException e) { 138 // missing cursor is handled below 139 } 140 141 try { 142 if (cursor != null && cursor.getCount() == 1) { 143 cursor.moveToFirst(); 144 return cursor.getString(2); 145 } else { 146 title = uri.getLastPathSegment(); 147 } 148 } finally { 149 if (cursor != null) { 150 cursor.close(); 151 } 152 } 153 } 154 } 155 156 if (title == null) { 157 title = context.getString(com.android.internal.R.string.ringtone_unknown); 158 159 if (title == null) { 160 title = ""; 161 } 162 } 163 164 return title; 165 } 166 167 /** 168 * Set {@link Uri} to be used for ringtone playback. Attempts to open 169 * locally, otherwise will delegate playback to remote 170 * {@link IRingtonePlayer}. 171 * 172 * @hide 173 */ 174 public void setUri(Uri uri) { 175 destroyLocalPlayer(); 176 177 mUri = uri; 178 if (mUri == null) { 179 return; 180 } 181 182 // TODO: detect READ_EXTERNAL and specific content provider case, instead of relying on throwing 183 184 // try opening uri locally before delegating to remote player 185 mLocalPlayer = new MediaPlayer(); 186 try { 187 mLocalPlayer.setDataSource(mContext, mUri); 188 mLocalPlayer.setAudioStreamType(mStreamType); 189 mLocalPlayer.prepare(); 190 191 } catch (SecurityException e) { 192 destroyLocalPlayer(); 193 if (!mAllowRemote) { 194 Log.w(TAG, "Remote playback not allowed: " + e); 195 } 196 } catch (IOException e) { 197 destroyLocalPlayer(); 198 if (!mAllowRemote) { 199 Log.w(TAG, "Remote playback not allowed: " + e); 200 } 201 } 202 203 if (LOGD) { 204 if (mLocalPlayer != null) { 205 Log.d(TAG, "Successfully created local player"); 206 } else { 207 Log.d(TAG, "Problem opening; delegating to remote player"); 208 } 209 } 210 } 211 212 /** {@hide} */ 213 public Uri getUri() { 214 return mUri; 215 } 216 217 /** 218 * Plays the ringtone. 219 */ 220 public void play() { 221 if (mLocalPlayer != null) { 222 // do not play ringtones if stream volume is 0 223 // (typically because ringer mode is silent). 224 if (mAudioManager.getStreamVolume(mStreamType) != 0) { 225 mLocalPlayer.start(); 226 } 227 } else if (mAllowRemote) { 228 final Uri canonicalUri = mUri.getCanonicalUri(); 229 try { 230 mRemotePlayer.play(mRemoteToken, canonicalUri, mStreamType); 231 } catch (RemoteException e) { 232 Log.w(TAG, "Problem playing ringtone: " + e); 233 } 234 } else { 235 Log.w(TAG, "Neither local nor remote playback available"); 236 } 237 } 238 239 /** 240 * Stops a playing ringtone. 241 */ 242 public void stop() { 243 if (mLocalPlayer != null) { 244 destroyLocalPlayer(); 245 } else if (mAllowRemote) { 246 try { 247 mRemotePlayer.stop(mRemoteToken); 248 } catch (RemoteException e) { 249 Log.w(TAG, "Problem stopping ringtone: " + e); 250 } 251 } 252 } 253 254 private void destroyLocalPlayer() { 255 if (mLocalPlayer != null) { 256 mLocalPlayer.reset(); 257 mLocalPlayer.release(); 258 mLocalPlayer = null; 259 } 260 } 261 262 /** 263 * Whether this ringtone is currently playing. 264 * 265 * @return True if playing, false otherwise. 266 */ 267 public boolean isPlaying() { 268 if (mLocalPlayer != null) { 269 return mLocalPlayer.isPlaying(); 270 } else if (mAllowRemote) { 271 try { 272 return mRemotePlayer.isPlaying(mRemoteToken); 273 } catch (RemoteException e) { 274 Log.w(TAG, "Problem checking ringtone: " + e); 275 return false; 276 } 277 } else { 278 Log.w(TAG, "Neither local nor remote playback available"); 279 return false; 280 } 281 } 282 283 void setTitle(String title) { 284 mTitle = title; 285 } 286} 287