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 number = Assert.isNotNull(getPhoneNumber(activity)); 76 String message = activity.getString(R.string.post_call_message); 77 EnrichedCallManager manager = EnrichedCallComponent.get(activity).getEnrichedCallManager(); 78 EnrichedCallCapabilities capabilities = manager.getCapabilities(number); 79 LogUtil.i( 80 "PostCall.promptUserToSendMessage", 81 "number: %s, capabilities: %s", 82 LogUtil.sanitizePhoneNumber(number), 83 capabilities); 84 85 boolean isRcsPostCall = capabilities != null && capabilities.isPostCallCapable(); 86 String actionText = 87 isRcsPostCall 88 ? activity.getString(R.string.post_call_add_message) 89 : activity.getString(R.string.post_call_send_message); 90 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