Back to project page feedhive.
The source code is released under:
SOFTWARE LICENSE ---------------- 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 l...
If you think the Android project feedhive listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/****************************************************************************** * Copyright (C) 2012, 2013, 2014/* ww w.ja v a 2 s. c om*/ * 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! } }