1 /* 2 * Copyright (C) 2008 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.keyguard; 18 19 import static com.android.systemui.DejankUtils.whitelistIpcs; 20 21 import android.app.ActivityOptions; 22 import android.app.ActivityTaskManager; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.res.Configuration; 26 import android.os.PowerManager; 27 import android.os.RemoteException; 28 import android.os.SystemClock; 29 import android.os.UserHandle; 30 import android.telecom.TelecomManager; 31 import android.telephony.TelephonyManager; 32 import android.util.AttributeSet; 33 import android.util.Log; 34 import android.util.Slog; 35 import android.view.MotionEvent; 36 import android.view.View; 37 import android.view.ViewConfiguration; 38 import android.widget.Button; 39 40 import com.android.internal.logging.MetricsLogger; 41 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 42 import com.android.internal.util.EmergencyAffordanceManager; 43 import com.android.internal.widget.LockPatternUtils; 44 import com.android.systemui.Dependency; 45 import com.android.systemui.util.EmergencyDialerConstants; 46 47 /** 48 * This class implements a smart emergency button that updates itself based 49 * on telephony state. When the phone is idle, it is an emergency call button. 50 * When there's a call in progress, it presents an appropriate message and 51 * allows the user to return to the call. 52 */ 53 public class EmergencyButton extends Button { 54 55 private static final String LOG_TAG = "EmergencyButton"; 56 private final EmergencyAffordanceManager mEmergencyAffordanceManager; 57 58 private int mDownX; 59 private int mDownY; 60 KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { 61 62 @Override 63 public void onSimStateChanged(int subId, int slotId, int simState) { 64 updateEmergencyCallButton(); 65 } 66 67 @Override 68 public void onPhoneStateChanged(int phoneState) { 69 updateEmergencyCallButton(); 70 } 71 }; 72 private boolean mLongPressWasDragged; 73 74 public interface EmergencyButtonCallback { onEmergencyButtonClickedWhenInCall()75 public void onEmergencyButtonClickedWhenInCall(); 76 } 77 78 private LockPatternUtils mLockPatternUtils; 79 private PowerManager mPowerManager; 80 private EmergencyButtonCallback mEmergencyButtonCallback; 81 82 private final boolean mIsVoiceCapable; 83 private final boolean mEnableEmergencyCallWhileSimLocked; 84 EmergencyButton(Context context)85 public EmergencyButton(Context context) { 86 this(context, null); 87 } 88 EmergencyButton(Context context, AttributeSet attrs)89 public EmergencyButton(Context context, AttributeSet attrs) { 90 super(context, attrs); 91 mIsVoiceCapable = getTelephonyManager().isVoiceCapable(); 92 mEnableEmergencyCallWhileSimLocked = mContext.getResources().getBoolean( 93 com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked); 94 mEmergencyAffordanceManager = new EmergencyAffordanceManager(context); 95 } 96 getTelephonyManager()97 private TelephonyManager getTelephonyManager() { 98 return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 99 } 100 101 @Override onAttachedToWindow()102 protected void onAttachedToWindow() { 103 super.onAttachedToWindow(); 104 Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback); 105 } 106 107 @Override onDetachedFromWindow()108 protected void onDetachedFromWindow() { 109 super.onDetachedFromWindow(); 110 Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback); 111 } 112 113 @Override onFinishInflate()114 protected void onFinishInflate() { 115 super.onFinishInflate(); 116 mLockPatternUtils = new LockPatternUtils(mContext); 117 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 118 setOnClickListener(v -> takeEmergencyCallAction()); 119 if (mEmergencyAffordanceManager.needsEmergencyAffordance()) { 120 setOnLongClickListener(v -> { 121 if (!mLongPressWasDragged 122 && mEmergencyAffordanceManager.needsEmergencyAffordance()) { 123 mEmergencyAffordanceManager.performEmergencyCall(); 124 return true; 125 } 126 return false; 127 }); 128 } 129 whitelistIpcs(this::updateEmergencyCallButton); 130 } 131 132 @Override onTouchEvent(MotionEvent event)133 public boolean onTouchEvent(MotionEvent event) { 134 final int x = (int) event.getX(); 135 final int y = (int) event.getY(); 136 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 137 mDownX = x; 138 mDownY = y; 139 mLongPressWasDragged = false; 140 } else { 141 final int xDiff = Math.abs(x - mDownX); 142 final int yDiff = Math.abs(y - mDownY); 143 int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); 144 if (Math.abs(yDiff) > touchSlop || Math.abs(xDiff) > touchSlop) { 145 mLongPressWasDragged = true; 146 } 147 } 148 return super.onTouchEvent(event); 149 } 150 151 @Override performLongClick()152 public boolean performLongClick() { 153 return super.performLongClick(); 154 } 155 156 @Override onConfigurationChanged(Configuration newConfig)157 protected void onConfigurationChanged(Configuration newConfig) { 158 super.onConfigurationChanged(newConfig); 159 updateEmergencyCallButton(); 160 } 161 162 /** 163 * Shows the emergency dialer or returns the user to the existing call. 164 */ takeEmergencyCallAction()165 public void takeEmergencyCallAction() { 166 MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_CALL); 167 if (mPowerManager != null) { 168 mPowerManager.userActivity(SystemClock.uptimeMillis(), true); 169 } 170 try { 171 ActivityTaskManager.getService().stopSystemLockTaskMode(); 172 } catch (RemoteException e) { 173 Slog.w(LOG_TAG, "Failed to stop app pinning"); 174 } 175 if (isInCall()) { 176 resumeCall(); 177 if (mEmergencyButtonCallback != null) { 178 mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall(); 179 } 180 } else { 181 KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class); 182 if (updateMonitor != null) { 183 updateMonitor.reportEmergencyCallAction(true /* bypassHandler */); 184 } else { 185 Log.w(LOG_TAG, "KeyguardUpdateMonitor was null, launching intent anyway."); 186 } 187 TelecomManager telecomManager = getTelecommManager(); 188 if (telecomManager == null) { 189 Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer"); 190 return; 191 } 192 Intent emergencyDialIntent = 193 telecomManager.createLaunchEmergencyDialerIntent(null /* number*/) 194 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 195 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 196 | Intent.FLAG_ACTIVITY_CLEAR_TOP) 197 .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, 198 EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON); 199 200 getContext().startActivityAsUser(emergencyDialIntent, 201 ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(), 202 new UserHandle(KeyguardUpdateMonitor.getCurrentUser())); 203 } 204 } 205 updateEmergencyCallButton()206 private void updateEmergencyCallButton() { 207 boolean visible = false; 208 if (mIsVoiceCapable) { 209 // Emergency calling requires voice capability. 210 if (isInCall()) { 211 visible = true; // always show "return to call" if phone is off-hook 212 } else { 213 final boolean simLocked = Dependency.get(KeyguardUpdateMonitor.class) 214 .isSimPinVoiceSecure(); 215 if (simLocked) { 216 // Some countries can't handle emergency calls while SIM is locked. 217 visible = mEnableEmergencyCallWhileSimLocked; 218 } else { 219 // Only show if there is a secure screen (pin/pattern/SIM pin/SIM puk); 220 visible = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()); 221 } 222 } 223 } 224 if (visible) { 225 setVisibility(View.VISIBLE); 226 227 int textId; 228 if (isInCall()) { 229 textId = com.android.internal.R.string.lockscreen_return_to_call; 230 } else { 231 textId = com.android.internal.R.string.lockscreen_emergency_call; 232 } 233 setText(textId); 234 } else { 235 setVisibility(View.GONE); 236 } 237 } 238 setCallback(EmergencyButtonCallback callback)239 public void setCallback(EmergencyButtonCallback callback) { 240 mEmergencyButtonCallback = callback; 241 } 242 243 /** 244 * Resumes a call in progress. 245 */ resumeCall()246 private void resumeCall() { 247 getTelecommManager().showInCallScreen(false); 248 } 249 250 /** 251 * @return {@code true} if there is a call currently in progress. 252 */ isInCall()253 private boolean isInCall() { 254 return getTelecommManager().isInCall(); 255 } 256 getTelecommManager()257 private TelecomManager getTelecommManager() { 258 return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); 259 } 260 } 261