zoukankan      html  css  js  c++  java
  • Android 装逼技术之暗码启动应用

    什么是暗码?

    在拨号盘中输入*#*#<code>#*#*后,APP 可以监控到这些输入,然后做相应的动作,比如启动应用,是不是有点骚。

    下面看下这个骚操作是如何实现的。

    效果预览

    源码

    DialtactsActivity#showDialpadFragment

    DialtactsActivity 中有个 showDialpadFragment 方法,用来加载显示拨号盘,因此入口就从 showDialpadFragment 看起,基于 Android P 分析。

    private void showDialpadFragment(boolean animate) {
      //……
      final FragmentTransaction ft = getFragmentManager().beginTransaction();
      if (dialpadFragment == null) {
        dialpadFragment = new DialpadFragment();
        ft.add(R.id.dialtacts_container, dialpadFragment, TAG_DIALPAD_FRAGMENT);
      } else {
        ft.show(dialpadFragment);
      }
      //……
    }
    

    具体实现在 DialpapFragment 中,看到 DialpapFragment 实现了 TextWatcher,TextWatcher 有 3 个重要方法,分别为:beforeTextChanged,onTextChanged 和 afterTextChanged,重点看 afterTextChanged 方法。

    DialpadFragment#afterTextChanged

    public class DialpadFragment extends Fragment
            implements View.OnClickListener,
            View.OnLongClickListener,
            View.OnKeyListener,
            AdapterView.OnItemClickListener,
            TextWatcher,
            PopupMenu.OnMenuItemClickListener,
            DialpadKeyButton.OnPressedListener {
        //……
        @Override
        public void afterTextChanged(Editable input) {
            // When DTMF dialpad buttons are being pressed, we delay SpecialCharSequenceMgr sequence,
            // since some of SpecialCharSequenceMgr's behavior is too abrupt for the "touch-down"
            // behavior.
            if (!digitsFilledByIntent
                    && SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), digits)) {
                // A special sequence was entered, clear the digits
                digits.getText().clear();
            }
    
            if (isDigitsEmpty()) {
                digitsFilledByIntent = false;
                digits.setCursorVisible(false);
            }
    
            if (dialpadQueryListener != null) {
                dialpadQueryListener.onDialpadQueryChanged(digits.getText().toString());
            }
    
            updateDeleteButtonEnabledState();
        }
        //……
    }
    

    这里调用了 SpecialCharSequenceMgr 辅助工具类的 handleChars 方法,看这个方法。

    SpecialCharSequenceMgr#handleChars

    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)
          || handleRegulatoryInfoDisplay(context, dialString)
          || handlePinEntry(context, dialString)
          || handleAdnEntry(context, dialString, textField)
          || handleSecretCode(context, dialString)) {
        return true;
      }
      if (MotorolaUtils.handleSpecialCharSequence(context, input)) {
        return true;
      }
      return false;
    }
    

    handleChars 方法中,会对各种特殊的 secret code 进行匹配处理,这里我们看 handleSecretCode。

    SpecialCharSequenceMgr#handleSecretCode

    static boolean handleSecretCode(Context context, String input) {
      // Secret code specific to OEMs should be handled first.
      if (TranssionUtils.isTranssionSecretCode(input)) {
        TranssionUtils.handleTranssionSecretCode(context, input);
        return true;
      }
      // Secret codes are accessed by dialing *#*#<code>#*#* or "*#<code_starting_with_number>#"
      if (input.length() > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
        String secretCode = input.substring(4, input.length() - 4);
        TelephonyManagerCompat.handleSecretCode(context, secretCode);
        return true;
      }
      return false;
    }
    

    再看下 TelephonyManagerCompat.handleSecretCode 方法。

    TelephonyManagerCompat#handleSecretCode

    public static void handleSecretCode(Context context, String secretCode) {
      // Must use system service on O+ to avoid using broadcasts, which are not allowed on O+.
      if (BuildCompat.isAtLeastO()) {
        if (!TelecomUtil.isDefaultDialer(context)) {
          LogUtil.e(
              "TelephonyManagerCompat.handleSecretCode",
              "not default dialer, cannot send special code");
          return;
        }
        context.getSystemService(TelephonyManager.class).sendDialerSpecialCode(secretCode);
      } else {
        // System service call is not supported pre-O, so must use a broadcast for N-.
        Intent intent =
            new Intent(SECRET_CODE_ACTION, Uri.parse("android_secret_code://" + secretCode));
        context.sendBroadcast(intent);
      }
    }
    

    可以看到在拨号中接收到*#*#<code>#*#* 这样的指令时,程序会对外发送广播,这就意味着我们能够接收这个广播然后可以做我们想做的事情。

    接下来我们看看这个接受广播代码是怎么写。

    应用

    首先在 AndroidManifest 文件中注册广播接收器。

    <receiver
        android:name=".SecretCodeReceiver">
        <intent-filter>
            <action android:name="android.provider.Telephony.SECRET_CODE" />
            <data android:scheme="android_secret_code" android:host="1010"  />
        </intent-filter>
    </receiver>
    

    接收广播,启动应用。

    public class SecretCodeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null && SECRET_CODE_ACTION.equals(intent.getAction())){
                Intent i = new Intent(Intent.ACTION_MAIN);
                i.setClass(context, MainActivity.class);
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(i);
            }
        }
    }
    

    这样只要在拨号中输入*#*#1010#*#*就能启动相应的应用程序,OK,收功。

    公众号

    我的公众号:吴小龙同学,欢迎关注交流~

  • 相关阅读:
    数组里面为对象根据某一属性排序
    理由<a>标签跳转到对应锚点
    jquery对复选框选中
    Fullcalendar
    es6学习
    vue 关于树杈图问题
    input 复选框样式修改
    GAMES101 作业2
    一份自己iOS 面试题,拿到15K35K,分享出来
    Cesium 一款面向三维地球和地图的,世界级的JavaScript开源产品
  • 原文地址:https://www.cnblogs.com/WuXiaolong/p/11187423.html
Copyright © 2011-2022 走看看