diff options
Diffstat (limited to 'main/src/cgeo/geocaching/TrackableActivity.java')
-rw-r--r-- | main/src/cgeo/geocaching/TrackableActivity.java | 368 |
1 files changed, 198 insertions, 170 deletions
diff --git a/main/src/cgeo/geocaching/TrackableActivity.java b/main/src/cgeo/geocaching/TrackableActivity.java index 0e784bd..7d10d34 100644 --- a/main/src/cgeo/geocaching/TrackableActivity.java +++ b/main/src/cgeo/geocaching/TrackableActivity.java @@ -4,40 +4,46 @@ import butterknife.ButterKnife; import butterknife.InjectView; import cgeo.geocaching.activity.AbstractActivity; -import cgeo.geocaching.activity.AbstractActivity.ActivitySharingInterface; import cgeo.geocaching.activity.AbstractViewPagerActivity; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.trackable.TrackableConnector; import cgeo.geocaching.connector.trackable.TravelBugConnector; import cgeo.geocaching.enumerations.LogType; -import cgeo.geocaching.geopoint.Units; +import cgeo.geocaching.location.Units; +import cgeo.geocaching.network.AndroidBeam; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.ui.AbstractCachingPageViewCreator; import cgeo.geocaching.ui.AnchorAwareLinkMovementMethod; import cgeo.geocaching.ui.CacheDetailsCreator; +import cgeo.geocaching.ui.ImagesList; import cgeo.geocaching.ui.UserActionsClickListener; import cgeo.geocaching.ui.UserNameClickListener; import cgeo.geocaching.ui.logs.TrackableLogsViewCreator; import cgeo.geocaching.utils.Formatter; import cgeo.geocaching.utils.HtmlUtils; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; import cgeo.geocaching.utils.UnknownTagsHandler; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; +import org.eclipse.jdt.annotation.Nullable; -import rx.android.observables.AndroidObservable; -import rx.android.observables.ViewObservable; +import rx.Observable; +import rx.android.app.AppObservable; +import rx.android.view.OnClickEvent; +import rx.android.view.ViewObservable; import rx.functions.Action1; +import rx.functions.Func0; +import rx.subscriptions.CompositeSubscription; import android.app.ProgressDialog; import android.content.Intent; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.Bundle; -import android.os.Handler; -import android.os.Message; import android.support.v7.app.ActionBar; import android.support.v7.view.ActionMode; import android.text.Html; @@ -57,11 +63,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivity.Page> implements ActivitySharingInterface { +public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivity.Page> implements AndroidBeam.ActivitySharingInterface { + + private CompositeSubscription createSubscriptions; public enum Page { DETAILS(R.string.detail), - LOGS(R.string.cache_logs); + LOGS(R.string.cache_logs), + IMAGES(R.string.cache_images); private final int resId; @@ -77,50 +86,9 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi private String id = null; private LayoutInflater inflater = null; private ProgressDialog waitDialog = null; - private final Handler loadTrackableHandler = new Handler() { - - @Override - public void handleMessage(final Message msg) { - if (trackable == null) { - if (waitDialog != null) { - waitDialog.dismiss(); - } - - if (StringUtils.isNotBlank(geocode)) { - showToast(res.getString(R.string.err_tb_find) + " " + geocode + "."); - } else { - showToast(res.getString(R.string.err_tb_find_that)); - } - - finish(); - return; - } - - try { - inflater = getLayoutInflater(); - geocode = trackable.getGeocode(); - - if (StringUtils.isNotBlank(trackable.getName())) { - setTitle(Html.fromHtml(trackable.getName()).toString()); - } else { - setTitle(trackable.getName()); - } - - invalidateOptionsMenuCompatible(); - reinitializeViewPager(); - - } catch (final Exception e) { - Log.e("TrackableActivity.loadTrackableHandler: ", e); - } - - if (waitDialog != null) { - waitDialog.dismiss(); - } - - } - }; - private CharSequence clickedItemText = null; + private ImagesList imagesList = null; + /** * Action mode of the current contextual action bar (e.g. for copy and share actions). */ @@ -129,16 +97,17 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.viewpager_activity); + createSubscriptions = new CompositeSubscription(); // set title in code, as the activity needs a hard coded title due to the intent filters setTitle(res.getString(R.string.trackable)); // get parameters final Bundle extras = getIntent().getExtras(); - final Uri uri = getIntent().getData(); - // try to get data from extras + final Uri uri = AndroidBeam.getUri(getIntent()); if (extras != null) { + // try to get data from extras geocode = extras.getString(Intents.EXTRA_GEOCODE); name = extras.getString(Intents.EXTRA_NAME); guid = extras.getString(Intents.EXTRA_GUID); @@ -150,7 +119,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi geocode = ConnectorFactory.getTrackableFromURL(uri.toString()); final String uriHost = uri.getHost().toLowerCase(Locale.US); - if (uriHost.contains("geocaching.com")) { + if (uriHost.endsWith("geocaching.com")) { geocode = uri.getQueryParameter("tracker"); guid = uri.getQueryParameter("guid"); id = uri.getQueryParameter("id"); @@ -172,17 +141,6 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi finish(); return; } - } else if (uriHost.contains("coord.info")) { - final String uriPath = uri.getPath().toLowerCase(Locale.US); - if (StringUtils.startsWith(uriPath, "/tb")) { - geocode = uriPath.substring(1).toUpperCase(Locale.US); - guid = null; - id = null; - } else { - showToast(res.getString(R.string.err_tb_details_open)); - finish(); - return; - } } } @@ -193,7 +151,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi return; } - String message; + final String message; if (StringUtils.isNotBlank(name)) { message = Html.fromHtml(name).toString(); } else if (StringUtils.isNotBlank(geocode)) { @@ -201,17 +159,34 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi } else { message = res.getString(R.string.trackable); } - waitDialog = ProgressDialog.show(this, message, res.getString(R.string.trackable_details_loading), true, true); - // If we have a newer Android device setup Android Beam for easy cache sharing - initializeAndroidBeam(this); + AndroidBeam.enable(this, this); - createViewPager(0, null); - final LoadTrackableThread thread = new LoadTrackableThread(loadTrackableHandler, geocode, guid, id); - thread.start(); + createViewPager(0, new OnPageSelectedListener() { + @Override + public void onPageSelected(final int position) { + // Lazy loading of trackable images + if (getPage(position) == Page.IMAGES) { + loadTrackableImages(); + } + } + }); + refreshTrackable(message); } + private void refreshTrackable(final String message) { + waitDialog = ProgressDialog.show(this, message, res.getString(R.string.trackable_details_loading), true, true); + createSubscriptions.add(AppObservable.bindActivity(this, loadTrackable(geocode, guid, id)).singleOrDefault(null).subscribe(new Action1<Trackable>() { + @Override + public void call(final Trackable trackable) { + TrackableActivity.this.trackable = trackable; + displayTrackable(); + } + })); + } + + @Nullable @Override public String getAndroidBeamUri() { return trackable != null ? trackable.getUrl() : null; @@ -227,7 +202,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case R.id.menu_log_touch: - LogTrackableActivity.startActivity(this, trackable); + startActivityForResult(LogTrackableActivity.getIntent(this, trackable), LogTrackableActivity.LOG_TRACKABLE); return true; case R.id.menu_browser_trackable: startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(trackable.getUrl()))); @@ -240,92 +215,90 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi public boolean onPrepareOptionsMenu(final Menu menu) { if (trackable != null) { menu.findItem(R.id.menu_log_touch).setVisible(StringUtils.isNotBlank(geocode) && trackable.isLoggable()); - menu.findItem(R.id.menu_browser_trackable).setVisible(StringUtils.isNotBlank(trackable.getUrl())); + menu.findItem(R.id.menu_browser_trackable).setVisible(trackable.hasUrl()); } return super.onPrepareOptionsMenu(menu); } - private class LoadTrackableThread extends Thread { - final private Handler handler; - final private String geocode; - final private String guid; - final private String id; - - public LoadTrackableThread(final Handler handlerIn, final String geocodeIn, final String guidIn, final String idIn) { - handler = handlerIn; - geocode = geocodeIn; - guid = guidIn; - id = idIn; - } - - @Override - public void run() { - if (StringUtils.isNotEmpty(geocode)) { - - // iterate over the connectors as some codes may be handled by multiple connectors - for (final TrackableConnector trackableConnector : ConnectorFactory.getTrackableConnectors()) { - if (trackableConnector.canHandleTrackable(geocode)) { - trackable = trackableConnector.searchTrackable(geocode, guid, id); - if (trackable != null) { - break; + private static Observable<Trackable> loadTrackable(final String geocode, final String guid, final String id) { + return Observable.defer(new Func0<Observable<Trackable>>() { + @Override + public Observable<Trackable> call() { + if (StringUtils.isNotEmpty(geocode)) { + // iterate over the connectors as some codes may be handled by multiple connectors + for (final TrackableConnector trackableConnector : ConnectorFactory.getTrackableConnectors()) { + if (trackableConnector.canHandleTrackable(geocode)) { + final Trackable trackable = trackableConnector.searchTrackable(geocode, guid, id); + if (trackable != null) { + return Observable.just(trackable); + } } } + // Check local storage (offline case) + final Trackable trackable = DataStore.loadTrackable(geocode); + if (trackable != null) { + return Observable.just(trackable); + } } - // Check local storage (offline case) - if (trackable == null) { - trackable = DataStore.loadTrackable(geocode); - } - } - // fall back to GC search by GUID - if (trackable == null) { - trackable = TravelBugConnector.getInstance().searchTrackable(geocode, guid, id); + + // Fall back to GC search by GUID + final Trackable trackable = TravelBugConnector.getInstance().searchTrackable(geocode, guid, id); + return trackable != null ? Observable.just(trackable) : Observable.<Trackable>empty(); } - handler.sendMessage(Message.obtain()); - } + }).subscribeOn(RxUtils.networkScheduler); } - private class TrackableIconThread extends Thread { - final private String url; - final private Handler handler; + public void displayTrackable() { + if (trackable == null) { + if (waitDialog != null) { + waitDialog.dismiss(); + } - public TrackableIconThread(final String urlIn, final Handler handlerIn) { - url = urlIn; - handler = handlerIn; + if (StringUtils.isNotBlank(geocode)) { + showToast(res.getString(R.string.err_tb_find) + " " + geocode + "."); + } else { + showToast(res.getString(R.string.err_tb_find_that)); + } + + finish(); + return; } - @Override - public void run() { - if (url == null || handler == null) { - return; + try { + inflater = getLayoutInflater(); + geocode = trackable.getGeocode(); + + if (StringUtils.isNotBlank(trackable.getName())) { + setTitle(Html.fromHtml(trackable.getName()).toString()); + } else { + setTitle(trackable.getName()); } - try { - final HtmlImage imgGetter = new HtmlImage(trackable.getGeocode(), false, 0, false); + invalidateOptionsMenuCompatible(); + reinitializeViewPager(); - final BitmapDrawable image = imgGetter.getDrawable(url); - final Message message = handler.obtainMessage(0, image); - handler.sendMessage(message); - } catch (final Exception e) { - Log.e("TrackableActivity.TrackableIconThread.run: ", e); - } + } catch (final Exception e) { + Log.e("TrackableActivity.loadTrackableHandler: ", e); } - } - - private static class TrackableIconHandler extends Handler { - final private ActionBar view; - public TrackableIconHandler(final ActionBar viewIn) { - view = viewIn; + if (waitDialog != null) { + waitDialog.dismiss(); } - @Override - public void handleMessage(final Message message) { - final BitmapDrawable image = (BitmapDrawable) message.obj; - if (image != null && view != null) { - image.setBounds(0, 0, view.getHeight(), view.getHeight()); - view.setIcon(image); + } + + private void setupIcon(final ActionBar actionBar, final String url) { + final HtmlImage imgGetter = new HtmlImage(HtmlImage.SHARED, false, 0, false); + AppObservable.bindActivity(this, imgGetter.fetchDrawable(url)).subscribe(new Action1<BitmapDrawable>() { + @Override + public void call(final BitmapDrawable image) { + if (actionBar != null) { + final int height = actionBar.getHeight(); + image.setBounds(0, 0, height, height); + actionBar.setIcon(image); + } } - } + }); } public static void startActivity(final AbstractActivity fromContext, @@ -343,11 +316,38 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi case DETAILS: return new DetailsViewCreator(); case LOGS: - return new TrackableLogsViewCreator(this, trackable); + return new TrackableLogsViewCreator(this); + case IMAGES: + return new ImagesViewCreator(); } throw new IllegalStateException(); // cannot happen as long as switch case is enum complete } + private class ImagesViewCreator extends AbstractCachingPageViewCreator<View> { + + @Override + public View getDispatchedView(final ViewGroup parentView) { + view = getLayoutInflater().inflate(R.layout.cachedetail_images_page, parentView, false); + return view; + } + } + + private void loadTrackableImages() { + if (imagesList != null) { + return; + } + final PageViewCreator creator = getViewCreator(Page.IMAGES); + if (creator == null) { + return; + } + final View imageView = creator.getView(null); + if (imageView == null) { + return; + } + imagesList = new ImagesList(this, trackable.getGeocode()); + createSubscriptions.add(imagesList.loadImages(imageView, trackable.getImages(), false)); + } + @Override protected String getTitle(final Page page) { return res.getString(page.resId); @@ -357,9 +357,12 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi protected Pair<List<? extends Page>, Integer> getOrderedPages() { final List<Page> pages = new ArrayList<>(); pages.add(Page.DETAILS); - if (!trackable.getLogs().isEmpty()) { + if (CollectionUtils.isNotEmpty(trackable.getLogs())) { pages.add(Page.LOGS); } + if (CollectionUtils.isNotEmpty(trackable.getImages())) { + pages.add(Page.IMAGES); + } return new ImmutablePair<List<? extends Page>, Integer>(pages, 0); } @@ -382,16 +385,14 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi // action bar icon if (StringUtils.isNotBlank(trackable.getIconUrl())) { - final TrackableIconHandler iconHandler = new TrackableIconHandler(getSupportActionBar()); - final TrackableIconThread iconThread = new TrackableIconThread(trackable.getIconUrl(), iconHandler); - iconThread.start(); + setupIcon(getSupportActionBar(), trackable.getIconUrl()); } // trackable name - addContextMenu(details.add(R.string.trackable_name, StringUtils.isNotBlank(trackable.getName()) ? Html.fromHtml(trackable.getName()).toString() : res.getString(R.string.trackable_unknown))); + addContextMenu(details.add(R.string.trackable_name, StringUtils.isNotBlank(trackable.getName()) ? Html.fromHtml(trackable.getName()).toString() : res.getString(R.string.trackable_unknown)).right); // trackable type - String tbType; + final String tbType; if (StringUtils.isNotBlank(trackable.getType())) { tbType = Html.fromHtml(trackable.getType()).toString(); } else { @@ -400,10 +401,10 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi details.add(R.string.trackable_type, tbType); // trackable geocode - addContextMenu(details.add(R.string.trackable_code, trackable.getGeocode())); + addContextMenu(details.add(R.string.trackable_code, trackable.getGeocode()).right); // trackable owner - final TextView owner = details.add(R.string.trackable_owner, res.getString(R.string.trackable_unknown)); + final TextView owner = details.add(R.string.trackable_owner, res.getString(R.string.trackable_unknown)).right; if (StringUtils.isNotBlank(trackable.getOwner())) { owner.setText(Html.fromHtml(trackable.getOwner()), TextView.BufferType.SPANNABLE); owner.setOnClickListener(new UserActionsClickListener(trackable)); @@ -413,34 +414,39 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi if (StringUtils.isNotBlank(trackable.getSpottedName()) || trackable.getSpottedType() == Trackable.SPOTTED_UNKNOWN || trackable.getSpottedType() == Trackable.SPOTTED_OWNER) { + + final StringBuilder text; boolean showTimeSpan = true; - StringBuilder text; - - if (trackable.getSpottedType() == Trackable.SPOTTED_CACHE) { - text = new StringBuilder(res.getString(R.string.trackable_spotted_in_cache) + ' ' + Html.fromHtml(trackable.getSpottedName()).toString()); - } else if (trackable.getSpottedType() == Trackable.SPOTTED_USER) { - text = new StringBuilder(res.getString(R.string.trackable_spotted_at_user) + ' ' + Html.fromHtml(trackable.getSpottedName()).toString()); - } else if (trackable.getSpottedType() == Trackable.SPOTTED_UNKNOWN) { - text = new StringBuilder(res.getString(R.string.trackable_spotted_unknown_location)); - } else if (trackable.getSpottedType() == Trackable.SPOTTED_OWNER) { - text = new StringBuilder(res.getString(R.string.trackable_spotted_owner)); - } else { - text = new StringBuilder("N/A"); - showTimeSpan = false; + switch (trackable.getSpottedType()) { + case Trackable.SPOTTED_CACHE: + text = new StringBuilder(res.getString(R.string.trackable_spotted_in_cache) + ' ' + Html.fromHtml(trackable.getSpottedName()).toString()); + break; + case Trackable.SPOTTED_USER: + text = new StringBuilder(res.getString(R.string.trackable_spotted_at_user) + ' ' + Html.fromHtml(trackable.getSpottedName()).toString()); + break; + case Trackable.SPOTTED_UNKNOWN: + text = new StringBuilder(res.getString(R.string.trackable_spotted_unknown_location)); + break; + case Trackable.SPOTTED_OWNER: + text = new StringBuilder(res.getString(R.string.trackable_spotted_owner)); + break; + default: + text = new StringBuilder("N/A"); + showTimeSpan = false; + break; } // days since last spotting - if (showTimeSpan && trackable.getLogs() != null) { + if (showTimeSpan) { for (final LogEntry log : trackable.getLogs()) { if (log.type == LogType.RETRIEVED_IT || log.type == LogType.GRABBED_IT || log.type == LogType.DISCOVERED_IT || log.type == LogType.PLACED_IT) { - final int days = log.daysSinceLog(); - text.append(" (").append(res.getQuantityString(R.plurals.days_ago, days, days)).append(')'); + text.append(" (").append(Formatter.formatDaysAgo(log.date)).append(')'); break; } } } - final TextView spotted = details.add(R.string.trackable_spotted, text.toString()); + final TextView spotted = details.add(R.string.trackable_spotted, text.toString()).right; spotted.setClickable(true); if (Trackable.SPOTTED_CACHE == trackable.getSpottedType()) { spotted.setOnClickListener(new View.OnClickListener() { @@ -467,19 +473,19 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi // trackable origin if (StringUtils.isNotBlank(trackable.getOrigin())) { - final TextView origin = details.add(R.string.trackable_origin, ""); + final TextView origin = details.add(R.string.trackable_origin, "").right; origin.setText(Html.fromHtml(trackable.getOrigin()), TextView.BufferType.SPANNABLE); addContextMenu(origin); } // trackable released if (trackable.getReleased() != null) { - addContextMenu(details.add(R.string.trackable_released, Formatter.formatDate(trackable.getReleased().getTime()))); + addContextMenu(details.add(R.string.trackable_released, Formatter.formatDate(trackable.getReleased().getTime())).right); } // trackable distance if (trackable.getDistance() >= 0) { - addContextMenu(details.add(R.string.trackable_distance, Units.getDistanceFromKilometers(trackable.getDistance()))); + addContextMenu(details.add(R.string.trackable_distance, Units.getDistanceFromKilometers(trackable.getDistance())).right); } // trackable goal @@ -507,14 +513,14 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi trackableImage.setImageResource(R.drawable.image_not_loaded); trackableImage.setClickable(true); - ViewObservable.clicks(trackableImage, false).subscribe(new Action1<View>() { + ViewObservable.clicks(trackableImage, false).subscribe(new Action1<OnClickEvent>() { @Override - public void call(final View view) { + public void call(final OnClickEvent onClickEvent) { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(trackable.getImage()))); } }); - AndroidObservable.bindActivity(TrackableActivity.this, new HtmlImage(geocode, true, 0, false).fetchDrawable(trackable.getImage())).subscribe(new Action1<BitmapDrawable>() { + AppObservable.bindActivity(TrackableActivity.this, new HtmlImage(geocode, true, 0, false).fetchDrawable(trackable.getImage())).subscribe(new Action1<BitmapDrawable>() { @Override public void call(final BitmapDrawable bitmapDrawable) { trackableImage.setImageDrawable(bitmapDrawable); @@ -555,6 +561,10 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override public boolean onPrepareActionMode(final ActionMode actionMode, final Menu menu) { + return prepareClipboardActionMode(view, actionMode, menu); + } + + private boolean prepareClipboardActionMode(final View view, final ActionMode actionMode, final Menu menu) { final int viewId = view.getId(); assert view instanceof TextView; clickedItemText = ((TextView) view).getText(); @@ -584,6 +594,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override public boolean onCreateActionMode(final ActionMode actionMode, final Menu menu) { actionMode.getMenuInflater().inflate(R.menu.details_context, menu); + prepareClipboardActionMode(view, actionMode, menu); return true; } @@ -595,4 +606,21 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi return false; } + @Override + protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { + // Refresh the logs view after coming back from logging a trackable + if (requestCode == LogTrackableActivity.LOG_TRACKABLE && resultCode == RESULT_OK) { + refreshTrackable(StringUtils.defaultIfBlank(trackable.getName(), trackable.getGeocode())); + } + } + + @Override + protected void onDestroy() { + createSubscriptions.unsubscribe(); + super.onDestroy(); + } + + public Trackable getTrackable() { + return trackable; + } } |