目前接触比较多的就是通过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();
}
import com.android.dialer.dialpad.DialpadFragment;
/**
* Fragment that displays a twelve-key phone dialpad.
*/
public class DialpadFragment extends Fragment
implements 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 correctly
String 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 *#*##*#*.
/// @}
/// M: for plug-in @{
|| ExtensionManager.getInstance().getDialPadExtension().handleChars(context,
dialString)
/// @}
) {
return true;
}
return false;
}
5.接下来分开两种讲,一种是直接弹出对话框的那种,类如*#06#,另一种则是调起别的应用等方式。
#*#*
/**
* Handles secret codes to launch arbitrary activities in the form of *#*##*#*.
* If a secret code is encountered an Intent is started with the android_secret_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 *#*##*#*
/// 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://287
context.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
/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 deviceIds = new ArrayList();
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方式启动暗码的大致源码流程分析。