1/*
2 * Copyright (C) 2013 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.server.telecom.testapps;
18
19import com.android.server.telecom.testapps.R;
20
21import android.app.Notification;
22import android.app.NotificationManager;
23import android.app.PendingIntent;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.graphics.Color;
28import android.graphics.drawable.Icon;
29import android.net.Uri;
30import android.os.Bundle;
31import android.telecom.PhoneAccount;
32import android.telecom.PhoneAccountHandle;
33import android.telecom.TelecomManager;
34import android.util.Log;
35import android.widget.Toast;
36
37import java.util.Arrays;
38import java.util.HashMap;
39import java.util.List;
40import java.util.Map;
41
42/**
43 * Class used to create, update and cancel the notification used to display and update call state
44 * for {@link TestConnectionService}.
45 */
46public class CallServiceNotifier {
47    private static final CallServiceNotifier INSTANCE = new CallServiceNotifier();
48
49    public static final String CALL_PROVIDER_ID = "testapps_TestConnectionService_CALL_PROVIDER_ID";
50    public static final String SIM_SUBSCRIPTION_ID =
51            "testapps_TestConnectionService_SIM_SUBSCRIPTION_ID";
52    public static final String SIM_SUBSCRIPTION_ID2 =
53            "testapps_TestConnectionService_SIM_SUBSCRIPTION_ID2";
54    public static final String CONNECTION_MANAGER_ID =
55            "testapps_TestConnectionService_CONNECTION_MANAGER_ID";
56
57    private Map<String, PhoneAccountHandle> mPhoneAccountMap = new HashMap<>();
58
59    /**
60     * Static notification IDs.
61     */
62    private static final int CALL_NOTIFICATION_ID = 1;
63    private static final int PHONE_ACCOUNT_NOTIFICATION_ID = 2;
64
65    /**
66     * Whether the added call should be started as a video call. Referenced by
67     * {@link TestConnectionService} to know whether to provide a call video provider.
68     */
69    public static boolean mStartVideoCall;
70
71    /**
72     * Singleton accessor.
73     */
74    public static CallServiceNotifier getInstance() {
75        return INSTANCE;
76    }
77
78    /**
79     * Creates a CallService & initializes notification manager.
80     */
81    private CallServiceNotifier() {
82    }
83
84    /**
85     * Updates the notification in the notification pane.
86     */
87    public void updateNotification(Context context) {
88        log("adding the notification ------------");
89        getNotificationManager(context).notify(CALL_NOTIFICATION_ID, getMainNotification(context));
90        getNotificationManager(context).notify(
91                PHONE_ACCOUNT_NOTIFICATION_ID, getPhoneAccountNotification(context));
92    }
93
94    /**
95     * Cancels the notification.
96     */
97    public void cancelNotifications(Context context) {
98        log("canceling notification");
99        getNotificationManager(context).cancel(CALL_NOTIFICATION_ID);
100        getNotificationManager(context).cancel(PHONE_ACCOUNT_NOTIFICATION_ID);
101    }
102
103    /**
104     * Registers a phone account with telecom.
105     */
106    public void registerPhoneAccount(Context context) {
107        TelecomManager telecomManager =
108                (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
109
110        telecomManager.clearAccounts();
111
112        Bundle testBundle = new Bundle();
113        testBundle.putInt("EXTRA_INT_1", 1);
114        testBundle.putInt("EXTRA_INT_100", 100);
115        testBundle.putBoolean("EXTRA_BOOL_TRUE", true);
116        testBundle.putBoolean("EXTRA_BOOL_FALSE", false);
117        testBundle.putString("EXTRA_STR1", "Hello");
118        testBundle.putString("EXTRA_STR2", "There");
119
120        PhoneAccountHandle handle1 = new PhoneAccountHandle(
121                new ComponentName(context, TestConnectionService.class), CALL_PROVIDER_ID);
122        mPhoneAccountMap.put(CALL_PROVIDER_ID, handle1);
123        telecomManager.registerPhoneAccount(PhoneAccount.builder(
124                handle1,
125                "TelecomTestApp Call Provider")
126                .setAddress(Uri.parse("tel:555-TEST"))
127                .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
128                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
129                        PhoneAccount.CAPABILITY_VIDEO_CALLING |
130                        PhoneAccount.CAPABILITY_RTT |
131                        PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
132                .setIcon(Icon.createWithResource(
133                        context.getResources(), R.drawable.stat_sys_phone_call))
134                .setHighlightColor(Color.RED)
135                // TODO: Add icon tint (Color.RED)
136                .setShortDescription("a short description for the call provider")
137                .setSupportedUriSchemes(Arrays.asList("tel"))
138                .setExtras(testBundle)
139                .build());
140
141        PhoneAccountHandle handle2 = new PhoneAccountHandle(
142                new ComponentName(context, TestConnectionService.class), SIM_SUBSCRIPTION_ID);
143        mPhoneAccountMap.put(SIM_SUBSCRIPTION_ID, handle2);
144        telecomManager.registerPhoneAccount(PhoneAccount.builder(
145                handle2,
146                "TelecomTestApp SIM Subscription")
147                .setAddress(Uri.parse("tel:555-TSIM"))
148                .setSubscriptionAddress(Uri.parse("tel:555-TSIM"))
149                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
150                        PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
151                        PhoneAccount.CAPABILITY_VIDEO_CALLING |
152                        PhoneAccount.CAPABILITY_RTT |
153                        PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
154                .setIcon(Icon.createWithResource(
155                        context.getResources(), R.drawable.stat_sys_phone_call))
156                .setHighlightColor(Color.GREEN)
157                // TODO: Add icon tint (Color.GREEN)
158                .setShortDescription("a short description for the sim subscription")
159                .build());
160
161        PhoneAccountHandle handle3 = new PhoneAccountHandle(
162                new ComponentName(context, TestConnectionService.class), SIM_SUBSCRIPTION_ID2);
163        mPhoneAccountMap.put(SIM_SUBSCRIPTION_ID2, handle3);
164        telecomManager.registerPhoneAccount(PhoneAccount.builder(
165                handle3,
166                "TelecomTestApp SIM Subscription 2")
167                .setAddress(Uri.parse("tel:555-TSI2"))
168                .setSubscriptionAddress(Uri.parse("tel:555-TSI2"))
169                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
170                        PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
171                        PhoneAccount.CAPABILITY_VIDEO_CALLING |
172                        PhoneAccount.CAPABILITY_RTT |
173                        PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
174                .setIcon(Icon.createWithResource(
175                        context.getResources(), R.drawable.stat_sys_phone_call))
176                .setHighlightColor(Color.CYAN)
177                .setShortDescription("a short description for the sim subscription")
178                .build());
179
180        PhoneAccountHandle handle4 = new PhoneAccountHandle(
181                new ComponentName(context, TestConnectionManager.class), CONNECTION_MANAGER_ID);
182        mPhoneAccountMap.put(CONNECTION_MANAGER_ID, handle4);
183        telecomManager.registerPhoneAccount(PhoneAccount.builder(
184                handle4,
185                "TelecomTestApp CONNECTION MANAGER")
186                .setAddress(Uri.parse("tel:555-CMGR"))
187                .setSubscriptionAddress(Uri.parse("tel:555-CMGR"))
188                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
189                .setIcon(Icon.createWithResource(
190                        context.getResources(), R.drawable.stat_sys_phone_call))
191                // TODO: Add icon tint (Color.BLUE)
192                .setShortDescription("a short description for the connection manager")
193                .build());
194    }
195
196    public PhoneAccountHandle getPhoneAccountHandle(String id) {
197        return mPhoneAccountMap.get(id);
198    }
199
200    /**
201     * Displays all phone accounts registered with telecom.
202     */
203    public void showAllPhoneAccounts(Context context) {
204        TelecomManager telecomManager =
205                (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
206        List<PhoneAccountHandle> accounts = telecomManager.getCallCapablePhoneAccounts();
207
208        Toast.makeText(context, accounts.toString(), Toast.LENGTH_LONG).show();
209    }
210
211    /**
212     * Returns the system's notification manager needed to add/remove notifications.
213     */
214    private NotificationManager getNotificationManager(Context context) {
215        return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
216    }
217
218    /**
219     * Creates a notification object for using the telecom APIs.
220     */
221    private Notification getPhoneAccountNotification(Context context) {
222        final Notification.Builder builder = new Notification.Builder(context);
223        // Both notifications have buttons and only the first one with buttons will show its
224        // buttons.  Since the phone accounts notification is always first, setting false ensures
225        // it can be dismissed to use the other notification.
226        builder.setOngoing(false);
227        builder.setPriority(Notification.PRIORITY_HIGH);
228
229        final PendingIntent intent = createShowAllPhoneAccountsIntent(context);
230        builder.setContentIntent(intent);
231
232        builder.setSmallIcon(android.R.drawable.stat_sys_phone_call);
233        // TODO: Consider moving this into a strings.xml
234        builder.setContentText("Test phone accounts via telecom APIs.");
235        builder.setContentTitle("Test Phone Accounts");
236
237        addRegisterPhoneAccountAction(builder, context);
238        addShowAllPhoneAccountsAction(builder, context);
239
240        return builder.build();
241    }
242
243    /**
244     * Creates a notification object out of the current calls state.
245     */
246    private Notification getMainNotification(Context context) {
247        final Notification.Builder builder = new Notification.Builder(context);
248        builder.setOngoing(true);
249        builder.setPriority(Notification.PRIORITY_HIGH);
250        builder.setSmallIcon(android.R.drawable.stat_sys_phone_call);
251        builder.setContentText("Test calls via CallService API");
252        builder.setContentTitle("Test Connection Service");
253
254        addAddOneWayVideoCallAction(builder, context);
255        addAddTwoWayVideoCallAction(builder, context);
256        addAddCallAction(builder, context);
257        addExitAction(builder, context);
258
259        return builder.build();
260    }
261
262    /**
263     * Creates the intent to remove the notification.
264     */
265    private PendingIntent createExitIntent(Context context) {
266        final Intent intent = new Intent(CallNotificationReceiver.ACTION_CALL_SERVICE_EXIT, null,
267                context, CallNotificationReceiver.class);
268
269        return PendingIntent.getBroadcast(context, 0, intent, 0);
270    }
271
272    /**
273     * Creates the intent to register a phone account.
274     */
275    private PendingIntent createRegisterPhoneAccountIntent(Context context) {
276        final Intent intent = new Intent(CallNotificationReceiver.ACTION_REGISTER_PHONE_ACCOUNT,
277                null, context, CallNotificationReceiver.class);
278        return PendingIntent.getBroadcast(context, 0, intent, 0);
279    }
280
281    /**
282     * Creates the intent to show all phone accounts.
283     */
284    private PendingIntent createShowAllPhoneAccountsIntent(Context context) {
285        final Intent intent = new Intent(CallNotificationReceiver.ACTION_SHOW_ALL_PHONE_ACCOUNTS,
286                null, context, CallNotificationReceiver.class);
287        return PendingIntent.getBroadcast(context, 0, intent, 0);
288    }
289
290    /**
291     * Creates the intent to start an incoming 1-way video call (receive-only)
292     */
293    private PendingIntent createOneWayVideoCallIntent(Context context) {
294        final Intent intent = new Intent(CallNotificationReceiver.ACTION_ONE_WAY_VIDEO_CALL,
295                null, context, CallNotificationReceiver.class);
296        return PendingIntent.getBroadcast(context, 0, intent, 0);
297    }
298
299    /**
300     * Creates the intent to start an incoming 2-way video call
301     */
302    private PendingIntent createTwoWayVideoCallIntent(Context context) {
303        final Intent intent = new Intent(CallNotificationReceiver.ACTION_TWO_WAY_VIDEO_CALL,
304                null, context, CallNotificationReceiver.class);
305        return PendingIntent.getBroadcast(context, 0, intent, 0);
306    }
307
308    /**
309     * Creates the intent to start an incoming audio call
310     */
311    private PendingIntent createIncomingAudioCall(Context context) {
312        final Intent intent = new Intent(CallNotificationReceiver.ACTION_AUDIO_CALL,
313                null, context, CallNotificationReceiver.class);
314        return PendingIntent.getBroadcast(context, 0, intent, 0);
315    }
316
317    /**
318     * Adds an action to the Notification Builder for adding an incoming call through Telecom.
319     * @param builder The Notification Builder.
320     */
321    private void addAddCallAction(Notification.Builder builder, Context context) {
322        builder.addAction(0, "Add Call", createIncomingAudioCall(context));
323    }
324
325    /**
326     * Adds an action to the Notification Builder to add an incoming one-way video call through
327     * Telecom.
328     */
329    private void addAddOneWayVideoCallAction(Notification.Builder builder, Context context) {
330        builder.addAction(0, "1-way Video", createOneWayVideoCallIntent(context));
331    }
332
333    /**
334     * Adds an action to the Notification Builder to add an incoming 2-way video call through
335     * Telecom.
336     */
337    private void addAddTwoWayVideoCallAction(Notification.Builder builder, Context context) {
338        builder.addAction(0, "2-way Video", createTwoWayVideoCallIntent(context));
339    }
340
341    /**
342     * Adds an action to remove the notification.
343     */
344    private void addExitAction(Notification.Builder builder, Context context) {
345        builder.addAction(0, "Exit", createExitIntent(context));
346    }
347
348    /**
349     * Adds an action to show all registered phone accounts on a device.
350     */
351    private void addShowAllPhoneAccountsAction(Notification.Builder builder, Context context) {
352        builder.addAction(0, "Show Accts", createShowAllPhoneAccountsIntent(context));
353    }
354
355    /**
356     * Adds an action to register a new phone account.
357     */
358    private void addRegisterPhoneAccountAction(Notification.Builder builder, Context context) {
359        builder.addAction(0, "Reg.Acct.", createRegisterPhoneAccountIntent(context));
360    }
361
362    public boolean shouldStartVideoCall() {
363        return mStartVideoCall;
364    }
365
366    private static void log(String msg) {
367        Log.w("testcallservice", "[CallServiceNotifier] " + msg);
368    }
369}
370