源码分析之Android通过Dialer实现暗码启动

2024-09-06 09:08

本文主要是介绍源码分析之Android通过Dialer实现暗码启动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目前接触比较多的就是通过dialer应用来启动/触发暗码。

也有通过Calculator来实现的。

本文以Dialer为例,

1.经过调试定位,发现拨号盘接对应的Activity为DialtactsActivity。

2.DialtactsActivity中有个showDialpadFragment方法,用来加载显示拨号盘,因为有可能此时拨号盘正处于收缩/隐藏状态。

    /*** Initiates a fragment transaction to show the dialpad fragment. Animations and other visual* updates are handled by a callback which is invoked after the dialpad fragment is shown.* @see #onDialpadShown*/private void showDialpadFragment(boolean animate) {if (mIsDialpadShown || mStateSaved) {return;}mIsDialpadShown = true;mListsFragment.setUserVisibleHint(false);final FragmentTransaction ft = getFragmentManager().beginTransaction();if (mDialpadFragment == null) {mDialpadFragment = new DialpadFragment();ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);} else {ft.show(mDialpadFragment);}//mDialpadFragment.setAnimate(animate);AnalyticsUtil.sendScreenView(mDialpadFragment);ft.commit();maybeEnterSearchUi();if (animate) {mFloatingActionButtonController.scaleOut();} else {mFloatingActionButtonController.setVisible(false);}mActionBarController.onDialpadUp();mListsFragment.getView().animate().alpha(0).withLayer();}

3.接下来重点处理实现就在DialpapFragment中,首先来看类的声明/继承。

import com.android.dialer.dialpad.DialpadFragment;

/*** Fragment that displays a twelve-key phone dialpad.*/
public class DialpadFragment extends Fragmentimplements View.OnClickListener,View.OnLongClickListener, View.OnKeyListener,AdapterView.OnItemClickListener, TextWatcher,PopupMenu.OnMenuItemClickListener,DialpadKeyButton.OnPressedListener,/// M: add for plug-in @{DialpadExtensionAction {

从以上类实现/继承中可以发现,其继承了TextWatcher类,也正是这个类使之能够监听实现输入变化。

TextWatcher有3个重要方法,分别为:beforeTextChanged,onTextChanged和afterTextChanged。分别看下面那份源码。


onTextChanged


其中最重点的是afterTextChanged方法,其调用了SpecialCharSequenceMgr辅助工具类的handleChars方法。


4.handleChars方法中,会对各种特殊的secret code进行匹配处理。

    public static boolean handleChars(Context context, String input, EditText textField) {//get rid of the separators so that the string gets parsed correctlyString dialString = PhoneNumberUtils.stripSeparators(input);if (handleDeviceIdDisplay(context, dialString) //*#06#|| handleRegulatoryInfoDisplay(context, dialString)|| handlePinEntry(context, dialString)|| handleAdnEntry(context, dialString, textField)|| handleSecretCode(context, dialString) //for the form of *#*#<code>#*#*./// @}/// M: for plug-in @{|| ExtensionManager.getInstance().getDialPadExtension().handleChars(context,dialString)/// @}) {return true;}return false;}
5.接下来分开两种讲,一种是直接弹出对话框的那种,类如*#06#,另一种则是调起别的应用等方式。
5.1 )*#*#<code>#*#*

    /*** Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*.* If a secret code is encountered an Intent is started with the android_secret_code://<code>* URI.** @param context the context to use* @param input the text to check for a secret code in* @return true if a secret code was encountered*/static boolean handleSecretCode(Context context, String input) {// Secret codes are in the form *#*#<code>#*#*/// M: for plug-in @{input = ExtensionManager.getInstance().getDialPadExtension().handleSecretCode(input);/// @}int len = input.length();if (len > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {final Intent intent = new Intent(SECRET_CODE_ACTION,Uri.parse("android_secret_code://" + input.substring(4, len - 4)));///android_secret_code://287context.sendBroadcast(intent);return true;}return false;}
有以上代码可知,最终是通过broadcast发送出去的,并往intent里面加载了2种数据:Uri和Action。

Action:

private static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
接受端的注册方式,Action 和 data必须和发送的broadcast相匹配才行:

/vendor/mediatek/proprietary/packages/apps/EngineerMode/AndroidManifest.xml

        <receiverandroid:name=".EngineerModeReceiver"android:exported="true" ><intent-filter><action android:name="android.provider.Telephony.SECRET_CODE" /><dataandroid:host="3646633"android:scheme="android_secret_code" /></intent-filter></receiver>

/vendor/mediatek/proprietary/packages/apps/EngineerMode/src/com/mediatek/engineermode/EngineerModeReceiver.java


由上面代码可知,这就对应上了,在Receiver接受到广播后,启动对应的应用/Activity来处理接下来的工作。


5.2 )*#06#  直接在Context中弹出对话框,显示IMEI信息

/packages/apps/Dialer/src/com/android/dialer/SpecialCharSequenceMgr.java

    // TODO: Use TelephonyCapabilities.getDeviceIdLabel() to get the device id label instead of a// hard-coded string.static boolean handleDeviceIdDisplay(Context context, String input) {TelephonyManager telephonyManager =(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);if (telephonyManager != null && input.equals(MMI_IMEI_DISPLAY)) {int labelResId = (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) ?R.string.imei : R.string.meid;List<String> deviceIds = new ArrayList<String>();if (TelephonyManagerCompat.getPhoneCount(telephonyManager) > 1 &&CompatUtils.isMethodAvailable(TelephonyManagerCompat.TELEPHONY_MANAGER_CLASS,"getDeviceId", Integer.TYPE)) {for (int slot = 0; slot < telephonyManager.getPhoneCount(); slot++) {String deviceId = telephonyManager.getDeviceId(slot);if (!TextUtils.isEmpty(deviceId)) {deviceIds.add(deviceId);}}} else {deviceIds.add(telephonyManager.getDeviceId());}AlertDialog alert = new AlertDialog.Builder(context).setTitle(labelResId).setItems(deviceIds.toArray(new String[deviceIds.size()]), null).setPositiveButton(android.R.string.ok, null).setCancelable(false).show();///直接在Context中弹出对话框,显示IMEI信息return true;}return false;}

*#07# 直接通过隐式intent启动相关应用

    private static boolean handleRegulatoryInfoDisplay(Context context, String input) {if (input.equals(MMI_REGULATORY_INFO_DISPLAY)) {Log.d(TAG, "handleRegulatoryInfoDisplay() sending intent to settings app");Intent showRegInfoIntent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);try {context.startActivity(showRegInfoIntent);} catch (ActivityNotFoundException e) {Log.e(TAG, "startActivity() failed: " + e);}return true;}return false;}

以上就是Android通过Dialer方式启动暗码的大致源码流程分析。


这篇关于源码分析之Android通过Dialer实现暗码启动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1141592

相关文章

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构