• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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