Java tutorial
package com.bellman.bible.android.view.activity.base; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Spinner; import android.widget.Toast; import com.bellman.bible.android.activity.R; import com.bellman.bible.android.control.ControlFactory; import com.bellman.bible.android.control.document.DocumentControl; import com.bellman.bible.service.download.DownloadManager; import com.bellman.bible.service.sword.SwordDocumentFacade; import org.apache.commons.lang3.StringUtils; import org.crosswire.common.util.Language; import org.crosswire.common.util.Version; import org.crosswire.jsword.book.Book; import org.crosswire.jsword.book.BookCategory; import org.crosswire.jsword.book.BookException; import org.crosswire.jsword.book.BookFilter; import org.crosswire.jsword.book.BookFilters; import org.crosswire.jsword.book.sword.SwordBook; import org.crosswire.jsword.book.sword.SwordBookMetaData; import org.crosswire.jsword.versification.Versification; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; /** * Choose Document (Book) * <p> * NotificationManager with ProgressBar example here: * http://united-coders.com/nico-heid/show-progressbar-in-notification-area-like-google-does-when-downloading-from-android * * @author Martin Denham [mjdenham at gmail dot com] * @see gnu.lgpl.License for license details.<br> * The copyright to this program is held by it's author. */ abstract public class DocumentSelectionBase extends ListActivityBase implements ListActionModeHelper.ActionModeActivity { private static final BookFilter[] DOCUMENT_TYPE_SPINNER_FILTERS = new BookFilter[] { BookFilters.getBibles(), BookFilters.getCommentaries(), BookFilters.getDictionaries(), BookFilters.getGeneralBooks(), BookFilters.getMaps() }; private static final int LIST_ITEM_TYPE = R.layout.list_item_2_image; private static final String TAG = "DocumentSelectionBase"; private static Language lastSelectedLanguage; // allow sticky language selection private final int actionModeMenuId; // document type spinner private Spinner documentTypeSpinner; private int selectedDocumentFilterNo = 0; // language spinner private Spinner langSpinner; private List<Language> languageList; private int selectedLanguageNo = -1; private ArrayAdapter<Language> langArrayAdapter; // the document list private List<Book> allDocuments; //TODO just use displayedDocuments with a model giving 2 lines in list private List<Book> displayedDocuments; private boolean isDeletePossible; // We only show installed ticks beside documents if in Document Downloads screen private boolean isInstallStatusIconsShown; private DocumentControl documentControl = ControlFactory.getInstance().getDocumentControl(); private ListActionModeHelper listActionModeHelper; public DocumentSelectionBase(int optionsMenuId, int actionModeMenuId) { super(optionsMenuId); this.actionModeMenuId = actionModeMenuId; } /** * ask subclass for documents to be displayed */ abstract protected List<Book> getDocumentsFromSource(boolean refresh); abstract protected void handleDocumentSelection(Book selectedDocument); abstract protected List<Language> sortLanguages(Collection<Language> languages); /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.document_selection); initialiseView(); } private void initialiseView() { // prepare action mode listActionModeHelper = new ListActionModeHelper(getListView(), actionModeMenuId); // trigger action mode on long press getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { return listActionModeHelper.startActionMode(DocumentSelectionBase.this, position); } }); languageList = new ArrayList<Language>(); displayedDocuments = new ArrayList<Book>(); ArrayAdapter<Book> listArrayAdapter = new DocumentItemAdapter(this, LIST_ITEM_TYPE, displayedDocuments, isInstallStatusIconsShown, this); setListAdapter(listArrayAdapter); //prepare the documentType spinner documentTypeSpinner = (Spinner) findViewById(R.id.documentTypeSpinner); setInitialDocumentType(); documentTypeSpinner.setSelection(selectedDocumentFilterNo); documentTypeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedDocumentFilterNo = position; DocumentSelectionBase.this.filterDocuments(); } @Override public void onNothingSelected(AdapterView<?> arg0) { } }); //prepare the language spinner { langSpinner = (Spinner) findViewById(R.id.languageSpinner); langSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedLanguageNo = position; lastSelectedLanguage = languageList.get(selectedLanguageNo); DocumentSelectionBase.this.filterDocuments(); } @Override public void onNothingSelected(AdapterView<?> arg0) { } }); langArrayAdapter = new ArrayAdapter<Language>(this, android.R.layout.simple_spinner_item, languageList); langArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); langSpinner.setAdapter(langArrayAdapter); } } @Override protected void onRestart() { super.onRestart(); // if downloading documents then ensure the correct ticks are checked after user presses More to return here notifyDataSetChanged(); } private void setDefaultLanguage() { if (selectedLanguageNo == -1) { Language lang = null; // make selected language sticky if (lastSelectedLanguage != null && languageList.contains(lastSelectedLanguage)) { lang = lastSelectedLanguage; } else { // set default lang to lang of mobile lang = getDefaultLanguage(); } selectedLanguageNo = languageList.indexOf(lang); } // if last doc in last lang was just deleted then need to adjust index checkSpinnerIndexesValid(); langSpinner.setSelection(selectedLanguageNo); } protected Language getDefaultLanguage() { // get the current language code String langCode = Locale.getDefault().getLanguage(); if (!new Language(langCode).isValidLanguage()) { langCode = Locale.ENGLISH.getLanguage(); } // create the JSword Language for current lang Language localLanguage = new Language(langCode); Log.d(TAG, "Local language is:" + localLanguage); // check a bible exists in current lang otherwise use english boolean foundBibleInLocalLanguage = false; for (Book book : getAllDocuments()) { if (book.getBookCategory().equals(BookCategory.BIBLE) && localLanguage.equals(book.getLanguage())) { foundBibleInLocalLanguage = true; break; } } // if no bibles exist in current lang then fall back to default language (English) so the user will not see an initially empty list if (!foundBibleInLocalLanguage) { Log.d(TAG, "No bibles found in local language so falling back to default lang"); localLanguage = Language.DEFAULT_LANG; } return localLanguage; } @Override protected void onListItemClick(ListView l, View v, int position, long id) { try { if (position >= 0 && position < displayedDocuments.size()) { Book selectedBook = displayedDocuments.get(position); if (selectedBook != null) { Log.d(TAG, "Selected " + selectedBook.getInitials()); handleDocumentSelection(selectedBook); } } } catch (Exception e) { Log.e(TAG, "document selection error", e); Dialogs.getInstance().showErrorMsg(R.string.error_occurred, e); } } protected void reloadDocuments() { populateMasterDocumentList(false); } protected void showPreLoadMessage() { // default to no message } protected void populateMasterDocumentList(final boolean refresh) { Log.d(TAG, "populate Master Document List"); new AsyncTask<Void, Boolean, Void>() { @Override protected void onPreExecute() { showHourglass(); showPreLoadMessage(); } @Override protected Void doInBackground(Void... noparam) { try { // Prevent occasional class loading errors on Samsung devices Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); allDocuments = getDocumentsFromSource(refresh); Log.i(TAG, "number of documents:" + allDocuments.size()); } catch (Exception e) { Log.e(TAG, "Error getting documents", e); Dialogs.getInstance().showErrorMsg(R.string.error_occurred, e); } return null; } @Override protected void onPostExecute(Void result) { try { if (allDocuments != null) { populateLanguageList(); // default language depends on doc availability so must do in onPostExecute setDefaultLanguage(); filterDocuments(); } } finally { //todo implement this: http://stackoverflow.com/questions/891451/android-dismissdialog-does-not-dismiss-the-dialog dismissHourglass(); } } }.execute((Void[]) null); } /** * a spinner has changed so refilter the doc list */ private void filterDocuments() { try { // documents list has changed so force action mode to exit, if displayed, because selections are invalidated listActionModeHelper.exitActionMode(); // re-filter documents if (allDocuments != null && allDocuments.size() > 0) { Log.d(TAG, "filtering documents"); displayedDocuments.clear(); Language lang = getSelectedLanguage(); for (Book doc : allDocuments) { BookFilter filter = DOCUMENT_TYPE_SPINNER_FILTERS[selectedDocumentFilterNo]; if (filter.test(doc) && doc.getLanguage().equals(lang)) { displayedDocuments.add(doc); } } // sort by initials because that is field 1 Collections.sort(displayedDocuments, new Comparator<Book>() { public int compare(Book o1, Book o2) { return o1.getAbbreviation().compareToIgnoreCase(o2.getAbbreviation()); } }); notifyDataSetChanged(); } } catch (Exception e) { Log.e(TAG, "Error initialising view", e); Toast.makeText(this, getString(R.string.error) + " " + e.getMessage(), Toast.LENGTH_SHORT).show(); } } /** * a spinner has changed so refilter the doc list */ private void populateLanguageList() { try { // temporary Set to remove duplicate Languages Set<Language> langSet = new HashSet<Language>(); if (allDocuments != null && allDocuments.size() > 0) { Log.d(TAG, "initialising language list"); for (Book doc : allDocuments) { langSet.add(doc.getLanguage()); } List<Language> sortedLanguages = sortLanguages(langSet); languageList.clear(); languageList.addAll(sortedLanguages); langArrayAdapter.notifyDataSetChanged(); } } catch (Exception e) { Log.e(TAG, "Error initialising view", e); Toast.makeText(this, getString(R.string.error) + " " + e.getMessage(), Toast.LENGTH_SHORT).show(); } } @Override public boolean onActionItemClicked(MenuItem item, List<Integer> selectedItemPositions) { List<Book> documents = new ArrayList<>(); for (int posn : selectedItemPositions) { documents.add(getDisplayedDocuments().get(posn)); } if (!documents.isEmpty()) { int id = item.getItemId(); if (id == R.id.about) { handleAbout(documents); return true; } else if (id == R.id.delete) { handleDelete(documents); return true; } else if (id == R.id.delete_index) { handleDeleteIndex(documents); return true; } } return false; } private void handleAbout(List<Book> documents) { Book document = documents.get(0); try { // ensure repo key is retained but reload sbmd to ensure About text is loaded SwordBookMetaData sbmd = (SwordBookMetaData) document.getBookMetaData(); String repoKey = sbmd.getProperty(DownloadManager.REPOSITORY_KEY); sbmd.reload(); sbmd.setProperty(DownloadManager.REPOSITORY_KEY, repoKey); showAbout(document); } catch (BookException e) { Log.e(TAG, "Error expanding SwordBookMetaData for " + document, e); Dialogs.getInstance().showErrorMsg(R.string.error_occurred, e); } } protected void handleDelete(final List<Book> documents) { for (final Book document : documents) { if (documentControl.canDelete(document)) { CharSequence msg = getString(R.string.delete_doc, document.getName()); new AlertDialog.Builder(this).setMessage(msg).setCancelable(true) .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int buttonId) { try { Log.d(TAG, "Deleting:" + document); documentControl.deleteDocument(document); // the doc list should now change reloadDocuments(); } catch (Exception e) { Dialogs.getInstance().showErrorMsg(R.string.error_occurred, e); } } }).create().show(); } } } protected void handleDeleteIndex(final List<Book> documents) { for (final Book document : documents) { CharSequence msg = getString(R.string.delete_search_index_doc, document.getName()); new AlertDialog.Builder(this).setMessage(msg).setCancelable(true) .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int buttonId) { try { Log.d(TAG, "Deleting index:" + document); SwordDocumentFacade.getInstance().deleteDocumentIndex(document); } catch (Exception e) { Dialogs.getInstance().showErrorMsg(R.string.error_occurred, e); } } }).create().show(); } } @Override public boolean isItemChecked(int position) { return getListView().isItemChecked(position); } /** * about display is generic so handle it here */ protected void showAbout(Book document) { //get about text String about = document.getBookMetaData().getProperty("About"); if (about != null) { // either process the odd formatting chars in about about = about.replace("\\par", "\n"); } else { // or default to name if there is no About about = document.getName(); } // Copyright and distribution information String shortCopyright = document.getBookMetaData().getProperty(SwordBookMetaData.KEY_SHORT_COPYRIGHT); String copyright = document.getBookMetaData().getProperty(SwordBookMetaData.KEY_COPYRIGHT); String distributionLicense = document.getBookMetaData() .getProperty(SwordBookMetaData.KEY_DISTRIBUTION_LICENSE); String copyrightMerged = ""; if (StringUtils.isNotBlank(shortCopyright)) { copyrightMerged += shortCopyright + "\n"; } else if (StringUtils.isNotBlank(copyright)) { copyrightMerged += copyright + "\n"; } if (StringUtils.isNotBlank(distributionLicense)) { copyrightMerged += distributionLicense + "\n"; } if (StringUtils.isNotBlank(copyrightMerged)) { String copyrightMsg = CurrentActivityHolder.getInstance().getApplication() .getString(R.string.about_copyright, copyrightMerged); about += "\n\n" + copyrightMsg; } // add version Version versionObj = new Version(document.getBookMetaData().getProperty("Version")); if (versionObj != null) { String versionMsg = CurrentActivityHolder.getInstance().getApplication() .getString(R.string.about_version, versionObj.toString()); about += "\n\n" + versionMsg; } // add versification if (document instanceof SwordBook) { Versification versification = ((SwordBook) document).getVersification(); String versificationMsg = CurrentActivityHolder.getInstance().getApplication() .getString(R.string.about_versification, versification.getName()); about += "\n\n" + versificationMsg; } new AlertDialog.Builder(this).setMessage(about).setCancelable(false) .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int buttonId) { //do nothing } }).create().show(); } /** * deletion may have removed a language or document type so need to check current spinner selection is still valid */ private void checkSpinnerIndexesValid() { if (selectedLanguageNo >= languageList.size()) { selectedLanguageNo = languageList.size() - 1; } } private Language getSelectedLanguage() { if (selectedLanguageNo == -1) { setDefaultLanguage(); } return languageList.get(selectedLanguageNo); } public void setDeletePossible(boolean isDeletePossible) { this.isDeletePossible = isDeletePossible; } public void setInstallStatusIconsShown(boolean isInstallStatusIconsShown) { this.isInstallStatusIconsShown = isInstallStatusIconsShown; } public Spinner getDocumentTypeSpinner() { return documentTypeSpinner; } public List<Book> getAllDocuments() { return allDocuments; } public List<Book> getDisplayedDocuments() { return displayedDocuments; } /** * allow selection of initial doc type */ protected void setInitialDocumentType() { selectedDocumentFilterNo = 0; } /** * map between book category and item no */ public void setSelectedBookCategory(BookCategory bookCategory) { switch (bookCategory) { case BIBLE: selectedDocumentFilterNo = 0; break; case COMMENTARY: selectedDocumentFilterNo = 1; break; case DICTIONARY: selectedDocumentFilterNo = 2; break; case GENERAL_BOOK: selectedDocumentFilterNo = 3; break; case MAPS: selectedDocumentFilterNo = 4; break; default: selectedDocumentFilterNo = 0; break; } } }