Ringtone.java revision 098d580cc2bb6c0891c756a4e5230c6c6b0d2376
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 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 138 try { 139 if (cursor != null && cursor.getCount() == 1) { 140 cursor.moveToFirst(); 141 return cursor.getString(2); 142 } else { 143 title = uri.getLastPathSegment(); 144 } 145 } finally { 146 if (cursor != null) { 147 cursor.close(); 148 } 149 } 150 } 151 } 152 153 if (title == null) { 154 title = context.getString(com.android.internal.R.string.ringtone_unknown); 155 156 if (title == null) { 157 title = ""; 158 } 159 } 160 161 return title; 162 } 163 164 /** 165 * Set {@link Uri} to be used for ringtone playback. Attempts to open 166 * locally, otherwise will delegate playback to remote 167 * {@link IRingtonePlayer}. 168 * 169 * @hide 170 */ 171 public void setUri(Uri uri) { 172 destroyLocalPlayer(); 173 174 mUri = uri; 175 if (mUri == null) { 176 return; 177 } 178 179 // TODO: detect READ_EXTERNAL and specific content provider case, instead of relying on throwing 180 181 // try opening uri locally before delegating to remote player 182 mLocalPlayer = new MediaPlayer(); 183 try { 184 mLocalPlayer.setDataSource(mContext, mUri); 185 mLocalPlayer.setAudioStreamType(mStreamType); 186 mLocalPlayer.prepare(); 187 188 } catch (SecurityException e) { 189 destroyLocalPlayer(); 190 if (!mAllowRemote) { 191 throw new IllegalStateException("Remote playback not allowed", e); 192 } 193 } catch (IOException e) { 194 destroyLocalPlayer(); 195 if (!mAllowRemote) { 196 throw new IllegalStateException("Remote playback not allowed", e); 197 } 198 } 199 200 if (LOGD) { 201 if (mLocalPlayer != null) { 202 Log.d(TAG, "Successfully created local player"); 203 } else { 204 Log.d(TAG, "Problem opening; delegating to remote player"); 205 } 206 } 207 } 208 209 /** {@hide} */ 210 public Uri getUri() { 211 return mUri; 212 } 213 214 /** 215 * Plays the ringtone. 216 */ 217 public void play() { 218 if (mLocalPlayer != null) { 219 // do not play ringtones if stream volume is 0 220 // (typically because ringer mode is silent). 221 if (mAudioManager.getStreamVolume(mStreamType) != 0) { 222 mLocalPlayer.start(); 223 } 224 } else if (mAllowRemote) { 225 try { 226 mRemotePlayer.play(mRemoteToken, mUri, mStreamType); 227 } catch (RemoteException e) { 228 Log.w(TAG, "Problem playing ringtone: " + e); 229 } 230 } else { 231 throw new IllegalStateException("Neither local nor remote playback available"); 232 } 233 } 234 235 /** 236 * Stops a playing ringtone. 237 */ 238 public void stop() { 239 if (mLocalPlayer != null) { 240 destroyLocalPlayer(); 241 } else if (mAllowRemote) { 242 try { 243 mRemotePlayer.stop(mRemoteToken); 244 } catch (RemoteException e) { 245 Log.w(TAG, "Problem stopping ringtone: " + e); 246 } 247 } 248 } 249 250 private void destroyLocalPlayer() { 251 if (mLocalPlayer != null) { 252 mLocalPlayer.reset(); 253 mLocalPlayer.release(); 254 mLocalPlayer = null; 255 } 256 } 257 258 /** 259 * Whether this ringtone is currently playing. 260 * 261 * @return True if playing, false otherwise. 262 */ 263 public boolean isPlaying() { 264 if (mLocalPlayer != null) { 265 return mLocalPlayer.isPlaying(); 266 } else if (mAllowRemote) { 267 try { 268 return mRemotePlayer.isPlaying(mRemoteToken); 269 } catch (RemoteException e) { 270 Log.w(TAG, "Problem checking ringtone: " + e); 271 return false; 272 } 273 } else { 274 throw new IllegalStateException("Neither local nor remote playback available"); 275 } 276 } 277 278 void setTitle(String title) { 279 mTitle = title; 280 } 281} 282