Home | History | Annotate | Download | only in keyguard
      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 {
     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 
     86     public EmergencyButton(Context context) {
     87         this(context, null);
     88     }
     89 
     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
    100     protected void onAttachedToWindow() {
    101         super.onAttachedToWindow();
    102         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
    103     }
    104 
    105     @Override
    106     protected void onDetachedFromWindow() {
    107         super.onDetachedFromWindow();
    108         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
    109     }
    110 
    111     @Override
    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
    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
    155     public boolean performLongClick() {
    156         return super.performLongClick();
    157     }
    158 
    159     @Override
    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      */
    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 
    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 
    225     public void setCallback(EmergencyButtonCallback callback) {
    226         mEmergencyButtonCallback = callback;
    227     }
    228 
    229     /**
    230      * Resumes a call in progress.
    231      */
    232     private void resumeCall() {
    233         getTelecommManager().showInCallScreen(false);
    234     }
    235 
    236     /**
    237      * @return {@code true} if there is a call currently in progress.
    238      */
    239     private boolean isInCall() {
    240         return getTelecommManager().isInCall();
    241     }
    242 
    243     private TelecomManager getTelecommManager() {
    244         return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
    245     }
    246 }
    247