Java tutorial
/* * Copyright 2013 Dmitry Monakhov. * * 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 monakhv.android.samlib; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.database.CursorIndexOutOfBoundsException; import android.database.MatrixCursor; import android.database.MergeCursor; import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; import android.support.v4.widget.SimpleCursorAdapter; import android.util.Log; import android.view.ContextMenu; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.actionbarsherlock.app.SherlockListFragment; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import java.text.SimpleDateFormat; import java.util.Date; import monakhv.android.samlib.data.SettingsHelper; import monakhv.android.samlib.sql.AuthorController; import monakhv.android.samlib.sql.AuthorProvider; import monakhv.android.samlib.sql.SQLController; import monakhv.android.samlib.sql.entity.Author; import monakhv.android.samlib.tasks.MarkRead; import static monakhv.android.samlib.ActivityUtils.cleanItemSelection; import static monakhv.android.samlib.ActivityUtils.getClipboardText; import static monakhv.android.samlib.ActivityUtils.setDivider; import monakhv.android.samlib.dialogs.EnterStringDialog; import monakhv.android.samlib.dialogs.FilterSelectDialog; import monakhv.android.samlib.dialogs.SingleChoiceSelectDialog; import monakhv.android.samlib.service.UpdateServiceIntent; import monakhv.android.samlib.sql.entity.SamLibConfig; import monakhv.android.samlib.tasks.DeleteAuthor; import uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.PullToRefreshLayout; import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh; import uk.co.senab.actionbarpulltorefresh.library.DefaultHeaderTransformer; import uk.co.senab.actionbarpulltorefresh.library.Options; import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener; /** * @author Dmitry Monakhov */ public class AuthorListFragment extends SherlockListFragment implements LoaderManager.LoaderCallbacks<Cursor>, ListSwipeListener.SwipeCallBack, OnRefreshListener { public static final int AUTHOR_LIST_LOADER = 0x01; private static final String DEBUG_TAG = "AuthorListHelper"; private static final String DATE_FORMAT = "dd.MM.yyyy HH:mm:ss"; private String selection = null; private SimpleCursorAdapter adapter; private TextView emptyText; private AuthorController sql; private View selected; private SortOrder order; private SingleChoiceSelectDialog sortDialog; private FilterSelectDialog filterDialog; private GestureDetector detector; private boolean updateAuthor = false;//true update the only selected author void onRefreshComplete() { mPullToRefreshLayout.setRefreshComplete(); updateTextView.setGravity(android.view.Gravity.CENTER); updateAuthor = false; } void updateProgress(String stringExtra) { updateTextView.setGravity(android.view.Gravity.CENTER_VERTICAL); updateTextView.setText(stringExtra); } public void onRefreshStarted(View view) { if (getActivity() == null) { return;//try to prevent some ANR reports } Intent service = new Intent(getActivity(), UpdateServiceIntent.class); service.putExtra(UpdateServiceIntent.CALLER_TYPE, UpdateServiceIntent.CALLER_IS_ACTIVITY); if (updateAuthor) { String str = SQLController.TABLE_AUTHOR + "." + SQLController.COL_ID + "=" + Integer.toString(author.getId()); service.putExtra(UpdateServiceIntent.SELECT_STRING, str); } else { service.putExtra(UpdateServiceIntent.SELECT_STRING, selection); } getActivity().startService(service); } void startRefresh() { mPullToRefreshLayout.setRefreshing(true); onRefreshStarted(null); } public interface Callbacks { public void onAuthorSelected(int id); public void selectBookSortOrder(); public void onTitleChange(String lTitle); public void addAuthorFromText(); } private static Callbacks mCallbacks; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String[] from = { SQLController.COL_NAME, SQLController.COL_mtime, SQLController.COL_isnew, SQLController.COL_TGNAMES, SQLController.COL_URL }; int[] to = { R.id.authorName, R.id.updated, R.id.icon, R.id.tgnames, R.id.authorURL }; adapter = new SimpleCursorAdapter(getActivity(), R.layout.rowlayout, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); SettingsHelper settings = new SettingsHelper(getActivity().getApplicationContext()); adapter.setViewBinder(new AuthorViewBinder()); order = settings.getAuthorSortOrder(); setListAdapter(adapter); getLoaderManager().initLoader(AUTHOR_LIST_LOADER, null, this); detector = new GestureDetector(getActivity(), new ListSwipeListener(this)); } private PullToRefreshLayout mPullToRefreshLayout; private TextView updateTextView; @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // This is the View which is created by ListFragment ViewGroup viewGroup = (ViewGroup) view; // We need to create a PullToRefreshLayout manually mPullToRefreshLayout = new PullToRefreshLayout(viewGroup.getContext()); // We can now setup the PullToRefreshLayout ActionBarPullToRefresh.from(getActivity()) .options( Options.create().refreshOnUp(true).headerLayout(R.layout.updateheader).noMinimize().build()) // We need to insert the PullToRefreshLayout into the Fragment's ViewGroup .insertLayoutInto(viewGroup) // Here we mark just the ListView and it's Empty View as pullable .theseChildrenArePullable(android.R.id.list, android.R.id.empty).listener(this) .setup(mPullToRefreshLayout); DefaultHeaderTransformer dht = (DefaultHeaderTransformer) mPullToRefreshLayout.getHeaderTransformer(); updateTextView = (TextView) dht.getHeaderView().findViewById(R.id.ptr_text); dht.setPullText(getActivity().getText(R.string.pull_to_refresh_pull_label)); dht.setReleaseText(getActivity().getText(R.string.pull_to_refresh_release_label)); dht.setRefreshingText(getActivity().getText(R.string.pull_to_refresh_refreshing_label)); } private int selectedAuthorPosition = 0; private void setEmptyText(int id) { if (sql == null) { return; } if (!sql.isEmpty(selection)) { emptyText.setText(R.string.pull_to_refresh_refreshing_label); } else { emptyText.setText(id); } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onCreate(savedInstanceState); emptyText = (TextView) getActivity().findViewById(R.id.id_empty_text); sql = new AuthorController(getActivity()); setEmptyText(R.string.no_authors); getListView().setEmptyView(emptyText); registerForContextMenu(getListView()); getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); //getListView().setSelector(R.drawable.author_item_bg); getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { selectedAuthorPosition = position; Cursor c = (Cursor) adapter.getItem(position); mCallbacks.onAuthorSelected(c.getInt(c.getColumnIndex(SQLController.COL_ID))); Log.i(DEBUG_TAG, "position: " + position + " view: " + view.getId() + " --- " + View.NO_ID); selectView(view); } }); setDivider(getListView()); EditText editText = (EditText) getActivity().findViewById(R.id.addUrlText); editText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEARCH) { mCallbacks.addAuthorFromText(); return true; } return false; } }); getListView().setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { detector.onTouchEvent(event); return false; } }); } public int getSelectedAuthorPosition() { return selectedAuthorPosition; } public int getSelectedAuthorId() { Cursor c = (Cursor) adapter.getItem(selectedAuthorPosition); if (c == null) { return 0; } try { return c.getInt(c.getColumnIndex(SQLController.COL_ID)); } catch (CursorIndexOutOfBoundsException ex) { Log.e(DEBUG_TAG, "Cursor is out of bounds"); return 0; } } public void restoreSelection(int position) { selectedAuthorPosition = position; View v; try { v = adapter.getView(position, null, getListView()); } catch (IllegalStateException ex) { Log.e(DEBUG_TAG, "restoreSelection: Can not move cursor to restore selection", ex); return; } if (v == null) { Log.e(DEBUG_TAG, "restoreSelection: View to select is null"); return; } selectView(v); getListView().setItemChecked(position, true); } @Override public void onAttach(Activity activity) { super.onAttach(activity); if (!(activity instanceof Callbacks)) { throw new IllegalStateException("Activity must implement fragment's callbacks."); } mCallbacks = (Callbacks) activity; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.options_menu, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { int sel = item.getItemId(); if (sel == android.R.id.home) { //mCallbacks.onOpenPanel(); if (getSelection() != null) { refresh(null, null); mCallbacks.onTitleChange(getString(R.string.app_name)); } } if (sel == R.id.menu_refresh) { startRefresh(); } if (sel == R.id.sort_option_item_books) { mCallbacks.selectBookSortOrder(); } if (sel == R.id.sort_option_item) { AdapterView.OnItemClickListener listener = new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { SortOrder so = SortOrder.values()[position]; mCallbacks.onAuthorSelected(0); setSortOrder(so); sortDialog.dismiss(); } }; sortDialog = SingleChoiceSelectDialog.getInstance(SortOrder.getTitles(getActivity()), listener, this.getString(R.string.dialog_title_sort_author), getSortOrder().ordinal()); sortDialog.show(getActivity().getSupportFragmentManager(), "DoSortDialog"); } if (sel == R.id.add_option_item) { View v = getActivity().findViewById(R.id.add_author_panel); v.setVisibility(View.VISIBLE); String txt = null; try { txt = getClipboardText(getActivity()); } catch (Exception ex) { Log.e(DEBUG_TAG, "Clipboard Error!", ex); } if (txt != null) { if (SamLibConfig.getParsedUrl(txt) != null) { EditText editText = (EditText) getActivity().findViewById(R.id.addUrlText); editText.setText(txt); } } } if (sel == R.id.settings_option_item) { Log.d(DEBUG_TAG, "go to Settings"); Intent prefsIntent = new Intent(getActivity().getApplicationContext(), SamlibPreferencesActivity.class); //prefsIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); getActivity().startActivityForResult(prefsIntent, MainActivity.PREFS_ACTIVITY); } if (sel == R.id.archive_option_item) { Log.d(DEBUG_TAG, "go to Archive"); Intent prefsIntent = new Intent(getActivity().getApplicationContext(), ArchiveActivity.class); //prefsIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); //startActivityForResult must be called via getActivity direct call produce wrong requestCode getActivity().startActivityForResult(prefsIntent, MainActivity.ARCHIVE_ACTIVITY); } if (sel == R.id.selected_option_item) { Log.d(DEBUG_TAG, "go to Selected"); cleanSelection(); mCallbacks.onAuthorSelected(SamLibConfig.SELECTED_BOOK_ID); } if (sel == R.id.menu_filter) { Log.d(DEBUG_TAG, "go to Filter"); Cursor tags = getActivity().getContentResolver().query(AuthorProvider.TAG_URI, null, null, null, SQLController.COL_TAG_NAME); MatrixCursor extras = new MatrixCursor( new String[] { SQLController.COL_ID, SQLController.COL_TAG_NAME }); extras.addRow(new String[] { Integer.toString(SamLibConfig.TAG_AUTHOR_ALL), getText(R.string.filter_all).toString() }); extras.addRow(new String[] { Integer.toString(SamLibConfig.TAG_AUTHOR_NEW), getText(R.string.filter_new).toString() }); Cursor[] cursors = { extras, tags }; final Cursor extendedCursor = new MergeCursor(cursors); AdapterView.OnItemClickListener listener = new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { extendedCursor.moveToPosition(position); int tag_id = extendedCursor.getInt(extendedCursor.getColumnIndex(SQLController.COL_ID)); String tg_name = extendedCursor .getString(extendedCursor.getColumnIndex(SQLController.COL_TAG_NAME)); filterDialog.dismiss(); selection = SQLController.TABLE_TAGS + "." + SQLController.COL_ID + "=" + tag_id; if (tag_id == SamLibConfig.TAG_AUTHOR_ALL) { selection = null; mCallbacks.onTitleChange(getActivity().getText(R.string.app_name).toString()); } else { mCallbacks.onTitleChange(tg_name); } if (tag_id == SamLibConfig.TAG_AUTHOR_NEW) { selection = SQLController.TABLE_AUTHOR + "." + SQLController.COL_isnew + "=1"; } Log.i(DEBUG_TAG, "WHERE " + selection); refresh(selection, null); mCallbacks.onAuthorSelected(0); } }; filterDialog = FilterSelectDialog.getInstance(extendedCursor, listener, getText(R.string.dialog_title_filtr).toString()); filterDialog.show(getActivity().getSupportFragmentManager(), "FilterDialogShow"); } return super.onOptionsItemSelected(item); } /** * Make view <b>selected<b/> and store it in the field * * @param view the view to select */ public void selectView(View view) { view.setSelected(true); selected = view; } /** * Clean selection for selected view */ public void cleanSelection() { cleanItemSelection(selected); getListView().clearChoices(); getListView().clearFocus(); } /** * set sort order and restart loader to make is actual * * @param so new sort order */ public void setSortOrder(SortOrder so) { cleanSelection(); order = so; getLoaderManager().restartLoader(AUTHOR_LIST_LOADER, null, this); } /** * update sort order and selection parameters and restart loader * * @param selection selection string * @param so sort order string */ public void refresh(String selection, SortOrder so) { Log.d(DEBUG_TAG, "set Selection: " + selection); cleanSelection(); this.selection = selection; if (so != null) { order = so; } if (selection == null) { setEmptyText(R.string.no_authors); } else { setEmptyText(R.string.no_authors_tag); } getLoaderManager().restartLoader(AUTHOR_LIST_LOADER, null, this); } public SortOrder getSortOrder() { return order; } public String getSelection() { return selection; } public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { Log.d(DEBUG_TAG, "order: " + order); return new CursorLoader(getActivity(), AuthorProvider.AUTHOR_URI, null, selection, null, order.getOrder()); } public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { adapter.swapCursor(cursor); } public void onLoaderReset(Loader<Cursor> loader) { adapter.swapCursor(null); } public boolean singleClick(MotionEvent e) { return false; } public boolean swipeRight(MotionEvent e) { int position = getListView().pointToPosition((int) e.getX(), (int) e.getY()); Cursor c = (Cursor) adapter.getItem(position); if (c.getPosition() != -1) { MarkRead marker = new MarkRead(getActivity().getApplicationContext()); marker.execute(AuthorController.Cursor2Author(getActivity().getApplicationContext(), c).getId()); return true; } return false; } public boolean swipeLeft(MotionEvent e) { int position = getListView().pointToPosition((int) e.getX(), (int) e.getY()); Cursor c = (Cursor) adapter.getItem(position); if (c.getPosition() != -1) { launchBrowser(AuthorController.Cursor2Author(getActivity().getApplicationContext(), c)); return true; } return false; } /** * Launch Browser to load Author home page * * @param a Author object */ public void launchBrowser(Author a) { Uri uri = Uri.parse(a.getUrlForBrowser(getActivity())); Intent launchBrowser = new Intent(Intent.ACTION_VIEW, uri); getActivity().startActivity(launchBrowser); } private static class AuthorViewBinder implements SimpleCursorAdapter.ViewBinder { public AuthorViewBinder() { } public boolean setViewValue(View view, Cursor cursor, int i) { int idx_isNew = cursor.getColumnIndex(SQLController.COL_isnew); int idx_name = cursor.getColumnIndex(SQLController.COL_NAME); int idx_mtime = cursor.getColumnIndex(SQLController.COL_mtime); int idx_tags = cursor.getColumnIndex(SQLController.COL_TGNAMES); int idx_url = cursor.getColumnIndex(SQLController.COL_URL); if (i == idx_tags) { TextView tv = ((TextView) view); String str = cursor.getString(idx_tags); if (str != null) { tv.setText(str.replaceAll(",", ", ")); return true; } return false; } if (i == idx_name) { TextView tv = ((TextView) view); tv.setText(cursor.getString(i)); if (cursor.getInt(idx_isNew) == 1) { tv.setTypeface(Typeface.DEFAULT_BOLD); //Log.d(DEBUG_TAG, "MAke Bold user: "+cursor.getString(idx_name)); } else { tv.setTypeface(Typeface.DEFAULT); } return true; } if (i == idx_mtime) { SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT); long dd = cursor.getLong(i); Date date = new Date(dd); ((TextView) view).setText(df.format(date)); return true; } if (i == idx_isNew) { if (cursor.getInt(i) == 1) { //((ImageView) view).setImageResource(R.drawable.bullet_green); ((ImageView) view).setImageResource(R.drawable.open); } else { //((ImageView) view).setImageResource(R.drawable.bullet_grey); ((ImageView) view).setImageResource(R.drawable.closed); } return true; } if (i == idx_url) { TextView tv = ((TextView) view); tv.setText(cursor.getString(i)); return true; } return false; } } private Author author = null; private final int read_option_item = 21; private final int tags_option_item = 22; private final int browser_option_item = 23; private final int edit_author_option_item = 24; private final int delete_option_item = 25; private final int update_option_item = 35; @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { if (v.getId() == getListView().getId()) { AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; Cursor cursor = (Cursor) adapter.getItem(info.position); author = AuthorController.Cursor2Author(getActivity().getApplicationContext(), cursor); if (author == null) { Log.d(DEBUG_TAG, "Context menu Created - author is NULL!!"); } else { Log.d(DEBUG_TAG, "Context menu Created - author is " + author.getName()); } if (author.isIsNew()) { menu.add(1, read_option_item, 10, getText(R.string.menu_read)); } menu.add(1, tags_option_item, 20, getText(R.string.menu_tags)); menu.add(1, browser_option_item, 30, getText(R.string.menu_open_web)); menu.add(1, edit_author_option_item, 40, getText(R.string.menu_edit)); menu.add(1, delete_option_item, 50, getText(R.string.menu_delete)); menu.add(1, update_option_item, 60, getText(R.string.menu_refresh)); menu.setHeaderTitle(author.getName()); } } @Override public boolean onContextItemSelected(android.view.MenuItem item) { boolean super_answer = super.onContextItemSelected(item); Log.d(DEBUG_TAG, "context menu item selected: " + item.getItemId() + " super: " + super_answer); if (author != null) { if (item.getItemId() == delete_option_item) { Dialog alert = createDeleteAuthorAlert(author.getName()); alert.show(); } if (item.getItemId() == read_option_item) { MarkRead marker = new MarkRead(getActivity().getApplicationContext()); marker.execute(author.getId()); } if (item.getItemId() == tags_option_item) { Intent intent = new Intent(getActivity(), AuthorTagsActivity.class); intent.putExtra(AuthorTagsActivity.AUTHOR_ID, author.getId()); intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); startActivity(intent); } if (item.getItemId() == browser_option_item) { launchBrowser(author); } if (item.getItemId() == update_option_item) { updateAuthor = true; startRefresh(); } if (item.getItemId() == edit_author_option_item) { EnterStringDialog ddialog = new EnterStringDialog(getActivity(), new EnterStringDialog.ClickListener() { public void okClick(String txt) { author.setName(txt); sql.update(author); } }, getText(R.string.dialog_title_edit_author).toString(), author.getName()); ddialog.show(); } } else { Log.e(DEBUG_TAG, "Author Object is NULL!!"); } return super.onContextItemSelected(item); } /** * Create Alert Dialog to wrn about Author delete * * @param authorName Name of the author * @return Warning Author delete dialog */ private Dialog createDeleteAuthorAlert(String authorName) { AlertDialog.Builder adb = new AlertDialog.Builder(getActivity()); adb.setTitle(R.string.Attention); String msg = getString(R.string.alert_delete_author); msg = msg.replaceAll("__", authorName); adb.setMessage(msg); adb.setIcon(android.R.drawable.ic_dialog_alert); adb.setPositiveButton(R.string.Yes, deleteAuthorListener); adb.setNegativeButton(R.string.No, deleteAuthorListener); return adb.create(); } private final DialogInterface.OnClickListener deleteAuthorListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { switch (which) { case Dialog.BUTTON_POSITIVE: if (author != null) { DeleteAuthor deleter = new DeleteAuthor(getActivity().getApplicationContext()); deleter.execute(author.getId()); } break; case Dialog.BUTTON_NEGATIVE: break; } } }; public enum SortOrder { DateUpdate(R.string.sort_update_date, SQLController.COL_mtime + " DESC"), AuthorName( R.string.sort_author_name, SQLController.COL_isnew + " DESC, " + SQLController.COL_NAME); private final int name; private final String order; private SortOrder(int name, String order) { this.name = name; this.order = order; } public String getOrder() { return order; } public static String[] getTitles(Context ctx) { String[] res = new String[values().length]; int i = 0; for (SortOrder so : values()) { res[i] = ctx.getString(so.name); ++i; } return res; } } }