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