diff options
Diffstat (limited to 'main/src/cgeo/geocaching/CacheDetailActivity.java')
| -rw-r--r-- | main/src/cgeo/geocaching/CacheDetailActivity.java | 325 |
1 files changed, 156 insertions, 169 deletions
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index 4068e38..6962317 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -3,6 +3,7 @@ package cgeo.geocaching; import butterknife.ButterKnife; import butterknife.InjectView; +import cgeo.calendar.CalendarAddon; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.AbstractViewPagerActivity; import cgeo.geocaching.activity.Progress; @@ -45,7 +46,6 @@ import cgeo.geocaching.utils.HtmlUtils; import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; -import cgeo.geocaching.utils.RunnableWithArgument; import cgeo.geocaching.utils.SimpleCancellableHandler; import cgeo.geocaching.utils.SimpleHandler; import cgeo.geocaching.utils.TextUtils; @@ -57,6 +57,14 @@ import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; +import rx.Observable; +import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Subscription; +import rx.android.observables.AndroidObservable; +import rx.concurrency.Schedulers; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action1; import android.R.color; import android.app.AlertDialog; @@ -80,7 +88,6 @@ import android.text.Editable; import android.text.Html; import android.text.Spannable; import android.text.Spanned; -import android.text.format.DateUtils; import android.text.style.ForegroundColorSpan; import android.text.style.StrikethroughSpan; import android.text.style.StyleSpan; @@ -109,7 +116,6 @@ import android.widget.TextView.BufferType; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.EnumSet; import java.util.List; import java.util.Locale; @@ -176,24 +182,15 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // set title in code, as the activity needs a hard coded title due to the intent filters setTitle(res.getString(R.string.cache)); - String geocode = null; - - // TODO Why can it happen that search is not null? onCreate should be called only once and it is not set before. - if (search != null) { - cache = search.getFirstCacheFromResult(LoadFlags.LOAD_ALL_DB_ONLY); - if (cache != null && cache.getGeocode() != null) { - geocode = cache.getGeocode(); - } - } - // get parameters final Bundle extras = getIntent().getExtras(); final Uri uri = getIntent().getData(); // try to get data from extras String name = null; + String geocode = null; String guid = null; - if (geocode == null && extras != null) { + if (extras != null) { geocode = extras.getString(Intents.EXTRA_GEOCODE); name = extras.getString(Intents.EXTRA_NAME); guid = extras.getString(Intents.EXTRA_GUID); @@ -327,6 +324,14 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } @Override + public void onDestroy() { + if (imagesList != null) { + imagesList.removeAllViews(); + } + super.onDestroy(); + } + + @Override public void onStop() { if (cache != null) { cache.setChangeNotificationHandler(null); @@ -382,6 +387,12 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc clickedItemText = ((TextView) view).getText(); buildDetailsContextMenu(menu, res.getString(R.string.cache_logs), false); break; + case R.id.date: // event date + assert view instanceof TextView; + clickedItemText = ((TextView) view).getText(); + buildDetailsContextMenu(menu, res.getString(R.string.cache_event), true); + menu.findItem(R.id.menu_calendar).setVisible(cache.canBeAddedToCalendar()); + break; case R.id.waypoint: menu.setHeaderTitle(selectedWaypoint.getName() + " (" + res.getString(R.string.waypoint) + ")"); getMenuInflater().inflate(R.menu.waypoint_options, menu); @@ -484,6 +495,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc new ResetCoordsThread(cache, handler, selectedWaypoint, true, false, progressDialog).start(); } return true; + case R.id.menu_calendar: + CalendarAddon.addToCalendarWithIntent(this, cache); + return true; default: break; } @@ -542,20 +556,19 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { updateStatusMsg((String) msg.obj); } else { - CacheDetailActivity activity = ((CacheDetailActivity) activityRef.get()); + final CacheDetailActivity activity = ((CacheDetailActivity) activityRef.get()); if (activity == null) { return; } - SearchResult search = activity.getSearch(); - if (search == null) { + if (activity.search == null) { showToast(R.string.err_dwld_details_failed); dismissProgress(); finishActivity(); return; } - if (search.getError() != null) { - activity.showToast(activity.getResources().getString(R.string.err_dwld_details_failed) + " " + search.getError().getErrorString(activity.getResources()) + "."); + if (activity.search.getError() != null) { + activity.showToast(activity.getResources().getString(R.string.err_dwld_details_failed) + " " + activity.search.getError().getErrorString(activity.getResources()) + "."); dismissProgress(); finishActivity(); return; @@ -972,17 +985,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc ownerView.setOnClickListener(new OwnerActionsClickListener(cache)); } - // cache hidden - final Date hiddenDate = cache.getHiddenDate(); - if (hiddenDate != null) { - final long time = hiddenDate.getTime(); - if (time > 0) { - String dateString = Formatter.formatFullDate(time); - if (cache.isEventCache()) { - dateString = DateUtils.formatDateTime(CgeoApplication.getInstance().getBaseContext(), time, DateUtils.FORMAT_SHOW_WEEKDAY) + ", " + dateString; - } - details.add(cache.isEventCache() ? R.string.cache_event : R.string.cache_hidden, dateString); - } + // hidden or event date + final TextView hiddenView = details.addHiddenDate(cache); + if (hiddenView != null) { + registerForContextMenu(hiddenView); } // cache location @@ -1051,9 +1057,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (Settings.getChooseList()) { // let user select list to store cache in new StoredList.UserInterface(CacheDetailActivity.this).promptForListSelection(R.string.list_title, - new RunnableWithArgument<Integer>() { + new Action1<Integer>() { @Override - public void run(final Integer selectedListId) { + public void call(final Integer selectedListId) { storeCache(selectedListId, new StoreCacheHandler(CacheDetailActivity.this, progress)); } }, true, StoredList.TEMPORARY_LIST_ID); @@ -1302,9 +1308,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc @Override public void onClick(View view) { new StoredList.UserInterface(CacheDetailActivity.this).promptForListSelection(R.string.list_title, - new RunnableWithArgument<Integer>() { + new Action1<Integer>() { @Override - public void run(final Integer selectedListId) { + public void call(final Integer selectedListId) { switchListById(selectedListId); } }, true, cache.getListId()); @@ -1368,7 +1374,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc final LinearLayout layout = (LinearLayout) view.findViewById(R.id.favpoint_box); final boolean supportsFavoritePoints = cache.supportsFavoritePoints(); layout.setVisibility(supportsFavoritePoints ? View.VISIBLE : View.GONE); - if (!supportsFavoritePoints || cache.isOwner() || !Settings.isPremiumMember()) { + if (!supportsFavoritePoints || cache.isOwner() || !Settings.isGCPremiumMember()) { return; } final Button buttonAdd = (Button) view.findViewById(R.id.add_to_favpoint); @@ -1470,6 +1476,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc protected class DescriptionViewCreator extends AbstractCachingPageViewCreator<ScrollView> { @InjectView(R.id.personalnote) protected TextView personalNoteView; + @InjectView(R.id.shortdesc) protected IndexOutOfBoundsAvoidingTextView shortDescView; + @InjectView(R.id.longdesc) protected IndexOutOfBoundsAvoidingTextView longDescView; + @InjectView(R.id.show_description) protected Button showDesc; + @InjectView(R.id.loading) protected View loadingView; @Override public ScrollView getDispatchedView() { @@ -1483,7 +1493,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // cache short description if (StringUtils.isNotBlank(cache.getShortDescription())) { - new LoadDescriptionTask(cache.getShortDescription(), view.findViewById(R.id.shortdesc), null, null).execute(); + loadDescription(cache.getShortDescription(), shortDescView, null); } // long description @@ -1491,7 +1501,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (Settings.isAutoLoadDescription()) { loadLongDescription(); } else { - final Button showDesc = (Button) view.findViewById(R.id.show_description); showDesc.setVisibility(View.VISIBLE); showDesc.setOnClickListener(new View.OnClickListener() { @Override @@ -1605,12 +1614,23 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } private void loadLongDescription() { - final Button showDesc = (Button) view.findViewById(R.id.show_description); showDesc.setVisibility(View.GONE); showDesc.setOnClickListener(null); view.findViewById(R.id.loading).setVisibility(View.VISIBLE); - new LoadDescriptionTask(cache.getDescription(), view.findViewById(R.id.longdesc), view.findViewById(R.id.loading), view.findViewById(R.id.shortdesc)).execute(); + final String longDescription = cache.getDescription(); + loadDescription(longDescription, longDescView, loadingView); + + // Hide the short description, if it is contained somewhere at the start of the long description. + if (shortDescView != null) { + final String shortDescription = cache.getShortDescription(); + if (StringUtils.isNotBlank(shortDescription)) { + final int index = longDescription.indexOf(shortDescription); + if (index >= 0 && index < 200) { + shortDescView.setVisibility(View.GONE); + } + } + } } private void warnPersonalNoteNeedsStoring() { @@ -1641,148 +1661,119 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } - /** - * Loads the description in background. <br /> - * <br /> - * Params: - * <ol> - * <li>description string (String)</li> - * <li>target description view (TextView)</li> - * <li>loading indicator view (View, may be null)</li> - * </ol> - */ - private class LoadDescriptionTask extends AsyncTask<Object, Void, Void> { - private final View loadingIndicatorView; - private final IndexOutOfBoundsAvoidingTextView descriptionView; - private final String descriptionString; - private Spanned description; - private final View shortDescView; + /** + * Load the description in the background. + * @param descriptionString the HTML description as retrieved from the connector + * @param descriptionView the view to fill + * @param loadingIndicatorView the loading indicator view, will be hidden when completed + */ + private void loadDescription(final String descriptionString, final IndexOutOfBoundsAvoidingTextView descriptionView, final View loadingIndicatorView) { + // The producer produces successives (without then with images) versions of the description. + final Observable<Spanned> producer = Observable.create(new OnSubscribeFunc<Spanned>() { + @Override + public Subscription onSubscribe(final Observer<? super Spanned> observer) { + try { + // Fast preview: parse only HTML without loading any images + final HtmlImageCounter imageCounter = new HtmlImageCounter(); + final UnknownTagsHandler unknownTagsHandler = new UnknownTagsHandler(); + Spanned description = Html.fromHtml(descriptionString, imageCounter, unknownTagsHandler); + addWarning(unknownTagsHandler, description); + observer.onNext(description); + + if (imageCounter.getImageCount() > 0) { + // Complete view: parse again with loading images - if necessary ! If there are any images causing problems the user can see at least the preview + description = Html.fromHtml(descriptionString, new HtmlImage(cache.getGeocode(), true, cache.getListId(), false), unknownTagsHandler); + addWarning(unknownTagsHandler, description); + observer.onNext(description); + } - public LoadDescriptionTask(final String description, final View descriptionView, final View loadingIndicatorView, final View shortDescView) { - assert descriptionView instanceof IndexOutOfBoundsAvoidingTextView; - this.descriptionString = description; - this.descriptionView = (IndexOutOfBoundsAvoidingTextView) descriptionView; - this.loadingIndicatorView = loadingIndicatorView; - this.shortDescView = shortDescView; - } + observer.onCompleted(); + } catch (final Exception e) { + Log.e("loadDescription", e); + observer.onError(e); + } + return Subscriptions.empty(); + } - @Override - protected Void doInBackground(Object... params) { - try { - // Fast preview: parse only HTML without loading any images - final HtmlImageCounter imageCounter = new HtmlImageCounter(); - final UnknownTagsHandler unknownTagsHandler = new UnknownTagsHandler(); - description = Html.fromHtml(descriptionString, imageCounter, unknownTagsHandler); - publishProgress(); - - boolean needsRefresh = false; - if (imageCounter.getImageCount() > 0) { - // Complete view: parse again with loading images - if necessary ! If there are any images causing problems the user can see at least the preview - description = Html.fromHtml(descriptionString, new HtmlImage(cache.getGeocode(), true, cache.getListId(), false), unknownTagsHandler); - needsRefresh = true; - } - - // If description has an HTML construct which may be problematic to render, add a note at the end of the long description. - // Technically, it may not be a table, but a pre, which has the same problems as a table, so the message is ok even though - // sometimes technically incorrect. - if (unknownTagsHandler.isProblematicDetected() && descriptionView != null) { + // If description has an HTML construct which may be problematic to render, add a note at the end of the long description. + // Technically, it may not be a table, but a pre, which has the same problems as a table, so the message is ok even though + // sometimes technically incorrect. + private void addWarning(final UnknownTagsHandler unknownTagsHandler, final Spanned description) { + if (unknownTagsHandler.isProblematicDetected()) { final int startPos = description.length(); final IConnector connector = ConnectorFactory.getConnector(cache); final Spanned tableNote = Html.fromHtml(res.getString(R.string.cache_description_table_note, "<a href=\"" + cache.getUrl() + "\">" + connector.getName() + "</a>")); ((Editable) description).append("\n\n").append(tableNote); ((Editable) description).setSpan(new StyleSpan(Typeface.ITALIC), startPos, description.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - needsRefresh = true; - } - - if (needsRefresh) { - publishProgress(); - } - } catch (final Exception e) { - Log.e("LoadDescriptionTask: ", e); - } - return null; - } - - @Override - protected void onProgressUpdate(Void... values) { - if (description == null) { - showToast(res.getString(R.string.err_load_descr_failed)); - return; - } - if (StringUtils.isNotBlank(descriptionString)) { - try { - descriptionView.setText(description, TextView.BufferType.SPANNABLE); - } catch (final Exception e) { - // On 4.1, there is sometimes a crash on measuring the layout: https://code.google.com/p/android/issues/detail?id=35412 - Log.e("Android bug setting text: ", e); - // remove the formatting by converting to a simple string - descriptionView.setText(description.toString()); } - descriptionView.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); - fixTextColor(descriptionView, descriptionString); - descriptionView.setVisibility(View.VISIBLE); - registerForContextMenu(descriptionView); - - hideDuplicatedShortDescription(); } - } + }); - /** - * Hide the short description, if it is contained somewhere at the start of the long description. - */ - private void hideDuplicatedShortDescription() { - if (shortDescView != null) { - final String shortDescription = cache.getShortDescription(); - if (StringUtils.isNotBlank(shortDescription)) { - final int index = descriptionString.indexOf(shortDescription); - if (index >= 0 && index < 200) { - shortDescView.setVisibility(View.GONE); + AndroidObservable.fromActivity(this, producer.subscribeOn(Schedulers.threadPoolForIO())) + .subscribe(new Observer<Spanned>() { + @Override + public void onCompleted() { + if (null != loadingIndicatorView) { + loadingIndicatorView.setVisibility(View.GONE); + } } - } - } - } - @Override - protected void onPostExecute(Void result) { - if (null != loadingIndicatorView) { - loadingIndicatorView.setVisibility(View.GONE); - } - } + @Override + public void onError(final Throwable throwable) { + showToast(res.getString(R.string.err_load_descr_failed)); + } - /** - * Handle caches with black font color in dark skin and white font color in light skin - * by changing background color of the view - * - * @param view - * containing the text - * @param text - * to be checked - */ - private void fixTextColor(final TextView view, final String text) { - int backcolor; - if (Settings.isLightSkin()) { - backcolor = color.white; - - for (final Pattern pattern : LIGHT_COLOR_PATTERNS) { - final MatcherWrapper matcher = new MatcherWrapper(pattern, text); - if (matcher.find()) { - view.setBackgroundResource(color.darker_gray); - return; + @Override + public void onNext(final Spanned description) { + if (StringUtils.isNotBlank(descriptionString)) { + try { + descriptionView.setText(description, TextView.BufferType.SPANNABLE); + } catch (final Exception e) { + // On 4.1, there is sometimes a crash on measuring the layout: https://code.google.com/p/android/issues/detail?id=35412 + Log.e("Android bug setting text: ", e); + // remove the formatting by converting to a simple string + descriptionView.setText(description.toString()); + } + descriptionView.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); + fixTextColor(descriptionString); + descriptionView.setVisibility(View.VISIBLE); + registerForContextMenu(descriptionView); + } } - } - } else { - backcolor = color.black; - for (final Pattern pattern : DARK_COLOR_PATTERNS) { - final MatcherWrapper matcher = new MatcherWrapper(pattern, text); - if (matcher.find()) { - view.setBackgroundResource(color.darker_gray); - return; + /** + * Handle caches with black font color in dark skin and white font color in light skin + * by changing background color of the view + * + * @param text + * to be checked + */ + private void fixTextColor(final String text) { + int backcolor; + if (Settings.isLightSkin()) { + backcolor = color.white; + + for (final Pattern pattern : LIGHT_COLOR_PATTERNS) { + final MatcherWrapper matcher = new MatcherWrapper(pattern, text); + if (matcher.find()) { + descriptionView.setBackgroundResource(color.darker_gray); + return; + } + } + } else { + backcolor = color.black; + + for (final Pattern pattern : DARK_COLOR_PATTERNS) { + final MatcherWrapper matcher = new MatcherWrapper(pattern, text); + if (matcher.find()) { + descriptionView.setBackgroundResource(color.darker_gray); + return; + } + } + } + descriptionView.setBackgroundResource(backcolor); } - } - } - view.setBackgroundResource(backcolor); - } + }); } private class WaypointsViewCreator extends AbstractCachingPageViewCreator<ListView> { @@ -2255,10 +2246,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return cache; } - public SearchResult getSearch() { - return search; - } - private static class StoreCacheHandler extends SimpleCancellableHandler { public StoreCacheHandler(CacheDetailActivity activity, Progress progress) { |
