Android - программная отправка MMS без использования приложения по умолчанию - PullRequest
3 голосов
/ 08 мая 2020

Часть MMS в Android SDK в основном не раскрывается, и, следовательно, действительно одна основная сторонняя библиотека , которая помогает в этом. Образец проекта, включенный в библиотеку, не отправляет MMS, однако форк проекта отправляет.

Когда я загружаю последний проект, я могу запустить образец и отправить MMS без необходимо установить приложение как приложение по умолчанию. Однако, когда я интегрирую код в свое собственное приложение, MMS будет отправлять только в том случае, если я сделаю свое приложение приложением для обмена сообщениями по умолчанию. Я читал, что Android 4.4+ не поддерживает MMS, не являясь приложением по умолчанию, но почему этот образец проекта, из которого я получаю свой код, работает без использования по умолчанию? Единственное отличие, которое я действительно замечаю, заключается в том, что минимальный SDK составляет 14 по сравнению с моим проектом, который равен 18.

Вот основной код из моего проекта:

Manifest. xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hacknow2">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.WRITE_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_MMS" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <application
        android:name=".init.App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".phase1.PermissionsActivity"/>

        <activity android:name=".phase2.youtube.YoutubeActivity" />

        <activity android:name=".phase2.posting.PostingActivity" />
        <activity
            android:name=".phase2.ui.main.SelectionActivity"
            android:label="@string/title_activity_selection"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <action android:name="android.intent.action.SENDTO" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </activity>
        <activity android:name=".phase1.OpeningActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".phase3.mms.KitSmsSentReceiver"
            android:permission="android.permission.BROADCAST_SMS">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_DELIVER" />
            </intent-filter>
        </receiver>

        <receiver
            android:name=".phase3.mms.KitMmsSentReceiver"
            android:permission="android.permission.BROADCAST_WAP_PUSH">
            <intent-filter>
                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />

                <data android:mimeType="application/vnd.wap.mms-message" />
            </intent-filter>
        </receiver>

        <receiver
            android:name="com.klinker.android.send_message.MmsSentReceiver"
            android:taskAffinity="com.klinker.android.messaging.MMS_SENT" />

        <receiver
            android:name="com.klinker.android.send_message.MmsReceivedReceiver"
            android:taskAffinity="com.klinker.android.messaging.MMS_RECEIVED" />

        <service android:name="com.android.mms.transaction.TransactionService" />

        <service
            android:name=".phase3.mms.HeadlessSmsSendService"
            android:exported="true"
            android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
            <intent-filter>
                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />

                <category android:name="android.intent.category.DEFAULT" />

                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </service>
    </application>

</manifest>

MMSManager, singleton, который управляет отправкой MMS

public class MMSManager {

    private static final String TAG = "MMSManager";
    private static ThreadPoolExecutor mThreadManager;
    private static MMSManager INSTANCE;
    private static BlockingQueue<Runnable> decodeWorkQueue;
    private static int NUMBER_OF_CORES =
            Runtime.getRuntime().availableProcessors();
    private Settings mSettings;
    private Context mContext;

    private MMSManager(Context c) {
        mContext = c;
        initSettings();
        initLogging();
        // A queue of Runnables
        decodeWorkQueue = new LinkedBlockingQueue<Runnable>();
        // setting the thread factory
        mThreadManager = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES,
                50, TimeUnit.MILLISECONDS, decodeWorkQueue);

        BroadcastUtils.sendExplicitBroadcast(c, new Intent(), "test action");
    }

    //See https://stackoverflow.com/questions/14057273/android-singleton-with-global-context
    private static synchronized MMSManager getSync() {
        if (INSTANCE == null) INSTANCE = new MMSManager(App.get());
        return INSTANCE;
    }

    public static MMSManager getInstance(Context c) {
        if (INSTANCE == null) {
            INSTANCE = getSync();
        }
        return INSTANCE;
    }

    private void initSettings() {
        mSettings = Settings.get(mContext);

        if (TextUtils.isEmpty(mSettings.getMmsc()) &&
                Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            initApns();
        }
    }

    private void initApns() {
        ApnUtils.initDefaultApns(mContext, new ApnUtils.OnApnFinishedListener() {
            @Override
            public void onFinished() {
                mSettings = Settings.get(mContext, true);
            }
        });
    }

    private void initLogging() {
        com.klinker.android.logger.Log.setDebug(true);
        com.klinker.android.logger.Log.setPath("messenger_log.txt");
        com.klinker.android.logger.Log.setLogListener(new OnLogListener() {
            @Override
            public void onLogged(String tag, String message) {
                //logAdapter.addItem(tag + ": " + message);
                android.util.Log.d("MMS_Manager " + tag, "onLogged: " + message);
            }
        });
    }

    //Not sure what exception might pop up but it's being handled anyway...
    public void sendMMS(String phoneNumber, Bitmap bm) {
        mThreadManager.execute(new Runnable() {
            @Override
            public void run() {
                Log.d("ThreadPool/MMSManager", "Trying to send MMS.");
                com.klinker.android.send_message.Settings sendSettings = new com.klinker.android.send_message.Settings();
                sendSettings.setMmsc(mSettings.getMmsc());
                sendSettings.setProxy(mSettings.getMmsProxy());
                sendSettings.setPort(mSettings.getMmsPort());
                sendSettings.setUseSystemSending(true);

                Transaction transaction = new Transaction(mContext, sendSettings);

                Message message = new Message(null, phoneNumber);

                if (bm != null)
                    message.setImage(bm);

                transaction.sendNewMessage(message, Transaction.NO_THREAD_ID);
            }
        });
    }
}

Фрагмент, вызывающий методы MMSManager, расположенный в его родительском элементе SelectionActivity:

public class SendDialog extends DialogFragment {

    private static final int MY_PERMISSIONS_REQUEST_SEND_SMS = 1000;
    private ArrayList<String> mPhoneNumbers;
    private DMessage mMessage;
    private OnFinished mCompletionListener;
    private MMSManager mMMSManager;
    private Bitmap mBitmap;
    private static String[] mMediaTypes;

    public SendDialog(ArrayList<String> phoneNumbers, DMessage m, OnFinished listener){
        mPhoneNumbers = phoneNumbers;
        mMessage = m;
        mCompletionListener = listener;
    }

    public SendDialog(ArrayList<String> phoneNumbers, DMessage m, Bitmap b, OnFinished listener){
        mPhoneNumbers = phoneNumbers;
        mMessage = m;
        mCompletionListener = listener;
        mBitmap = b;
    }

    public interface OnFinished{
        void complete(boolean success);
    }

    private String getCautionMessage(){
        int numContacts = 0;
        if(mPhoneNumbers!=null && mPhoneNumbers.size()>0) {
            for (String number :
                    mPhoneNumbers) {
                numContacts++;
            }
        }
        return "Are you sure you'd like to send the highlighted post to " + numContacts + " people?";
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        mMediaTypes = getResources().getStringArray(R.array.postoptions);
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(getCautionMessage())
                .setPositiveButton(R.string.OK, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        sendBulkTexts();
                        dismiss();
                    }
                })
                .setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        mCompletionListener.complete(false);
                    }
                });
        // Create the AlertDialog object and return it
        return builder.create();
    }

    private boolean isMMS(){
        return mMessage.getMediaType().equals(mMediaTypes[2]);//mediaTypes[2] == "Image"
    }

    /**
     * Converts phone number to only numbers.
     * I.e. (XXX)-XXX-XXXX to XXXXXXXXXX
     */
    private static String phoneNumberFormatter(String unformatted) {
        return unformatted.replaceAll("\\D+", "");
    }

    private void sendBulkTexts(){

        for (String number :
                mPhoneNumbers) {
            try {
                String formattedNumber = phoneNumberFormatter(number);
                if(!isMMS()) {
                    //Send a Youtube video or text
                    sendSMS(formattedNumber, mMessage.getBody());
                }
                else{
                    if(mMMSManager==null)//instantiate MMSManager and load bitmap
                        mMMSManager = MMSManager.getInstance(getActivity());
                    if(mBitmap!=null)
                        mMMSManager.sendMMS(formattedNumber, mBitmap);
                }
            }
            catch(Exception e){
                Toast.makeText(getActivity(), "Could not send text to " + number + ".", Toast.LENGTH_LONG).show();
                Log.e("SendDialog", "sendBulkTexts: ", e);
                mCompletionListener.complete(false);
            }
        }
        Toast.makeText(getActivity(), "Finished sending texts.", Toast.LENGTH_LONG).show();
        mCompletionListener.complete(true);
    }

    private void sendSMS(String phoneNumber, String message) throws Exception{
        SmsManager smsManager = SmsManager.getDefault();
        smsManager.sendTextMessage(phoneNumber, null, message, null, null);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_SEND_SMS: {
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    sendBulkTexts();
                } else {
                    Toast.makeText(getActivity(),
                            "Permission for sending SMSs denied.", Toast.LENGTH_LONG).show();
                    mCompletionListener.complete(false);
                    return;
                }
            }
        }
    }
}
...