android M Power Button three times Make Emergency Call
Public utility and Emergency Services - Mobile handsets shall have an SOS alert button. The SOS alert button shall be implemented as per Government of India regulatory requirement:
- There shall be a dedicated configuring digit “9” in the dialler. On long press of this digit, device shall initiate emergency service number “112”
- On pressing the Power Button three times, device shall initiate emergency service number “112”.
这是印度政府为了女性权益的保障性政策吗?额,也许是我想多了,不过想到印度就想到了。。。。。。下面进入正题,哈哈
先来说说长按9键拨打紧急号码的实现:
在android/packages/apps/Dialer 的DialpadFragment.onLongClick里面进行处理:这里很简单,直接调用系统提供的方法就可以了,DialerUtils.startActivityWithErrorToast其实最终还是调用了6.0新提供的接口TelecomManager.placeCall
case R.id.nine: {
if (mDigits.length() == 1) {
if (R.id.nine == id) {
final DialtactsActivity activity = getActivity() instanceof DialtactsActivity
? (DialtactsActivity) getActivity() : null;
final Intent intent =
IntentUtil.getCallIntent("112", activity != null
? activity.getCallOrigin()
: null);
DialerUtils.startActivityWithErrorToast(getActivity(), intent);
hideAndClearDialpad(false);
return true;
}
再来说说连续点击三次power建拨打紧急号码的实现:
1.因为是点击power键后的处理,所以我们应该在android/frameworks/base/services PhoneWindowManager.java里面去截获按键的点击事件,在interceptPowerKeyDown里我们看到了GestureLauncherService
GestureLauncherService gestureService = LocalServices.getService(
GestureLauncherService.class);
boolean gesturedServiceIntercepted = false;
if (gestureService != null) {
- gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive);
+ //On pressing the Power Button three times,device shall initiate emergency service number“112”.
+ gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive, mHandler);
}
跳到GestureLauncherService. interceptPowerKeyDown方法中去看看
- public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) {
- boolean launched = false;
+ //[Feature]On pressing the Power Button three times,device shall initiate emergency service number“112”.
+ public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive, Handler handler) {
boolean intercept = false;
+ boolean launched = false;
long doubleTapInterval;
synchronized (this) {
doubleTapInterval = event.getEventTime() - mLastPowerDown;
- if (mCameraDoubleTapPowerEnabled
- && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
+ if (doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
&& doubleTapInterval > CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS) {
- launched = true;
- intercept = interactive;
+ mAccount++;
+ if(mCameraDoubleTapPowerEnabled){
+ launched = true;
+ intercept = interactive;
+ }
+ if (mAccount == 3) {
+ Log.d(TAG, "tap three times");
+ handler.removeCallbacks(mCameraLaunchRunnable);
+ interceptEmergencyCall();
+ mAccount = 1;
+ } else if (launched) {
+ Log.d(TAG, "Tap two times camera launched " + launched);
+ handler.postDelayed(mCameraLaunchRunnable, 450L);
+ }else if (!launched){
+ Log.d(TAG, "Tap two times but not launch camera");
+ handler.postDelayed(mTriplePressDelay, 450L);
+ }
}
mLastPowerDown = event.getEventTime();
}
- if (launched) {
- Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
- + doubleTapInterval + "ms");
- launched = handleCameraLaunchGesture(false /* useWakelock */,
+ mCopydoubleTapInterval = doubleTapInterval ;
+ MetricsLogger.histogram(mContext, "power_double_tap_interval", (int) doubleTapInterval);
+ return intercept && launched;
+ }
这里我们看到系统给我们提供了一个判断连续点击两次power键启动系统camera的例子,然而我们要做的feature是判断连续点击三次拨打紧急号码,好,那我们就在这个方法的基础之上实现我们的需求。
+ private final Runnable mCameraLaunchRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mAccount = 1;
+ boolean mHasLaunched = true;
+ mHasLaunched = handleCameraLaunchGesture(false /* useWakelock */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
- if (launched) {
+ if (mHasLaunched) {
MetricsLogger.action(mContext, MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
- (int) doubleTapInterval);
+ (int) mCopydoubleTapInterval);
}
}
- MetricsLogger.histogram(mContext, "power_double_tap_interval", (int) doubleTapInterval);
- return intercept && launched;
+ };
+
+ private final Runnable mTriplePressDelay = new Runnable() {
+ @Override
+ public void run() {
+ mAccount = 1;
+ }
+ };
+
+ private void interceptEmergencyCall() {
+ Intent intent = new Intent();
+ intent.setAction(EMERGENCY_CALL);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
+ //@end
这里我写了两个Runnable,mCameraLaunchRunnable 是执行原本启动camera的代码,并做延时处理,mTriplePressDelay 因为用户可以通过设置 enable和disenable power两次启动camera这个功能,通过mCameraDoubleTapPowerEnabled这个值来判断,所以我加了个mTriplePressDelay 的延时来处理当用户disenable 启动camera的功能,最后通过interceptEmergencyCall方法发个广播出去。当然还需要一些变量的定义如下:
+ /**
+ * On pressing the Power Button three times, device shall initiate emergency service number 112
+ */
+ private int mAccount = 1;
+ private long mCopydoubleTapInterval;
+ private static final String EMERGENCY_CALL = "android.intent.action.ACTION_SEND_EMERGENCY_CALL";
2.广播发出去了我们需要个接受者,来处理拨打紧急号码这个动作,当手机处于锁屏界面的时候,我们可以点击界面上的紧急号码按钮直接拨打紧急号码,这里我们就在处理锁屏界面的紧急号码里面呼出我们的紧急号码,在android/packages/services/Telephony下面我们新建一个EmergencyCallReceiver.java,用于接收广播并拨打号码,因为是静态注册所以需要在AndroidManifest.xml声明一下
+package com.android.phone;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.telecom.PhoneAccount;
+import android.util.Log;
+
+/**
+ * On pressing the Power Button three times, device shall initiate emergency service number“112”
+ */
+public class EmergencyCallReceiver extends BroadcastReceiver {
+ private static final String TAG = "EmergencyCallReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG,"EmergencyCall_Receive_Success ");
+ String mEmergencyNumber = "112";
+ Intent mintent = new Intent(Intent.ACTION_CALL_EMERGENCY);
+ mintent.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, mEmergencyNumber, null));
+ mintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(mintent);
+ }
+}
+ <!--[Feature]On pressing the Power Button three times,device shall initiate emergency service number“112”.-->
+ <receiver android:name=".EmergencyCallReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.ACTION_SEND_EMERGENCY_CALL"/>
+ </intent-filter>
+ </receiver>
总结:
- 为什么要用两个Runnable来做延时处理?
因为我们和系统的两次power启动相机的快捷方式有点儿冲突,当我们连续点击了两次power键时系统就应该启动相机了,再点击一次去打电话但是相机已经启动了,所以就采用了一个延时操作,在450毫秒之内有没有下次power事件操作,有的话就remove准备做的操作。 - 为什么要发送广播让另外一个模块去拨打电话?
因为我们没有权限,而且在PhoneWindowManager里面我也不知道怎么添加权限进去,所以,索性就让有权限的人去做事情。
/**
* Places a new outgoing call to the provided address using the system telecom service with
* the specified extras.
*
* This method is equivalent to placing an outgoing call using {@link Intent#ACTION_CALL},
* except that the outgoing call will always be sent via the system telecom service. If
* method-caller is either the user selected default dialer app or preloaded system dialer
* app, then emergency calls will also be allowed.
*
* Requires permission: {@link android.Manifest.permission#CALL_PHONE}
*
* Usage example:
* <pre>
* Uri uri = Uri.fromParts("tel", "12345", null);
* Bundle extras = new Bundle();
* extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
* telecomManager.placeCall(uri, extras);
* </pre>
*
* The following keys are supported in the supplied extras.
* <ul>
* <li>{@link #EXTRA_OUTGOING_CALL_EXTRAS}</li>
* <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
* </ul>
*
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
if (address == null) {
Log.w(TAG, "Cannot place call to empty address.");
}
try {
service.placeCall(address, extras == null ? new Bundle() : extras,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}