blob: 7bec55f66c7f6728aab650e13e0945a55e385afc [file] [log] [blame]
Santos Cordon1ae2b852014-03-19 03:03:10 -07001/*
2 * Copyright 2014, 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 com.android.telecomm;
18
Santos Cordonb64c1502014-05-21 21:21:49 -070019import android.media.AudioManager;
Santos Cordon1ae2b852014-03-19 03:03:10 -070020import android.media.Ringtone;
21import android.media.RingtoneManager;
Santos Cordon5ba7f272014-05-28 13:59:49 -070022import android.net.Uri;
Santos Cordon1ae2b852014-03-19 03:03:10 -070023import android.os.Handler;
24import android.os.HandlerThread;
25import android.os.Message;
26import android.provider.Settings;
27
28import com.google.common.base.Preconditions;
29
30/**
31 * Plays the default ringtone. Uses {@link Ringtone} in a separate thread so that this class can be
32 * used from the main thread.
33 */
34class AsyncRingtonePlayer {
35 // Message codes used with the ringtone thread.
36 static final int EVENT_PLAY = 1;
37 static final int EVENT_STOP = 2;
38
39 /** Handler running on the ringtone thread. */
40 private Handler mHandler;
41
42 /** The current ringtone. Only used by the ringtone thread. */
43 private Ringtone mRingtone;
44
45 /** Plays the ringtone. */
Santos Cordon5ba7f272014-05-28 13:59:49 -070046 void play(Uri ringtone) {
Santos Cordon1ae2b852014-03-19 03:03:10 -070047 Log.d(this, "Posting play.");
Santos Cordon5ba7f272014-05-28 13:59:49 -070048 postMessage(EVENT_PLAY, true /* shouldCreateHandler */, ringtone);
Santos Cordon1ae2b852014-03-19 03:03:10 -070049 }
50
51 /** Stops playing the ringtone. */
52 void stop() {
53 Log.d(this, "Posting stop.");
Santos Cordon5ba7f272014-05-28 13:59:49 -070054 postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
Santos Cordon1ae2b852014-03-19 03:03:10 -070055 }
56
57 /**
58 * Posts a message to the ringtone-thread handler. Creates the handler if specified by the
59 * parameter shouldCreateHandler.
60 *
61 * @param messageCode The message to post.
62 * @param shouldCreateHandler True when a handler should be created to handle this message.
63 */
Santos Cordon5ba7f272014-05-28 13:59:49 -070064 private void postMessage(int messageCode, boolean shouldCreateHandler, Uri ringtone) {
Santos Cordon1ae2b852014-03-19 03:03:10 -070065 synchronized(this) {
66 if (mHandler == null && shouldCreateHandler) {
67 mHandler = getNewHandler();
68 }
69
70 if (mHandler == null) {
71 Log.d(this, "Message %d skipped because there is no handler.", messageCode);
72 } else {
Santos Cordon5ba7f272014-05-28 13:59:49 -070073 mHandler.obtainMessage(messageCode, ringtone).sendToTarget();
Santos Cordon1ae2b852014-03-19 03:03:10 -070074 }
75 }
76 }
77
78 /**
79 * Creates a new ringtone Handler running in its own thread.
80 */
81 private Handler getNewHandler() {
82 Preconditions.checkState(mHandler == null);
83
84 HandlerThread thread = new HandlerThread("ringtone-player");
85 thread.start();
86
87 return new Handler(thread.getLooper()) {
88 @Override
89 public void handleMessage(Message msg) {
90 switch(msg.what) {
91 case EVENT_PLAY:
Santos Cordon5ba7f272014-05-28 13:59:49 -070092 handlePlay((Uri) msg.obj);
Santos Cordon1ae2b852014-03-19 03:03:10 -070093 break;
94 case EVENT_STOP:
95 handleStop();
96 break;
97 }
98 }
99 };
100 }
101
102 /**
103 * Starts the actual playback of the ringtone. Executes on ringtone-thread.
104 */
Santos Cordon5ba7f272014-05-28 13:59:49 -0700105 private void handlePlay(Uri ringtoneUri) {
Santos Cordon1ae2b852014-03-19 03:03:10 -0700106 ThreadUtil.checkNotOnMainThread();
107 Log.i(this, "Play ringtone.");
108
109 if (mRingtone == null) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700110 mRingtone = getRingtone(ringtoneUri);
Santos Cordon1ae2b852014-03-19 03:03:10 -0700111
112 // Cancel everything if there is no ringtone.
113 if (mRingtone == null) {
114 handleStop();
115 return;
116 }
117 }
118
119 if (mRingtone.isPlaying()) {
120 Log.d(this, "Ringtone already playing.");
121 } else {
122 mRingtone.play();
123 Log.d(this, "Ringtone.play() invoked.");
124 }
125 }
126
127 /**
128 * Stops the playback of the ringtone. Executes on the ringtone-thread.
129 */
130 private void handleStop() {
131 ThreadUtil.checkNotOnMainThread();
132 Log.i(this, "Stop ringtone.");
133
134 if (mRingtone != null) {
135 Log.d(this, "Ringtone.stop() invoked.");
136 mRingtone.stop();
137 mRingtone = null;
138 }
139
140 synchronized(this) {
141 if (mHandler.hasMessages(EVENT_PLAY)) {
142 Log.v(this, "Keeping alive ringtone thread for pending messages.");
143 } else {
144 mHandler.removeMessages(EVENT_STOP);
145 mHandler.getLooper().quitSafely();
146 mHandler = null;
147 Log.v(this, "Handler cleared.");
148 }
149 }
150 }
151
Santos Cordon5ba7f272014-05-28 13:59:49 -0700152 private Ringtone getRingtone(Uri ringtoneUri) {
153 if (ringtoneUri == null) {
154 ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
155 }
156
157 Ringtone ringtone = RingtoneManager.getRingtone(TelecommApp.getInstance(), ringtoneUri);
Santos Cordonb64c1502014-05-21 21:21:49 -0700158 ringtone.setStreamType(AudioManager.STREAM_RING);
159 return ringtone;
Santos Cordon1ae2b852014-03-19 03:03:10 -0700160 }
161}