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.dialer.postcall;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.SharedPreferences;
23 import android.support.annotation.Nullable;
24 import android.support.design.widget.BaseTransientBottomBar.BaseCallback;
25 import android.support.design.widget.Snackbar;
26 import android.telephony.TelephonyManager;
27 import android.view.View;
28 import android.view.View.OnClickListener;
29 import com.android.dialer.common.Assert;
30 import com.android.dialer.common.LogUtil;
31 import com.android.dialer.configprovider.ConfigProvider;
32 import com.android.dialer.configprovider.ConfigProviderComponent;
33 import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
34 import com.android.dialer.enrichedcall.EnrichedCallComponent;
35 import com.android.dialer.enrichedcall.EnrichedCallManager;
36 import com.android.dialer.logging.DialerImpression;
37 import com.android.dialer.logging.Logger;
38 import com.android.dialer.performancereport.PerformanceReport;
39 import com.android.dialer.storage.StorageComponent;
40 import com.android.dialer.util.DialerUtils;
41 import com.android.dialer.util.IntentUtil;
42 
43 /** Helper class to handle all post call actions. */
44 public class PostCall {
45 
46   private static final String KEY_POST_CALL_CALL_DISCONNECT_TIME = "post_call_call_disconnect_time";
47   private static final String KEY_POST_CALL_CALL_CONNECT_TIME = "post_call_call_connect_time";
48   private static final String KEY_POST_CALL_CALL_NUMBER = "post_call_call_number";
49   private static final String KEY_POST_CALL_MESSAGE_SENT = "post_call_message_sent";
50   private static final String KEY_POST_CALL_DISCONNECT_PRESSED = "post_call_disconnect_pressed";
51 
52   private static Snackbar activeSnackbar;
53 
promptUserForMessageIfNecessary(Activity activity, View rootView)54   public static void promptUserForMessageIfNecessary(Activity activity, View rootView) {
55     if (isEnabled(activity)) {
56       if (shouldPromptUserToViewSentMessage(activity)) {
57         promptUserToViewSentMessage(activity, rootView);
58       } else if (shouldPromptUserToSendMessage(activity)) {
59         promptUserToSendMessage(activity, rootView);
60       } else {
61         clear(activity);
62       }
63     }
64   }
65 
closePrompt()66   public static void closePrompt() {
67     if (activeSnackbar != null && activeSnackbar.isShown()) {
68       activeSnackbar.dismiss();
69       activeSnackbar = null;
70     }
71   }
72 
promptUserToSendMessage(Activity activity, View rootView)73   private static void promptUserToSendMessage(Activity activity, View rootView) {
74     LogUtil.i("PostCall.promptUserToSendMessage", "returned from call, showing post call SnackBar");
75     String message = activity.getString(R.string.post_call_message);
76     EnrichedCallManager manager = EnrichedCallComponent.get(activity).getEnrichedCallManager();
77     EnrichedCallCapabilities capabilities = manager.getCapabilities(getPhoneNumber(activity));
78     LogUtil.i(
79         "PostCall.promptUserToSendMessage",
80         "number: %s, capabilities: %s",
81         LogUtil.sanitizePhoneNumber(getPhoneNumber(activity)),
82         capabilities);
83 
84     boolean isRcsPostCall = capabilities != null && capabilities.isPostCallCapable();
85     String actionText =
86         isRcsPostCall
87             ? activity.getString(R.string.post_call_add_message)
88             : activity.getString(R.string.post_call_send_message);
89 
90     String number = Assert.isNotNull(getPhoneNumber(activity));
91     OnClickListener onClickListener =
92         v -> {
93           Logger.get(activity)
94               .logImpression(DialerImpression.Type.POST_CALL_PROMPT_USER_TO_SEND_MESSAGE_CLICKED);
95           activity.startActivity(PostCallActivity.newIntent(activity, number, isRcsPostCall));
96         };
97 
98     int durationMs =
99         (int)
100             ConfigProviderComponent.get(activity)
101                 .getConfigProvider()
102                 .getLong("post_call_prompt_duration_ms", 8_000);
103     activeSnackbar =
104         Snackbar.make(rootView, message, durationMs)
105             .setAction(actionText, onClickListener)
106             .setActionTextColor(
107                 activity.getResources().getColor(R.color.dialer_snackbar_action_text_color));
108     activeSnackbar.show();
109     Logger.get(activity).logImpression(DialerImpression.Type.POST_CALL_PROMPT_USER_TO_SEND_MESSAGE);
110     StorageComponent.get(activity)
111         .unencryptedSharedPrefs()
112         .edit()
113         .remove(KEY_POST_CALL_CALL_DISCONNECT_TIME)
114         .apply();
115   }
116 
promptUserToViewSentMessage(Activity activity, View rootView)117   private static void promptUserToViewSentMessage(Activity activity, View rootView) {
118     LogUtil.i(
119         "PostCall.promptUserToViewSentMessage",
120         "returned from sending a post call message, message sent.");
121     String message = activity.getString(R.string.post_call_message_sent);
122     String addMessage = activity.getString(R.string.view);
123     String number = Assert.isNotNull(getPhoneNumber(activity));
124     OnClickListener onClickListener =
125         v -> {
126           Logger.get(activity)
127               .logImpression(
128                   DialerImpression.Type.POST_CALL_PROMPT_USER_TO_VIEW_SENT_MESSAGE_CLICKED);
129           Intent intent = IntentUtil.getSendSmsIntent(number);
130           DialerUtils.startActivityWithErrorToast(activity, intent);
131         };
132 
133     activeSnackbar =
134         Snackbar.make(rootView, message, Snackbar.LENGTH_LONG)
135             .setAction(addMessage, onClickListener)
136             .setActionTextColor(
137                 activity.getResources().getColor(R.color.dialer_snackbar_action_text_color))
138             .addCallback(
139                 new BaseCallback<Snackbar>() {
140                   @Override
141                   public void onDismissed(Snackbar snackbar, int i) {
142                     super.onDismissed(snackbar, i);
143                     clear(snackbar.getContext());
144                   }
145                 });
146     activeSnackbar.show();
147     Logger.get(activity)
148         .logImpression(DialerImpression.Type.POST_CALL_PROMPT_USER_TO_VIEW_SENT_MESSAGE);
149     StorageComponent.get(activity)
150         .unencryptedSharedPrefs()
151         .edit()
152         .remove(KEY_POST_CALL_MESSAGE_SENT)
153         .apply();
154   }
155 
onDisconnectPressed(Context context)156   public static void onDisconnectPressed(Context context) {
157     StorageComponent.get(context)
158         .unencryptedSharedPrefs()
159         .edit()
160         .putBoolean(KEY_POST_CALL_DISCONNECT_PRESSED, true)
161         .apply();
162   }
163 
onCallDisconnected(Context context, String number, long callConnectedMillis)164   public static void onCallDisconnected(Context context, String number, long callConnectedMillis) {
165     StorageComponent.get(context)
166         .unencryptedSharedPrefs()
167         .edit()
168         .putLong(KEY_POST_CALL_CALL_CONNECT_TIME, callConnectedMillis)
169         .putLong(KEY_POST_CALL_CALL_DISCONNECT_TIME, System.currentTimeMillis())
170         .putString(KEY_POST_CALL_CALL_NUMBER, number)
171         .apply();
172   }
173 
onMessageSent(Context context, String number)174   public static void onMessageSent(Context context, String number) {
175     StorageComponent.get(context)
176         .unencryptedSharedPrefs()
177         .edit()
178         .putString(KEY_POST_CALL_CALL_NUMBER, number)
179         .putBoolean(KEY_POST_CALL_MESSAGE_SENT, true)
180         .apply();
181   }
182 
183   /**
184    * Restart performance recording if there is a recent call (disconnect time to now is under
185    * threshold)
186    */
restartPerformanceRecordingIfARecentCallExist(Context context)187   public static void restartPerformanceRecordingIfARecentCallExist(Context context) {
188     long disconnectTimeMillis =
189         StorageComponent.get(context)
190             .unencryptedSharedPrefs()
191             .getLong(PostCall.KEY_POST_CALL_CALL_DISCONNECT_TIME, -1);
192     if (disconnectTimeMillis != -1 && PerformanceReport.isRecording()) {
193       PerformanceReport.startRecording();
194     }
195   }
196 
clear(Context context)197   private static void clear(Context context) {
198     activeSnackbar = null;
199 
200     StorageComponent.get(context)
201         .unencryptedSharedPrefs()
202         .edit()
203         .remove(KEY_POST_CALL_CALL_DISCONNECT_TIME)
204         .remove(KEY_POST_CALL_CALL_NUMBER)
205         .remove(KEY_POST_CALL_MESSAGE_SENT)
206         .remove(KEY_POST_CALL_CALL_CONNECT_TIME)
207         .remove(KEY_POST_CALL_DISCONNECT_PRESSED)
208         .apply();
209   }
210 
shouldPromptUserToSendMessage(Context context)211   private static boolean shouldPromptUserToSendMessage(Context context) {
212     SharedPreferences manager = StorageComponent.get(context).unencryptedSharedPrefs();
213     long disconnectTimeMillis = manager.getLong(KEY_POST_CALL_CALL_DISCONNECT_TIME, -1);
214     long connectTimeMillis = manager.getLong(KEY_POST_CALL_CALL_CONNECT_TIME, -1);
215 
216     long timeSinceDisconnect = System.currentTimeMillis() - disconnectTimeMillis;
217     long callDurationMillis = disconnectTimeMillis - connectTimeMillis;
218 
219     boolean callDisconnectedByUser = manager.getBoolean(KEY_POST_CALL_DISCONNECT_PRESSED, false);
220 
221     ConfigProvider binding = ConfigProviderComponent.get(context).getConfigProvider();
222     return disconnectTimeMillis != -1
223         && connectTimeMillis != -1
224         && isSimReady(context)
225         && binding.getLong("postcall_last_call_threshold", 30_000) > timeSinceDisconnect
226         && (connectTimeMillis == 0
227             || binding.getLong("postcall_call_duration_threshold", 35_000) > callDurationMillis)
228         && getPhoneNumber(context) != null
229         && callDisconnectedByUser;
230   }
231 
shouldPromptUserToViewSentMessage(Context context)232   private static boolean shouldPromptUserToViewSentMessage(Context context) {
233     return StorageComponent.get(context)
234         .unencryptedSharedPrefs()
235         .getBoolean(KEY_POST_CALL_MESSAGE_SENT, false);
236   }
237 
238   @Nullable
getPhoneNumber(Context context)239   private static String getPhoneNumber(Context context) {
240     return StorageComponent.get(context)
241         .unencryptedSharedPrefs()
242         .getString(KEY_POST_CALL_CALL_NUMBER, null);
243   }
244 
isEnabled(Context context)245   private static boolean isEnabled(Context context) {
246     return ConfigProviderComponent.get(context)
247         .getConfigProvider()
248         .getBoolean("enable_post_call_prod", true);
249   }
250 
isSimReady(Context context)251   private static boolean isSimReady(Context context) {
252     return context.getSystemService(TelephonyManager.class).getSimState()
253         == TelephonyManager.SIM_STATE_READY;
254   }
255 }
256