free.yhc.feeder.ChannelListActivity.java Source code

Java tutorial

Introduction

Here is the source code for free.yhc.feeder.ChannelListActivity.java

Source

/******************************************************************************
 * Copyright (C) 2012, 2013, 2014
 * Younghyung Cho. <yhcting77@gmail.com>
 * All rights reserved.
 *
 * This file is part of FeedHive
 *
 * This program is licensed under the FreeBSD license
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation
 * are those of the authors and should not be interpreted as representing
 * official policies, either expressed or implied, of the FreeBSD Project.
 *****************************************************************************/

package free.yhc.feeder;

import static free.yhc.feeder.model.Utils.eAssert;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.PopupMenu;
import android.widget.TextView;
import free.yhc.feeder.UiHelper.EditTextDialogAction;
import free.yhc.feeder.UiHelper.OnConfirmDialogAction;
import free.yhc.feeder.db.DB;
import free.yhc.feeder.db.DBPolicy;
import free.yhc.feeder.model.Environ;
import free.yhc.feeder.model.Feed;
import free.yhc.feeder.model.ListenerManager;
import free.yhc.feeder.model.RTTask;
import free.yhc.feeder.model.UnexpectedExceptionHandler;
import free.yhc.feeder.model.UsageReport;
import free.yhc.feeder.model.Utils;

public class ChannelListActivity extends FragmentActivity
        implements ActionBar.TabListener, UnexpectedExceptionHandler.TrackedModule {
    private static final boolean DBG = false;
    private static final Utils.Logger P = new Utils.Logger(ChannelListActivity.class);

    // Request codes.
    private static final int REQC_PICK_PREDEFINED_CHANNEL = 1;

    private final DBPolicy mDbp = DBPolicy.get();
    private final RTTask mRtt = RTTask.get();
    private final UsageReport mUr = UsageReport.get();

    private ActionBar mAb = null;
    private ViewPager mPager = null;
    private ChannelListFragment mContextMenuOwner = null;
    private DBWatcher mDbWatcher = null;

    private final ViewPager.OnPageChangeListener mPCListener = new OnPageViewChange();

    private static class TabTag {
        long categoryid;
    }

    private static class DBWatcher implements ListenerManager.Listener {
        // NOTE
        // Comparing with "ChannelListFragment" and "ItemListActivity", initial value of
        //   _mCategoryTableUpdated is 'false'
        // Why?
        // Please see 'onResume'.
        // Even if this looses consistency... but... fair enough.
        private boolean _mCategoryTableUpdated = false;

        void register() {
            DBPolicy.get().registerUpdatedListener(this, DB.UpdateType.CATEGORY_TABLE.flag());
        }

        void unregister() {
            DBPolicy.get().unregisterUpdatedListener(this);
        }

        void reset() {
            _mCategoryTableUpdated = false;
        }

        boolean isCategoryTableUpdated() {
            return _mCategoryTableUpdated;
        }

        @Override
        public void onNotify(Object user, ListenerManager.Type type, Object arg0, Object arg1) {
            if (DB.UpdateType.CATEGORY_TABLE == type)
                _mCategoryTableUpdated = true;
            else
                eAssert(false);
        }
    }

    private class OnPageViewChange implements ViewPager.OnPageChangeListener {
        @Override
        public void onPageSelected(int arg0) {
            if (DBG)
                P.v("arg : " + arg0);
            mAb.setSelectedNavigationItem(arg0);
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
            if (DBG)
                P.v("arg : " + arg0);
            // Fragment is changed. So, context menu is no more valid.
            ChannelListActivity.this.closeContextMenu();
        }
    }

    private void restartThisActivity() {
        // category table is changed outside of this activity.
        // restarting is required!!
        Intent intent = new Intent(this, ChannelListActivity.class);
        startActivity(intent);
        finish();
    }

    private TabTag getTag(Tab tab) {
        return (TabTag) tab.getTag();
    }

    private ChannelListPagerAdapter getPagerAdapter() {
        if (null != mPager)
            return (ChannelListPagerAdapter) mPager.getAdapter();
        return null;
    }

    private void selectDefaultAsSelected() {
        // 0 is index of default tab
        mAb.setSelectedNavigationItem(0);
    }

    private long getCategoryId(Tab tab) {
        return getTag(tab).categoryid;
    }

    private long getCurrentCategoryId() {
        return getPagerAdapter().getPrimaryFragment().getCategoryId();
    }

    private Tab addCategory(Feed.Category cat) {
        String text;
        if (mDbp.isDefaultCategoryId(cat.id) && !Utils.isValidValue(mDbp.getCategoryName(cat.id)))
            text = getResources().getText(R.string.default_category_name).toString();
        else
            text = cat.name;

        // Add new tab to action bar
        Tab tab = mAb.newTab().setText(text).setTag(cat.id).setTabListener(this);

        TabTag tag = new TabTag();
        tag.categoryid = cat.id;

        tab.setTag(tag);
        mAb.addTab(tab, false);
        if (null != getPagerAdapter())
            getPagerAdapter().newCategoryAdded(cat.id);
        return tab;
    }

    /**
     * All channels belonging to this category will be moved to default category.
     * @param categoryid
     */
    private void deleteCategory(long categoryid) {
        mDbp.deleteCategory(categoryid);

        // NOTE
        // PagerView's working mechanism is NOT GOOD for deleting item in the middle.
        // So, just restart channel list activity...to reload all!!!
        restartThisActivity();
        return;
        /*
        Tab curTab = mAb.getSelectedTab();
        mAb.removeTab(curTab);
        if (null != getPagerAdapter())
        getPagerAdapter().categoryDeleted(categoryid);
        selectDefaultAsSelected();
        */
    }

    private String getTabText(Tab tab) {
        return tab.getText().toString();
    }

    /**
     * Add channel to current selected category.
     * List will be scrolled to newly added channel.
     * @param url
     */
    private void addChannel(String url, String iconurl) {
        getPagerAdapter().getPrimaryFragment().addChannel(url, iconurl);
    }

    private void onOpt_addChannel_youtubeEditDiag(final MenuItem item) {
        // Set action for dialog.
        final EditTextDialogAction action = new EditTextDialogAction() {
            @Override
            public void prepare(Dialog dialog, EditText edit) {
            }

            @Override
            public void onOk(Dialog dialog, EditText edit) {
                switch (item.getItemId()) {
                case R.id.uploader:
                    addChannel(Utils.buildYoutubeFeedUrl_uploader(edit.getText().toString()), null);
                    break;
                case R.id.search:
                    addChannel(Utils.buildYoutubeFeedUrl_search(edit.getText().toString()), null);
                    break;
                default:
                    eAssert(false);
                }
            }
        };
        UiHelper.buildOneLineEditTextDialog(this, item.getTitle(), action).show();
    }

    private void onOpt_addChannel_youtube(final View anchor) {
        PopupMenu popup = new PopupMenu(this, anchor);
        popup.getMenuInflater().inflate(R.menu.popup_addchannel_youtube, popup.getMenu());
        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                onOpt_addChannel_youtubeEditDiag(item);
                return true;
            }
        });
        popup.show();
    }

    private void onOpt_addChannel_url(final View anchor) {
        // Set action for dialog.
        final EditTextDialogAction action = new EditTextDialogAction() {
            @Override
            public void prepare(Dialog dialog, EditText edit) {
                // start edit box with 'http://'
                final String prefix = "http://";
                edit.setText(prefix);
                edit.setSelection(prefix.length());
            }

            @Override
            public void onOk(Dialog dialog, EditText edit) {
                String url = edit.getText().toString();
                if (!url.matches("http\\:\\/\\/\\s*")) {
                    addChannel(url, null);
                    mUr.storeUsageReport("URL : " + url + "\n");
                }
            }
        };
        UiHelper.buildOneLineEditTextDialog(this, R.string.channel_url, action).show();
    }

    private void onOpt_addChannel_predefined(View anchor) {
        Intent intent = new Intent(this, PredefinedChannelActivity.class);
        startActivityForResult(intent, REQC_PICK_PREDEFINED_CHANNEL);
    }

    private void onOpt_addChannel(final View anchor) {
        if (0 == mAb.getNavigationItemCount()) {
            eAssert(false);
            return;
        }

        if (0 > mAb.getSelectedNavigationIndex()) {
            UiHelper.showTextToast(ChannelListActivity.this, R.string.warn_select_category_to_add);
            return;
        }

        if (!Utils.isNetworkAvailable()) {
            // TODO Handling error
            UiHelper.showTextToast(ChannelListActivity.this, R.string.warn_network_unavailable);
            return;
        }

        PopupMenu popup = new PopupMenu(this, anchor);
        popup.getMenuInflater().inflate(R.menu.popup_addchannel, popup.getMenu());
        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                case R.id.predefined:
                    onOpt_addChannel_predefined(anchor);
                    break;
                case R.id.url:
                    onOpt_addChannel_url(anchor);
                    break;
                case R.id.youtube:
                    onOpt_addChannel_youtube(anchor);
                    break;
                default:
                    eAssert(false);
                }
                return true;
            }
        });
        popup.show();
    }

    private void onOpt_category_add(final View anchor) {
        // Set action for dialog.
        final EditTextDialogAction action = new EditTextDialogAction() {
            @Override
            public void prepare(Dialog dialog, EditText edit) {
                edit.setHint(R.string.enter_name);
            }

            @Override
            public void onOk(Dialog dialog, EditText edit) {
                String name = edit.getText().toString();
                if (mDbp.isDuplicatedCategoryName(name)) {
                    UiHelper.showTextToast(ChannelListActivity.this, R.string.warn_duplicated_category);
                } else {
                    Feed.Category cat = new Feed.Category(name);
                    if (0 > mDbp.insertCategory(cat))
                        UiHelper.showTextToast(ChannelListActivity.this, R.string.warn_add_category);
                    else {
                        eAssert(cat.id >= 0);
                        addCategory(cat);
                    }
                }
            }
        };
        UiHelper.buildOneLineEditTextDialog(this, R.string.add_category, action).show();
    }

    private void onOpt_category_rename(final View anchor) {
        // Set action for dialog.
        final EditTextDialogAction action = new EditTextDialogAction() {
            @Override
            public void prepare(Dialog dialog, EditText edit) {
                edit.setHint(R.string.enter_name);
            }

            @Override
            public void onOk(Dialog dialog, EditText edit) {
                String name = edit.getText().toString();
                if (mDbp.isDuplicatedCategoryName(name)) {
                    UiHelper.showTextToast(ChannelListActivity.this, R.string.warn_duplicated_category);
                } else {
                    mAb.getSelectedTab().setText(name);
                    mDbp.updateCategory(getCurrentCategoryId(), name);
                }
            }
        };
        UiHelper.buildOneLineEditTextDialog(this, R.string.rename_category, action).show();
    }

    private void onOpt_category_delete(final View anchor) {
        final long categoryid = getCurrentCategoryId();
        if (DBG)
            P.v("category(" + categoryid + ")");
        if (mDbp.isDefaultCategoryId(categoryid)) {
            UiHelper.showTextToast(this, R.string.warn_delete_default_category);
            return;
        }

        // 0 should be default category index!
        eAssert(mAb.getSelectedNavigationIndex() > 0);

        OnConfirmDialogAction action = new OnConfirmDialogAction() {
            @Override
            public void onOk(Dialog dialog) {
                deleteCategory(categoryid);
            }

            @Override
            public void onCancel(Dialog dialog) {
            }
        };
        UiHelper.buildConfirmDialog(this, R.string.delete_category, R.string.delete_category_msg, action).show();
    }

    private void onOpt_category(final View anchor) {
        PopupMenu popup = new PopupMenu(this, anchor);
        popup.getMenuInflater().inflate(R.menu.popup_category, popup.getMenu());
        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                case R.id.add:
                    onOpt_category_add(anchor);
                    break;
                case R.id.rename:
                    onOpt_category_rename(anchor);
                    break;
                case R.id.delete:
                    onOpt_category_delete(anchor);
                    break;
                default:
                    eAssert(false);
                }
                return true;
            }
        });
        popup.show();
    }

    private void onOpt_itemsAll(final View anchor) {
        Intent intent = new Intent(ChannelListActivity.this, ItemListActivity.class);
        intent.putExtra(ItemListActivity.IKEY_MODE, ItemListActivity.MODE_ALL);
        intent.putExtra(ItemListActivity.IKEY_FILTER, ItemListActivity.FILTER_NONE);
        startActivity(intent);
    }

    private void onOpt_updateAll(final View anchor) {
        UiHelper.OnConfirmDialogAction action = new UiHelper.OnConfirmDialogAction() {
            @Override
            public void onOk(Dialog dialog) {
                ScheduledUpdateService.scheduleImmediateUpdate(mDbp.getChannelIds());
            }

            @Override
            public void onCancel(Dialog dialog) {
            }
        };

        UiHelper.buildConfirmDialog(this, R.string.update_all_channels, R.string.update_all_channels_msg, action)
                .show();
    }

    private void onOpt_itemsCategory(final View anchor) {
        Intent intent = new Intent(ChannelListActivity.this, ItemListActivity.class);
        intent.putExtra(ItemListActivity.IKEY_MODE, ItemListActivity.MODE_CATEGORY);
        intent.putExtra(ItemListActivity.IKEY_FILTER, ItemListActivity.FILTER_NONE);
        intent.putExtra("categoryid", getCurrentCategoryId());
        startActivity(intent);
    }

    private void onOpt_itemsFavorite(final View anchor) {
        Intent intent = new Intent(ChannelListActivity.this, ItemListActivity.class);
        intent.putExtra(ItemListActivity.IKEY_MODE, ItemListActivity.MODE_FAVORITE);
        intent.putExtra(ItemListActivity.IKEY_FILTER, ItemListActivity.FILTER_NONE);
        startActivity(intent);
    }

    private void onOpt_moreMenu_about(final View anchor) {
        PackageManager pm = getPackageManager();
        PackageInfo pi = null;
        try {
            pi = pm.getPackageInfo(getPackageName(), 0);
        } catch (NameNotFoundException e) {
            ;
        }

        if (null == pi)
            return; // never happen

        CharSequence title = getResources().getText(R.string.about_app);
        StringBuilder strbldr = new StringBuilder();
        strbldr.append(getResources().getText(R.string.version)).append(" : ").append(pi.versionName).append("\n")
                .append(getResources().getText(R.string.about_app_email)).append("\n")
                .append(getResources().getText(R.string.about_app_blog)).append("\n")
                .append(getResources().getText(R.string.about_app_page)).append("\n");
        AlertDialog diag = UiHelper.createAlertDialog(this, 0, title, strbldr.toString());
        diag.show();
    }

    private void onOpt_moreMenu_license(final View anchor) {
        View v = UiHelper.inflateLayout(this, R.layout.info_dialog);
        TextView tv = ((TextView) v.findViewById(R.id.text));
        tv.setTypeface(Typeface.MONOSPACE);
        tv.setText(R.string.license_desc);
        AlertDialog.Builder bldr = new AlertDialog.Builder(this);
        bldr.setView(v);
        AlertDialog aDiag = bldr.create();
        aDiag.show();
    }

    private void onOpt_moreMenu_deleteAllDnFiles(final View anchor) {
        AlertDialog diag = UiHelper.buildDeleteAllDnFilesConfirmDialog(this, null, null);
        if (null == diag)
            UiHelper.showTextToast(this, R.string.del_dnfiles_not_allowed_msg);
        diag.show();
    }

    private void onOpt_moreMenu_deleteAllUsedDnFiles(final View anchor) {
        AlertDialog diag = UiHelper.buildDeleteUsedDnFilesConfirmDialog(0, this, null, null);
        if (null == diag)
            UiHelper.showTextToast(this, R.string.del_dnfiles_not_allowed_msg);
        diag.show();
    }

    private void onOpt_moreMenu_feedbackOpinion(final View anchor) {
        if (!Utils.isNetworkAvailable()) {
            UiHelper.showTextToast(this, R.string.warn_network_unavailable);
            return;
        }

        if (!mUr.sendFeedbackReportMain(this))
            UiHelper.showTextToast(this, R.string.warn_find_email_app);
    }

    private void onOpt_moreMenu_dbManagement(final View anchor) {
        Intent intent = new Intent(this, DBManagerActivity.class);
        startActivity(intent);
    }

    private void onOpt_moreMenu(final View anchor) {
        PopupMenu popup = new PopupMenu(this, anchor);
        popup.getMenuInflater().inflate(R.menu.popup_more_menu, popup.getMenu());
        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                case R.id.about:
                    onOpt_moreMenu_about(anchor);
                    break;
                case R.id.license:
                    onOpt_moreMenu_license(anchor);
                    break;
                case R.id.media_delete_all:
                    onOpt_moreMenu_deleteAllDnFiles(anchor);
                    break;
                case R.id.media_delete_used_all:
                    onOpt_moreMenu_deleteAllUsedDnFiles(anchor);
                    break;
                case R.id.feedback_opinion:
                    onOpt_moreMenu_feedbackOpinion(anchor);
                    break;
                case R.id.db_management:
                    onOpt_moreMenu_dbManagement(anchor);
                    break;
                default:
                    eAssert(false);
                }
                return true;
            }
        });
        popup.show();
    }

    private void onOpt_setting(final View anchor) {
        Intent intent = new Intent(this, FeederPreferenceActivity.class);
        startActivity(intent);
    }

    private void onResult_pickPredefinedChannel(int resultCode, Intent data) {
        if (RESULT_OK != resultCode)
            return;

        final String[] urls = data.getStringArrayExtra(PredefinedChannelActivity.KEY_URLS);
        final String[] iconurls = data.getStringArrayExtra(PredefinedChannelActivity.KEY_ICONURLS);
        for (int i = 0; i < urls.length; i++) {
            eAssert(Utils.isValidValue(urls[i]));
            final String url = urls[i];
            final String iconurl = iconurls[i];
            // NOTE
            // Without using 'post', user may feel bad ui response.
            Environ.getUiHandler().post(new Runnable() {
                @Override
                public void run() {
                    addChannel(url, iconurl);
                }
            });
        }
    }

    private void setupToolButtons() {
        findViewById(R.id.btn_items_favorite).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onOpt_itemsFavorite(v);
            }
        });

        findViewById(R.id.btn_items_category).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onOpt_itemsCategory(v);
            }
        });

        findViewById(R.id.btn_items_all).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onOpt_itemsAll(v);
            }
        });

        findViewById(R.id.btn_update_all).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onOpt_updateAll(v);
            }
        });

        findViewById(R.id.btn_add_channel).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onOpt_addChannel(v);
            }
        });

        findViewById(R.id.btn_category).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onOpt_category(v);
            }
        });

        findViewById(R.id.btn_more_menu).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onOpt_moreMenu(v);
            }
        });

        findViewById(R.id.btn_setting).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onOpt_setting(v);
            }
        });
    }

    public boolean isContextMenuOwner(ChannelListFragment fragment) {
        return fragment == mContextMenuOwner;
    }

    public void categoryDataSetChanged(long catId) {
        ChannelListFragment fragmentTo = getPagerAdapter().getFragment(catId);
        if (null != fragmentTo)
            fragmentTo.refreshListAsync();
    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        onTabReselected(tab, ft);
    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
        mPager.setCurrentItem(getPagerAdapter().getPosition(getTag(tab).categoryid));
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
        case REQC_PICK_PREDEFINED_CHANNEL:
            onResult_pickPredefinedChannel(resultCode, data);
            break;
        }
    }

    @Override
    public String dump(UnexpectedExceptionHandler.DumpLevel lv) {
        return "[ ChannelListActivity ]";
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        mContextMenuOwner = getPagerAdapter().getPrimaryFragment();
        ;
        mContextMenuOwner.onCreateContextMenu2(menu, v, menuInfo);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        UnexpectedExceptionHandler.get().registerModule(this);
        super.onCreate(savedInstanceState);

        //logI("==> ChannelListActivity : onCreate");

        // TODO
        // Is this best place to put this line of code (sendReportMail())???
        // More consideration is required.

        // Send error report if exists.
        mUr.sendErrReportMail(this);
        // Send usage report if exists and time is passed enough.
        mUr.sendUsageReportMail(this);

        setContentView(R.layout.channel_list);

        setupToolButtons();

        // Setup Tabs
        mAb = getActionBar();
        mAb.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        mAb.setDisplayShowTitleEnabled(false);
        mAb.setDisplayShowHomeEnabled(false);

        Feed.Category[] cats;
        cats = mDbp.getCategories();
        eAssert(cats.length > 0);

        for (Feed.Category cat : cats)
            addCategory(cat);

        mPager = (ViewPager) findViewById(R.id.pager);
        ChannelListPagerAdapter adapter = new ChannelListPagerAdapter(getSupportFragmentManager(), cats);
        mPager.setAdapter(adapter);

        selectDefaultAsSelected();

        mPager.setOnPageChangeListener(mPCListener);
        mDbWatcher = new DBWatcher();
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();

        mDbWatcher.unregister();
        boolean needReload = mDbWatcher.isCategoryTableUpdated();
        mDbWatcher.reset();
        if (needReload) {
            // category table is changed outside of this activity.
            // restarting is required!!
            restartThisActivity();
            return;
        }

        if (null == mAb) {
            if (DBG)
                P.w("mAb(action bar) is NULL");
            mAb = getActionBar();
        }

    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);

    }

    @Override
    protected void onPause() {
        mDbWatcher.register();
        super.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mDbWatcher.unregister();
        UnexpectedExceptionHandler.get().unregisterModule(this);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Do nothing!
    }
}