org.tlhInganHol.android.klingonassistant.BaseActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.tlhInganHol.android.klingonassistant.BaseActivity.java

Source

/*
 * Copyright (C) 2014 De'vID jonpIn (David Yonge-Mallo)
 *
 * 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 org.tlhInganHol.android.klingonassistant;

import java.util.ArrayList;
import java.util.List;

import android.app.SearchManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Factory;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import net.simonvt.menudrawer.MenuDrawer;
import net.simonvt.menudrawer.Position;
import wei.mark.standout.StandOutWindow;

import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;

// TUTORIAL:
// import com.google.android.gms.plus.PlusShare;
// import com.google.android.gms.plus.PlusShare.Builder;
// import com.google.android.gms.plus.model.people.Person;
// import java.util.Arrays;

public class BaseActivity extends ActionBarActivity implements SlideMenuAdapter.MenuListener {
    private static final String TAG = "BaseActivity";

    // This must uniquely identify the {boQwI'} entry.
    protected static final String QUERY_FOR_ABOUT = "boQwI':n";

    private static final String STATE_ACTIVE_POSITION = "org.tlhInganHol.android.klingonassistant.activePosition";

    // Help pages.
    private static final String QUERY_FOR_PRONUNCIATION = "QIch:n";
    private static final String QUERY_FOR_PREFIXES = "moHaq:n";
    private static final String QUERY_FOR_NOUN_SUFFIXES = "DIp:n";
    private static final String QUERY_FOR_VERB_SUFFIXES = "wot:n";

    // Classes of phrases.
    private static final String QUERY_FOR_EMPIRE_UNION_DAY = "*:sen:eu";
    // private static final String QUERY_FOR_IDIOMS                 = "*:sen:idiom";
    private static final String QUERY_FOR_CURSE_WARFARE = "*:sen:mv";
    private static final String QUERY_FOR_NENTAY = "*:sen:nt";
    // private static final String QUERY_FOR_PROVERBS               = "*:sen:prov";
    private static final String QUERY_FOR_QI_LOP = "*:sen:Ql";
    private static final String QUERY_FOR_REJECTION = "*:sen:rej";
    private static final String QUERY_FOR_REPLACEMENT_PROVERBS = "*:sen:rp";
    private static final String QUERY_FOR_SECRECY_PROVERBS = "*:sen:sp";
    private static final String QUERY_FOR_TOASTS = "*:sen:toast";
    private static final String QUERY_FOR_LYRICS = "*:sen:lyr";
    private static final String QUERY_FOR_BEGINNERS_CONVERSATION = "*:sen:bc";
    private static final String QUERY_FOR_JOKES = "*:sen:joke";

    private KillReceiver mKillReceiver;
    protected static final String ACTION_KILL = "org.tlhInganHol.android.klingonassistant.intent.action.KILL";
    protected static final String KILL_TYPE = "org.tlhInganHol.android.klingonassistant.intent.action/kill";

    // Request code to change FloatingWindow's data.
    public static final int DATA_CHANGED_QUERY = 0;

    private MenuDrawer mDrawer;

    protected SlideMenuAdapter mAdapter;
    protected ListView mList;

    private int mActivePosition = 0;

    // Helper method to determine whether the device is (likely) a tablet in horizontal orientation.
    public boolean isHorizontalTablet() {
        Configuration config = getResources().getConfiguration();
        if (config.orientation == Configuration.ORIENTATION_LANDSCAPE && (config.screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE) {
            return true;
        }
        return false;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState != null) {
            mActivePosition = savedInstanceState.getInt(STATE_ACTIVE_POSITION);
        }

        // Close the floating window, if there is one. Work around a race condition.
        Log.d(TAG, "Starting activity with non-floating window. Close floating window.");
        Handler killFloatingWindowHandler = new Handler();
        Runnable killFloatingWindowRunnable = new Runnable() {
            public void run() {
                StandOutWindow.closeAll(BaseActivity.this, FloatingWindow.class);
            }
        };
        killFloatingWindowHandler.postDelayed(killFloatingWindowRunnable, 100); // 100 ms

        // Get the action bar.
        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);

        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
        if (sharedPrefs.getBoolean(Preferences.KEY_KLINGON_FONT_CHECKBOX_PREFERENCE, /* default */false)) {
            // Display the action bar title in Klingon font.
            SpannableString title = new SpannableString("?");
            Typeface klingonTypeface = KlingonAssistant.getKlingonFontTypeface(getBaseContext());
            title.setSpan(new KlingonTypefaceSpan("", klingonTypeface), 0, title.length(),
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            actionBar.setTitle(title);
        }

        // If the device is in landscape orientation and the screen size is large (or bigger), then
        // make the slide-out menu static. Otherwise, hide it by default.
        MenuDrawer.Type drawerType = MenuDrawer.Type.BEHIND;
        if (isHorizontalTablet()) {
            drawerType = MenuDrawer.Type.STATIC;
        }
        mDrawer = MenuDrawer.attach(this, drawerType, Position.LEFT, MenuDrawer.MENU_DRAG_CONTENT);

        List<Object> items = new ArrayList<Object>();
        if (sharedPrefs.getBoolean(Preferences.KEY_KLINGON_UI_CHECKBOX_PREFERENCE, /* default */false)) {
            items.add(new SlideMenuCategory(R.string.menu_reference_tlh));
            items.add(new SlideMenuItem(R.string.menu_pronunciation_tlh, R.id.pronunciation, 0));
            items.add(new SlideMenuItem(R.string.menu_prefixes_tlh, R.id.prefixes, 0));
            items.add(new SlideMenuItem(R.string.menu_prefix_charts_tlh, R.id.prefix_charts, 0));
            items.add(new SlideMenuItem(R.string.menu_noun_suffixes_tlh, R.id.noun_suffixes, 0));
            items.add(new SlideMenuItem(R.string.menu_verb_suffixes_tlh, R.id.verb_suffixes, 0));
            items.add(new SlideMenuCategory(R.string.menu_phrases_tlh));
            items.add(new SlideMenuItem(R.string.beginners_conversation_tlh, R.id.beginners_conversation, 0));
            items.add(new SlideMenuItem(R.string.jokes_tlh, R.id.jokes, 0));
            items.add(new SlideMenuItem(R.string.nentay_tlh, R.id.nentay, 0));
            items.add(new SlideMenuItem(R.string.military_celebration_tlh, R.id.military_celebration, 0));
            items.add(new SlideMenuItem(R.string.toasts_tlh, R.id.toasts, 0));
            items.add(new SlideMenuItem(R.string.lyrics_tlh, R.id.lyrics, 0));
            items.add(new SlideMenuItem(R.string.curse_warfare_tlh, R.id.curse_warfare, 0));
            items.add(new SlideMenuItem(R.string.replacement_proverbs_tlh, R.id.replacement_proverbs, 0));
            items.add(new SlideMenuItem(R.string.secrecy_proverbs_tlh, R.id.secrecy_proverbs, 0));
            items.add(new SlideMenuItem(R.string.empire_union_day_tlh, R.id.empire_union_day, 0));
            items.add(new SlideMenuItem(R.string.rejection_tlh, R.id.rejection, 0));
            items.add(new SlideMenuCategory(R.string.menu_media_tlh));
            items.add(new SlideMenuItem(R.string.media_1_title_tlh, R.id.media_1, 0));
            items.add(new SlideMenuItem(R.string.media_2_title_tlh, R.id.media_2, 0));
            items.add(new SlideMenuItem(R.string.media_3_title_tlh, R.id.media_3, 0));
            items.add(new SlideMenuItem(R.string.media_4_title_tlh, R.id.media_4, 0));
            items.add(new SlideMenuItem(R.string.media_5_title_tlh, R.id.media_5, 0));
            items.add(new SlideMenuItem(R.string.media_6_title_tlh, R.id.media_6, 0));
            /*
            items.add(new SlideMenuCategory(R.string.menu_social_tlh));
            items.add(new SlideMenuItem(R.string.menu_gplus_tlh, R.id.gplus, 0));
            items.add(new SlideMenuItem(R.string.menu_facebook_tlh, R.id.facebook, 0));
            items.add(new SlideMenuItem(R.string.menu_kag_tlh, R.id.kag, 0));
            items.add(new SlideMenuItem(R.string.menu_kidc_tlh, R.id.kidc, 0));
            */
        } else {
            items.add(new SlideMenuCategory(R.string.menu_reference));
            items.add(new SlideMenuItem(R.string.menu_pronunciation, R.id.pronunciation, 0));
            items.add(new SlideMenuItem(R.string.menu_prefixes, R.id.prefixes, 0));
            items.add(new SlideMenuItem(R.string.menu_prefix_charts, R.id.prefix_charts, 0));
            items.add(new SlideMenuItem(R.string.menu_noun_suffixes, R.id.noun_suffixes, 0));
            items.add(new SlideMenuItem(R.string.menu_verb_suffixes, R.id.verb_suffixes, 0));
            items.add(new SlideMenuCategory(R.string.menu_phrases));
            items.add(new SlideMenuItem(R.string.beginners_conversation, R.id.beginners_conversation, 0));
            items.add(new SlideMenuItem(R.string.jokes, R.id.jokes, 0));
            items.add(new SlideMenuItem(R.string.nentay, R.id.nentay, 0));
            items.add(new SlideMenuItem(R.string.military_celebration, R.id.military_celebration, 0));
            items.add(new SlideMenuItem(R.string.toasts, R.id.toasts, 0));
            items.add(new SlideMenuItem(R.string.lyrics, R.id.lyrics, 0));
            items.add(new SlideMenuItem(R.string.curse_warfare, R.id.curse_warfare, 0));
            items.add(new SlideMenuItem(R.string.replacement_proverbs, R.id.replacement_proverbs, 0));
            items.add(new SlideMenuItem(R.string.secrecy_proverbs, R.id.secrecy_proverbs, 0));
            items.add(new SlideMenuItem(R.string.empire_union_day, R.id.empire_union_day, 0));
            items.add(new SlideMenuItem(R.string.rejection, R.id.rejection, 0));
            // Not all general proverbs are properly tagged yet.
            // Too many idioms; also no known Klingon term for "idiom".
            items.add(new SlideMenuCategory(R.string.menu_media));
            items.add(new SlideMenuItem(R.string.media_1_title, R.id.media_1, 0));
            items.add(new SlideMenuItem(R.string.media_2_title, R.id.media_2, 0));
            items.add(new SlideMenuItem(R.string.media_3_title, R.id.media_3, 0));
            items.add(new SlideMenuItem(R.string.media_4_title, R.id.media_4, 0));
            items.add(new SlideMenuItem(R.string.media_5_title, R.id.media_5, 0));
            items.add(new SlideMenuItem(R.string.media_6_title, R.id.media_6, 0));
            /*
            items.add(new SlideMenuCategory(R.string.menu_social));
            items.add(new SlideMenuItem(R.string.menu_gplus, R.id.gplus, 0));
            items.add(new SlideMenuItem(R.string.menu_facebook, R.id.facebook, 0));
            items.add(new SlideMenuItem(R.string.menu_kag, R.id.kag, 0));
            items.add(new SlideMenuItem(R.string.menu_kidc, R.id.kidc, 0));
            */
        }
        mList = new ListView(this);

        mAdapter = new SlideMenuAdapter(this, items);
        mAdapter.setListener(this);
        mAdapter.setActivePosition(mActivePosition);

        mList.setAdapter(mAdapter);
        mList.setOnItemClickListener(mItemClickListener);

        mDrawer.setMenuView(mList);

        // Allow the menu to slide out when any part of the screen is dragged.
        mDrawer.setTouchMode(MenuDrawer.TOUCH_MODE_FULLSCREEN);

        // The drawable that replaces the up indicator in the action bar.
        mDrawer.setSlideDrawable(R.drawable.ic_drawer);
        // Whether the previous drawable should be shown.
        mDrawer.setDrawerIndicatorEnabled(true);

        // Activate type-to-search for local search. Typing will automatically
        // start a search of the database.
        setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);

        // Register a receiver for the kill order.
        mKillReceiver = new KillReceiver();
        registerReceiver(mKillReceiver, IntentFilter.create(ACTION_KILL, KILL_TYPE));
    }

    @Override
    protected void onDestroy() {
        unregisterReceiver(mKillReceiver);
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // TODO: Use Klingon typeface in options menu too.
        MenuInflater inflater = getMenuInflater();
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
        if (sharedPrefs.getBoolean(Preferences.KEY_KLINGON_UI_CHECKBOX_PREFERENCE, /* default */false)) {
            inflater.inflate(R.menu.options_menu_tlh, menu);
        } else {
            inflater.inflate(R.menu.options_menu, menu);
        }

        /* TUTORIAL
        // if (isHoneycombOrAbove()) {
        //   // Note: Request translation has been removed from the menu. It is now accessed indirectly through the G+ button.
        //   if (KlingonAssistant.INCLUDE_TUTORIAL) {
        //     if (isJellyBeanOrAbove()) {
        //       // The Google Play Services version we are using does not work in Froyo and below.
        //       // Furthermore, the TTS services we use require Jelly Bean.
        //       MenuItem requestTranslationItem = (MenuItem) menu.findItem(R.id.request_translation);
        //       MenuItemCompat.setVisible(requestTranslationItem, true);
        //     }
        //   }
            
        //   // Note: This is commented out because the way that we are implementing the search button
        //   // is incompatible with the appcompat search view.
        //   // SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        //   // SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.search));
        //   // searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        //   // searchView.setIconifiedByDefault(false);
        // }
        */

        return true;
    }

    public static boolean isHoneycombOrAbove() {
        // API 11+.
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    }

    public static boolean isJellyBeanOrAbove() {
        // API 16+.
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
    }

    // Set the content view for the menu drawer.
    protected void setDrawerContentView(int layoutResId) {
        mDrawer.setContentView(layoutResId);
    }

    protected void onSlideMenuItemClicked(int position, SlideMenuItem item) {
        mDrawer.closeMenu();

        switch (item.getItemId()) {
        case R.id.pronunciation:
            // Show "Pronunciation" screen.
            displayHelp(QUERY_FOR_PRONUNCIATION);
            break;
        case R.id.prefixes:
            // Show "Prefixes" screen.
            displayHelp(QUERY_FOR_PREFIXES);
            break;
        case R.id.prefix_charts:
            // Show "Prefix chart" screen.
            displayPrefixChart();
            break;
        case R.id.noun_suffixes:
            // Show "Noun Suffixes" screen.
            displayHelp(QUERY_FOR_NOUN_SUFFIXES);
            break;
        case R.id.verb_suffixes:
            // Show "Verb Suffixes" screen.
            displayHelp(QUERY_FOR_VERB_SUFFIXES);
            break;

        // Handle media.
        case R.id.media_1:
            launchYouTubePlaylist(getBaseContext().getResources().getString(R.string.media_1_list_id));
            break;
        case R.id.media_2:
            launchYouTubePlaylist(getBaseContext().getResources().getString(R.string.media_2_list_id));
            break;
        case R.id.media_3:
            launchYouTubePlaylist(getBaseContext().getResources().getString(R.string.media_3_list_id));
            break;
        case R.id.media_4:
            launchYouTubePlaylist(getBaseContext().getResources().getString(R.string.media_4_list_id));
            break;
        case R.id.media_5:
            launchYouTubePlaylist(getBaseContext().getResources().getString(R.string.media_5_list_id));
            break;
        case R.id.media_6:
            launchYouTubePlaylist(getBaseContext().getResources().getString(R.string.media_6_list_id));
            break;

        // Handle social networks.
        /*
        case R.id.gplus:
          // Launch Google+ Klingon speakers community.
          launchExternal("https://plus.google.com/communities/108380135139365833546");
          break;
            
        case R.id.facebook:
          // Launch Facebook "Learn Klingon" group.
          launchFacebook("LearnKlingon");
          break;
            
        case R.id.kag:
          // Launch KAG Communications.
          launchExternal("http://www.kag.org/groups/hol-ampas/forum/");
          break;
            
        case R.id.kidc:
          // Launch KIDC's Klingon Imperial Forums.
          launchExternal("http://www.klingon.org/smboard/index.php?board=6.0");
          break;
        */

        // Handle classes of phrases.
        case R.id.empire_union_day:
            displaySearchResults(QUERY_FOR_EMPIRE_UNION_DAY);
            break;
        /*
         * case R.id.idioms: displaySearchResults(QUERY_FOR_IDIOMS); return true;
         */
        case R.id.curse_warfare:
            displaySearchResults(QUERY_FOR_CURSE_WARFARE);
            break;
        case R.id.nentay:
            displaySearchResults(QUERY_FOR_NENTAY);
            break;
        /*
         * case R.id.proverbs: displaySearchResults(QUERY_FOR_PROVERBS); return true;
         */
        case R.id.military_celebration:
            displaySearchResults(QUERY_FOR_QI_LOP);
            break;
        case R.id.rejection:
            displaySearchResults(QUERY_FOR_REJECTION);
            break;
        case R.id.replacement_proverbs:
            displaySearchResults(QUERY_FOR_REPLACEMENT_PROVERBS);
            break;
        case R.id.secrecy_proverbs:
            displaySearchResults(QUERY_FOR_SECRECY_PROVERBS);
            break;
        case R.id.toasts:
            displaySearchResults(QUERY_FOR_TOASTS);
            break;
        case R.id.lyrics:
            displaySearchResults(QUERY_FOR_LYRICS);
            break;
        case R.id.beginners_conversation:
            displaySearchResults(QUERY_FOR_BEGINNERS_CONVERSATION);
            break;
        case R.id.jokes:
            displaySearchResults(QUERY_FOR_JOKES);
            break;

        // Lists.
        // TODO: Handle lists here.

        default:
        }

    }

    // Private method to launch a YouTube playlist.
    private void launchYouTubePlaylist(String listId) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        // Set CLEAR_TOP so that hitting the "back" key comes back here.
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.setData(Uri.parse("http://www.youtube.com/playlist?list=" + listId));
        startActivity(intent);
    }

    // Private method to launch an external app or web site.
    private void launchExternal(String externalUrl) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        // Set NEW_TASK so the external app or web site is independent.
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setData(Uri.parse(externalUrl));
        startActivity(intent);
    }

    // Private method to request a translation.
    private void requestTranslation() {
        /* TUTORIAL
        //  // See: https://developers.google.com/+/mobile/android/share/prefill
        //  // TODO: Ideally, this should post to the "Klingon language" community under the "Requests for
        //  // translation" category.
        //  ArrayList<Person> recipients = new ArrayList<Person>();
        //  recipients.add(PlusShare.createPerson("+KlingonTeacher","KlingonTeacher"));
        //  recipients.add(PlusShare.createPerson("110116202842822234244","De'vID"));
            
        //  try {
        //    Intent requestTranslationIntent = new PlusShare.Builder(this)
        //                                                   .setType("text/plain")
        //                                                   .setText("I request a Klingon translation for:\n\n")
        //                                                   .setRecipients(recipients)
        //                                                   .getIntent();
        //    startActivity(requestTranslationIntent);
        //  } catch(Exception e) {
        //    // Fail gracefully if Google+ is not found.
        //    Toast.makeText(
        //            getBaseContext(),
        //            getBaseContext().getResources().getString(R.string.gplus_missing),
        //            Toast.LENGTH_LONG).show();
        //  }
        */
    }

    // Private method to launch a Facebook group.
    private void launchFacebook(String groupId) {
        Intent intent;
        try {
            // adb shell
            // am start -a android.intent.action.VIEW -d fb://group/LearnKlingon
            getBaseContext().getPackageManager().getPackageInfo("com.facebook.katana", 0);
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse("fb://group/" + groupId));
        } catch (Exception e) {
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.facebook.com/groups/" + groupId));
        }
        // Set CLEAR_TOP so that hitting the "back" key comes back here.
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
    }

    // Protected method to display the "help" entries.
    protected void displayHelp(String helpQuery) {
        // Note: managedQuery is deprecated since API 11.
        Cursor cursor = managedQuery(Uri.parse(KlingonContentProvider.CONTENT_URI + "/lookup"),
                null /* all columns */, null, new String[] { helpQuery }, null);
        // Assume cursor.getCount() == 1.
        Uri uri = Uri.parse(KlingonContentProvider.CONTENT_URI + "/get_entry_by_id/"
                + cursor.getString(KlingonContentDatabase.COLUMN_ID));

        Intent entryIntent = new Intent(this, EntryActivity.class);

        // Form the URI for the entry.
        entryIntent.setData(uri);

        startActivity(entryIntent);
        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    }

    // Protected method to display the prefix chart.
    protected void displayPrefixChart() {
        Intent prefixChartIntent = new Intent(this, PrefixChartActivity.class);
        startActivity(prefixChartIntent);
        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    }

    // Protected method to display search results.
    protected void displaySearchResults(String helpQuery) {
        Intent intent = new Intent(this, KlingonAssistant.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setAction(Intent.ACTION_SEARCH);
        intent.putExtra(SearchManager.QUERY, helpQuery);

        startActivity(intent);
        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    }

    private AdapterView.OnItemClickListener mItemClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            mActivePosition = position;
            mDrawer.setActiveView(view, position);
            mAdapter.setActivePosition(position);
            onSlideMenuItemClicked(position, (SlideMenuItem) mAdapter.getItem(position));
        }
    };

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_ACTIVE_POSITION, mActivePosition);
    }

    @Override
    public void onActiveViewChanged(View v) {
        mDrawer.setActiveView(v, mActivePosition);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.search:
            onSearchRequested();
            return true;
        case android.R.id.home:
            mDrawer.toggleMenu();
            break;
        case R.id.social_network:
            SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
            if (sharedPrefs.getString(Preferences.KEY_SOCIAL_NETWORK_LIST_PREFERENCE, /* default */"gplus")
                    .equals("gplus")) {
                // Launch Google+ Klingon speakers community.
                launchExternal("https://plus.google.com/communities/108380135139365833546");
            } else {
                // Launch Facebook "Learn Klingon" group.
                launchFacebook("LearnKlingon");
            }
            break;
        /* TUTORIAL */
        case R.id.request_translation:
            requestTranslation();
            break;
        case R.id.float_mode:
            // Minimize the app and cause it to "float".
            Log.d(TAG, "Show floating window.");
            StandOutWindow.show(this, FloatingWindow.class, StandOutWindow.DEFAULT_ID);

            // Broadcast the kill order to finish all non-floating activities.
            // Work around race condition.
            Log.d(TAG, "Broadcast kill order to non-floating window.");
            final Intent intent = new Intent(ACTION_KILL);
            intent.setType(KILL_TYPE);
            Handler killNonFloatingWindowHandler = new Handler();
            Runnable killNonFloatingWindowRunnable = new Runnable() {
                public void run() {
                    sendBroadcast(intent);
                }
            };
            killNonFloatingWindowHandler.postDelayed(killNonFloatingWindowRunnable, 100); // 100 ms

            return true;
        case R.id.about:
            // Show "About" screen.
            displayHelp(QUERY_FOR_ABOUT);
            return true;
        case R.id.preferences:
            // Show "Preferences" screen.
            startActivity(new Intent(this, Preferences.class));
            overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
            return true;
        default:
        }

        return super.onOptionsItemSelected(item);
    }

    // Collapse slide-out menu if "Back" key is pressed and it's open.
    @Override
    public void onBackPressed() {
        final int drawerState = mDrawer.getDrawerState();
        if (drawerState == MenuDrawer.STATE_OPEN || drawerState == MenuDrawer.STATE_OPENING) {
            mDrawer.closeMenu();
            return;
        }

        super.onBackPressed();
    }

    private final class KillReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "Received kill order, finishing.");
            finish();
        }
    }
}