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.ConfigProviderBindings; 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) ConfigProviderBindings.get(activity).getLong("post_call_prompt_duration_ms", 8_000); 100 activeSnackbar = 101 Snackbar.make(rootView, message, durationMs) 102 .setAction(actionText, onClickListener) 103 .setActionTextColor( 104 activity.getResources().getColor(R.color.dialer_snackbar_action_text_color)); 105 activeSnackbar.show(); 106 Logger.get(activity).logImpression(DialerImpression.Type.POST_CALL_PROMPT_USER_TO_SEND_MESSAGE); 107 StorageComponent.get(activity) 108 .unencryptedSharedPrefs() 109 .edit() 110 .remove(KEY_POST_CALL_CALL_DISCONNECT_TIME) 111 .apply(); 112 } 113 promptUserToViewSentMessage(Activity activity, View rootView)114 private static void promptUserToViewSentMessage(Activity activity, View rootView) { 115 LogUtil.i( 116 "PostCall.promptUserToViewSentMessage", 117 "returned from sending a post call message, message sent."); 118 String message = activity.getString(R.string.post_call_message_sent); 119 String addMessage = activity.getString(R.string.view); 120 String number = Assert.isNotNull(getPhoneNumber(activity)); 121 OnClickListener onClickListener = 122 v -> { 123 Logger.get(activity) 124 .logImpression( 125 DialerImpression.Type.POST_CALL_PROMPT_USER_TO_VIEW_SENT_MESSAGE_CLICKED); 126 Intent intent = IntentUtil.getSendSmsIntent(number); 127 DialerUtils.startActivityWithErrorToast(activity, intent); 128 }; 129 130 activeSnackbar = 131 Snackbar.make(rootView, message, Snackbar.LENGTH_LONG) 132 .setAction(addMessage, onClickListener) 133 .setActionTextColor( 134 activity.getResources().getColor(R.color.dialer_snackbar_action_text_color)) 135 .addCallback( 136 new BaseCallback<Snackbar>() { 137 @Override 138 public void onDismissed(Snackbar snackbar, int i) { 139 super.onDismissed(snackbar, i); 140 clear(snackbar.getContext()); 141 } 142 }); 143 activeSnackbar.show(); 144 Logger.get(activity) 145 .logImpression(DialerImpression.Type.POST_CALL_PROMPT_USER_TO_VIEW_SENT_MESSAGE); 146 StorageComponent.get(activity) 147 .unencryptedSharedPrefs() 148 .edit() 149 .remove(KEY_POST_CALL_MESSAGE_SENT) 150 .apply(); 151 } 152 onDisconnectPressed(Context context)153 public static void onDisconnectPressed(Context context) { 154 StorageComponent.get(context) 155 .unencryptedSharedPrefs() 156 .edit() 157 .putBoolean(KEY_POST_CALL_DISCONNECT_PRESSED, true) 158 .apply(); 159 } 160 onCallDisconnected(Context context, String number, long callConnectedMillis)161 public static void onCallDisconnected(Context context, String number, long callConnectedMillis) { 162 StorageComponent.get(context) 163 .unencryptedSharedPrefs() 164 .edit() 165 .putLong(KEY_POST_CALL_CALL_CONNECT_TIME, callConnectedMillis) 166 .putLong(KEY_POST_CALL_CALL_DISCONNECT_TIME, System.currentTimeMillis()) 167 .putString(KEY_POST_CALL_CALL_NUMBER, number) 168 .apply(); 169 } 170 onMessageSent(Context context, String number)171 public static void onMessageSent(Context context, String number) { 172 StorageComponent.get(context) 173 .unencryptedSharedPrefs() 174 .edit() 175 .putString(KEY_POST_CALL_CALL_NUMBER, number) 176 .putBoolean(KEY_POST_CALL_MESSAGE_SENT, true) 177 .apply(); 178 } 179 180 /** 181 * Restart performance recording if there is a recent call (disconnect time to now is under 182 * threshold) 183 */ restartPerformanceRecordingIfARecentCallExist(Context context)184 public static void restartPerformanceRecordingIfARecentCallExist(Context context) { 185 long disconnectTimeMillis = 186 StorageComponent.get(context) 187 .unencryptedSharedPrefs() 188 .getLong(PostCall.KEY_POST_CALL_CALL_DISCONNECT_TIME, -1); 189 if (disconnectTimeMillis != -1 && PerformanceReport.isRecording()) { 190 PerformanceReport.startRecording(); 191 } 192 } 193 clear(Context context)194 private static void clear(Context context) { 195 activeSnackbar = null; 196 197 StorageComponent.get(context) 198 .unencryptedSharedPrefs() 199 .edit() 200 .remove(KEY_POST_CALL_CALL_DISCONNECT_TIME) 201 .remove(KEY_POST_CALL_CALL_NUMBER) 202 .remove(KEY_POST_CALL_MESSAGE_SENT) 203 .remove(KEY_POST_CALL_CALL_CONNECT_TIME) 204 .remove(KEY_POST_CALL_DISCONNECT_PRESSED) 205 .apply(); 206 } 207 shouldPromptUserToSendMessage(Context context)208 private static boolean shouldPromptUserToSendMessage(Context context) { 209 SharedPreferences manager = StorageComponent.get(context).unencryptedSharedPrefs(); 210 long disconnectTimeMillis = manager.getLong(KEY_POST_CALL_CALL_DISCONNECT_TIME, -1); 211 long connectTimeMillis = manager.getLong(KEY_POST_CALL_CALL_CONNECT_TIME, -1); 212 213 long timeSinceDisconnect = System.currentTimeMillis() - disconnectTimeMillis; 214 long callDurationMillis = disconnectTimeMillis - connectTimeMillis; 215 216 boolean callDisconnectedByUser = manager.getBoolean(KEY_POST_CALL_DISCONNECT_PRESSED, false); 217 218 ConfigProvider binding = ConfigProviderBindings.get(context); 219 return disconnectTimeMillis != -1 220 && connectTimeMillis != -1 221 && isSimReady(context) 222 && binding.getLong("postcall_last_call_threshold", 30_000) > timeSinceDisconnect 223 && (connectTimeMillis == 0 224 || binding.getLong("postcall_call_duration_threshold", 35_000) > callDurationMillis) 225 && getPhoneNumber(context) != null 226 && callDisconnectedByUser; 227 } 228 shouldPromptUserToViewSentMessage(Context context)229 private static boolean shouldPromptUserToViewSentMessage(Context context) { 230 return StorageComponent.get(context) 231 .unencryptedSharedPrefs() 232 .getBoolean(KEY_POST_CALL_MESSAGE_SENT, false); 233 } 234 235 @Nullable getPhoneNumber(Context context)236 private static String getPhoneNumber(Context context) { 237 return StorageComponent.get(context) 238 .unencryptedSharedPrefs() 239 .getString(KEY_POST_CALL_CALL_NUMBER, null); 240 } 241 isEnabled(Context context)242 private static boolean isEnabled(Context context) { 243 return ConfigProviderBindings.get(context).getBoolean("enable_post_call_prod", true); 244 } 245 isSimReady(Context context)246 private static boolean isSimReady(Context context) { 247 return context.getSystemService(TelephonyManager.class).getSimState() 248 == TelephonyManager.SIM_STATE_READY; 249 } 250 } 251