com.tct.mail.ui.OnePaneController.java Source code

Java tutorial

Introduction

Here is the source code for com.tct.mail.ui.OnePaneController.java

Source

/*******************************************************************************
 *      Copyright (C) 2012 Google Inc.
 *      Licensed to The Android Open Source Project.
 *
 *      Licensed under the Apache License, Version 2.0 (the "License");
 *      you may not use this file except in compliance with the License.
 *      You may obtain a copy of the License at
 *
 *           http://www.apache.org/licenses/LICENSE-2.0
 *
 *      Unless required by applicable law or agreed to in writing, software
 *      distributed under the License is distributed on an "AS IS" BASIS,
 *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *      See the License for the specific language governing permissions and
 *      limitations under the License.
 *******************************************************************************/
/*
==========================================================================
*HISTORY
*
*Tag             Date         Author          Description
*============== ============ =============== ==============================
*BUGFIX_953177  2015/03/23   gengkexue        [Scenario Test][Email][FC][monitor]Email force close happen when login sunmail account
*BUGFIX-961694  2015/04/21   peng-zhang       [Android5.0][Email] [UI] The 'All, Sender, Receiver, Subject, Body' bar is orange in mail list.
*BUGFIX_1008675  2015/05/21   zhaotianyong     [Android5.0][Email] [Monitor][Force close]Email FC after exit a subfolder
*BUGFIX_1033368  2015/07/01   zheng.zou       [GAPP][Monitor][FC][Email]Pop-up "Email has stopped" after press back key when launch email from widget
*BUGFIX_1075110  2015/08/28  jin.dong        [jrdlog]com.tct.email
*BUGFIX_708075  2015/10/17   jian.xu         [Android L][Email][Monkey][Crash][Monitor]com.tct.email crashs by Short Msg: java.lang.IllegalStateException
*BUGFIX_1117013 2015/11/26   jin.dong        [Monkey][Crash][Email] 'com.tct.email' crashes during system monkey test.
============================================================================
*/

package com.tct.mail.ui;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.v4.widget.DrawerLayout;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ListView;

import com.tct.email.R;
import com.tct.mail.ConversationListContext;
import com.tct.mail.providers.Account;
import com.tct.mail.providers.Conversation;
import com.tct.mail.providers.Folder;
import com.tct.mail.providers.UIProvider;
import com.tct.mail.utils.FolderUri;
import com.tct.mail.utils.LogUtils;
import com.tct.mail.utils.SortHelper;
import com.tct.mail.utils.Utils;
import android.support.v7.app.ActionBar;

/**
 * Controller for one-pane Mail activity. One Pane is used for phones, where screen real estate is
 * limited. This controller also does the layout, since the layout is simpler in the one pane case.
 */

public final class OnePaneController extends AbstractActivityController {
    /** Key used to store {@link #mLastConversationListTransactionId} */
    private static final String CONVERSATION_LIST_TRANSACTION_KEY = "conversation-list-transaction";
    /** Key used to store {@link #mLastConversationTransactionId}. */
    private static final String CONVERSATION_TRANSACTION_KEY = "conversation-transaction";
    /** Key used to store {@link #mConversationListVisible}. */
    private static final String CONVERSATION_LIST_VISIBLE_KEY = "conversation-list-visible";
    /** Key used to store {@link #mConversationListNeverShown}. */
    private static final String CONVERSATION_LIST_NEVER_SHOWN_KEY = "conversation-list-never-shown";

    private static final int INVALID_ID = -1;
    private boolean mConversationListVisible = false;
    private int mLastConversationListTransactionId = INVALID_ID;
    private int mLastConversationTransactionId = INVALID_ID;
    /** Whether a conversation list for this account has ever been shown.*/
    private boolean mConversationListNeverShown = true;

    public OnePaneController(MailActivity activity, ViewMode viewMode) {
        super(activity, viewMode);
    }

    @Override
    public void onRestoreInstanceState(Bundle inState) {
        super.onRestoreInstanceState(inState);
        if (inState == null) {
            return;
        }
        mLastConversationListTransactionId = inState.getInt(CONVERSATION_LIST_TRANSACTION_KEY, INVALID_ID);
        mLastConversationTransactionId = inState.getInt(CONVERSATION_TRANSACTION_KEY, INVALID_ID);
        mConversationListVisible = inState.getBoolean(CONVERSATION_LIST_VISIBLE_KEY);
        mConversationListNeverShown = inState.getBoolean(CONVERSATION_LIST_NEVER_SHOWN_KEY);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(CONVERSATION_LIST_TRANSACTION_KEY, mLastConversationListTransactionId);
        outState.putInt(CONVERSATION_TRANSACTION_KEY, mLastConversationTransactionId);
        outState.putBoolean(CONVERSATION_LIST_VISIBLE_KEY, mConversationListVisible);
        outState.putBoolean(CONVERSATION_LIST_NEVER_SHOWN_KEY, mConversationListNeverShown);
    }

    @Override
    public void resetActionBarIcon() {
        // Calling resetActionBarIcon should never remove the up affordance
        // even when waiting for sync (Folder list should still show with one
        // account. Currently this method is blank to avoid any changes.
    }

    /**
     * Returns true if the candidate URI is the URI for the default inbox for the given account.
     * @param candidate the URI to check
     * @param account the account whose default Inbox the candidate might be
     * @return true if the candidate is indeed the default inbox for the given account.
     */
    private static boolean isDefaultInbox(FolderUri candidate, Account account) {
        return (candidate != null && account != null) && candidate.equals(account.settings.defaultInbox);
    }

    /**
     * Returns true if the user is currently in the conversation list view, viewing the default
     * inbox.
     * @return true if user is in conversation list mode, viewing the default inbox.
     */
    private static boolean inInbox(final Account account, final ConversationListContext context) {
        // If we don't have valid state, then we are not in the inbox.
        LogUtils.i(LOG_TAG, "OPC inInbox()");//TS: jin.dong 2015-11-26 EMAIL BUGFIX-1117013 MOD
        return !(account == null || context == null || context.folder == null || account.settings == null)
                && !ConversationListContext.isSearchResult(context)
                && isDefaultInbox(context.folder.folderUri, account);
    }

    /**
     * On account change, carry out super implementation, load FolderListFragment
     * into drawer (to avoid repetitive calls to replaceFragment).
     */
    @Override
    public void changeAccount(Account account) {
        super.changeAccount(account);
        mConversationListNeverShown = true;
        closeDrawerIfOpen();
    }

    @Override
    public @LayoutRes int getContentViewResource() {
        return R.layout.one_pane_activity;
    }

    @Override
    public boolean onCreate(Bundle savedInstanceState) {
        mDrawerContainer = (DrawerLayout) mActivity.findViewById(R.id.drawer_container);
        mDrawerContainer.setDrawerTitle(Gravity.START,
                mActivity.getActivityContext().getString(R.string.drawer_title));
        final String drawerPulloutTag = mActivity.getString(R.string.drawer_pullout_tag);
        mDrawerPullout = mDrawerContainer.findViewWithTag(drawerPulloutTag);
        mDrawerPullout.setBackgroundResource(R.color.list_background_color);

        // CV is initially GONE on 1-pane (mode changes trigger visibility changes)
        mActivity.findViewById(R.id.conversation_pager).setVisibility(View.GONE);

        // The parent class sets the correct viewmode and starts the application off.
        return super.onCreate(savedInstanceState);
    }

    @Override
    protected ActionableToastBar findActionableToastBar(MailActivity activity) {
        final ActionableToastBar tb = super.findActionableToastBar(activity);

        // notify the toast bar of its sibling floating action button so it can move them together
        // as they animate
        tb.setFloatingActionButton(activity.findViewById(R.id.compose_button));
        return tb;
    }

    @Override
    protected boolean isConversationListVisible() {
        return mConversationListVisible;
    }

    @Override
    public void onViewModeChanged(int newMode) {
        super.onViewModeChanged(newMode);

        // When entering conversation list mode, hide and clean up any currently visible
        // conversation.
        if (ViewMode.isListMode(newMode)) {
            mPagerController.hide(true /* changeVisibility */);
        }
        // When we step away from the conversation mode, we don't have a current conversation
        // anymore. Let's blank it out so clients calling getCurrentConversation are not misled.
        if (!ViewMode.isConversationMode(newMode)) {
            setCurrentConversation(null);
        }
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder(super.toString());
        sb.append(" lastConvListTransId=");
        sb.append(mLastConversationListTransactionId);
        sb.append("}");
        return sb.toString();
    }

    @Override
    public void showConversationList(ConversationListContext listContext) {
        super.showConversationList(listContext);
        // TS: tao.gan 2015-09-21 EMAIL FEATURE-559893 ADD_S
        //we change from ConversationViewFramgent to ConversationListFragment, should let the toolbar show
        animateShow(null);
        // TS: tao.gan 2015-09-21 EMAIL FEATURE-559893 ADD_E
        enableCabMode();
        mConversationListVisible = true;
        if (ConversationListContext.isSearchResult(listContext)) {
            mViewMode.enterSearchResultsListMode();
        } else {
            mViewMode.enterConversationListMode();
        }
        final int transition = mConversationListNeverShown ? FragmentTransaction.TRANSIT_FRAGMENT_FADE
                : FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
        final Fragment conversationListFragment = ConversationListFragment.newInstance(listContext);

        if (!inInbox(mAccount, listContext)) {
            // Maintain fragment transaction history so we can get back to the
            // fragment used to launch this list.
            // AM: Kexue.Geng 2015-03-23 EMAIL BUGFIX_953177 MOD_S
            // mLastConversationListTransactionId = replaceFragment(conversationListFragment,
            //         transition, TAG_CONVERSATION_LIST, R.id.content_pane);
            try {
                mLastConversationListTransactionId = replaceFragment(conversationListFragment, transition,
                        TAG_CONVERSATION_LIST, R.id.content_pane);
            } catch (IllegalStateException e) {
                e.printStackTrace();
                mLastConversationListTransactionId = INVALID_ID;
            }
            // AM: Kexue.Geng 2015-03-23 EMAIL BUGFIX_953177 MOD_E
        } else {
            // If going to the inbox, clear the folder list transaction history.
            mInbox = listContext.folder;
            // TS: zhaotianyong 2015-05-21 EMAIL BUGFIX_1008675 MOD_S
            try {
                replaceFragment(conversationListFragment, transition, TAG_CONVERSATION_LIST, R.id.content_pane);
            } catch (IllegalStateException e) {
                LogUtils.e(LogUtils.TAG, e, "showConversationList error"); //TS: zheng.zou 2015-07-01 EMAIL BUGFIX-1033368 MOD
            }
            // TS: zhaotianyong 2015-05-21 EMAIL BUGFIX_1008675 MOD_E

            // If we ever to to the inbox, we want to unset the transation id for any other
            // non-inbox folder.
            mLastConversationListTransactionId = INVALID_ID;
        }
        //TS: zheng.zou 2015-07-01 EMAIL BUGFIX-1033368 MOD_S
        if (!mActivity.isDestroyed()) {
            try {
                mActivity.getFragmentManager().executePendingTransactions();
            } catch (IllegalStateException e) {
                LogUtils.e(LogUtils.TAG, e, "showConversationList error");
            }
        } else {
            LogUtils.w(LOG_TAG, "showConversationList activity is destroyed");
        }
        //TS: zheng.zou 2015-07-01 EMAIL BUGFIX-1033368 MOD_E

        onConversationVisibilityChanged(false);
        onConversationListVisibilityChanged(true);
        mConversationListNeverShown = false;
    }

    //TS: tao.gan 2015-09-12 EMAIL BUGFIX-1080620 ADD_S
    /*
     * Same logic as showConversation,but the parameter conversation is always null,
     * so we can exit the view framgent and back to the list fragment
     */
    @Override
    public void backToList(Conversation conversation) {
        super.showConversation(conversation);
        mConversationListVisible = false;
        if (conversation == null) {
            transitionBackToConversationListMode();
            return;
        }
    }
    //TS: tao.gan 2015-09-12 EMAIL BUGFIX-1080620 ADD_S

    @Override
    protected void showConversation(Conversation conversation) {
        super.showConversation(conversation);
        // TS: tao.gan 2015-09-21 EMAIL FEATURE-559893 ADD_S
        //we change from ConversationListFragment to ConversationViewFragment, should let the toolbar show
        animateShow(null);
        // TS: tao.gan 2015-09-21 EMAIL FEATURE-559893 ADD_E
        mConversationListVisible = false;
        if (conversation == null) {
            transitionBackToConversationListMode();
            return;
        }
        disableCabMode();
        if (ConversationListContext.isSearchResult(mConvListContext)) {
            mViewMode.enterSearchResultsConversationMode();
        } else {
            mViewMode.enterConversationMode();
        }
        final FragmentManager fm = mActivity.getFragmentManager();
        final FragmentTransaction ft = fm.beginTransaction();
        // Switching to conversation view is an incongruous transition:
        // we are not replacing a fragment with another fragment as
        // usual. Instead, reveal the heretofore inert conversation
        // ViewPager and just remove the previously visible fragment
        // e.g. conversation list, or possibly label list?).
        final Fragment f = fm.findFragmentById(R.id.content_pane);
        // FragmentManager#findFragmentById can return fragments that are not added to the activity.
        // We want to make sure that we don't attempt to remove fragments that are not added to the
        // activity, as when the transaction is popped off, the FragmentManager will attempt to
        // readd the same fragment twice
        if (f != null && f.isAdded()) {
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            ft.remove(f);
            ft.commitAllowingStateLoss();
            //TS: jin.dong 2015-8-28 EMAIL BUGFIX-1075110 MOD_S
            try {
                fm.executePendingTransactions();
            } catch (IllegalArgumentException e) {
                LogUtils.d(LOG_TAG, e, "IllegalArgumentException occurred");
            }
            //TS: jin.dong 2015-8-28 EMAIL BUGFIX-1075110 MOD_E
        }
        mPagerController.show(mAccount, mFolder, conversation, true /* changeVisibility */);
        onConversationVisibilityChanged(true);
        onConversationListVisibilityChanged(false);
    }

    @Override
    public void onConversationFocused(Conversation conversation) {
        // Do nothing
    }

    @Override
    public void showWaitForInitialization() {
        //TS: jian.xu 2015-10-17 EMAIL BUGFIX-708075 MOD_S
        try {
            super.showWaitForInitialization();
            replaceFragment(getWaitFragment(), FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT,
                    R.id.content_pane);
        } catch (IllegalStateException e) {
            LogUtils.e(LogUtils.TAG, e, "showWaitForInitialization error");
        }
        //TS: jian.xu 2015-10-17 EMAIL BUGFIX-708075 MOD_E
    }

    @Override
    protected void hideWaitForInitialization() {
        transitionToInbox();
        super.hideWaitForInitialization();
    }

    /**
     * Switch to the Inbox by creating a new conversation list context that loads the inbox.
     */
    private void transitionToInbox() {
        // The inbox could have changed, in which case we should load it again.
        if (mInbox == null || !isDefaultInbox(mInbox.folderUri, mAccount)) {
            loadAccountInbox();
        } else {
            onFolderChanged(mInbox, false /* force */);
        }
    }

    @Override
    public boolean doesActionChangeConversationListVisibility(final int action) {
        if (action == R.id.archive || action == R.id.remove_folder || action == R.id.delete
                || action == R.id.discard_drafts || action == R.id.discard_outbox || action == R.id.mark_important
                || action == R.id.mark_not_important || action == R.id.mute || action == R.id.report_spam
                || action == R.id.mark_not_spam || action == R.id.report_phishing || action == R.id.refresh
                || action == R.id.change_folders) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Replace the content_pane with the fragment specified here. The tag is specified so that
     * the {@link ActivityController} can look up the fragments through the
     * {@link android.app.FragmentManager}.
     * @param fragment the new fragment to put
     * @param transition the transition to show
     * @param tag a tag for the fragment manager.
     * @param anchor ID of view to replace fragment in
     * @return transaction ID returned when the transition is committed.
     */
    private int replaceFragment(Fragment fragment, int transition, String tag, int anchor) {
        final FragmentManager fm = mActivity.getFragmentManager();
        FragmentTransaction fragmentTransaction = fm.beginTransaction();
        fragmentTransaction.setTransition(transition);
        fragmentTransaction.replace(anchor, fragment, tag);
        final int id = fragmentTransaction.commitAllowingStateLoss();
        fm.executePendingTransactions();
        return id;
    }

    /**
     * Back works as follows:
     * 1) If the drawer is pulled out (Or mid-drag), close it - handled.
     * 2) If the user is in the folder list view, go back
     * to the account default inbox.
     * 3) If the user is in a conversation list
     * that is not the inbox AND:
     *  a) they got there by going through the folder
     *  list view, go back to the folder list view.
     *  b) they got there by using some other means (account dropdown), go back to the inbox.
     * 4) If the user is in a conversation, go back to the conversation list they were last in.
     * 5) If the user is in the conversation list for the default account inbox,
     * back exits the app.
     */
    @Override
    public boolean handleBackPress() {
        final int mode = mViewMode.getMode();

        /// TCT: Close Search View first if expanded
        if (mActionBarController != null && mActionBarController.isExpandedSearch()) {
            mActionBarController.collapseSearch();
        } else if (mode == ViewMode.SEARCH_RESULTS_LIST) {
            mActivity.finish();
        } else if (mViewMode.isListMode() && !inInbox(mAccount, mConvListContext)) {
            navigateUpFolderHierarchy();
        } else if (mViewMode.isConversationMode() || mViewMode.isAdMode()) {
            transitionBackToConversationListMode();
        } else {
            //reset order when quit app
            SortHelper.resetCurrentOrder(); //TS: zheng.zou 2016-1-14 EMAIL TASK_1431225 ADD
            mActivity.finish();
        }
        mToastBar.hide(false, false /* actionClicked */);
        return true;
    }

    @Override
    public void onFolderSelected(Folder folder) {
        if (mViewMode.isSearchMode()) {
            // We are in an activity on top of the main navigation activity.
            // We need to return to it with a result code that indicates it should navigate to
            // a different folder.
            final Intent intent = new Intent();
            intent.putExtra(AbstractActivityController.EXTRA_FOLDER, folder);
            mActivity.setResult(Activity.RESULT_OK, intent);
            mActivity.finish();
            return;
        }
        setHierarchyFolder(folder);
        super.onFolderSelected(folder);
    }

    /**
     * Up works as follows:
     * 1) If the user is in a conversation list that is not the default account inbox,
     * a conversation, or the folder list, up follows the rules of back.
     * 2) If the user is in search results, up exits search
     * mode and returns the user to whatever view they were in when they began search.
     * 3) If the user is in the inbox, there is no up.
     */
    @Override
    public boolean handleUpPress() {
        final int mode = mViewMode.getMode();
        if (mode == ViewMode.SEARCH_RESULTS_LIST) {
            mActivity.finish();
            // Not needed, the activity is going away anyway.
        } else if (mode == ViewMode.CONVERSATION_LIST || mode == ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION) {
            final boolean isTopLevel = Folder.isRoot(mFolder);

            if (isTopLevel) {
                // Show the drawer.
                toggleDrawerState();
            } else {
                navigateUpFolderHierarchy();
            }
        } else if (mode == ViewMode.CONVERSATION || mode == ViewMode.SEARCH_RESULTS_CONVERSATION
                || mode == ViewMode.AD) {
            // Same as go back.
            handleBackPress();
        }
        return true;
    }

    private void transitionBackToConversationListMode() {
        final int mode = mViewMode.getMode();
        enableCabMode();
        mConversationListVisible = true;
        if (mode == ViewMode.SEARCH_RESULTS_CONVERSATION) {
            mViewMode.enterSearchResultsListMode();
        } else {
            mViewMode.enterConversationListMode();
        }

        final Folder folder = mFolder != null ? mFolder : mInbox;
        onFolderChanged(folder, true /* force */);

        onConversationVisibilityChanged(false);
        onConversationListVisibilityChanged(true);
    }

    @Override
    public boolean shouldShowFirstConversation() {
        return false;
    }

    @Override
    public void onUndoAvailable(ToastBarOperation op) {
        if (op != null && mAccount.supportsCapability(UIProvider.AccountCapabilities.UNDO)) {
            final int mode = mViewMode.getMode();
            final ConversationListFragment convList = getConversationListFragment();
            switch (mode) {
            case ViewMode.SEARCH_RESULTS_CONVERSATION:
            case ViewMode.CONVERSATION:
                mToastBar.show(getUndoClickedListener(convList != null ? convList.getAnimatedAdapter() : null),
                        Utils.convertHtmlToPlainText(op.getDescription(mActivity.getActivityContext())),
                        R.string.undo, true, /* replaceVisibleToast */
                        op);
                break;
            case ViewMode.SEARCH_RESULTS_LIST:
            case ViewMode.CONVERSATION_LIST:
                if (convList != null) {
                    mToastBar.show(getUndoClickedListener(convList.getAnimatedAdapter()),
                            Utils.convertHtmlToPlainText(op.getDescription(mActivity.getActivityContext())),
                            R.string.undo, true, /* replaceVisibleToast */
                            op);
                } else {
                    mActivity.setPendingToastOperation(op);
                }
                break;
            }
        }
    }

    @Override
    public void onError(final Folder folder, boolean replaceVisibleToast) {
        final int mode = mViewMode.getMode();
        switch (mode) {
        case ViewMode.SEARCH_RESULTS_LIST:
        case ViewMode.CONVERSATION_LIST:
            showErrorToast(folder, replaceVisibleToast);
            break;
        default:
            break;
        }
    }

    @Override
    public boolean isDrawerEnabled() {
        // The drawer is enabled for one pane mode
        return true;
    }

    @Override
    public int getFolderListViewChoiceMode() {
        // By default, we do not want to allow any item to be selected in the folder list
        return ListView.CHOICE_MODE_NONE;
    }

    @Override
    public void launchFragment(final Fragment fragment, final int selectPosition) {
        //TS: jian.xu 2015-10-17 EMAIL BUGFIX-708075 MOD_S
        try {
            replaceFragment(fragment, FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_CUSTOM_FRAGMENT,
                    R.id.content_pane);
        } catch (IllegalStateException e) {
            LogUtils.e(LogUtils.TAG, e, "launchFragment error");
        }
        //TS: jian.xu 2015-10-17 EMAIL BUGFIX-708075 MOD_E
    }

    @Override
    public boolean onInterceptKeyFromCV(int keyCode, KeyEvent keyEvent, boolean navigateAway) {
        // Not applicable
        return false;
    }

    @Override
    public boolean isTwoPaneLandscape() {
        return false;
    }

    @Override
    protected void initializeActionBar() {
        final ActionBar actionBar = mActivity.getSupportActionBar();
        if (actionBar == null) {
            return;
        }

        Intent intent = mActivity.getIntent();
        /// TCT: Action is search but no extra account means a global search
        final boolean isSearch = intent != null && Intent.ACTION_SEARCH.equals(intent.getAction())
                && intent.hasExtra(Utils.EXTRA_ACCOUNT);
        mActionBarController = isSearch ? new SearchActionBarController(mContext)
                : new EmailActionBarController(mContext);
        mActionBarController.initialize(mActivity, this, actionBar);

        // init the action bar to allow the 'up' affordance.
        // any configurations that disallow 'up' should do that later.
        mActionBarController.setBackButton();
    }

    //AM: peng-zhang 2015-04-21 EMAIL BUGFIX_961694 MOD_S
    @Override
    public void onSetChanged(ConversationSelectionSet set) {
        // TODO Auto-generated method stub
        if (mActionBarController != null && mActionBarController instanceof EmailActionBarController) {
            ((Callback) mActionBarController).change(set.size());
        }
        super.onSetChanged(set);
    }

    public interface Callback {
        void change(int count);
    }
    //AM: peng-zhang 2015-04-21 EMAIL BUGFIX_961694 MOD_E

}