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