6.0联系人跳转到短信的逻辑

本文分析了Android 6.0中从联系人详情页跳转到短信应用后,无法通过Back键返回原应用的问题。问题根源在于启动方式和TaskStackBuilder的使用。在点击发送短信按钮后,系统调用LaunchConversationActivity,但该活动立即finish(),并通过TaskStackBuilder创建回退栈,导致回退到ConversationListActivity而不是原应用。
摘要由CSDN通过智能技术生成

Android 6.0中其他app跳转到messaging后,按back键无法返回原应用,分析了下原因,原来是处在messaging的启动方式上。
在Android 6.0中联系人的详情统一为了QuickContactActivity,乍看里面内容很多、很杂,整理了下,发现主要是onCreate里的这几个自定义的ExpandingEntryCardView。

@Override
    protected void onCreate(Bundle savedInstanceState) {
        ……………………
        mContactCard = (ExpandingEntryCardView) findViewById(R.id.communication_card);
        mNoContactDetailsCard = (ExpandingEntryCardView) findViewById(R.id.no_contact_data_card);
        mRecentCard = (ExpandingEntryCardView) findViewById(R.id.recent_card);
        mAboutCard = (ExpandingEntryCardView) findViewById(R.id.about_card);
        ……………………
    }

跳转到短信,必然是有点击事件的,这些内容和上面的代码一样都是在onCreate中:

mNoContactDetailsCard.setOnClickListener(mEntryClickHandler);
mContactCard.setOnClickListener(mEntryClickHandler);
mContactCard.setExpandButtonText(
        getResources().getString(R.string.expanding_entry_card_view_see_all));
        mContactCard.setOnCreateContextMenuListener(mEntryContextMenuListener);

mRecentCard.setOnClickListener(mEntryClickHandler);
        mRecentCard.setTitle(getResources().getString(R.string.recent_card_title));

mAboutCard.setOnClickListener(mEntryClickHandler);
        mAboutCard.setOnCreateContextMenuListener(mEntryContextMenuListener);

跳转逻辑就在这个mEntryClickHandler中:

final OnClickListener mEntryClickHandler = new OnClickListener() {
        @Override
        public void onClick(View v) {
            final Object entryTagObject = v.getTag();
            if (entryTagObject == null || !(entryTagObject instanceof EntryTag)) {
                Log.w(TAG, "EntryTag was not used correctly");
                return;
            }
            final EntryTag entryTag = (EntryTag) entryTagObject;
            final Intent intent = entryTag.getIntent();
            final int dataId = entryTag.getId();
            if (dataId == CARD_ENTRY_ID_EDIT_CONTACT) {
                editContact();
                return;
            }
            //?????
            //intent.setAction(Intent.ACTION_DIAL);
            //-----------------------------------//
            // Pass the touch point through the intent for use in the InCallUI
            if (Intent.ACTION_CALL.equals(intent.getAction())) {
                if (TouchPointManager.getInstance().hasValidPoint()) {
                    Bundle extras = new Bundle();
                    extras.putParcelable(TouchPointManager.TOUCH_POINT,
                            TouchPointManager.getInstance().getPoint());
                    intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
                }
            }
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mHasIntentLaunched = true;
            try {
                startActivity(intent);
            } catch (SecurityException ex) {
                Toast.makeText(QuickContactActivity.this, R.string.missing_app,
                        Toast.LENGTH_SHORT).show();
                Log.e(TAG, "QuickContacts does not have permission to launch "
                        + intent);
            } catch (ActivityNotFoundException ex) {
                Toast.makeText(QuickContactActivity.this, R.string.missing_app,
                        Toast.LENGTH_SHORT).show();
            }

            // Default to USAGE_TYPE_CALL. Usage is summed among all types for sorting each data id
            // so the exact usage type is not necessary in all cases
            String usageType = DataUsageFeedback.USAGE_TYPE_CALL;

            final Uri intentUri = intent.getData();
            if ((intentUri != null && intentUri.getScheme() != null &&
                    intentUri.getScheme().equals(ContactsUtils.SCHEME_SMSTO)) ||
                    (intent.getType() != null && intent.getType().equals(MIMETYPE_SMS))) {
                usageType = DataUsageFeedback.USAGE_TYPE_SHORT_TEXT;
            }

            // Data IDs start at 1 so anything less is invalid
            if (dataId > 0) {
                final Uri dataUsageUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
                        .appendPath(String.valueOf(dataId))
                        .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, usageType)
                        .build();
                try {
                    final boolean successful = getContentResolver().update(
                            dataUsageUri, new ContentValues(), null, null) > 0;
                    if (!successful) {
                        Log.w(TAG, "DataUsageFeedback increment failed");
                    }
                } catch (SecurityException ex) {
                    Log.w(TAG, "DataUsageFeedback increment failed", ex);
                }
            } else {
                Log.w(TAG, "Invalid Data ID");
            }
        }
    };

在onClick中,intent的获取是通过view -> getTag -> getIntent来获取的。发送短信这个button对应view获取到的intent的action自然为ACTION_SENDTO,这个action是会由默认的短信应用来处理的。在Android 6.0中,我们将老旧的Mms替换为了messaging,因此被启动的activity为LaunchConversationActivity,到了这里就开始有趣了。
本来我以为这个activity就是编辑短信并发送的activity,查看源码发现其仅有区区137行代码,而且还在onCreate中把自己finish了!?!

@Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (UiUtils.redirectToPermissionCheckIfNeeded(this)) {
            return;
        }

        final Intent intent = getIntent();
        final String action = intent.getAction();
        if (Intent.ACTION_SENDTO.equals(action) || Intent.ACTION_VIEW.equals(action)) {
            String[] recipients = UriUtil.parseRecipientsFromSmsMmsUri(intent.getData());
            final boolean haveAddress = !TextUtils.isEmpty(intent.getStringExtra(ADDRESS));
            final boolean haveEmail = !TextUtils.isEmpty(intent.getStringExtra(Intent.EXTRA_EMAIL));
            if (recipients == null && (haveAddress || haveEmail)) {
                if (haveAddress) {
                    recipients = new String[] { intent.getStringExtra(ADDRESS) };
                } else {
                    recipients = new String[] { intent.getStringExtra(Intent.EXTRA_EMAIL) };
                }
            }
            mSmsBody = intent.getStringExtra(SMS_BODY);
            if (TextUtils.isEmpty(mSmsBody)) {
                // Used by intents sent from the web YouTube (and perhaps others).
                mSmsBody = getBody(intent.getData());
                if (TextUtils.isEmpty(mSmsBody)) {
                    // If that fails, try yet another method apps use to share text
                    if (ContentType.TEXT_PLAIN.equals(intent.getType())) {
                        mSmsBody = intent.getStringExtra(Intent.EXTRA_TEXT);
                    }
                }
            }
            if (recipients != null) {
                mBinding.bind(DataModel.get().createLaunchConversationData(this));
                mBinding.getData().getOrCreateConversation(mBinding, recipients);
            } else {
                // No recipients were specified in the intent.
                // Start a new conversation with contact picker. The new conversation will be
                // primed with the (optional) message in mSmsBody.
                onGetOrCreateNewConversation(null);
            }
        } else {
            LogUtil.w(LogUtil.BUGLE_TAG, "Unsupported conversation intent action : " + action);
        }
        // As of M, activities without a visible window must finish before onResume completes.
        finish();
    }

原来这是一个没有UI的activity,它的作用就是启动ConversationActivity,启动代码如下:

    @Override
    public void onGetOrCreateNewConversation(final String conversationId) {
        final Context context = Factory.get().getApplicationContext();
                                                            UIIntents.get().launchConversationActivityWithParentStack(context, conversationId, mSmsBody);
    }

launchConversationActivityWithParentStack就是这个方法导致了back无法退回,其实现在UIIntentsImpl.java中:

@Override
    public void launchConversationActivityWithParentStack(final Context context,
                final String conversationId, final String smsBody) {
        final MessageData messageData = TextUtils.isEmpty(smsBody)
                ? null
                : MessageData.createDraftSmsMessage(conversationId, null, smsBody);
        TaskStackBuilder.create(context)
                .addNextIntentWithParentStack(
                        getConversationActivityIntent(context, conversationId, messageData,
                                false /* withCustomTransition */))
                .startActivities();
    }

做过通知栏的朋友可能很早就猜到原因了,就是TaskStackBuilder设置了回退栈的内容,虽然这是在Android 4.1就引入了的内容,以前还真没遇到:

 <activity
            android:name=".ui.conversation.ConversationActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="user"
            android:windowSoftInputMode="stateHidden|adjustResize"
            android:theme="@style/BugleTheme.ConversationActivity"
            android:parentActivityName="com.android.messaging.ui.conversationlist.ConversationListActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.android.messaging.ui.conversationlist.ConversationListActivity" />
        </activity>

parentActivityName设置为了ConversationListActivity. So,只要回退,肯定是回到短信列表页面。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值