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.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 {
     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 
     89     public EmergencyButton(Context context) {
     90         this(context, null);
     91     }
     92 
     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
    103     protected void onAttachedToWindow() {
    104         super.onAttachedToWindow();
    105         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
    106     }
    107 
    108     @Override
    109     protected void onDetachedFromWindow() {
    110         super.onDetachedFromWindow();
    111         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
    112     }
    113 
    114     @Override
    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
    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
    158     public boolean performLongClick() {
    159         return super.performLongClick();
    160     }
    161 
    162     @Override
    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      */
    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 
    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 
    228     public void setCallback(EmergencyButtonCallback callback) {
    229         mEmergencyButtonCallback = callback;
    230     }
    231 
    232     /**
    233      * Resumes a call in progress.
    234      */
    235     private void resumeCall() {
    236         getTelecommManager().showInCallScreen(false);
    237     }
    238 
    239     /**
    240      * @return {@code true} if there is a call currently in progress.
    241      */
    242     private boolean isInCall() {
    243         return getTelecommManager().isInCall();
    244     }
    245 
    246     private TelecomManager getTelecommManager() {
    247         return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
    248     }
    249 }
    250