com.granita.tasks.TaskListActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.granita.tasks.TaskListActivity.java

Source

/*
 * Copyright (C) 2013 Marten Gajda <marten@dmfs.org>
 *
 * 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.
 * 
 */

package com.granita.tasks;

import java.io.IOException;

import org.dmfs.android.retentionmagic.annotations.Retain;

import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import com.granita.installicloud;
import com.granita.provider.tasks.TaskContract.Tasks;
import com.granita.tasks.groupings.AbstractGroupingFactory;
import com.granita.tasks.groupings.ByDueDate;
import com.granita.tasks.groupings.ByList;
import com.granita.tasks.groupings.ByPriority;
import com.granita.tasks.groupings.ByProgress;
import com.granita.tasks.groupings.BySearch;
import com.granita.tasks.groupings.ByStartDate;
import com.granita.tasks.model.ContentSet;
import com.granita.tasks.notification.AlarmBroadcastReceiver;
import com.granita.tasks.util.IabHelper;
import com.granita.tasks.util.IabResult;
import com.granita.tasks.util.Inventory;
import com.granita.tasks.util.Purchase;
import com.granita.tasks.utils.ActionBarActivity;
import com.granita.tasks.utils.ExpandableGroupDescriptor;
import com.granita.tasks.utils.SearchHistoryHelper;
import org.dmfs.xmlobjects.pull.XmlObjectPullParserException;
import org.xmlpull.v1.XmlPullParserException;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.MenuItemCompat.OnActionExpandListener;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.SearchView.OnQueryTextListener;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;

import com.astuetz.PagerSlidingTabStrip;

/**
 * An activity representing a list of Tasks. This activity has different presentations for handset and tablet-size devices. On handsets, the activity presents a
 * list of items, which when touched, lead to a {@link ViewTaskActivity} representing item details. On tablets, the activity presents the list of items and item
 * details side-by-side using two vertical panes.
 * <p>
 * The activity makes heavy use of fragments. The list of items is a {@link TaskListFragment} and the item details (if present) is a {@link ViewTaskFragment}.
 * <p>
 * This activity also implements the required {@link TaskListFragment.Callbacks} interface to listen for item selections.
 * 
 * <p>
 * TODO: move the code to persist the expanded groups into a the GroupingDescriptor class
 * </p>
 * 
 * @author Tobias Reinsch <tobias@dmfs.org>
 */
public class TaskListActivity extends ActionBarActivity
        implements TaskListFragment.Callbacks, ViewTaskFragment.Callback {

    /** Tells the activity to display the details of the task with the URI from the intent data. **/
    public static final String EXTRA_DISPLAY_TASK = "com.granita.tasks.DISPLAY_TASK";

    /** Tells the activity to select the task in the list with the URI from the intent data. **/
    public static final String EXTRA_FORCE_LIST_SELECTION = "com.granita.tasks.FORCE_LIST_SELECTION";

    private static final String TAG = "TaskListActivity";

    private final static int REQUEST_CODE_NEW_TASK = 2924;

    /**
     * The time to wait for a new key before updating the search view.
     */
    private final static int SEARCH_UPDATE_DELAY = 400; // ms

    private final static String DETAIL_FRAGMENT_TAG = "taskListActivity.ViewTaskFragment";

    /**
     * Array of {@link ExpandableGroupDescriptor}s.
     */
    private AbstractGroupingFactory[] mGroupingFactories;

    /**
     * Whether or not the activity is in two-pane mode, i.e. running on a tablet device.
     */
    private boolean mTwoPane;
    private ViewTaskFragment mTaskDetailFrag;
    private ViewPager mViewPager;
    private TaskGroupPagerAdapter mPagerAdapter;

    @Retain(permanent = true)
    private int mCurrentPageId;

    /** The current pager position **/
    private int mCurrentPagePosition = 0;

    private int mPreviousPagePosition = -1;

    private String mAuthority;

    private MenuItem mSearchItem;

    private PagerSlidingTabStrip mTabs;

    private final Handler mHandler = new Handler();

    private SearchHistoryHelper mSearchHistoryHelper;

    private boolean mAutoExpandSearchView = false;

    /** Indicates that the activity switched to detail view due to rotation. **/
    @Retain
    private boolean mSwitchedToDetail = false;

    /** The Uri of the task to display/highlight in the list view. **/
    @Retain
    private Uri mSelectedTaskUri;

    /** The Uri of the task to display/highlight in the list view coming from the widget. **/
    private Uri mSelectedTaskUriOnLaunch;

    /** Indicates to display the two pane layout with details **/
    @Retain
    private boolean mShouldShowDetails = false;

    /** Indicates to show ViewTaskActivity when rotating to single pane. **/
    @Retain
    private boolean mShouldSwitchToDetail = false;

    /** Indicates the TaskListFragments to select/highlight the mSelectedTaskUri item **/
    private boolean mShouldSelectTaskListItem = false;

    /** Indicates a transient state after rotation to redirect to the TaskViewActivtiy **/
    private boolean mTransientState = false;

    //Custom start
    IabHelper mHelper;
    static final String ITEM_SKU = "com.granita.tasks";
    //static final String ITEM_SKU = "android.test.purchased";
    boolean mIsPremium = false;

    IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
        public void onQueryInventoryFinished(IabResult result, Inventory inventory) {

            if (result.isFailure()) {
                // handle error here
            } else {
                // does the user have the premium upgrade?
                mIsPremium = inventory.hasPurchase(ITEM_SKU);
                if (mIsPremium) {
                    SharedPreferences sharedPref = getSharedPreferences("localPreferences", Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = sharedPref.edit();
                    editor.putBoolean(getString(R.string.HIDE_AD_BOOLEAN), true);
                    editor.commit();
                    View adFragmentView = findViewById(R.id.fragment2);
                    adFragmentView.setVisibility(View.GONE);
                } else {
                    SharedPreferences sharedPref = getSharedPreferences("localPreferences", Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = sharedPref.edit();
                    editor.putBoolean(getString(R.string.HIDE_AD_BOOLEAN), false);
                    editor.commit();
                }

            }
        }
    };

    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            if (result.isFailure()) {
                // Handle error
                return;
            } else if (purchase.getSku().equals(ITEM_SKU)) {
                SharedPreferences sharedPref = getSharedPreferences("localPreferences", Context.MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPref.edit();
                editor.putBoolean(getString(R.string.HIDE_AD_BOOLEAN), true);
                editor.commit();
                View adFragmentView = findViewById(R.id.fragment2);
                adFragmentView.setVisibility(View.GONE);
            }

        }
    };

    @Override
    public void onStart() {
        super.onStart();
        final SharedPreferences settings = getSharedPreferences("localPreferences", MODE_PRIVATE);
        if (settings.getBoolean("isFirstRun", true)) {
            new AlertDialog.Builder(this).setTitle("Cookies").setMessage(
                    "We use device identifiers to personalise content and ads, to provide social media features and to analyse our traffic. We also share such identifiers and other information from your device with our social media, advertising and analytics partners.")
                    .setNeutralButton("Close message", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            settings.edit().putBoolean("isFirstRun", false).commit();
                        }
                    }).show();
        }
    }
    //Custom end

    @SuppressLint("NewApi")
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate called again");
        super.onCreate(savedInstanceState);

        // check for single pane activity change
        mTwoPane = getResources().getBoolean(R.bool.has_two_panes);

        resolveIntentAction(getIntent());
        //custom start

        Tracker t = ((com.granita.tasks.Tasks) getApplication())
                .getTracker(com.granita.tasks.Tasks.TrackerName.APP_TRACKER);
        t.setScreenName("Tasks: List activity");
        t.send(new HitBuilders.AppViewBuilder().build());

        //check if sync for icloud calendar exists
        String packageName = "com.granita.caldavsync";
        Intent intent = this.getPackageManager().getLaunchIntentForPackage(packageName);
        if (intent == null) {
            /* bring user to the market or let them choose an app? */
            intent = new Intent(this, installicloud.class);
            startActivity(intent);
        }

        try {

            //Start premium features
            String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvczcgR9FJqJ/LK94t4VcmdfVizoy66QWJRxF+o0ZZbFeMi1jQ6kBcYb1kogQnLLBeS+Q0DtRjsxap9Z6B8yP5rcZxIs51yF2LKy9+nmcLeJF0wPU2cvIjZLf7dD7Umh/LMi88VHSLExwkDMb2vBpcROz7DwjhgF5fzXHWh986ZxWmJiMiYLX1cg5toWaOPYgxcSELyCsfkvfmtX8ctJ+QcovWXevSBJsOQCLNkmKdBd5biExy96WicjVUZ/e31/CCjb8xYcMJnMaBGXEBDdtJFMFAczpdrB+Zyw6gEr1ZUH1U7trsbTrC2TvOa1MTcwXHE2JwqjU2eke4FLYIcAdbwIDAQAB";
            mHelper = new IabHelper(this, base64EncodedPublicKey);

            mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
                public void onIabSetupFinished(IabResult result) {
                    if (!result.isSuccess()) {
                        Log.d(TAG, "In-app Billing setup failed: " + result);
                    } else {
                        mHelper.queryInventoryAsync(mGotInventoryListener);
                        Log.d(TAG, "In-app Billing is set up OK");
                    }
                }
            });

            //End premium features
        } catch (Exception e) {

        }
        //custom end

        if (mSelectedTaskUri != null) {
            if (mShouldShowDetails && mShouldSwitchToDetail) {
                Intent viewTaskIntent = new Intent(Intent.ACTION_VIEW);
                viewTaskIntent.setData(mSelectedTaskUri);
                startActivity(viewTaskIntent);
                mSwitchedToDetail = true;
                mShouldSwitchToDetail = false;
                mTransientState = true;
            }
        } else {
            mShouldShowDetails = false;
        }

        setContentView(R.layout.activity_task_list);

        //custom start
        mAuthority = getString(R.string.org_dmfs_tasks_authority);
        //custom end
        mSearchHistoryHelper = new SearchHistoryHelper(this);

        if (findViewById(R.id.task_detail_container) != null) {
            // In two-pane mode, list items should be given the
            // 'activated' state when touched.

            // get list fragment
            // mTaskListFrag = (TaskListFragment) getSupportFragmentManager().findFragmentById(R.id.task_list);
            // mTaskListFrag.setListViewScrollbarPositionLeft(true);

            // mTaskListFrag.setActivateOnItemClick(true);

            /*
             * Create a detail fragment, but don't load any URL yet, we do that later when the fragment gets attached
             */
            mTaskDetailFrag = ViewTaskFragment.newInstance(mSelectedTaskUri);
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.task_detail_container, mTaskDetailFrag, DETAIL_FRAGMENT_TAG).commit();
        } else {
            FragmentManager fragmentManager = getSupportFragmentManager();
            Fragment detailFragment = fragmentManager.findFragmentByTag(DETAIL_FRAGMENT_TAG);
            if (detailFragment != null) {
                fragmentManager.beginTransaction().remove(detailFragment).commit();
            }
        }

        mGroupingFactories = new AbstractGroupingFactory[] { new ByList(mAuthority), new ByDueDate(mAuthority),
                new ByStartDate(mAuthority), new ByPriority(mAuthority), new ByProgress(mAuthority),
                new BySearch(mAuthority, mSearchHistoryHelper) };

        // set up pager adapter
        try {
            mPagerAdapter = new TaskGroupPagerAdapter(getSupportFragmentManager(), mGroupingFactories, this,
                    R.xml.listview_tabs);
        } catch (XmlPullParserException e) {
            // TODO Automatisch generierter Erfassungsblock
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Automatisch generierter Erfassungsblock
            e.printStackTrace();
        } catch (XmlObjectPullParserException e) {
            // TODO Automatisch generierter Erfassungsblock
            e.printStackTrace();
        }

        // Setup ViewPager
        mPagerAdapter.setTwoPaneLayout(mTwoPane);
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mPagerAdapter);

        int currentPageIndex = mPagerAdapter.getPagePosition(mCurrentPageId);

        if (currentPageIndex >= 0) {
            mCurrentPagePosition = currentPageIndex;
            mViewPager.setCurrentItem(currentPageIndex);
            if (VERSION.SDK_INT >= 14 && mCurrentPageId == R.id.task_group_search) {
                if (mSearchItem != null) {
                    // that's actually quite impossible to happen
                    MenuItemCompat.expandActionView(mSearchItem);
                } else {
                    mAutoExpandSearchView = true;
                }
            }
        }
        updateTitle(currentPageIndex);

        // Bind the tabs to the ViewPager
        mTabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
        mTabs.setViewPager(mViewPager);

        mTabs.setOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                mSelectedTaskUri = null;
                mCurrentPagePosition = position;

                int newPageId = mPagerAdapter.getPageId(position);

                if (newPageId == R.id.task_group_search) {
                    int oldPageId = mCurrentPageId;
                    mCurrentPageId = newPageId;

                    // store the page position we're coming from
                    mPreviousPagePosition = mPagerAdapter.getPagePosition(oldPageId);
                } else if (mCurrentPageId == R.id.task_group_search) {
                    // we've been on the search page before, so commit the search and close the search view
                    mSearchHistoryHelper.commitSearch();
                    mHandler.post(mSearchUpdater);
                    mCurrentPageId = newPageId;
                    hideSearchActionView();
                }
                mCurrentPageId = newPageId;

                updateTitle(mCurrentPageId);
            }

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {
                if (state == ViewPager.SCROLL_STATE_IDLE && mCurrentPageId == R.id.task_group_search) {
                    // the search page is selected now, expand the search view
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            MenuItemCompat.expandActionView(mSearchItem);
                        }
                    });
                }
            }
        });

        // make sure the status bar color is set properly on Android 5+ devices
        if (VERSION.SDK_INT >= 21) {
            Window window = getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDarker));
        }
    }

    @Override
    protected void onResume() {
        updateTitle(mCurrentPageId);
        super.onResume();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        resolveIntentAction(intent);
        super.onNewIntent(intent);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSearchHistoryHelper.close();

        //custom start
        if (mHelper != null)
            mHelper.dispose();
        mHelper = null;
        //custom end
    }

    /**
     * Callback method from {@link TaskListFragment.Callbacks} indicating that the item with the given ID was selected.
     */
    @Override
    public void onItemSelected(Uri uri, boolean forceReload, int pagePosition) {
        // only accept selections from the current visible task fragment or the activity itself
        if (pagePosition == -1 || pagePosition == mCurrentPagePosition) {
            if (mTwoPane) {
                mShouldShowDetails = true;
                if (forceReload) {
                    mSelectedTaskUri = null;
                    mShouldSwitchToDetail = false;
                    mTaskDetailFrag.loadUri(uri);
                } else {
                    mTaskDetailFrag.loadUri(uri);
                }
            } else if (forceReload) {
                mSelectedTaskUri = uri;

                // In single-pane mode, simply start the detail activity
                // for the selected item ID.
                Intent detailIntent = new Intent(Intent.ACTION_VIEW);
                detailIntent.setData(uri);
                startActivity(detailIntent);
                mSwitchedToDetail = true;
                mShouldSwitchToDetail = false;
            }
        }
    }

    private void updateTitle(int pageId) {
        switch (pageId) {
        case R.id.task_group_by_list:
            setTitle(R.string.task_group_title_list);
            break;
        case R.id.task_group_by_start:
            setTitle(R.string.task_group_title_start);
            break;
        case R.id.task_group_by_due:
            setTitle(R.string.task_group_title_due);
            break;
        case R.id.task_group_by_priority:
            setTitle(R.string.task_group_title_priority);
            break;
        case R.id.task_group_by_progress:
            setTitle(R.string.task_group_title_progress);
            break;

        default:
            setTitle(R.string.task_group_title_default);
            break;
        }
    }

    @Override
    public void onEditTask(Uri taskUri, ContentSet data) {
        Intent editTaskIntent = new Intent(Intent.ACTION_EDIT);
        editTaskIntent.setData(taskUri);
        if (data != null) {
            editTaskIntent.putExtra(EditTaskActivity.EXTRA_DATA_CONTENT_SET, data);
        }
        startActivity(editTaskIntent);
    }

    @Override
    public void onAddNewTask() {
        Intent editTaskIntent = new Intent(Intent.ACTION_INSERT);
        editTaskIntent.setData(Tasks.getContentUri(mAuthority));
        startActivityForResult(editTaskIntent, REQUEST_CODE_NEW_TASK);
    }

    private void resolveIntentAction(Intent intent) {
        // check which task should be selected
        if (intent.hasExtra(EXTRA_DISPLAY_TASK))

        {
            mShouldSwitchToDetail = true;
            mSelectedTaskUri = intent.getData();
        }

        if (intent != null && intent.hasExtra(EXTRA_DISPLAY_TASK)
                && intent.getBooleanExtra(EXTRA_FORCE_LIST_SELECTION, true) && mTwoPane) {
            mShouldSwitchToDetail = true;
            Uri newSelection = intent.getData();
            mSelectedTaskUriOnLaunch = newSelection;
            mShouldSelectTaskListItem = true;
            mPagerAdapter.notifyDataSetChanged();
        } else {
            mSelectedTaskUriOnLaunch = null;
            mShouldSelectTaskListItem = false;
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (resultCode == RESULT_OK) {
            if (intent != null) {
                Uri taskUri = intent.getData();
                if (taskUri != null) {
                    // select the new task
                    onItemSelected(taskUri, false, -1);
                }
            }
        }
    }

    @Override
    public void onDelete(Uri taskUri) {
        // nothing to do here, the loader will take care of reloading the list and the list view will take care of selecting the next element.

        // empty the detail fragment
        if (mTwoPane) {
            mTaskDetailFrag.loadUri(null);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.task_list_activity_menu, menu);

        // restore menu state
        MenuItem item = menu.findItem(R.id.menu_alarms);
        if (item != null) {
            item.setChecked(AlarmBroadcastReceiver.getAlarmPreference(this));
        }

        // search
        setupSearch(menu);

        //customs start
        SharedPreferences sharedPref = getSharedPreferences("localPreferences", Context.MODE_PRIVATE);
        if (sharedPref.getBoolean(getString(R.string.HIDE_AD_BOOLEAN), false)) {
            //remove all ads
            MenuItem removeAdItem = menu.findItem(R.id.removeads);
            removeAdItem.setVisible(false);
        } else {
            MenuItem removeAdItem = menu.findItem(R.id.removeads);
            removeAdItem.setVisible(true);
        }
        //custom end

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        //custom start
        if (item.getItemId() == R.id.removeads) {
            mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001, mPurchaseFinishedListener, "mypurchasetoken");
        }

        if (item.getItemId() == R.id.add_account) {
            Intent myIntent = new Intent();
            myIntent.setClassName("com.granita.icloudcalsync",
                    "com.granita.caldavsync.ui.setup.AddAccountActivity");
            startActivity(myIntent);
        }
        //custom end
        if (item.getItemId() == R.id.menu_visible_list) {
            Intent settingsIntent = new Intent(getBaseContext(), SyncSettingsActivity.class);
            startActivity(settingsIntent);
            return true;
        } else if (item.getItemId() == R.id.menu_alarms) {
            // set and save state
            boolean activatedAlarms = !item.isChecked();
            item.setChecked(activatedAlarms);
            AlarmBroadcastReceiver.setAlarmPreference(this, activatedAlarms);
            return true;
        } else {
            return super.onOptionsItemSelected(item);
        }
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void hideSearchActionView() {
        MenuItemCompat.collapseActionView(mSearchItem);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void setupSearch(Menu menu) {
        // bail early on unsupported devices
        if (Build.VERSION.SDK_INT < 11) {
            return;
        }

        mSearchItem = menu.findItem(R.id.search);
        MenuItemCompat.setOnActionExpandListener(mSearchItem, new OnActionExpandListener() {

            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                // always allow expansion of the search action view
                return mCurrentPageId == R.id.task_group_search;
            }

            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                // return to previous view
                if (mPreviousPagePosition >= 0 && mCurrentPageId == R.id.task_group_search) {
                    mViewPager.setCurrentItem(mPreviousPagePosition);
                    mCurrentPageId = mPagerAdapter.getPageId(mPreviousPagePosition);
                }
                return mPreviousPagePosition >= 0 || mCurrentPageId != R.id.task_group_search;
            }
        });
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(mSearchItem);

        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        if (null != searchManager) {
            searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        }

        searchView.setQueryHint(getString(R.string.menu_search_hint));
        searchView.setIconified(true);
        searchView.setOnQueryTextListener(new OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(String query) {
                // persist current search
                mSearchHistoryHelper.commitSearch();
                mHandler.post(mSearchUpdater);
                return true;
            }

            @Override
            public boolean onQueryTextChange(String query) {
                if (mCurrentPageId != R.id.task_group_search) {
                    return true;
                }

                mHandler.removeCallbacks(mSearchUpdater);
                if (query.length() > 0) {
                    mSearchHistoryHelper.updateSearch(query);
                    mHandler.postDelayed(mSearchUpdater, SEARCH_UPDATE_DELAY);
                } else {
                    mSearchHistoryHelper.removeCurrentSearch();
                    mHandler.post(mSearchUpdater);
                }
                return true;
            }
        });

        if (mAutoExpandSearchView) {
            mSearchItem.expandActionView();
        }

    }

    @Override
    public ExpandableGroupDescriptor getGroupDescriptor(int pageId) {
        for (AbstractGroupingFactory factory : mGroupingFactories) {
            if (factory.getId() == pageId) {
                return factory.getExpandableGroupDescriptor();
            }
        }
        return null;
    }

    /**
     * Notifies the search fragment of an update.
     */
    private final Runnable mSearchUpdater = new Runnable() {

        @Override
        public void run() {
            TaskListFragment fragment = (TaskListFragment) mPagerAdapter.instantiateItem(mViewPager,
                    mViewPager.getCurrentItem());
            fragment.notifyDataSetChanged(true);
            fragment.expandCurrentSearchGroup();
        }
    };

    @SuppressLint("NewApi")
    @Override
    public void updateColor(int color) {
        getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color));
        mTabs.setBackgroundColor(color);

        if (VERSION.SDK_INT >= 21) {
            Window window = getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(color);
        }
    }

    public Uri getSelectedTaskUri() {
        if (mShouldSelectTaskListItem) {
            return mSelectedTaskUriOnLaunch;
        }
        return null;
    }

    public boolean isInTransientState() {
        return mTransientState;
    }
}