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.content.Context;
21 import android.content.Intent;
22 import android.content.res.Configuration;
23 import android.os.PowerManager;
24 import android.os.SystemClock;
25 import android.os.UserHandle;
26 import android.telecom.TelecomManager;
27 import android.util.AttributeSet;
28 import android.view.View;
29 import android.widget.Button;
30 
31 import com.android.internal.logging.MetricsLogger;
32 import com.android.internal.telephony.IccCardConstants.State;
33 import com.android.internal.widget.LockPatternUtils;
34 
35 /**
36  * This class implements a smart emergency button that updates itself based
37  * on telephony state.  When the phone is idle, it is an emergency call button.
38  * When there's a call in progress, it presents an appropriate message and
39  * allows the user to return to the call.
40  */
41 public class EmergencyButton extends Button {
42     private static final Intent INTENT_EMERGENCY_DIAL = new Intent()
43             .setAction("com.android.phone.EmergencyDialer.DIAL")
44             .setPackage("com.android.phone")
45             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
46                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
47                     | Intent.FLAG_ACTIVITY_CLEAR_TOP);
48 
49     KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
50 
51         @Override
52         public void onSimStateChanged(int subId, int slotId, State simState) {
53             updateEmergencyCallButton();
54         }
55 
56         @Override
57         public void onPhoneStateChanged(int phoneState) {
58             updateEmergencyCallButton();
59         }
60     };
61 
62     public interface EmergencyButtonCallback {
onEmergencyButtonClickedWhenInCall()63         public void onEmergencyButtonClickedWhenInCall();
64     }
65 
66     private LockPatternUtils mLockPatternUtils;
67     private PowerManager mPowerManager;
68     private EmergencyButtonCallback mEmergencyButtonCallback;
69 
70     private final boolean mIsVoiceCapable;
71     private final boolean mEnableEmergencyCallWhileSimLocked;
72 
EmergencyButton(Context context)73     public EmergencyButton(Context context) {
74         this(context, null);
75     }
76 
EmergencyButton(Context context, AttributeSet attrs)77     public EmergencyButton(Context context, AttributeSet attrs) {
78         super(context, attrs);
79         mIsVoiceCapable = context.getResources().getBoolean(
80                 com.android.internal.R.bool.config_voice_capable);
81         mEnableEmergencyCallWhileSimLocked = mContext.getResources().getBoolean(
82                 com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
83     }
84 
85     @Override
onAttachedToWindow()86     protected void onAttachedToWindow() {
87         super.onAttachedToWindow();
88         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
89     }
90 
91     @Override
onDetachedFromWindow()92     protected void onDetachedFromWindow() {
93         super.onDetachedFromWindow();
94         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
95     }
96 
97     @Override
onFinishInflate()98     protected void onFinishInflate() {
99         super.onFinishInflate();
100         mLockPatternUtils = new LockPatternUtils(mContext);
101         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
102         setOnClickListener(new OnClickListener() {
103             public void onClick(View v) {
104                 takeEmergencyCallAction();
105             }
106         });
107         updateEmergencyCallButton();
108     }
109 
110     @Override
onConfigurationChanged(Configuration newConfig)111     protected void onConfigurationChanged(Configuration newConfig) {
112         super.onConfigurationChanged(newConfig);
113         updateEmergencyCallButton();
114     }
115 
116     /**
117      * Shows the emergency dialer or returns the user to the existing call.
118      */
takeEmergencyCallAction()119     public void takeEmergencyCallAction() {
120         MetricsLogger.action(mContext, MetricsLogger.ACTION_EMERGENCY_CALL);
121         // TODO: implement a shorter timeout once new PowerManager API is ready.
122         // should be the equivalent to the old userActivity(EMERGENCY_CALL_TIMEOUT)
123         mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
124         if (isInCall()) {
125             resumeCall();
126             if (mEmergencyButtonCallback != null) {
127                 mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
128             }
129         } else {
130             KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(
131                     true /* bypassHandler */);
132             getContext().startActivityAsUser(INTENT_EMERGENCY_DIAL,
133                     ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(),
134                     new UserHandle(KeyguardUpdateMonitor.getCurrentUser()));
135         }
136     }
137 
updateEmergencyCallButton()138     private void updateEmergencyCallButton() {
139         boolean visible = false;
140         if (mIsVoiceCapable) {
141             // Emergency calling requires voice capability.
142             if (isInCall()) {
143                 visible = true; // always show "return to call" if phone is off-hook
144             } else {
145                 final boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext)
146                         .isSimPinVoiceSecure();
147                 if (simLocked) {
148                     // Some countries can't handle emergency calls while SIM is locked.
149                     visible = mEnableEmergencyCallWhileSimLocked;
150                 } else {
151                     // Only show if there is a secure screen (pin/pattern/SIM pin/SIM puk);
152                     visible = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser());
153                 }
154             }
155         }
156         if (visible) {
157             setVisibility(View.VISIBLE);
158 
159             int textId;
160             if (isInCall()) {
161                 textId = com.android.internal.R.string.lockscreen_return_to_call;
162             } else {
163                 textId = com.android.internal.R.string.lockscreen_emergency_call;
164             }
165             setText(textId);
166         } else {
167             setVisibility(View.GONE);
168         }
169     }
170 
setCallback(EmergencyButtonCallback callback)171     public void setCallback(EmergencyButtonCallback callback) {
172         mEmergencyButtonCallback = callback;
173     }
174 
175     /**
176      * Resumes a call in progress.
177      */
resumeCall()178     private void resumeCall() {
179         getTelecommManager().showInCallScreen(false);
180     }
181 
182     /**
183      * @return {@code true} if there is a call currently in progress.
184      */
isInCall()185     private boolean isInCall() {
186         return getTelecommManager().isInCall();
187     }
188 
getTelecommManager()189     private TelecomManager getTelecommManager() {
190         return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
191     }
192 }
193