Java tutorial
/****************************************************************************** * Copyright (C) 2012, 2013, 2014 * Younghyung Cho. <yhcting77@gmail.com> * All rights reserved. * * This file is part of NetMBuddy * * 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.netmbuddy; import static free.yhc.netmbuddy.utils.Utils.eAssert; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import free.yhc.netmbuddy.model.Policy; import free.yhc.netmbuddy.model.YTFeed; import free.yhc.netmbuddy.model.YTSearchHelper; import free.yhc.netmbuddy.utils.Utils; public abstract class YTSearchFragment extends Fragment implements YTSearchHelper.SearchDoneReceiver { private static final boolean DBG = false; private static final Utils.Logger P = new Utils.Logger(YTSearchFragment.class); private static final int INVALID_PAGE = -1; private static final int NR_ENTRY_PER_PAGE = Policy.YTSEARCH_MAX_RESULTS; private static final String KEY_SEARCH_DONE_RESPONSE = "YTSearchFragment:search_done_response"; private static final String KEY_TYPE = "YTSearchFragment:type"; private static final String KEY_TEXT = "YTSearchFragment:text"; private static final String KEY_TITLE = "YTSearchFragment:title"; private static final String KEY_PAGE = "YTSearchFragment:page"; private static final String KEY_PRIMARY = "YTSearchFragment:primary"; protected YTSearchHelper mSearchHelper; protected ListView mListv = null; protected ViewGroup mRootv = null; private boolean mSearchDoneResponseRequired = false; private boolean mPrimary = false; private YTSearchHelper.SearchType mType; // TODO // refactoing is required for below two variable... is it really required? private String mText; private String mTitle; private int mPage = INVALID_PAGE; private YTSearchActivity getMyActivity() { return (YTSearchActivity) super.getActivity(); } private int getStarti(int pageNum) { int starti = (pageNum - 1) * NR_ENTRY_PER_PAGE + 1; return starti < 1 ? 1 : starti; } /** * * @param pagei * 1-based page number */ private void loadPage(YTSearchHelper.SearchType type, String text, String title, int pageNumber) { // close helper to cancel all existing work. mSearchHelper.close(true); // Create new helper instance to know owner instance at callback from helper. mSearchHelper = new YTSearchHelper(); mSearchHelper.setSearchDoneRecevier(this); // open again to support new search. mSearchHelper.open(); YTSearchHelper.SearchArg arg = new YTSearchHelper.SearchArg(pageNumber, type, text, title, getStarti(pageNumber), NR_ENTRY_PER_PAGE); YTSearchHelper.Err err = mSearchHelper.searchAsync(arg); if (YTSearchHelper.Err.NO_ERR == err) showLoadingLookAndFeel(); else showErrorMessage(Err.map(err).getMessage()); } // ======================================================================== // // // // ======================================================================== abstract protected void onListItemClick(View view, int position, long itemId); protected final YTSearchHelper.SearchType getType() { return mType; } private final YTSearchAdapter getAdapter() { if (null != mListv) return (YTSearchAdapter) mListv.getAdapter(); return null; } protected void showLoadingLookAndFeel() { View loadingv = mRootv.findViewById(R.id.loading); if (View.VISIBLE == loadingv.getVisibility()) { eAssert(View.VISIBLE != mListv.getVisibility()); return; } ImageView iv = (ImageView) loadingv.findViewById(R.id.loading_img); TextView tv = (TextView) loadingv.findViewById(R.id.loading_msg); tv.setText(R.string.loading); loadingv.setVisibility(View.VISIBLE); mRootv.findViewById(R.id.error_text).setVisibility(View.GONE); mListv.setVisibility(View.GONE); iv.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate)); } protected void stopLoadingLookAndFeel() { View loadingv = mRootv.findViewById(R.id.loading); if (View.VISIBLE != loadingv.getVisibility()) return; ImageView iv = (ImageView) loadingv.findViewById(R.id.loading_img); if (null != iv.getAnimation()) { iv.getAnimation().cancel(); iv.setAnimation(null); } mRootv.findViewById(R.id.error_text).setVisibility(View.GONE); loadingv.setVisibility(View.GONE); mListv.setVisibility(View.VISIBLE); } protected void showErrorMessage(int msg) { mListv.setVisibility(View.GONE); mRootv.findViewById(R.id.loading).setVisibility(View.GONE); TextView tv = (TextView) mRootv.findViewById(R.id.error_text); tv.setVisibility(View.VISIBLE); tv.setText(msg); } /** * * @param helper * @param arg * @param result * @param err * @return * false : there is error in search, otherwise true. */ protected boolean handleSearchResult(YTSearchHelper helper, YTSearchHelper.SearchArg arg, YTFeed.Result result, YTSearchHelper.Err err) { if (null == mSearchHelper || mSearchHelper != helper || null == getActivity()) return false; Err r = Err.NO_ERR; int totalResults = 0; do { if (YTSearchHelper.Err.NO_ERR != err) { r = Err.map(err); break; } int curPage = (Integer) arg.tag; if (result.entries.length <= 0 && 1 == curPage) { r = Err.NO_MATCH; break; } try { totalResults = Integer.parseInt(result.header.totalResults); } catch (NumberFormatException e) { r = Err.YTSEARCH; break; } } while (false); if (mSearchDoneResponseRequired) getMyActivity().onFragmentSearchDone(this, r, totalResults); if (Err.NO_ERR != r) { stopLoadingLookAndFeel(); showErrorMessage(r.getMessage()); return false; } return true; } // ======================================================================== // // // // ======================================================================== public YTSearchFragment() { } public final void setToPrimary(boolean primary) { mPrimary = primary; if (null != getActivity() && null != mListv) { if (primary) getActivity().registerForContextMenu(mListv); else getActivity().unregisterForContextMenu(mListv); } onSetToPrimary(primary); } protected void onSetToPrimary(boolean primary) { } public final boolean isPrimary() { return mPrimary; } public final int getPage() { return mPage; } public final void setAttributes(YTSearchHelper.SearchType type, String text, String title, int page) { mType = type; mText = text; mTitle = title; mPage = page; } public final void setSearchDoneResponseRequired(boolean required) { mSearchDoneResponseRequired = required; } public void reloadPage() { loadPage(mType, mText, mTitle, mPage); } public void onCreateContextMenu2(ContextMenu menu, View v, ContextMenuInfo menuInfo) { } @Override public void onAttach(Activity activity) { super.onAttach(activity); } @Override public void onSaveInstanceState(Bundle outState) { outState.putBoolean(KEY_SEARCH_DONE_RESPONSE, mSearchDoneResponseRequired); outState.putString(KEY_TYPE, mType.name()); outState.putString(KEY_TEXT, mText); outState.putString(KEY_TITLE, mTitle); outState.putInt(KEY_PAGE, mPage); outState.putBoolean(KEY_PRIMARY, mPrimary); } private void restoreInstanceState(Bundle data) { if (null == data) return; mSearchDoneResponseRequired = data.getBoolean(KEY_SEARCH_DONE_RESPONSE, mSearchDoneResponseRequired); String tmp = data.getString(KEY_TYPE); if (null != tmp) mType = YTSearchHelper.SearchType.valueOf(tmp); tmp = data.getString(KEY_TEXT); if (null != tmp) mText = tmp; tmp = data.getString(KEY_TITLE); if (null != tmp) mTitle = tmp; mPage = data.getInt(KEY_PAGE, mPage); mPrimary = data.getBoolean(KEY_PRIMARY, mPrimary); } @Override public void onViewStateRestored(Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); restoreInstanceState(savedInstanceState); mSearchHelper = new YTSearchHelper(); // initialization. } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); FrameLayout fl = (FrameLayout) inflater.inflate(R.layout.ytsearch_fragment, null); mListv = (ListView) fl.findViewById(R.id.list); mRootv = fl; mListv.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long itemId) { onListItemClick(view, position, itemId); } }); setToPrimary(isPrimary()); return fl; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); loadPage(mType, mText, mTitle, mPage); } @Override public void onStart() { super.onStart(); } @Override public void onResume() { super.onResume(); } @Override public void onPause() { super.onPause(); } @Override public void onStop() { super.onStop(); } /** * Pager adapter - YTSearchPagerAdapter - calls this function when the fragment is no more used. * In general, onDestroy() is the callback for cleanup. * But, in case that system is very busy, calling onDestroy() is delayed * and this may make huge amount of garbage resource usage. * To reduce above issue 'onEarlyDestroy()' is introduced. */ public void onEarlyDestroy() { // release resources that should be freed as soon as possible. onDestroyViewInternal(); onDestroyInternal(); } private void onDestroyViewInternal() { if (null != getAdapter()) getAdapter().cleanup(); } @Override public void onDestroyView() { onDestroyViewInternal(); super.onDestroyView(); } private void onDestroyInternal() { mSearchHelper.close(true); } @Override public void onDestroy() { onDestroyInternal(); super.onDestroy(); } @Override public void onDetach() { super.onDetach(); } }