1 /*
2  * Copyright (C) 2017 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.server.telecom.testapps;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.app.PendingIntent;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.graphics.drawable.Icon;
25 import android.media.AudioAttributes;
26 import android.media.MediaPlayer;
27 import android.media.RingtoneManager;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.telecom.Call;
31 import android.telecom.CallAudioState;
32 import android.telecom.Connection;
33 import android.telecom.ConnectionService;
34 import android.telecom.DisconnectCause;
35 import android.telecom.VideoProfile;
36 
37 import com.android.server.telecom.testapps.R;
38 
39 /**
40  * Sample self-managed {@link Connection} for a self-managed {@link ConnectionService}.
41  * <p>
42  * See {@link android.telecom} for more information on self-managed {@link ConnectionService}s.
43  */
44 public class SelfManagedConnection extends Connection {
45     public static class Listener {
onConnectionStateChanged(SelfManagedConnection connection)46         public void onConnectionStateChanged(SelfManagedConnection connection) {}
onConnectionRemoved(SelfManagedConnection connection)47         public void onConnectionRemoved(SelfManagedConnection connection) {}
48     }
49     public static final String INCOMING_CALL_CHANNEL_ID = "INCOMING_CALL_CHANNEL_ID";
50     public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
51             "com.android.server.telecom.testapps.extra.PHONE_ACCOUNT_HANDLE";
52     public static final String CALL_NOTIFICATION = "com.android.server.telecom.testapps.CALL";
53 
54     private static int sNextCallId = 1;
55 
56     private final int mCallId;
57     private final Context mContext;
58     private final SelfManagedCallList mCallList;
59     private final MediaPlayer mMediaPlayer;
60     private final boolean mIsIncomingCall;
61     private boolean mIsIncomingCallUiShowing;
62     private Listener mListener;
63     private boolean mIsHandover;
64     private Notification.Builder mNotificationBuilder;
65 
SelfManagedConnection(SelfManagedCallList callList, Context context, boolean isIncoming)66     SelfManagedConnection(SelfManagedCallList callList, Context context, boolean isIncoming) {
67         mCallList = callList;
68         mMediaPlayer = createMediaPlayer(context);
69         mIsIncomingCall = isIncoming;
70         mContext = context;
71         mCallId = sNextCallId++;
72     }
73 
setListener(Listener listener)74     public void setListener(Listener listener) {
75         mListener = listener;
76     }
77 
78     /**
79      * Handles updates to the audio state of the connection.
80      * @param state The new connection audio state.
81      */
82     @Override
onCallAudioStateChanged(CallAudioState state)83     public void onCallAudioStateChanged(CallAudioState state) {
84         mCallList.notifyCallModified();
85     }
86 
87     @Override
onShowIncomingCallUi()88     public void onShowIncomingCallUi() {
89         if (isHandover()) {
90             return;
91         }
92         // Create the fullscreen intent used to show the fullscreen incoming call UX.
93         Intent intent = new Intent(Intent.ACTION_MAIN, null);
94         intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
95         intent.setClass(mContext, IncomingSelfManagedCallActivity.class);
96         intent.putExtra(IncomingSelfManagedCallActivity.EXTRA_CALL_ID, mCallId);
97         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 1, intent, 0);
98 
99         // Build the notification as an ongoing high priority item.
100         final Notification.Builder builder = new Notification.Builder(mContext,
101                 INCOMING_CALL_CHANNEL_ID);
102         builder.setOngoing(true);
103         builder.setPriority(Notification.PRIORITY_HIGH);
104 
105         // Set up the main intent to send the user to the incoming call screen.
106         builder.setContentIntent(pendingIntent);
107         builder.setFullScreenIntent(pendingIntent, true);
108 
109         // Setup notification content.
110         builder.setSmallIcon(R.drawable.ic_android_black_24dp);
111         builder.setContentTitle("Incoming call...");
112         builder.setContentText("Incoming test call from " + getAddress());
113 
114         // Setup answer and reject call button
115         final Intent answerIntent = new Intent(
116                 SelfManagedCallNotificationReceiver.ACTION_ANSWER_CALL, null, mContext,
117                 SelfManagedCallNotificationReceiver.class);
118         answerIntent.putExtra(IncomingSelfManagedCallActivity.EXTRA_CALL_ID, mCallId);
119         final Intent rejectIntent = new Intent(
120                 SelfManagedCallNotificationReceiver.ACTION_REJECT_CALL, null, mContext,
121                 SelfManagedCallNotificationReceiver.class);
122         rejectIntent.putExtra(IncomingSelfManagedCallActivity.EXTRA_CALL_ID, mCallId);
123 
124         builder.addAction(
125                 new Notification.Action.Builder(
126                         Icon.createWithResource(mContext, R.drawable.ic_android_black_24dp),
127                         "Answer",
128                         PendingIntent.getBroadcast(mContext, 0, answerIntent,
129                                 PendingIntent.FLAG_UPDATE_CURRENT))
130                         .build());
131         builder.addAction(
132                 new Notification.Action.Builder(
133                         Icon.createWithResource(mContext, R.drawable.ic_android_black_24dp),
134                         "Reject",
135                         PendingIntent.getBroadcast(mContext, 0, rejectIntent,
136                                 PendingIntent.FLAG_UPDATE_CURRENT))
137                         .build());
138 
139         Notification notification = builder.build();
140         notification.flags |= Notification.FLAG_INSISTENT;
141         NotificationManager notificationManager = mContext.getSystemService(
142                 NotificationManager.class);
143         mNotificationBuilder = builder;
144         notificationManager.notify(CALL_NOTIFICATION, mCallId, notification);
145     }
146 
147     @Override
onHold()148     public void onHold() {
149         if (mMediaPlayer != null) {
150             mMediaPlayer.pause();
151         }
152         setOnHold();
153     }
154 
155     @Override
onUnhold()156     public void onUnhold() {
157         if (mMediaPlayer != null) {
158             mMediaPlayer.start();
159         }
160         setActive();
161     }
162 
163     @Override
onAnswer(int videoState)164     public void onAnswer(int videoState) {
165         setConnectionActive();
166     }
167 
168     @Override
onAnswer()169     public void onAnswer() {
170         onAnswer(VideoProfile.STATE_AUDIO_ONLY);
171     }
172 
173     @Override
onReject()174     public void onReject() {
175         setConnectionDisconnected(DisconnectCause.REJECTED);
176     }
177 
178     @Override
onDisconnect()179     public void onDisconnect() {
180         setConnectionDisconnected(DisconnectCause.LOCAL);
181     }
182 
183     @Override
onSilence()184     public void onSilence() {
185         // Re-post our notification without a ringtone.
186         mNotificationBuilder.setOnlyAlertOnce(true);
187         NotificationManager notificationManager = mContext.getSystemService(
188                 NotificationManager.class);
189         notificationManager.notify(CALL_NOTIFICATION, mCallId, mNotificationBuilder.build());
190     }
191 
setConnectionActive()192     public void setConnectionActive() {
193         mMediaPlayer.start();
194         setActive();
195         if (mListener != null ) {
196             mListener.onConnectionStateChanged(this);
197         }
198     }
199 
setConnectionHeld()200     public void setConnectionHeld() {
201         mMediaPlayer.pause();
202         setOnHold();
203         if (mListener != null ) {
204             mListener.onConnectionStateChanged(this);
205         }
206     }
207 
setConnectionDisconnected(int cause)208     public void setConnectionDisconnected(int cause) {
209         NotificationManager notificationManager = mContext.getSystemService(
210                 NotificationManager.class);
211         notificationManager.cancel(CALL_NOTIFICATION, mCallId);
212         mMediaPlayer.stop();
213         setDisconnected(new DisconnectCause(cause));
214         destroy();
215         if (mListener != null ) {
216             mListener.onConnectionRemoved(this);
217         }
218     }
219 
setIsIncomingCallUiShowing(boolean showing)220     public void setIsIncomingCallUiShowing(boolean showing) {
221         mIsIncomingCallUiShowing = showing;
222     }
223 
isIncomingCallUiShowing()224     public boolean isIncomingCallUiShowing() {
225         return mIsIncomingCallUiShowing;
226     }
227 
isIncomingCall()228     public boolean isIncomingCall() {
229         return mIsIncomingCall;
230     }
231 
getCallId()232     public int getCallId() {
233         return mCallId;
234     }
235 
setIsHandover(boolean isHandover)236     public void setIsHandover(boolean isHandover) {
237         mIsHandover = isHandover;
238     }
239 
isHandover()240     public boolean isHandover() {
241         return mIsHandover;
242     }
243 
isHoldable()244     public boolean isHoldable() {
245         return (getConnectionCapabilities() & Connection.CAPABILITY_HOLD) != 0;
246     }
247 
createMediaPlayer(Context context)248     private MediaPlayer createMediaPlayer(Context context) {
249         int audioToPlay = (Math.random() > 0.5f) ? R.raw.sample_audio : R.raw.sample_audio2;
250         MediaPlayer mediaPlayer = MediaPlayer.create(context, audioToPlay);
251         mediaPlayer.setLooping(true);
252         return mediaPlayer;
253     }
254 }
255