1. 程式人生 > >Android簡訊傳送:考慮動態許可權和相容性問題

Android簡訊傳送:考慮動態許可權和相容性問題

小結:剛開始覺得簡訊傳送很簡單啊,不就是這樣這樣,再那樣一下就好了嘛。但其實內容聽繁瑣的。坑不多,但需要判斷的東西挺多。

首先需要判斷有無SIM卡

                TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
                String simState = tm.getSubscriberId();
                if (TextUtils.isEmpty(simState)) {
                    Toast.makeText
(MobileVerifed.this, "未檢測到Sim卡", Toast.LENGTH_SHORT).show(); return; }

其實這個getSubscriberId在後面也會用到。如果有卡的話,會獲得到運營商的IMSI

判斷是否雙卡【選擇哪一個卡去傳送簡訊】

                List<Integer> idList=new ArrayList<Integer>();
                SubscriptionManager subscriptionManager =  SubscriptionManager.from
(MobileVerifed.this); List<SubscriptionInfo> infoList = subscriptionManager.getActiveSubscriptionInfoList(); for (SubscriptionInfo info : infoList) { idList.add(info.getSubscriptionId()); } SmsManager.getSmsManagerForSubscriptionId(idList.get
(0)) .sendTextMessage("目的電話號碼",null,"簡訊訊息",null,null); SmsManager.getSmsManagerForSubscriptionId(idList.get(1)) .sendTextMessage("目的電話號碼",null,"簡訊訊息",null,null);

我的手機是雙卡,可以看到infoList 值為:
這裡寫圖片描述
idList值為:
這裡寫圖片描述

其中如果選擇一個變數區分兩個手機卡,應選擇,iccid。每個sim卡都有自己的iccid。

ps. SubscriptionManager 是谷歌官方5.0以上正式支援雙卡雙待,所以會有相關類。以前是各大廠商自己加的。如果試圖相容5.0以下必須用反射,而且必須考慮運營商的不同。

判斷是否開通了傳送簡訊許可權【詢問、允許】

這兒有點複雜,和相容性相關
有人會問,檢測這個許可權幹什麼呀。檢測許可權的主要目的是為了告訴使用者去開通,或者提示使用者不是軟體卡了,是你沒開通。

6.0以下,一般在初始的時候就會把許可權就要到,所以不需要檢視是否開通了傳送簡訊許可權。但如果查詢的話,也有一個相容方法 ContextCompat.checkSelfPermission()

但6.0以下也存在一些系統,比如我試的這臺魅藍2,使用的是YunOS系統,盜版的Android系統,haha,可以使用動態許可權。但使用者在設定中禁止後,上面檢測的方法檢測不到許可權是否開通。也就是使用者點選了之後,就沒反應了,體驗不好。但是目前沒有找到方法。

6.0以上,不同團隊的targetSdkVersion不同,還分為兩種情況
首先是targetSdkVersion<23時,如果使用checkSelfPermission()方法,累死也檢測不出來。即檢測結果都是PERMISSION_GRANTED
targetSdkVersion指的是編譯時期的版本,按照我的理解:APK 在呼叫系統 的時候,系統首先會查一下呼叫的 APK 的 targetSdkVersion 資訊,如果小於 23,就還是按照老的行為,否者執行新的行為。
解決辦法:使用PermissionChecker.checkSelfPermission()

6.0以上,且targetSdkVersion>=23,就一切好說啦,直接使用checkSelfPermission()就行。

檢測targetSdkVersion 版本值

info = MobileVerifed.this.getPackageManager().getPackageInfo(
                    MobileVerifed.this.getPackageName(), 0);
            int targetSdkVersion = info.applicationInfo.targetSdkVersion;

許可權不再提醒

許可權檢測的返回值有兩種錯誤。
一種是PERMISSION_DENIED,出現在動態授權時 ,“拒絕一次”
一種是SIGNATURE_SECOND_NOT_SIGNED,出現在 設定中直接禁止傳送簡訊,或者拒絕是選上不再提醒
那麼怎麼檢測第二種呢?

ActivityCompat.shouldShowRequestPermissionRationale()

返回值為false時,代表使用者設定中禁止傳送簡訊,或者不再提醒

發簡訊

這個就直接上程式碼啦

IntentFilter mFilter01;
        mFilter01 = new IntentFilter(SENT_SMS_ACTION);
        sendReceiver = new SmsSendReceiver();
        registerReceiver(sendReceiver, mFilter01);

        mFilter01 = new IntentFilter(DELIVERED_SMS_ACTION);
        receiveReceiver = new SmsSendReceiver();
        registerReceiver(receiveReceiver, mFilter01);

以上是註冊廣播時的程式碼,主要是兩種,【簡訊已傳送】會傳一個廣播,而接收方接收到簡訊,會回傳給運營商一個訊號,這時候會收到【簡訊已接收】的廣播。

主要,這些廣播註冊一定要在onCreate()裡,否則會出現傳送簡訊一次多次廣播回撥的現象。就是,點選第一次,廣播回撥兩次【傳送,接收】;點選兩次,廣播回撥四冊… ….

註冊了廣播,就別忘了解綁啊

if (sendReceiver != null && receiveReceiver != null) {
            unregisterReceiver(sendReceiver);
            unregisterReceiver(receiveReceiver);
        }

傳送簡訊:


        Intent sendIntent = new Intent(SENT_SMS_ACTION);
        sendIntent.putExtra("key", "sms_send");
        PendingIntent sendPI = PendingIntent.getBroadcast(MobileVerifed.this, (int) System.currentTimeMillis(),
                sendIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        Intent receiveIntent = new Intent(DELIVERED_SMS_ACTION);
        receiveIntent.putExtra("key", "sms_receive");
        PendingIntent deliverPI = PendingIntent.getBroadcast(MobileVerifed.this, (int) System.currentTimeMillis(),
                receiveIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        String telNum = "****";//目的電話號碼
        SmsManager manager = SmsManager.getDefault();
        ArrayList<String> smsList = manager.divideMessage("傳送簡訊test");
        for (String s : smsList) {
            manager.sendTextMessage(telNum, null, s, sendPI, deliverPI);
        }

以上PendingIntent的第二個和第四個引數要當心哦

接收廣播:

@Override
        public void onReceive(Context _context, Intent _intent) {
            String key = _intent.getStringExtra("key");
            if (key.equals("sms_send") || key.equals("sms_receive")) {
                String flag = key.equals("sms_send") ? "簡訊傳送" : "簡訊接收";
                switch (getResultCode()) {
                    case Activity.RESULT_OK:
                        Toast.makeText(MobileVerifed.this,
                                flag + "成功", Toast.LENGTH_SHORT)
                                .show();
                        // todo 請求後臺

                        break;
                    case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                    case SmsManager.RESULT_ERROR_RADIO_OFF:
                    case SmsManager.RESULT_ERROR_NULL_PDU:
                    default:
                        Toast.makeText(_context, flag + "失敗", Toast.LENGTH_SHORT).show();
                        break;
                }
            }

        }

沒話費

沒話費…,會走廣播,返回值是 SmsManager.RESULT_ERROR_GENERIC_FAILURE

跳轉到許可權設定頁面

Intent localIntent = new Intent();
                localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                if (Build.VERSION.SDK_INT >= 9) {
                    localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
                    localIntent.setData(Uri.fromParts("package", getPackageName(), null));
                } else if (Build.VERSION.SDK_INT <= 8) {
                    localIntent.setAction(Intent.ACTION_VIEW);
                    localIntent.setClassName("com.android.settings","com.android.settings.InstalledAppDetails");
                    localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
                }
                startActivity(localIntent);

這個巨坑啊。因為要跳轉到不同廠商的應用許可權,我們這兒小米手機測試的時候,出現了以上方法,在8.0以上的手機許可權頁面,處於只讀狀態,而非可讀可寫。然後就是更改許可權不成功。

然後就去網上查資料,發現不同MIUI系統,他們許可權頁面甚至包名不同,甚至activity命名都不同 QAQ

後來專門針對小米,進行了許可權頁面跳轉的修改。
首先判斷是MIUI系統,其次判斷是MIUI5/6/7/8
MIUI5使用一般的跳轉方法【如上面的】即可。MIUI6和MIUI7跳轉方式一樣。MIUI8跳轉也得專門挑出來

程式碼如下:

private void jumpPermissionActivity() {
        if (Build.MANUFACTURER.equals("Xiaomi")) {
            String miuiVersion = Util.getMiuiVersion();
            String permissionDetailActivityNameV6 = "com.miui.permcenter.permissions.AppPermissionsEditorActivity";
            String permissionDetailActivityNameV8 = "com.miui.permcenter.permissions.PermissionsEditorActivity";
            if (!TextUtils.isEmpty(miuiVersion)) {
                switch (miuiVersion) {
                    case "V5":
                        jumpOriginalPermissionActivity();
                        break;
                    case "V6":
                    case "V7":
                        jumpXimiPermissionActivity(permissionDetailActivityNameV6);
                        break;
                    case "V8":
                        jumpXimiPermissionActivity(permissionDetailActivityNameV8);
                        break;
                    default:
                        jumpOriginalPermissionActivity();
                        break;
                }
            } else {
                Toast.makeText(DzhApplication.getContext(), "應用許可權頁跳轉失敗,請手動在設定中進行更改", Toast.LENGTH_SHORT).show();
            }

        } else {
            jumpOriginalPermissionActivity();
        }
    }

    /**
     * 根據MIUI系統,判斷需要跳轉的頁面
     *
     * @param permissionDetailActivityName
     */
    private void jumpXimiPermissionActivity(String permissionDetailActivityName) {
        try {

            Intent i = new Intent("miui.intent.action.APP_PERM_EDITOR");
            ComponentName componentName = new ComponentName("com.miui.securitycenter", permissionDetailActivityName);
            i.setComponent(componentName);
            i.putExtra("extra_pkgname", getPackageName());
            startActivity(i);
        } catch (Exception e) {
            jumpOriginalPermissionActivity();
        }
    }

    /**
     * 普通Android手機通用的許可權設定頁面跳轉
     */
    private void jumpOriginalPermissionActivity() {
        Intent localIntent = new Intent();
        localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= 9) {
            localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
            localIntent.setData(Uri.fromParts("package", getPackageName(), null));
        } else if (Build.VERSION.SDK_INT <= 8) {
            localIntent.setAction(Intent.ACTION_VIEW);
            localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
            localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
        }
        startActivity(localIntent);

    }

pssssssssss. AS的Gradle外掛預設會啟用Manifest Merger Tool,若Library專案中也定義了與主專案相同的屬性(例如預設生成的Android:icon和android:theme),則此時會合並失敗,並報上面的錯誤。

在Manifest.xml的application標籤下新增tools:replace=”android:icon, android:theme”(多個屬性用,隔開,並且記住在manifest根標籤上加入xmlns:tools=”http://schemas.android.com/tools”,否則會找不到namespace哦)
————————— the end —————————