diff options
author | SammysHP <sven@sammyshp.de> | 2011-11-23 16:03:44 +0100 |
---|---|---|
committer | SammysHP <sven@sammyshp.de> | 2011-11-23 16:03:44 +0100 |
commit | b4eaf66670760969f72b1244f298654484ee5731 (patch) | |
tree | aa345cd6acada40c3af70871f61a07b3542dd610 /main | |
parent | df4bf84d783744790b1aa2975e8373261eb6ca06 (diff) | |
download | cgeo-b4eaf66670760969f72b1244f298654484ee5731.zip cgeo-b4eaf66670760969f72b1244f298654484ee5731.tar.gz cgeo-b4eaf66670760969f72b1244f298654484ee5731.tar.bz2 |
New: Horizontal scrollable pages for cache-details using a ViewPager.
This moves most of the code from "cgeodetail" into a new class "CacheDetailActivity". Some important parts are rewritten and the code is much more structured.
This closes #83 "Redesign detail-view".
Some minor changes will be added later (e.g. removing old files, improve log-count-display)
Diffstat (limited to 'main')
27 files changed, 3094 insertions, 317 deletions
diff --git a/main/.classpath b/main/.classpath index db7b7b6..e8b6f86 100644 --- a/main/.classpath +++ b/main/.classpath @@ -7,5 +7,6 @@ <classpathentry kind="lib" path="libs/locus-api-4.0.jar"/> <classpathentry kind="lib" path="libs/mapsforge-map-0.2.4.jar"/> <classpathentry kind="lib" path="libs/commons-collections-3.2.1.jar"/> + <classpathentry kind="lib" path="libs/android-support-v4.jar"/> <classpathentry kind="output" path="bin/classes"/> </classpath> diff --git a/main/AndroidManifest.xml b/main/AndroidManifest.xml index 91626cc..269b90e 100644 --- a/main/AndroidManifest.xml +++ b/main/AndroidManifest.xml @@ -213,7 +213,7 @@ </intent-filter> </activity> <activity - android:name=".cgeodetail" + android:name=".CacheDetailActivity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation" > <intent-filter> diff --git a/main/libs/android-support-v4.jar b/main/libs/android-support-v4.jar Binary files differnew file mode 100644 index 0000000..b9a4279 --- /dev/null +++ b/main/libs/android-support-v4.jar diff --git a/main/proguard.cfg b/main/proguard.cfg index 02ab774..3b5251d 100644 --- a/main/proguard.cfg +++ b/main/proguard.cfg @@ -6,6 +6,10 @@ -dontwarn java.beans.* -dontwarn org.mapsforge.android.maps.Test* +-dontwarn CompatHoneycomb +-dontwarn **CompatCreatorHoneycombMR2 +-dontwarn **ActivityCompatHoneycomb +-dontwarn **MenuCompatHoneycomb -keep public class * extends android.app.Activity -keep public class * extends android.app.Application diff --git a/main/res/drawable-hdpi/pagerindicator_background.9.png b/main/res/drawable-hdpi/pagerindicator_background.9.png Binary files differnew file mode 100644 index 0000000..2474e5c --- /dev/null +++ b/main/res/drawable-hdpi/pagerindicator_background.9.png diff --git a/main/res/drawable-ldpi/pagerindicator_background.9.png b/main/res/drawable-ldpi/pagerindicator_background.9.png Binary files differnew file mode 100644 index 0000000..d9f91d6 --- /dev/null +++ b/main/res/drawable-ldpi/pagerindicator_background.9.png diff --git a/main/res/drawable/pagerindicator_background.9.png b/main/res/drawable/pagerindicator_background.9.png Binary files differnew file mode 100644 index 0000000..697675b --- /dev/null +++ b/main/res/drawable/pagerindicator_background.9.png diff --git a/main/res/layout/cacheview.xml b/main/res/layout/cacheview.xml new file mode 100644 index 0000000..05df377 --- /dev/null +++ b/main/res/layout/cacheview.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="?background_color"
+ android:orientation="vertical" >
+
+ <LinearLayout style="@style/action_bar" >
+
+ <ImageView
+ style="@style/action_bar_action"
+ android:onClick="goHome" />
+
+ <View style="@style/action_bar_separator" />
+
+ <TextView style="@style/action_bar_title" />
+
+ <View style="@style/action_bar_separator" />
+
+ <ImageView
+ style="@style/action_bar_action"
+ android:onClick="startCompassNavigation"
+ android:src="@drawable/actionbar_compass" />
+
+ <View style="@style/action_bar_separator" />
+
+ <ImageView
+ style="@style/action_bar_action"
+ android:onClick="goManual"
+ android:src="@drawable/actionbar_manual" />
+ </LinearLayout>
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/viewpager"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <RelativeLayout style="@style/pager_indicator" >
+
+ <TextView
+ android:id="@+id/indicator_prev"
+ style="@style/pager_indicator.side"
+ android:layout_alignParentLeft="true" />
+
+ <TextView
+ android:id="@+id/indicator_current"
+ style="@style/pager_indicator.title" />
+
+ <TextView
+ android:id="@+id/indicator_next"
+ style="@style/pager_indicator.side"
+ android:layout_alignParentRight="true" />
+ </RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file diff --git a/main/res/layout/cacheview_description.xml b/main/res/layout/cacheview_description.xml new file mode 100644 index 0000000..12abe7e --- /dev/null +++ b/main/res/layout/cacheview_description.xml @@ -0,0 +1,160 @@ +<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:padding="4dip" >
+
+ <!-- Short description -->
+
+ <TextView
+ android:id="@+id/shortdesc"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:linksClickable="true"
+ android:textColor="?text_color"
+ android:textColorLink="?text_color_link"
+ android:textSize="14dip"
+ android:visibility="gone" />
+
+ <!-- Long description -->
+
+ <TextView
+ android:id="@+id/longdesc"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:linksClickable="true"
+ android:textColor="?text_color"
+ android:textColorLink="?text_color_link"
+ android:textSize="14dip"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/show_description"
+ style="@style/button"
+ android:text="@string/cache_description_long"
+ android:visibility="gone" />
+
+ <RelativeLayout
+ android:id="@+id/loading"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" >
+
+ <ProgressBar
+ style="@android:style/Widget.ProgressBar.Large.Inverse"
+ android:layout_width="76dip"
+ android:layout_height="76dip"
+ android:layout_centerInParent="true"
+ android:gravity="center"
+ android:indeterminate="true" />
+ </RelativeLayout>
+
+ <!-- Attributes box -->
+
+ <LinearLayout
+ android:id="@+id/attributes_box"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone" >
+
+ <RelativeLayout style="@style/separator_horizontal_layout" >
+
+ <View style="@style/separator_horizontal" />
+
+ <TextView
+ style="@style/separator_horizontal_headline"
+ android:text="@string/cache_attributes" />
+ </RelativeLayout>
+ <!-- innerbox is only needed to define the paddings easily -->
+
+ <LinearLayout
+ android:id="@+id/attributes_innerbox"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp" />
+ </LinearLayout>
+
+ <!-- Hint and spoiler-images box -->
+
+ <LinearLayout
+ android:id="@+id/hint_box"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:orientation="vertical"
+ android:visibility="gone" >
+
+ <RelativeLayout style="@style/separator_horizontal_layout" >
+
+ <View style="@style/separator_horizontal" />
+
+ <TextView
+ style="@style/separator_horizontal_headline"
+ android:text="@string/cache_hint" />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/hint"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:linksClickable="true"
+ android:textColor="?text_color"
+ android:textColorLink="?text_color_link"
+ android:textSize="14dip" />
+
+ <TextView
+ android:id="@+id/hint_spoilerlink"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:layout_marginTop="6dip"
+ android:drawableLeft="?log_img_icon"
+ android:drawablePadding="3dip"
+ android:text="@string/cache_menu_spoilers"
+ android:textColor="?text_color"
+ android:textSize="14dip" />
+ </LinearLayout>
+
+ <!-- Personal note box -->
+
+ <LinearLayout
+ android:id="@+id/personalnote_box"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:orientation="vertical"
+ android:visibility="gone" >
+
+ <RelativeLayout style="@style/separator_horizontal_layout" >
+
+ <View style="@style/separator_horizontal" />
+
+ <TextView
+ style="@style/separator_horizontal_headline"
+ android:text="@string/cache_personal_note" />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/personalnote"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:linksClickable="true"
+ android:textColor="?text_color"
+ android:textColorLink="?text_color_link"
+ android:textSize="14dip"
+ android:visibility="gone" />
+ </LinearLayout>
+ </LinearLayout>
+
+</ScrollView>
\ No newline at end of file diff --git a/main/res/layout/cacheview_details.xml b/main/res/layout/cacheview_details.xml new file mode 100644 index 0000000..45d067e --- /dev/null +++ b/main/res/layout/cacheview_details.xml @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:scrollbars="none" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:padding="4dip" >
+
+ <!-- Map preview -->
+
+ <ImageView
+ android:id="@+id/map_preview"
+ android:layout_width="fill_parent"
+ android:layout_height="80dip"
+ android:layout_gravity="center"
+ android:layout_marginBottom="5dip"
+ android:gravity="center"
+ android:scaleType="centerCrop"
+ android:src="@null"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:id="@+id/details_list"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+ </LinearLayout>
+
+ <!-- Offline box -->
+
+ <LinearLayout
+ android:id="@+id/offline_box"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <RelativeLayout style="@style/separator_horizontal_layout" >
+
+ <View style="@style/separator_horizontal" />
+
+ <TextView
+ style="@style/separator_horizontal_headline"
+ android:text="@string/cache_offline" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/offline_text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_gravity="left"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="130dip"
+ android:paddingRight="3dip"
+ android:textColor="?text_color"
+ android:textSize="14dip" />
+
+ <Button
+ android:id="@+id/offline_refresh"
+ style="@style/button_small"
+ android:layout_width="60dip"
+ android:layout_alignParentRight="true"
+ android:layout_marginRight="71dip"
+ android:text="@string/cache_offline_refresh"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/offline_store"
+ style="@style/button_small"
+ android:layout_width="60dip"
+ android:layout_alignParentRight="true"
+ android:text="@string/cache_offline_store" />
+ </RelativeLayout>
+ </LinearLayout>
+
+ <!-- Watchlist box -->
+
+ <LinearLayout
+ android:id="@+id/watchlist_box"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <RelativeLayout style="@style/separator_horizontal_layout" >
+
+ <View style="@style/separator_horizontal" />
+
+ <TextView
+ style="@style/separator_horizontal_headline"
+ android:text="@string/cache_watchlist" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/watchlist_text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_gravity="left"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="130dip"
+ android:paddingRight="3dip"
+ android:textColor="?text_color"
+ android:textSize="14dip" />
+
+ <Button
+ android:id="@+id/add_to_watchlist"
+ style="@style/button_small"
+ android:layout_alignParentRight="true"
+ android:text="@string/cache_watchlist_add"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/remove_from_watchlist"
+ style="@style/button_small"
+ android:layout_alignParentRight="true"
+ android:text="@string/cache_watchlist_remove"
+ android:visibility="gone" />
+ </RelativeLayout>
+ </LinearLayout>
+
+ <!-- License Box -->
+
+ <LinearLayout
+ android:id="@+id/license_box"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone" >
+
+ <RelativeLayout style="@style/separator_horizontal_layout" >
+
+ <View style="@style/separator_horizontal" />
+
+ <TextView
+ style="@style/separator_horizontal_headline"
+ android:text="@string/cache_license" />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/license"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:linksClickable="true"
+ android:padding="3dip"
+ android:textColor="?text_color"
+ android:textColorLink="?text_color_link"
+ android:textSize="14dip" />
+ </LinearLayout>
+ </LinearLayout>
+
+</ScrollView>
\ No newline at end of file diff --git a/main/res/layout/cacheview_inventory.xml b/main/res/layout/cacheview_inventory.xml new file mode 100644 index 0000000..9effe8c --- /dev/null +++ b/main/res/layout/cacheview_inventory.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?>
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
</ListView>
\ No newline at end of file diff --git a/main/res/layout/cacheview_logs.xml b/main/res/layout/cacheview_logs.xml new file mode 100644 index 0000000..d7babb9 --- /dev/null +++ b/main/res/layout/cacheview_logs.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:padding="4dip" >
+
+ <TextView
+ android:id="@+id/log_count"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:textColor="?text_color"
+ android:textSize="14dip" />
+
+ <LinearLayout
+ android:id="@+id/log_list"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+ </LinearLayout>
+ </LinearLayout>
+
+</ScrollView>
\ No newline at end of file diff --git a/main/res/layout/cacheview_waypoints.xml b/main/res/layout/cacheview_waypoints.xml new file mode 100644 index 0000000..35e4948 --- /dev/null +++ b/main/res/layout/cacheview_waypoints.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:padding="4dip" >
+
+ <LinearLayout
+ android:id="@+id/waypoints"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/add_waypoint"
+ style="@style/button"
+ android:text="@string/cache_waypoints_add" />
+ </LinearLayout>
+
+</ScrollView>
\ No newline at end of file diff --git a/main/res/values/attrs.xml b/main/res/values/attrs.xml index 187d4cb..e6d7bba 100644 --- a/main/res/values/attrs.xml +++ b/main/res/values/attrs.xml @@ -17,6 +17,7 @@ <attr name="background_color_notice" format="color" /> <attr name="background_color_transparent" format="color" /> <attr name="separator_color" format="color" /> + <attr name="text_color_pagerindicator" format="color" /> <!-- drawables --> <attr name="button" format="integer" /> diff --git a/main/res/values/colors.xml b/main/res/values/colors.xml index 169f319..91fb6a5 100644 --- a/main/res/values/colors.xml +++ b/main/res/values/colors.xml @@ -1,27 +1,31 @@ <?xml version="1.0" encoding="UTF-8"?> <resources> - <color name="just_white">#FFFFFFFF</color> - <color name="just_black">#FF000000</color> - <color name="background_dark">#FF000000</color> - <color name="background_light">#FFFFFFFF</color> - <color name="background_dark_notice">#FF191919</color> - <color name="background_light_notice">#FFDFDFDF</color> - <color name="background_dark_transparent">#44000000</color> - <color name="background_light_transparent">#44FFFFFF</color> - <color name="owncache_background_dark">#FF222222</color> - <color name="owncache_background_light">#FFDDDDDD</color> - <color name="separator_dark">#44FFFFFF</color> - <color name="separator_light">#44000000</color> - <color name="text_icon">#FFFFFFFF</color> - <color name="text_dark">#FFFFFFFF</color> - <color name="text_light">#FF000000</color> - <color name="text_headline_dark">#88FFFFFF</color> - <color name="text_headline_light">#88000000</color> - <color name="text_grey_dark">#AAFFFFFF</color> - <color name="text_grey_light">#AA000000</color> - <color name="text_hint_dark">#44FFFFFF</color> - <color name="text_hint_light">#44000000</color> - <color name="link">#FF00C0FF</color> - <color name="button_enabled">#FF000000</color> - <color name="button_disabled">#66000000</color> -</resources> + + <color name="just_white">#FFFFFFFF</color> + <color name="just_black">#FF000000</color> + <color name="background_dark">#FF000000</color> + <color name="background_light">#FFFFFFFF</color> + <color name="background_dark_notice">#FF191919</color> + <color name="background_light_notice">#FFDFDFDF</color> + <color name="background_dark_transparent">#44000000</color> + <color name="background_light_transparent">#44FFFFFF</color> + <color name="owncache_background_dark">#FF222222</color> + <color name="owncache_background_light">#FFDDDDDD</color> + <color name="separator_dark">#44FFFFFF</color> + <color name="separator_light">#44000000</color> + <color name="text_icon">#FFFFFFFF</color> + <color name="text_dark">#FFFFFFFF</color> + <color name="text_light">#FF000000</color> + <color name="text_headline_dark">#88FFFFFF</color> + <color name="text_headline_light">#88000000</color> + <color name="text_grey_dark">#AAFFFFFF</color> + <color name="text_grey_light">#AA000000</color> + <color name="text_hint_dark">#44FFFFFF</color> + <color name="text_hint_light">#44000000</color> + <color name="link">#FF00C0FF</color> + <color name="button_enabled">#FF000000</color> + <color name="button_disabled">#66000000</color> + <color name="text_pagerindicator">#FFFFFFFF</color> + <color name="text_pagerindicator_sides">#88FFFFFF</color> + +</resources>
\ No newline at end of file diff --git a/main/res/values/styles.xml b/main/res/values/styles.xml index 06c5c43..58b404f 100644 --- a/main/res/values/styles.xml +++ b/main/res/values/styles.xml @@ -1,305 +1,340 @@ <?xml version="1.0" encoding="utf-8"?> <resources> -<!-- system definitions --> - <style name="cgeo" parent="android:Theme.NoTitleBar"> - </style> - <style name="button" parent="@android:style/Widget.Button"> - <item name="android:padding">6dip</item> - <item name="android:lines">1</item> - <item name="android:singleLine">true</item> - <item name="android:scrollHorizontally">true</item> - <item name="android:ellipsize">marquee</item> - <item name="android:textSize">22dip</item> - <item name="android:textColor">?text_color</item> - <item name="android:background">?button</item> - <item name="android:focusable">true</item> - <item name="android:clickable">true</item> - <item name="android:gravity">center</item> - </style> + <!-- system definitions --> + <style name="cgeo" parent="android:Theme.NoTitleBar"></style> - <style name="edittext" parent="@android:style/Widget.EditText"> - <item name="android:padding">6dip</item> - <item name="android:singleLine">true</item> - <item name="android:autoText">true</item> - <item name="android:textSize">22dip</item> - <item name="android:textColor">?text_color</item> - <item name="android:textColorHint">?text_color_hint</item> - <item name="android:background">?input</item> - <item name="android:focusable">true</item> - <item name="android:gravity">top|left</item> - <item name="android:capitalize">none</item> - </style> + <style name="button" parent="@android:style/Widget.Button"> + <item name="android:padding">6dip</item> + <item name="android:lines">1</item> + <item name="android:singleLine">true</item> + <item name="android:scrollHorizontally">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">22dip</item> + <item name="android:textColor">?text_color</item> + <item name="android:background">?button</item> + <item name="android:focusable">true</item> + <item name="android:clickable">true</item> + <item name="android:gravity">center</item> + </style> -<!-- own definitions --> -<!-- actionbar --> - <style name="action_bar"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">@dimen/actionbar_height</item> - <item name="android:orientation">horizontal</item> - <item name="android:background">@drawable/actionbar_background</item> - <item name="android:gravity">center</item> - </style> + <style name="edittext" parent="@android:style/Widget.EditText"> + <item name="android:padding">6dip</item> + <item name="android:singleLine">true</item> + <item name="android:autoText">true</item> + <item name="android:textSize">22dip</item> + <item name="android:textColor">?text_color</item> + <item name="android:textColorHint">?text_color_hint</item> + <item name="android:background">?input</item> + <item name="android:focusable">true</item> + <item name="android:gravity">top|left</item> + <item name="android:capitalize">none</item> + </style> - <style name="action_bar_icon_cgeo"> - <item name="android:layout_width">@dimen/actionbar_height</item> - <item name="android:layout_height">@dimen/actionbar_height</item> - <item name="android:padding">4dip</item> - <item name="android:scaleType">center</item> - <item name="android:focusable">true</item> - <item name="android:src">@drawable/actionbar_cgeo</item> - <item name="android:background">@drawable/actionbar_button</item> - </style> + <!-- own definitions --> + <!-- actionbar --> + <style name="action_bar"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">@dimen/actionbar_height</item> + <item name="android:orientation">horizontal</item> + <item name="android:background">@drawable/actionbar_background</item> + <item name="android:gravity">center</item> + </style> - <style name="action_bar_action"> - <item name="android:layout_width">@dimen/actionbar_height</item> - <item name="android:layout_height">@dimen/actionbar_height</item> - <item name="android:padding">2dip</item> - <item name="android:scaleType">center</item> - <item name="android:focusable">true</item> - <item name="android:src">@drawable/actionbar_home</item> - <item name="android:background">@drawable/actionbar_button</item> - </style> + <style name="action_bar_icon_cgeo"> + <item name="android:layout_width">@dimen/actionbar_height</item> + <item name="android:layout_height">@dimen/actionbar_height</item> + <item name="android:padding">4dip</item> + <item name="android:scaleType">center</item> + <item name="android:focusable">true</item> + <item name="android:src">@drawable/actionbar_cgeo</item> + <item name="android:background">@drawable/actionbar_button</item> + </style> - <style name="action_bar_separator"> - <item name="android:layout_width">@dimen/actionbar_separator_width</item> - <item name="android:layout_height">@dimen/actionbar_separator_height</item> - <item name="android:background">@drawable/actionbar_separator</item> - </style> + <style name="action_bar_action"> + <item name="android:layout_width">@dimen/actionbar_height</item> + <item name="android:layout_height">@dimen/actionbar_height</item> + <item name="android:padding">2dip</item> + <item name="android:scaleType">center</item> + <item name="android:focusable">true</item> + <item name="android:src">@drawable/actionbar_home</item> + <item name="android:background">@drawable/actionbar_button</item> + </style> - <style name="action_bar_progress" parent="@android:style/Widget.ProgressBar.Small"> - <item name="android:id">@id/actionbar_progress</item> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginLeft">15dip</item> - <item name="android:layout_marginRight">15dip</item> - <item name="android:indeterminate">true</item> - </style> + <style name="action_bar_separator"> + <item name="android:layout_width">@dimen/actionbar_separator_width</item> + <item name="android:layout_height">@dimen/actionbar_separator_height</item> + <item name="android:background">@drawable/actionbar_separator</item> + </style> - <style name="action_bar_title"> - <item name="android:id">@id/actionbar_title</item> - <item name="android:layout_width">0dip</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_weight">1</item> - <item name="android:paddingLeft">6dip</item> - <item name="android:singleLine">true</item> - <item name="android:scrollHorizontally">true</item> - <item name="android:ellipsize">marquee</item> - <item name="android:lines">1</item> - <item name="android:textSize">20dip</item> - <item name="android:textColor">@color/just_white</item> - <item name="android:text">c:geo</item> - </style> + <style name="action_bar_progress" parent="@android:style/Widget.ProgressBar.Small"> + <item name="android:id">@id/actionbar_progress</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginLeft">15dip</item> + <item name="android:layout_marginRight">15dip</item> + <item name="android:indeterminate">true</item> + </style> -<!-- button: full width --> - <style name="button"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginLeft">10dip</item> - <item name="android:layout_marginRight">10dip</item> - <item name="android:layout_marginBottom">5dip</item> - </style> + <style name="action_bar_title"> + <item name="android:id">@id/actionbar_title</item> + <item name="android:layout_width">0dip</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_weight">1</item> + <item name="android:paddingLeft">6dip</item> + <item name="android:singleLine">true</item> + <item name="android:scrollHorizontally">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:lines">1</item> + <item name="android:textSize">20dip</item> + <item name="android:textColor">@color/just_white</item> + <item name="android:text">c:geo</item> + </style> -<!-- button: small --> - <style name="button_small"> - <item name="android:layout_width">125dip</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_gravity">right</item> - <item name="android:layout_marginLeft">6dip</item> - <item name="android:layout_marginRight">6dip</item> - <item name="android:layout_marginTop">3dip</item> - <item name="android:layout_marginBottom">3dip</item> - <item name="android:padding">3dip</item> - <item name="android:textSize">14dip</item> - </style> + <!-- button: full width --> + <style name="button"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginLeft">10dip</item> + <item name="android:layout_marginRight">10dip</item> + <item name="android:layout_marginBottom">5dip</item> + </style> -<!-- edittext --> - <style name="edittext"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginLeft">10dip</item> - <item name="android:layout_marginRight">10dip</item> - <item name="android:layout_marginBottom">5dip</item> - </style> + <!-- button: small --> + <style name="button_small"> + <item name="android:layout_width">125dip</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">right</item> + <item name="android:layout_marginLeft">6dip</item> + <item name="android:layout_marginRight">6dip</item> + <item name="android:layout_marginTop">3dip</item> + <item name="android:layout_marginBottom">3dip</item> + <item name="android:padding">3dip</item> + <item name="android:textSize">14dip</item> + </style> - <style name="edittext_dialog"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginLeft">10dip</item> - <item name="android:layout_marginRight">10dip</item> - <item name="android:layout_marginBottom">5dip</item> - <item name="android:textColor">@color/text_dark</item> - <item name="android:textColorHint">@color/text_hint_dark</item> - <item name="android:background">@drawable/input_bcg_dark</item> - </style> + <!-- edittext --> + <style name="edittext"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginLeft">10dip</item> + <item name="android:layout_marginRight">10dip</item> + <item name="android:layout_marginBottom">5dip</item> + </style> -<!-- mainscreen icon --> - <style name="icon_mainscreen"> - <item name="android:layout_width">48dip</item> - <item name="android:layout_height">48dip</item> - <item name="android:layout_gravity">center_horizontal</item> - <item name="android:gravity">center</item> - <item name="android:layout_margin">4dip</item> - <item name="android:focusable">true</item> - </style> + <style name="edittext_dialog"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginLeft">10dip</item> + <item name="android:layout_marginRight">10dip</item> + <item name="android:layout_marginBottom">5dip</item> + <item name="android:textColor">@color/text_dark</item> + <item name="android:textColorHint">@color/text_hint_dark</item> + <item name="android:background">@drawable/input_bcg_dark</item> + </style> - <style name="icon_mainscreen_text"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_gravity">center_horizontal</item> - <item name="android:gravity">center</item> - <item name="android:paddingTop">1dip</item> - <item name="android:paddingBottom">1dip</item> - <item name="android:paddingLeft">5dip</item> - <item name="android:paddingRight">5dip</item> - <item name="android:singleLine">true</item> - <item name="android:lines">1</item> - <item name="android:ellipsize">marquee</item> - <item name="android:textSize">13dip</item> - <item name="android:textColor">@color/text_icon</item> - <item name="android:background">@drawable/icon_bcg</item> - </style> + <!-- mainscreen icon --> + <style name="icon_mainscreen"> + <item name="android:layout_width">48dip</item> + <item name="android:layout_height">48dip</item> + <item name="android:layout_gravity">center_horizontal</item> + <item name="android:gravity">center</item> + <item name="android:layout_margin">4dip</item> + <item name="android:focusable">true</item> + </style> - <style name="icon_mainscreen_count"> - <item name="android:visibility">gone</item> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_gravity">center_horizontal</item> - <item name="android:layout_alignParentTop">true</item> - <item name="android:layout_alignParentRight">true</item> - <item name="android:layout_marginRight">4dip</item> - <item name="android:layout_marginLeft">4dip</item> - <item name="android:gravity">center</item> - <item name="android:paddingTop">2dip</item> - <item name="android:paddingBottom">2dip</item> - <item name="android:paddingLeft">5dip</item> - <item name="android:paddingRight">5dip</item> - <item name="android:singleLine">true</item> - <item name="android:lines">1</item> - <item name="android:ellipsize">marquee</item> - <item name="android:textSize">11dip</item> - <item name="android:textColor">@color/just_white</item> - <item name="android:background">@drawable/count_bcg</item> - <item name="android:text"></item> - </style> + <style name="icon_mainscreen_text"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_horizontal</item> + <item name="android:gravity">center</item> + <item name="android:paddingTop">1dip</item> + <item name="android:paddingBottom">1dip</item> + <item name="android:paddingLeft">5dip</item> + <item name="android:paddingRight">5dip</item> + <item name="android:singleLine">true</item> + <item name="android:lines">1</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">13dip</item> + <item name="android:textColor">@color/text_icon</item> + <item name="android:background">@drawable/icon_bcg</item> + </style> -<!-- current location --> - <style name="location_current"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:gravity">center_horizontal</item> - <item name="android:paddingLeft">5dip</item> - <item name="android:paddingRight">5dip</item> - <item name="android:lines">1</item> - <item name="android:singleLine">true</item> - <item name="android:scrollHorizontally">true</item> - <item name="android:ellipsize">marquee</item> - <item name="android:textSize">14dip</item> - <item name="android:textColor">@color/text_icon</item> - <item name="android:background">@drawable/icon_bcg</item> - </style> + <style name="icon_mainscreen_count"> + <item name="android:visibility">gone</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_horizontal</item> + <item name="android:layout_alignParentTop">true</item> + <item name="android:layout_alignParentRight">true</item> + <item name="android:layout_marginRight">4dip</item> + <item name="android:layout_marginLeft">4dip</item> + <item name="android:gravity">center</item> + <item name="android:paddingTop">2dip</item> + <item name="android:paddingBottom">2dip</item> + <item name="android:paddingLeft">5dip</item> + <item name="android:paddingRight">5dip</item> + <item name="android:singleLine">true</item> + <item name="android:lines">1</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">11dip</item> + <item name="android:textColor">@color/just_white</item> + <item name="android:background">@drawable/count_bcg</item> + <item name="android:text"></item> + </style> - <style name="location_current_type"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_alignParentBottom">true</item> - <item name="android:layout_alignParentRight">true</item> - <item name="android:layout_gravity">right</item> - <item name="android:paddingLeft">3dip</item> - <item name="android:paddingRight">3dip</item> - <item name="android:lines">1</item> - <item name="android:singleLine">true</item> - <item name="android:scrollHorizontally">true</item> - <item name="android:ellipsize">marquee</item> - <item name="android:textSize">12dip</item> - <item name="android:textColor">@color/text_dark</item> - </style> + <!-- current location --> + <style name="location_current"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:gravity">center_horizontal</item> + <item name="android:paddingLeft">5dip</item> + <item name="android:paddingRight">5dip</item> + <item name="android:lines">1</item> + <item name="android:singleLine">true</item> + <item name="android:scrollHorizontally">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">14dip</item> + <item name="android:textColor">@color/text_icon</item> + <item name="android:background">@drawable/icon_bcg</item> + </style> - <style name="location_current_accuracy"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_alignParentBottom">true</item> - <item name="android:layout_centerHorizontal">true</item> - <item name="android:layout_gravity">right</item> - <item name="android:paddingLeft">3dip</item> - <item name="android:paddingRight">3dip</item> - <item name="android:lines">1</item> - <item name="android:singleLine">true</item> - <item name="android:scrollHorizontally">true</item> - <item name="android:ellipsize">marquee</item> - <item name="android:textSize">12dip</item> - <item name="android:textColor">@color/text_dark</item> - </style> + <style name="location_current_type"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentBottom">true</item> + <item name="android:layout_alignParentRight">true</item> + <item name="android:layout_gravity">right</item> + <item name="android:paddingLeft">3dip</item> + <item name="android:paddingRight">3dip</item> + <item name="android:lines">1</item> + <item name="android:singleLine">true</item> + <item name="android:scrollHorizontally">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">12dip</item> + <item name="android:textColor">@color/text_dark</item> + </style> - <style name="location_current_satellites"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_alignParentBottom">true</item> - <item name="android:layout_alignParentLeft">true</item> - <item name="android:layout_gravity">left</item> - <item name="android:paddingLeft">3dip</item> - <item name="android:paddingRight">3dip</item> - <item name="android:lines">1</item> - <item name="android:singleLine">true</item> - <item name="android:scrollHorizontally">true</item> - <item name="android:ellipsize">marquee</item> - <item name="android:textSize">12dip</item> - <item name="android:textColor">@color/text_dark</item> - </style> + <style name="location_current_accuracy"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentBottom">true</item> + <item name="android:layout_centerHorizontal">true</item> + <item name="android:layout_gravity">right</item> + <item name="android:paddingLeft">3dip</item> + <item name="android:paddingRight">3dip</item> + <item name="android:lines">1</item> + <item name="android:singleLine">true</item> + <item name="android:scrollHorizontally">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">12dip</item> + <item name="android:textColor">@color/text_dark</item> + </style> -<!-- separators --> - <style name="separator_horizontal"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">1dip</item> - <item name="android:layout_centerInParent">true</item> - <item name="android:background">?separator_color</item> - </style> + <style name="location_current_satellites"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentBottom">true</item> + <item name="android:layout_alignParentLeft">true</item> + <item name="android:layout_gravity">left</item> + <item name="android:paddingLeft">3dip</item> + <item name="android:paddingRight">3dip</item> + <item name="android:lines">1</item> + <item name="android:singleLine">true</item> + <item name="android:scrollHorizontally">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">12dip</item> + <item name="android:textColor">@color/text_dark</item> + </style> - <style name="separator_horizontal_layout"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginTop">2dip</item> - <item name="android:layout_marginBottom">2dip</item> - </style> + <!-- separators --> + <style name="separator_horizontal"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">1dip</item> + <item name="android:layout_centerInParent">true</item> + <item name="android:background">?separator_color</item> + </style> - <style name="separator_horizontal_headline"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_alignParentLeft">true</item> - <item name="android:layout_marginLeft">15dip</item> - <item name="android:padding">3dip</item> - <item name="android:lines">1</item> - <item name="android:singleLine">true</item> - <item name="android:scrollHorizontally">true</item> - <item name="android:ellipsize">marquee</item> - <item name="android:textSize">22dip</item> - <item name="android:textColor">?text_color_headline</item> - <item name="android:background">?background_color</item> - </style> - -<!-- filter bar --> - <style name="filter_bar"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:orientation">horizontal</item> - <item name="android:background">@drawable/filter_bar_background</item> - <item name="android:gravity">center_vertical</item> - </style> - - <style name="filter_bar_image"> - <item name="android:layout_width">30dp</item> - <item name="android:layout_height">30dp</item> - <item name="android:padding">6dp</item> - <item name="android:scaleType">fitCenter</item> - </style> - - <style name="filter_bar_text"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:singleLine">true</item> - <item name="android:scrollHorizontally">true</item> - <item name="android:ellipsize">marquee</item> - <item name="android:lines">1</item> - <item name="android:textColor">@android:color/white</item> - </style> -</resources> + <style name="separator_horizontal_layout"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginTop">2dip</item> + <item name="android:layout_marginBottom">2dip</item> + </style> + + <style name="separator_horizontal_headline"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_alignParentLeft">true</item> + <item name="android:layout_marginLeft">15dip</item> + <item name="android:padding">3dip</item> + <item name="android:lines">1</item> + <item name="android:singleLine">true</item> + <item name="android:scrollHorizontally">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:textSize">22dip</item> + <item name="android:textColor">?text_color_headline</item> + <item name="android:background">?background_color</item> + </style> + + <!-- filter bar --> + <style name="filter_bar"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:orientation">horizontal</item> + <item name="android:background">@drawable/filter_bar_background</item> + <item name="android:gravity">center_vertical</item> + </style> + + <style name="filter_bar_image"> + <item name="android:layout_width">30dp</item> + <item name="android:layout_height">30dp</item> + <item name="android:padding">6dp</item> + <item name="android:scaleType">fitCenter</item> + </style> + + <style name="filter_bar_text"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:singleLine">true</item> + <item name="android:scrollHorizontally">true</item> + <item name="android:ellipsize">marquee</item> + <item name="android:lines">1</item> + <item name="android:textColor">@android:color/white</item> + </style> + + <!-- pager --> + <style name="pager_indicator"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">@drawable/pagerindicator_background</item> + </style> + + <style name="pager_indicator.title"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_margin">5dip</item> + <item name="android:layout_centerInParent">true</item> + <item name="android:lines">1</item> + <item name="android:singleLine">true</item> + <item name="android:ellipsize">end</item> + <item name="android:textStyle">bold</item> + <item name="android:textSize">16dip</item> + <item name="android:textColor">@color/text_pagerindicator</item> + <item name="android:background">@null</item> + </style> + + <style name="pager_indicator.side"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_margin">1dip</item> + <item name="android:layout_centerVertical">true</item> + <item name="android:lines">1</item> + <item name="android:singleLine">true</item> + <item name="android:ellipsize">end</item> + <item name="android:textSize">12dip</item> + <item name="android:textColor">@color/text_pagerindicator_sides</item> + <item name="android:background">@null</item> + </style> + +</resources>
\ No newline at end of file diff --git a/main/res/values/themes.xml b/main/res/values/themes.xml index ba07eb2..8b8c914 100644 --- a/main/res/values/themes.xml +++ b/main/res/values/themes.xml @@ -31,6 +31,7 @@ <item name="text_color_grey">@color/text_grey_dark</item> <item name="text_color_hint">@color/text_hint_dark</item> <item name="text_color_link">@color/link</item> + <item name="text_color_pagerindicator">@color/text_pagerindicator</item> <item name="button_color_enabled">@color/button_enabled</item> <item name="button_color_disabled">@color/button_disabled</item> <item name="background_color">@color/background_dark</item> @@ -63,6 +64,7 @@ <item name="text_color_grey">@color/text_grey_light</item> <item name="text_color_hint">@color/text_hint_light</item> <item name="text_color_link">@color/link</item> + <item name="text_color_pagerindicator">@color/text_pagerindicator</item> <item name="button_color_enabled">@color/button_enabled</item> <item name="button_color_disabled">@color/button_disabled</item> <item name="background_color">@color/background_light</item> diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java new file mode 100644 index 0000000..242033f --- /dev/null +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -0,0 +1,2268 @@ +package cgeo.geocaching; + +import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.activity.Progress; +import cgeo.geocaching.apps.cache.GeneralAppsFactory; +import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; +import cgeo.geocaching.compatibility.Compatibility; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.IConnector; +import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.network.HtmlImage; +import cgeo.geocaching.utils.BaseUtils; +import cgeo.geocaching.utils.CancellableHandler; +import cgeo.geocaching.utils.CryptUtils; +import cgeo.geocaching.utils.UnknownTagsHandler; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; + +import android.R.color; +import android.app.AlertDialog; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.database.Cursor; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Parcelable; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.text.Html; +import android.text.Spannable; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.StrikethroughSpan; +import android.util.Log; +import android.view.ContextMenu; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.ViewParent; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.TextView.BufferType; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Activity to handle all single-cache-stuff. + * + * e.g. details, description, logs, waypoints, inventory... + */ +public class CacheDetailActivity extends AbstractActivity { + + private static final int MENU_SHARE = 12; + private static final int MENU_CALENDAR = 11; + private static final int MENU_CACHES_AROUND = 10; + private static final int MENU_BROWSER = 7; + private static final int MENU_NAVIGATE = 2; + + private static final int CONTEXT_MENU_WAYPOINT_DELETE = 1235; + private static final int CONTEXT_MENU_WAYPOINT_DUPLICATE = 1234; + + private cgGeo geolocation; + private cgCache cache; + private final Map<Integer, String> calendars = new HashMap<Integer, String>(); + private final Progress progress = new Progress(); + private cgSearch search; + private final LocationUpdater locationUpdater = new LocationUpdater(); + private String contextMenuUser = null; + + /** + * The index of the current page. We need this hack because {@link onPageSelected()} is not called after + * initialization. + */ + private int currentPageIndex = 0; + + /** + * A {@link List} of all available pages. + */ + private final List<Page> pageOrder = new ArrayList<Page>(); + + /** + * Instances of all {@link PageViewCreator}. + */ + private final Map<Page, PageViewCreator> viewCreators = new HashMap<Page, PageViewCreator>(); + + /** + * The {@link ViewPagerAdapter} for this activity. + */ + private ViewPagerAdapter viewPagerAdapter; + + /** + * The {@link ViewPagerIndicator} for this activity. + */ + private ViewPagerIndicator viewPagerIndicator; + + /** + * If another activity is called and can modify the data of this activity, we refresh it on resume. + */ + private boolean refreshOnResume = false; + + // some views that must be available from everywhere // TODO: Reference can block GC? + private TextView cacheDistanceView; + + public CacheDetailActivity() { + // identifier for manual + super("c:geolocation-cache-details"); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // initialize the main view and set a default title + setTheme(); + setContentView(R.layout.cacheview); + setTitle(res.getString(R.string.cache)); + + if (geolocation == null) { + geolocation = app.startGeo(this, locationUpdater, 0, 0); + } + + String geocode = null; + String guid = null; + String name = 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 = app.getCache(search); + 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 + if (geocode == null && extras != null) { + geocode = extras.getString("geocode"); + name = extras.getString("name"); + guid = extras.getString("guid"); + } + + // try to get data from URI + if (geocode == null && guid == null && uri != null) { + String uriHost = uri.getHost().toLowerCase(); + String uriPath = uri.getPath().toLowerCase(); + String uriQuery = uri.getQuery(); + + if (uriQuery != null) { + Log.i(Settings.tag, "Opening URI: " + uriHost + uriPath + "?" + uriQuery); + } else { + Log.i(Settings.tag, "Opening URI: " + uriHost + uriPath); + } + + if (uriHost.contains("geocaching.com")) { + geocode = uri.getQueryParameter("wp"); + guid = uri.getQueryParameter("guid"); + + if (StringUtils.isNotBlank(geocode)) { + geocode = geocode.toUpperCase(); + guid = null; + } else if (StringUtils.isNotBlank(guid)) { + geocode = null; + guid = guid.toLowerCase(); + } else { + showToast(res.getString(R.string.err_detail_open)); + finish(); + return; + } + } else if (uriHost.contains("coord.info")) { + if (uriPath != null && uriPath.startsWith("/gc")) { + geocode = uriPath.substring(1).toUpperCase(); + } else { + showToast(res.getString(R.string.err_detail_open)); + finish(); + return; + } + } + } + + // no given data + if (geocode == null && guid == null) { + showToast(res.getString(R.string.err_detail_cache)); + finish(); + return; + } + + // Go4Cache + if (StringUtils.isNotBlank(geocode)) { + app.setAction(geocode); + } + + final LoadCacheHandler loadCacheHandler = new LoadCacheHandler(); + + try { + String title = res.getString(R.string.cache); + if (StringUtils.isNotBlank(name)) { + title = name; + } else if (StringUtils.isNotBlank(geocode)) { + title = geocode.toUpperCase(); + } + progress.show(this, title, res.getString(R.string.cache_dialog_loading_details), true, loadCacheHandler.cancelMessage()); + } catch (Exception e) { + // nothing, we lost the window + } + + // initialize ViewPager + ViewPager pager = (ViewPager) findViewById(R.id.viewpager); + viewPagerIndicator = new ViewPagerIndicator(); + pager.setOnPageChangeListener(viewPagerIndicator); + viewPagerAdapter = new ViewPagerAdapter(); + pager.setAdapter(viewPagerAdapter); + + // Initialization done. Let's load the data with the given information. + new LoadCacheThread(geocode, guid, loadCacheHandler).start(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + // Maybe keyboard hidden or display orientation changed. No need for update the UI or something else. + } + + @Override + public void onResume() { + super.onResume(); + + if (geolocation == null) { + geolocation = app.startGeo(this, locationUpdater, 0, 0); + } + + if (refreshOnResume) { + notifyDataSetChanged(); + refreshOnResume = false; + } + } + + @Override + public void onDestroy() { + if (geolocation != null) { + geolocation = app.removeGeo(); + } + + super.onDestroy(); + } + + @Override + public void onStop() { + if (geolocation != null) { + geolocation = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (geolocation != null) { + geolocation = app.removeGeo(); + } + + super.onPause(); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo info) { + super.onCreateContextMenu(menu, view, info); + final int viewId = view.getId(); + switch (viewId) { + case R.id.author: + case R.id.value: + if (viewId == R.id.author) { // Author of a log entry + contextMenuUser = ((TextView) view).getText().toString(); + } else if (viewId == R.id.value) { // The owner of the cache + if (StringUtils.isNotBlank(cache.getOwnerReal())) { + contextMenuUser = cache.getOwnerReal(); + } else { + contextMenuUser = cache.getOwner(); + } + } + + menu.setHeaderTitle(res.getString(R.string.user_menu_title) + " " + contextMenuUser); + menu.add(viewId, 1, 0, res.getString(R.string.user_menu_view_hidden)); + menu.add(viewId, 2, 0, res.getString(R.string.user_menu_view_found)); + menu.add(viewId, 3, 0, res.getString(R.string.user_menu_open_browser)); + break; + case -1: + if (null != cache.getWaypoints()) { + try { + final ViewGroup parent = ((ViewGroup) view.getParent()); + for (int i = 0; i < parent.getChildCount(); i++) { + if (parent.getChildAt(i) == view) { + final List<cgWaypoint> sortedWaypoints = new ArrayList<cgWaypoint>(cache.getWaypoints()); + Collections.sort(sortedWaypoints); + final cgWaypoint waypoint = sortedWaypoints.get(i); + final int index = cache.getWaypoints().indexOf(waypoint); + menu.setHeaderTitle(res.getString(R.string.waypoint)); + menu.add(CONTEXT_MENU_WAYPOINT_DUPLICATE, index, 0, R.string.waypoint_duplicate); + if (waypoint.isUserDefined()) { + menu.add(CONTEXT_MENU_WAYPOINT_DELETE, index, 0, R.string.waypoint_delete); + } + } + } + } catch (Exception e) { + } + } + break; + default: + break; + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + final int groupId = item.getGroupId(); + final int index = item.getItemId(); + switch (groupId) { + case R.id.author: + case R.id.value: + final int itemId = item.getItemId(); + switch (itemId) { + case 1: + cgeocaches.startActivityOwner(this, contextMenuUser); + return true; + case 2: + cgeocaches.startActivityUserName(this, contextMenuUser); + return true; + case 3: + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?u=" + URLEncoder.encode(contextMenuUser)))); + return true; + default: + break; + } + break; + case CONTEXT_MENU_WAYPOINT_DUPLICATE: + if (null != cache.getWaypoints() && index < cache.getWaypoints().size()) { + final cgWaypoint copy = new cgWaypoint(cache.getWaypoints().get(index)); + copy.setUserDefined(); + copy.setName(res.getString(R.string.waypoint_copy_of) + " " + copy.getName()); + cache.getWaypoints().add(index + 1, copy); + app.saveOwnWaypoint(-1, cache.getGeocode(), copy); + app.removeCacheFromCache(cache.getGeocode()); + notifyDataSetChanged(); + } + break; + case CONTEXT_MENU_WAYPOINT_DELETE: + if (null != cache.getWaypoints() && index < cache.getWaypoints().size()) { + final cgWaypoint waypoint = cache.getWaypoints().get(index); + if (waypoint.isUserDefined()) { + cache.getWaypoints().remove(index); + app.deleteWaypoint(waypoint.getId()); + app.removeCacheFromCache(cache.getGeocode()); + notifyDataSetChanged(); + } + } + break; + default: + return onOptionsItemSelected(item); + } + return false; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (null != cache) { + menu.add(0, MENU_NAVIGATE, 0, res.getString(R.string.cache_menu_compass)).setIcon(android.R.drawable.ic_menu_compass); // compass + + final SubMenu subMenu = menu.addSubMenu(1, 0, 0, res.getString(R.string.cache_menu_navigate)).setIcon(android.R.drawable.ic_menu_mapmode); + NavigationAppFactory.addMenuItems(subMenu, this, res); + GeneralAppsFactory.addMenuItems(subMenu, this, res, cache); + + menu.add(1, MENU_CALENDAR, 0, res.getString(R.string.cache_menu_event)).setIcon(android.R.drawable.ic_menu_agenda); // add event to calendar + addVisitMenu(menu, cache); + menu.add(0, MENU_CACHES_AROUND, 0, res.getString(R.string.cache_menu_around)).setIcon(android.R.drawable.ic_menu_rotate); // caches around + menu.add(1, MENU_BROWSER, 0, res.getString(R.string.cache_menu_browser)).setIcon(R.drawable.ic_menu_globe); // browser + menu.add(0, MENU_SHARE, 0, res.getString(R.string.cache_menu_share)).setIcon(android.R.drawable.ic_menu_share); // share cache + } + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.findItem(MENU_NAVIGATE).setVisible(null != cache.getCoords()); + menu.findItem(MENU_CALENDAR).setVisible(cache.canBeAddedToCalendar()); + menu.findItem(MENU_CACHES_AROUND).setVisible(null != cache.getCoords() && cache.supportsCachesAround()); + menu.findItem(MENU_BROWSER).setVisible(cache.canOpenInBrowser()); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int menuItem = item.getItemId(); + + // no menu selected, but a new sub menu shown + if (menuItem == 0) { + return false; + } + + if (menuItem == MENU_NAVIGATE) { + startCompassNavigation(); + return true; + } else if (menuItem == MENU_LOG_VISIT) { + cache.logVisit(this); + return true; + } else if (menuItem == MENU_BROWSER) { + cache.openInBrowser(this); + return true; + } else if (menuItem == MENU_CACHES_AROUND) { + cachesAround(); + return true; + } else if (menuItem == MENU_CALENDAR) { + addToCalendar(); + return true; + } else if (menuItem == MENU_SHARE) { + if (cache != null) { + cache.shareCache(this, res); + return true; + } + return false; + } + if (NavigationAppFactory.onMenuItemSelected(item, geolocation, this, res, cache, search, null, null)) { + return true; + } + if (GeneralAppsFactory.onMenuItemSelected(item, this, cache)) { + return true; + } + + int logType = menuItem - MENU_LOG_VISIT_OFFLINE; + cache.logOffline(this, logType); + return true; + } + + private class LoadCacheHandler extends CancellableHandler { + @Override + public void handleRegularMessage(final Message msg) { + if (cgBase.UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { + updateStatusMsg((String) msg.obj); + } else { + if (search == null) { + showToast(res.getString(R.string.err_dwld_details_failed)); + + finish(); + return; + } + + if (cgeoapplication.getError(search) != null) { + showToast(res.getString(R.string.err_dwld_details_failed_reason) + " " + cgeoapplication.getError(search) + "."); + + finish(); + return; + } + + updateStatusMsg(res.getString(R.string.cache_dialog_loading_details_status_render)); + + // Data loaded, we're ready to show it! + notifyDataSetChanged(); + } + } + + private void updateStatusMsg(final String msg) { + progress.setMessage(res.getString(R.string.cache_dialog_loading_details) + + "\n\n" + + msg); + } + + @Override + public void handleCancel(final Object extra) { + finish(); + } + + } + + private void notifyDataSetChanged() { + if (search == null) { + return; + } + + cache = app.getCache(search); + + if (cache == null) { + progress.dismiss(); + showToast(res.getString(R.string.err_detail_cache_find_some)); + finish(); + return; + } + + // notify all creators that the data has changed + for (PageViewCreator creator : viewCreators.values()) { + creator.notifyDataSetChanged(); + } + + // actionbar: title and icon (default: mystery-icon) + setTitle(cache.getGeocode().toUpperCase()); + ((TextView) findViewById(R.id.actionbar_title)).setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(cgBase.getCacheIcon(cache.getType())), null, null, null); + + // add available pages (remove old pages first) + pageOrder.clear(); + + pageOrder.add(Page.DETAILS); + pageOrder.add(Page.DESCRIPTION); + if (CollectionUtils.isNotEmpty(cache.getLogs())) { + pageOrder.add(Page.LOGS); + } + pageOrder.add(Page.WAYPOINTS); + if (CollectionUtils.isNotEmpty(cache.getInventory())) { + pageOrder.add(Page.INVENTORY); + } + + // notify the adapter that the data has changed + viewPagerAdapter.notifyDataSetChanged(); + + // notify the indicator about the change + viewPagerIndicator.onPageSelected(currentPageIndex); + + // rendering done! remove progress-popup if any there + progress.dismiss(); + } + + private class LocationUpdater extends cgUpdateLoc { + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + if (cacheDistanceView == null) { + return; + } + + try { + StringBuilder dist = new StringBuilder(); + + if (geo.coordsNow != null && cache != null && cache.getCoords() != null) { + dist.append(cgBase.getHumanDistance(geo.coordsNow.distanceTo(cache.getCoords()))); + } + + if (cache != null && cache.getElevation() != null) { + if (geo.altitudeNow != null) { + Double diff = (cache.getElevation() - geo.altitudeNow); + if (diff >= 0) { + dist.append(" ↗"); + } else if (diff < 0) { + dist.append(" ↘"); + } + if (Settings.isUseMetricUnits()) { + dist.append(String.format("%.0f", (Math.abs(diff)))); + dist.append(" m"); + } else { + dist.append(String.format("%.0f", (Math.abs(diff) * 3.2808399))); + dist.append(" ft"); + } + } + } + + cacheDistanceView.setText(dist.toString()); + cacheDistanceView.bringToFront(); + } catch (Exception e) { + Log.w(Settings.tag, "Failed to update location."); + } + } + } + + /** + * Loads the cache with the given geocode or guid. + */ + private class LoadCacheThread extends Thread { + + private CancellableHandler handler = null; + private String geocode; + private String guid; + + public LoadCacheThread(final String geocode, final String guid, final CancellableHandler handlerIn) { + handler = handlerIn; + + if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid)) { + showToast(res.getString(R.string.err_detail_cache_forgot)); + + finish(); + return; + } + + this.geocode = geocode; + this.guid = guid; + } + + @Override + public void run() { + search = cgBase.searchByGeocode(geocode, StringUtils.isBlank(geocode) ? guid : null, 0, false, handler); + handler.sendMessage(new Message()); + } + } + + /** + * Starts activity to search for caches near this cache. + * + * Also finishes this activity. + */ + private void cachesAround() { + cgeocaches.startActivityCachesAround(this, cache.getCoords()); + + finish(); + } + + /** + * Adds the cache to the Android-calendar if it is an event. + */ + private void addToCalendar() { + String[] projection = new String[] { "_id", "displayName" }; + Uri calendarProvider = Compatibility.getCalendarProviderURI(); + + Cursor cursor = managedQuery(calendarProvider, projection, "selected=1", null, null); + + calendars.clear(); + int cnt = 0; + if (cursor != null) { + cnt = cursor.getCount(); + + if (cnt > 0) { + cursor.moveToFirst(); + + int calId = 0; + String calIdPre = null; + String calName = null; + int calIdIn = cursor.getColumnIndex("_id"); + int calNameIn = cursor.getColumnIndex("displayName"); + + do { + calIdPre = cursor.getString(calIdIn); + if (calIdPre != null) { + calId = new Integer(calIdPre); + } + calName = cursor.getString(calNameIn); + + if (calId > 0 && calName != null) { + calendars.put(calId, calName); + } + } while (cursor.moveToNext()); + } + } + + final CharSequence[] items = calendars.values().toArray(new CharSequence[calendars.size()]); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.cache_calendars); + builder.setItems(items, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + addToCalendarFn(item); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + } + + /** + * Helper for {@link addToCalendar()}. + * + * @param index + * The selected calendar + */ + private void addToCalendarFn(int index) { + if (MapUtils.isEmpty(calendars)) { + return; + } + + try { + Uri calendarProvider = Compatibility.getCalenderEventsProviderURI(); + + final Integer[] keys = calendars.keySet().toArray(new Integer[calendars.size()]); + final Integer calId = keys[index]; + + final Date eventDate = cache.getHiddenDate(); + eventDate.setHours(0); + eventDate.setMinutes(0); + eventDate.setSeconds(0); + + StringBuilder description = new StringBuilder(); + description.append(cache.getUrl()); + description.append("\n\n"); + if (StringUtils.isNotBlank(cache.getShortdesc())) { + description.append(Html.fromHtml(cache.getShortdesc()).toString()); + } + + if (StringUtils.isNotBlank(cache.getPersonalNote())) { + description.append("\n\n" + Html.fromHtml(cache.getPersonalNote()).toString()); + } + + ContentValues event = new ContentValues(); + event.put("calendar_id", calId); + event.put("dtstart", eventDate.getTime() + 43200000); // noon + event.put("dtend", eventDate.getTime() + 43200000 + 3600000); // + one hour + event.put("eventTimezone", "UTC"); + event.put("title", Html.fromHtml(cache.getName()).toString()); + event.put("description", description.toString()); + String location = ""; + if (cache.getCoords() != null) { + location += cache.getCoords(); + } + if (StringUtils.isNotBlank(cache.getLocation())) { + boolean addParenteses = false; + if (location.length() > 0) { + addParenteses = true; + location += " ("; + } + + location += Html.fromHtml(cache.getLocation()).toString(); + if (addParenteses) { + location += ")"; + } + } + if (location.length() > 0) { + event.put("eventLocation", location); + } + event.put("allDay", 1); + event.put("hasAlarm", 0); + + getContentResolver().insert(calendarProvider, event); + + showToast(res.getString(R.string.event_success)); + } catch (Exception e) { + showToast(res.getString(R.string.event_fail)); + + Log.e(Settings.tag, "CacheDetailActivity.addToCalendarFn: " + e.toString()); + } + } + + /** + * Creates a {@link List} of all coordinates (cache and waypoints) for the current cache. + * + * @return A {@link List} of all coordinates + */ + public List<cgCoord> getCoordinates() { + List<cgCoord> coordinates = new ArrayList<cgCoord>(); + + // cache + try { + final cgCoord coords = new cgCoord(); + coords.setCoordType("cache"); + if (StringUtils.isNotBlank(cache.getName())) { + coords.setName(cache.getName()); + } else { + coords.setName(cache.getGeocode().toUpperCase()); + } + coords.setCoords(cache.getCoords()); + coordinates.add(coords); + } catch (Exception e) { + Log.e(Settings.tag, "CacheDetailActivity.getCoordinates (cache)", e); + } + + // waypoints + try { + if (null != cache.getWaypoints()) { + for (cgWaypoint waypoint : cache.getWaypoints()) { + if (null != waypoint.getCoords()) { + final cgCoord coords = new cgCoord(); + coords.setCoordType("waypoint"); + coords.setName(waypoint.getName()); + coords.setCoords(waypoint.getCoords()); + coordinates.add(coords); + } + } + } + } catch (Exception e) { + Log.e(Settings.tag, "CacheDetailActivity.getCoordinates (waypoint)", e); + } + + return coordinates; + } + + /** + * Tries to navigate to the {@link cgCache} of this activity. + */ + private void startCompassNavigation() { + if (cache == null || cache.getCoords() == null) { + showToast(res.getString(R.string.err_location_unknown)); + return; + } + + cgeonavigate.startActivity(this, cache.getGeocode(), cache.getName(), cache.getCoords(), getCoordinates()); + } + + /** + * Wrapper for the referenced method in the xml-layout. + */ + public void startCompassNavigation(@SuppressWarnings("unused") View view) { + startCompassNavigation(); + } + + /** + * Opens a context menu to do actions on an username + */ + private class UserActionsClickListener implements View.OnClickListener { + + public void onClick(View view) { + if (view == null) { + return; + } + if (!cache.supportsUserActions()) { + return; + } + + try { + registerForContextMenu(view); + openContextMenu(view); + } catch (Exception e) { + // nothing + } + } + } + + public static void startActivity(final Context context, final String geocode) { + final Intent detailIntent = new Intent(context, CacheDetailActivity.class); + detailIntent.putExtra("geocode", geocode.toUpperCase()); + context.startActivity(detailIntent); + } + + /** + * The ViewPagerAdapter for scrolling through pages of the CacheDetailActivity. + */ + private class ViewPagerAdapter extends PagerAdapter { + + @Override + public void destroyItem(View container, int position, Object object) { + ((ViewPager) container).removeView((View) object); + } + + @Override + public void finishUpdate(View container) { + } + + @Override + public int getCount() { + return pageOrder.size(); + } + + @Override + public Object instantiateItem(View container, int position) { + final Page page = pageOrder.get(position); + + PageViewCreator creator = viewCreators.get(page); + + if (null == creator) { + // The creator is not instantiated yet, let's do it. + switch (page) { + case DETAILS: + creator = new DetailsViewCreator(); + break; + + case DESCRIPTION: + creator = new DescriptionViewCreator(); + break; + + case LOGS: + creator = new LogsViewCreator(); + break; + + case WAYPOINTS: + creator = new WaypointsViewCreator(); + break; + + case INVENTORY: + creator = new InventoryViewCreator(); + break; + } + viewCreators.put(page, creator); + } + + View view = null; + + try { + if (null != creator) { + // Result from getView() is maybe cached, but it should be valid because the + // creator should be informed about data-changes with notifyDataSetChanged() + view = creator.getView(); + ((ViewPager) container).addView(view, 0); + } + } catch (Exception e) { + Log.e(Settings.tag, "ViewPagerAdapter.instantiateItem ", e); + } + + return view; + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return (view == object); + } + + @Override + public void restoreState(Parcelable arg0, ClassLoader arg1) { + } + + @Override + public Parcelable saveState() { + return null; + } + + @Override + public void startUpdate(View arg0) { + } + + @Override + public int getItemPosition(Object object) { + // We are doing the caching. So pretend that the view is gone. + // The ViewPager will get it back in instantiateItem() + return POSITION_NONE; + } + } + + private class ViewPagerIndicator implements ViewPager.OnPageChangeListener { + + // TODO: Clickable prev + next + private TextView indicatorPrev; + private TextView indicatorCurrent; + private TextView indicatorNext; + + public ViewPagerIndicator() { + super(); + + indicatorPrev = (TextView) findViewById(R.id.indicator_prev); + indicatorCurrent = (TextView) findViewById(R.id.indicator_current); + indicatorNext = (TextView) findViewById(R.id.indicator_next); + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + currentPageIndex = position; + + indicatorCurrent.setText(res.getString(pageOrder.get(position).titleStringId)); + + if (position > 0) { + indicatorPrev.setText(res.getString(pageOrder.get(position - 1).titleStringId)); + indicatorPrev.setVisibility(View.VISIBLE); + } else { + indicatorPrev.setVisibility(View.GONE); + } + + if (position < (pageOrder.size() - 1)) { + indicatorNext.setText(res.getString(pageOrder.get(position + 1).titleStringId)); + indicatorNext.setVisibility(View.VISIBLE); + } else { + indicatorNext.setVisibility(View.GONE); + } + } + } + + /** + * Enum of all possible pages with methods to get the view and a title. + */ + private enum Page { + DETAILS(R.string.detail), + DESCRIPTION(R.string.cache_description), + LOGS(R.string.cache_logs), + WAYPOINTS(R.string.cache_waypoints), + INVENTORY(R.string.cache_inventory); + + public final int titleStringId; + + private Page(final int titleStringId) { + this.titleStringId = titleStringId; + } + } + + private interface PageViewCreator { + /** + * Returns a validated view. + * + * @return + */ + public View getDispatchedView(); + + /** + * Returns a (maybe cached) view. + * + * @return + */ + public View getView(); + + /** + * Handles changed data-sets. + */ + public void notifyDataSetChanged(); + } + + /** + * Creator for details-view. + */ + private class DetailsViewCreator implements PageViewCreator { + /** + * The main view for this creator + */ + private ScrollView view; + + /** + * Reference to the details list, so that the helper-method can access it without an additional argument + */ + private LinearLayout detailsList; + + // TODO Do we need this thread-references? + private StoreCacheThread storeThread; + private RefreshCacheThread refreshThread; + private Thread watchlistThread; + + @Override + public void notifyDataSetChanged() { + // There is a lot of data in this view, let's update everything + view = null; + } + + @Override + public View getView() { + if (view == null) { + view = (ScrollView) getDispatchedView(); + } + + return view; + } + + @Override + public View getDispatchedView() { + if (cache == null) { + // something is really wrong + return null; + } + + view = (ScrollView) getLayoutInflater().inflate(R.layout.cacheview_details, null); + + detailsList = (LinearLayout) view.findViewById(R.id.details_list); + + // cache name (full name) + Spannable span = (new Spannable.Factory()).newSpannable(Html.fromHtml(cache.getName()).toString()); + if (cache.isDisabled() || cache.isArchived()) { // strike + span.setSpan(new StrikethroughSpan(), 0, span.toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + addCacheDetail(R.string.cache_name, span); + + // cache type + addCacheDetail(R.string.cache_type, cache.getType().getL10n()); + + // size + if (null != cache.getSize() && cache.showSize()) { + addCacheDetail(R.string.cache_size, cache.getSize().getL10n()); + } + + // gc-code + addCacheDetail(R.string.cache_geocode, cache.getGeocode().toUpperCase()); + + // cache state + if (cache.isLogOffline() || cache.isArchived() || cache.isDisabled() || cache.isMembers() || cache.isFound()) { + final StringBuilder state = new StringBuilder(); + if (cache.isLogOffline()) { + state.append(res.getString(R.string.cache_status_offline_log)); + } + if (cache.isFound()) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_found)); + } + if (cache.isArchived()) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_archived)); + } + if (cache.isDisabled()) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_disabled)); + } + if (cache.isMembers()) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_premium)); + } + + addCacheDetail(R.string.cache_status, state.toString()); + } + + // distance + cacheDistanceView = addCacheDetail(R.string.cache_distance, cache.getDistance() != null ? "~" + cgBase.getHumanDistance(cache.getDistance()) : "--"); + + // difficulty + if (cache.getDifficulty() != null && cache.getDifficulty() > 0) { + addStarRating(R.string.cache_difficulty, cache.getDifficulty()); + } + + // terrain + if (cache.getTerrain() != null && cache.getTerrain() > 0) { + addStarRating(R.string.cache_terrain, cache.getTerrain()); + } + + // rating + if (cache.getRating() != null && cache.getRating() > 0) { + final RelativeLayout itemLayout = addStarRating(R.string.cache_rating, cache.getRating()); + if (cache.getVotes() != null) { + final TextView itemAddition = (TextView) itemLayout.findViewById(R.id.addition); + itemAddition.setText("(" + cache.getVotes() + ")"); + itemAddition.setVisibility(View.VISIBLE); + } + } + + // favourite count + if (cache.getFavouriteCnt() != null) { + addCacheDetail(R.string.cache_favourite, String.format("%d", cache.getFavouriteCnt()) + "×"); + } + + // cache author + if (StringUtils.isNotBlank(cache.getOwner()) || StringUtils.isNotBlank(cache.getOwnerReal())) { + TextView ownerView = addCacheDetail(R.string.cache_owner, ""); + if (StringUtils.isNotBlank(cache.getOwner())) { + ownerView.setText(Html.fromHtml(cache.getOwner()), TextView.BufferType.SPANNABLE); + } else if (StringUtils.isNotBlank(cache.getOwnerReal())) { + ownerView.setText(Html.fromHtml(cache.getOwnerReal()), TextView.BufferType.SPANNABLE); + } + ownerView.setOnClickListener(new UserActionsClickListener()); + } + + // cache hidden + if (cache.getHiddenDate() != null && cache.getHiddenDate().getTime() > 0) { + addCacheDetail(cache.isEventCache() ? R.string.cache_event : R.string.cache_hidden, cgBase.formatFullDate(cache.getHiddenDate().getTime())); + } + + // cache location + if (StringUtils.isNotBlank(cache.getLocation())) { + addCacheDetail(R.string.cache_location, cache.getLocation()); + } + + // cache coordinates + if (cache.getCoords() != null) { + addCacheDetail(R.string.cache_coordinates, cache.getCoords().toString()) + .setOnClickListener(new View.OnClickListener() { + private int position = 0; + private GeopointFormatter.Format[] availableFormats = new GeopointFormatter.Format[] { + GeopointFormatter.Format.LAT_LON_DECMINUTE, + GeopointFormatter.Format.LAT_LON_DECSECOND, + GeopointFormatter.Format.LAT_LON_DECDEGREE + }; + + // rotate coordinate formats on click + @Override + public void onClick(View view) { + position = (position + 1) % availableFormats.length; + + final TextView valueView = (TextView) view.findViewById(R.id.value); + valueView.setText(cache.getCoords().format(availableFormats[position])); + } + }); + } + + updateOfflineBox(); + + // watchlist + Button buttonWatchlistAdd = (Button) view.findViewById(R.id.add_to_watchlist); + Button buttonWatchlistRemove = (Button) view.findViewById(R.id.remove_from_watchlist); + buttonWatchlistAdd.setOnClickListener(new AddToWatchlistClickListener()); + buttonWatchlistRemove.setOnClickListener(new RemoveFromWatchlistClickListener()); + updateWatchlistBox(); + + // data license + IConnector connector = ConnectorFactory.getConnector(cache); + if (connector != null) { + String license = connector.getLicenseText(cache); + if (StringUtils.isNotBlank(license)) { + ((LinearLayout) view.findViewById(R.id.license_box)).setVisibility(View.VISIBLE); + TextView licenseView = ((TextView) view.findViewById(R.id.license)); + licenseView.setText(Html.fromHtml(license), BufferType.SPANNABLE); + licenseView.setClickable(true); + licenseView.setMovementMethod(LinkMovementMethod.getInstance()); + } else { + ((LinearLayout) view.findViewById(R.id.license_box)).setVisibility(View.GONE); + } + } + + if (geolocation != null) { + locationUpdater.updateLoc(geolocation); + } + + return view; + } + + private TextView addCacheDetail(final int nameId, final CharSequence value) { + final RelativeLayout layout = (RelativeLayout) getLayoutInflater().inflate(R.layout.cache_item, null); + ((TextView) layout.findViewById(R.id.name)).setText(res.getString(nameId)); + final TextView valueView = (TextView) layout.findViewById(R.id.value); + valueView.setText(value); + detailsList.addView(layout); + return valueView; + } + + private RelativeLayout addStarRating(final int nameId, final float value) { + final RelativeLayout layout = (RelativeLayout) getLayoutInflater().inflate(R.layout.cache_layout, null); + TextView viewName = (TextView) layout.findViewById(R.id.name); + TextView viewValue = (TextView) layout.findViewById(R.id.value); + LinearLayout layoutStars = (LinearLayout) layout.findViewById(R.id.stars); + + viewName.setText(res.getString(nameId)); + viewValue.setText(String.format("%.1f", value) + ' ' + res.getString(R.string.cache_rating_of) + " 5"); + layoutStars.addView(cgBase.createStarRating(value, 5, CacheDetailActivity.this), 1); + + detailsList.addView(layout); + return layout; + } + + private class StoreCacheHandler extends CancellableHandler { + @Override + public void handleRegularMessage(Message msg) { + storeThread = null; + + try { + cache = app.getCache(search); // reload cache details + } catch (Exception e) { + showToast(res.getString(R.string.err_store_failed)); + + Log.e(Settings.tag, "CacheDetailActivity.storeCacheHandler: " + e.toString()); + } + + CacheDetailActivity.this.notifyDataSetChanged(); + } + } + + private class RefreshCacheHandler extends CancellableHandler { + @Override + public void handleRegularMessage(Message msg) { + refreshThread = null; + + try { + cache = app.getCache(search); // reload cache details + } catch (Exception e) { + showToast(res.getString(R.string.err_refresh_failed)); + + Log.e(Settings.tag, "CacheDetailActivity.refreshCacheHandler: " + e.toString()); + } + + CacheDetailActivity.this.notifyDataSetChanged(); + } + } + + private class DropCacheHandler extends Handler { + @Override + public void handleMessage(Message msg) { + CacheDetailActivity.this.notifyDataSetChanged(); + } + } + + private class StoreCacheClickListener implements View.OnClickListener { + public void onClick(View arg0) { + if (progress.isShowing()) { + showToast(res.getString(R.string.err_detail_still_working)); + return; + } + + final StoreCacheHandler storeCacheHandler = new StoreCacheHandler(); + + progress.show(CacheDetailActivity.this, res.getString(R.string.cache_dialog_offline_save_title), res.getString(R.string.cache_dialog_offline_save_message), true, storeCacheHandler.cancelMessage()); + + if (storeThread != null) { + storeThread.interrupt(); + } + + storeThread = new StoreCacheThread(storeCacheHandler); + storeThread.start(); + } + } + + private class RefreshCacheClickListener implements View.OnClickListener { + public void onClick(View arg0) { + if (progress.isShowing()) { + showToast(res.getString(R.string.err_detail_still_working)); + return; + } + + final RefreshCacheHandler refreshCacheHandler = new RefreshCacheHandler(); + + progress.show(CacheDetailActivity.this, res.getString(R.string.cache_dialog_refresh_title), res.getString(R.string.cache_dialog_refresh_message), true, refreshCacheHandler.cancelMessage()); + + if (refreshThread != null) { + refreshThread.interrupt(); + } + + refreshThread = new RefreshCacheThread(refreshCacheHandler); + refreshThread.start(); + } + } + + private class StoreCacheThread extends Thread { + final private CancellableHandler handler; + + public StoreCacheThread(final CancellableHandler handler) { + this.handler = handler; + } + + @Override + public void run() { + int reason = cache.getReason() > 1 ? cache.getReason() : 1; + cgBase.storeCache(app, CacheDetailActivity.this, cache, null, reason, handler); + } + } + + private class RefreshCacheThread extends Thread { + final private CancellableHandler handler; + + public RefreshCacheThread(final CancellableHandler handler) { + this.handler = handler; + } + + @Override + public void run() { + app.removeCacheFromCache(cache.getGeocode()); + search = cgBase.searchByGeocode(cache.getGeocode(), null, 0, true, handler); + + handler.sendEmptyMessage(0); + } + } + + private class DropCacheClickListener implements View.OnClickListener { + public void onClick(View arg0) { + if (progress.isShowing()) { + showToast(res.getString(R.string.err_detail_still_working)); + return; + } + + final DropCacheHandler dropCacheHandler = new DropCacheHandler(); + + progress.show(CacheDetailActivity.this, res.getString(R.string.cache_dialog_offline_drop_title), res.getString(R.string.cache_dialog_offline_drop_message), true, null); + new DropCacheThread(dropCacheHandler).start(); + } + } + + private class DropCacheThread extends Thread { + + private Handler handler = null; + + public DropCacheThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + cgBase.dropCache(app, cache, handler); + } + } + + /** + * Abstract Listener for add / remove buttons for watchlist + */ + private abstract class AbstractWatchlistClickListener implements View.OnClickListener { + public void doExecute(int titleId, int messageId, Thread thread) { + if (progress.isShowing()) { + showToast(res.getString(R.string.err_watchlist_still_managing)); + return; + } + progress.show(CacheDetailActivity.this, res.getString(titleId), res.getString(messageId), true, null); + + if (watchlistThread != null) { + watchlistThread.interrupt(); + } + + watchlistThread = thread; + watchlistThread.start(); + } + } + + /** + * Listener for "add to watchlist" button + */ + private class AddToWatchlistClickListener extends AbstractWatchlistClickListener { + public void onClick(View arg0) { + doExecute(R.string.cache_dialog_watchlist_add_title, + R.string.cache_dialog_watchlist_add_message, + new WatchlistAddThread(new WatchlistHandler())); + } + } + + /** + * Listener for "remove from watchlist" button + */ + private class RemoveFromWatchlistClickListener extends AbstractWatchlistClickListener { + public void onClick(View arg0) { + doExecute(R.string.cache_dialog_watchlist_remove_title, + R.string.cache_dialog_watchlist_remove_message, + new WatchlistRemoveThread(new WatchlistHandler())); + } + } + + /** Thread to add this cache to the watchlist of the user */ + private class WatchlistAddThread extends Thread { + private final Handler handler; + + public WatchlistAddThread(Handler handler) { + this.handler = handler; + } + + @Override + public void run() { + handler.sendEmptyMessage(cgBase.addToWatchlist(cache)); + } + } + + /** Thread to remove this cache from the watchlist of the user */ + private class WatchlistRemoveThread extends Thread { + private final Handler handler; + + public WatchlistRemoveThread(Handler handler) { + this.handler = handler; + } + + @Override + public void run() { + handler.sendEmptyMessage(cgBase.removeFromWatchlist(cache)); + } + } + + /** + * shows/hides buttons, sets text in watchlist box + */ + private void updateWatchlistBox() { + LinearLayout layout = (LinearLayout) view.findViewById(R.id.watchlist_box); + boolean supportsWatchList = cache.supportsWatchList(); + layout.setVisibility(supportsWatchList ? View.VISIBLE : View.GONE); + if (!supportsWatchList) { + return; + } + Button buttonAdd = (Button) view.findViewById(R.id.add_to_watchlist); + Button buttonRemove = (Button) view.findViewById(R.id.remove_from_watchlist); + TextView text = (TextView) view.findViewById(R.id.watchlist_text); + + if (cache.isOnWatchlist()) { + buttonAdd.setVisibility(View.GONE); + buttonRemove.setVisibility(View.VISIBLE); + text.setText(R.string.cache_watchlist_on); + } else { + buttonAdd.setVisibility(View.VISIBLE); + buttonRemove.setVisibility(View.GONE); + text.setText(R.string.cache_watchlist_not_on); + } + } + + /** + * Handler, called when watchlist add or remove is done + */ + private class WatchlistHandler extends Handler { + @Override + public void handleMessage(Message msg) { + watchlistThread = null; + progress.dismiss(); + if (msg.what == -1) { + showToast(res.getString(R.string.err_watchlist_failed)); + } else { + updateWatchlistBox(); + } + } + } + + private void updateOfflineBox() { + // offline use + final TextView offlineText = (TextView) view.findViewById(R.id.offline_text); + final Button offlineRefresh = (Button) view.findViewById(R.id.offline_refresh); + final Button offlineStore = (Button) view.findViewById(R.id.offline_store); + + if (cache.getReason() >= 1) { + Long diff = (System.currentTimeMillis() / (60 * 1000)) - (cache.getDetailedUpdate() / (60 * 1000)); // minutes + + String ago = ""; + if (diff < 15) { + ago = res.getString(R.string.cache_offline_time_mins_few); + } else if (diff < 50) { + ago = res.getString(R.string.cache_offline_time_about) + " " + diff + " " + res.getString(R.string.cache_offline_time_mins); + } else if (diff < 90) { + ago = res.getString(R.string.cache_offline_time_about) + " " + res.getString(R.string.cache_offline_time_hour); + } else if (diff < (48 * 60)) { + ago = res.getString(R.string.cache_offline_time_about) + " " + (diff / 60) + " " + res.getString(R.string.cache_offline_time_hours); + } else { + ago = res.getString(R.string.cache_offline_time_about) + " " + (diff / (24 * 60)) + " " + res.getString(R.string.cache_offline_time_days); + } + + offlineText.setText(res.getString(R.string.cache_offline_stored) + "\n" + ago); + offlineRefresh.setOnClickListener(new StoreCacheClickListener()); + + offlineStore.setText(res.getString(R.string.cache_offline_drop)); + offlineStore.setClickable(true); + offlineStore.setOnClickListener(new DropCacheClickListener()); + } else { + offlineText.setText(res.getString(R.string.cache_offline_not_ready)); + offlineRefresh.setOnClickListener(new RefreshCacheClickListener()); + + offlineStore.setText(res.getString(R.string.cache_offline_store)); + offlineStore.setClickable(true); + offlineStore.setOnClickListener(new StoreCacheClickListener()); + } + offlineRefresh.setVisibility(cache.supportsRefresh() ? View.VISIBLE : View.GONE); + offlineRefresh.setClickable(true); + } + } + + private class DescriptionViewCreator implements PageViewCreator { + + private ViewGroup attributeIconsLayout; // layout for attribute icons + private ViewGroup attributeDescriptionsLayout; // layout for attribute descriptions + private boolean attributesShowAsIcons = true; // default: show icons + /** + * True, if the cache was imported with an older version of c:geo. + * These older versions parsed the attribute description from the tooltip in the web + * page and put them into the DB. No icons can be matched for these. + */ + private boolean noAttributeIconsFound = false; + private int attributeBoxMaxWidth; + + ScrollView view; + + @Override + public void notifyDataSetChanged() { + view = null; + } + + @Override + public View getView() { + if (view == null) { + view = (ScrollView) getDispatchedView(); + } + + return view; + } + + @Override + public View getDispatchedView() { + if (cache == null) { + // something is really wrong + return null; + } + + view = (ScrollView) getLayoutInflater().inflate(R.layout.cacheview_description, null); + + // cache short description + if (StringUtils.isNotBlank(cache.getShortdesc())) { + TextView descView = (TextView) view.findViewById(R.id.shortdesc); + descView.setVisibility(View.VISIBLE); + descView.setText(Html.fromHtml(cache.getShortdesc().trim(), new HtmlImage(CacheDetailActivity.this, cache.getGeocode(), true, cache.getReason(), false), null), TextView.BufferType.SPANNABLE); + descView.setMovementMethod(LinkMovementMethod.getInstance()); + } + + // long description + if (StringUtils.isNotBlank(cache.getDescription())) { + if (Settings.isAutoLoadDescription()) { + loadLongDescription(); + } else { + Button showDesc = (Button) view.findViewById(R.id.show_description); + showDesc.setVisibility(View.VISIBLE); + showDesc.setOnClickListener(new View.OnClickListener() { + public void onClick(View arg0) { + loadLongDescription(); + } + }); + } + } + + // cache attributes + if (CollectionUtils.isNotEmpty(cache.getAttributes())) { + + final LinearLayout attribBox = (LinearLayout) view.findViewById(R.id.attributes_innerbox); + + // maximum width for attribute icons is screen width - paddings of parents + attributeBoxMaxWidth = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay().getWidth(); + ViewParent child = attribBox; + do { + if (child instanceof View) { + attributeBoxMaxWidth = attributeBoxMaxWidth - ((View) child).getPaddingLeft() + - ((View) child).getPaddingRight(); + } + child = child.getParent(); + } while (child != null); + + // delete views holding description / icons + attributeDescriptionsLayout = null; + attributeIconsLayout = null; + + attribBox.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // toggle between attribute icons and descriptions + toggleAttributeDisplay(attribBox, attributeBoxMaxWidth); + } + }); + + // icons or text? + // + // also show icons when noAttributeImagesFound == true. Explanation: + // 1. no icons could be found in the first invocation of this method + // 2. user refreshes cache from web + // 3. now this method is called again + // 4. attributeShowAsIcons is false but noAttributeImagesFound is true + // => try to show them now + if (attributesShowAsIcons || noAttributeIconsFound) { + showAttributeIcons(attribBox, attributeBoxMaxWidth); + } else { + showAttributeDescriptions(attribBox); + } + + view.findViewById(R.id.attributes_box).setVisibility(View.VISIBLE); + } + + // cache personal note + if (StringUtils.isNotBlank(cache.getPersonalNote())) { + ((LinearLayout) view.findViewById(R.id.personalnote_box)).setVisibility(View.VISIBLE); + + TextView personalNoteText = (TextView) view.findViewById(R.id.personalnote); + personalNoteText.setVisibility(View.VISIBLE); + personalNoteText.setText(cache.getPersonalNote(), TextView.BufferType.SPANNABLE); + personalNoteText.setMovementMethod(LinkMovementMethod.getInstance()); + } + else { + ((LinearLayout) view.findViewById(R.id.personalnote_box)).setVisibility(View.GONE); + } + + // cache hint and spoiler images + if (StringUtils.isNotBlank(cache.getHint()) || CollectionUtils.isNotEmpty(cache.getSpoilers())) { + ((LinearLayout) view.findViewById(R.id.hint_box)).setVisibility(View.VISIBLE); + } else { + ((LinearLayout) view.findViewById(R.id.hint_box)).setVisibility(View.GONE); + } + + if (StringUtils.isNotBlank(cache.getHint())) { + TextView hintView = ((TextView) view.findViewById(R.id.hint)); + hintView.setText(CryptUtils.rot13(cache.getHint().trim())); + hintView.setVisibility(View.VISIBLE); + hintView.setClickable(true); + hintView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // code hint + TextView hintView = (TextView) view; + hintView.setText(CryptUtils.rot13(hintView.getText().toString())); + } + }); + } else { + TextView hintView = ((TextView) view.findViewById(R.id.hint)); + hintView.setVisibility(View.GONE); + hintView.setClickable(false); + hintView.setOnClickListener(null); + } + + if (CollectionUtils.isNotEmpty(cache.getSpoilers())) { + TextView spoilerlinkView = ((TextView) view.findViewById(R.id.hint_spoilerlink)); + spoilerlinkView.setVisibility(View.VISIBLE); + spoilerlinkView.setClickable(true); + spoilerlinkView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View arg0) { + if (cache == null || CollectionUtils.isEmpty(cache.getSpoilers())) { + showToast(res.getString(R.string.err_detail_no_spoiler)); + return; + } + + cgeoimages.startActivitySpoilerImages(CacheDetailActivity.this, cache.getGeocode(), cache.getSpoilers()); + } + }); + } else { + TextView spoilerlinkView = ((TextView) view.findViewById(R.id.hint_spoilerlink)); + spoilerlinkView.setVisibility(View.GONE); + spoilerlinkView.setClickable(true); + spoilerlinkView.setOnClickListener(null); + } + + return view; + } + + private void loadLongDescription() { + 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 LoadLongDescriptionThread(new LoadLongDescriptionHandler()).start(); + } + + private class LoadLongDescriptionHandler extends Handler { + @Override + public void handleMessage(Message msg) { + Spanned longDesc = (Spanned) msg.obj; + + if (longDesc != null) { + TextView descView = (TextView) view.findViewById(R.id.longdesc); + if (StringUtils.isNotBlank(cache.getDescription())) { + descView.setText(longDesc, TextView.BufferType.SPANNABLE); + descView.setMovementMethod(LinkMovementMethod.getInstance()); + // handle caches with black font color + if (!Settings.isLightSkin()) { + if (cache.getDescription().contains("color=\"#000000")) { + descView.setBackgroundResource(color.darker_gray); + } + else { + descView.setBackgroundResource(color.black); + } + } + } + + descView.setVisibility(View.VISIBLE); + } else { + showToast(res.getString(R.string.err_load_descr_failed)); + } + + view.findViewById(R.id.loading).setVisibility(View.GONE); + } + } + + private class LoadLongDescriptionThread extends Thread { + private Handler handler = null; + + public LoadLongDescriptionThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + Spanned longDesc = Html.fromHtml(cache.getDescription().trim(), new HtmlImage(CacheDetailActivity.this, cache.getGeocode(), true, cache.getReason(), false), new UnknownTagsHandler()); + + handler.obtainMessage(0, longDesc).sendToTarget(); + } + } + + /** + * lazy-creates the layout holding the icons of the chaches attributes + * and makes it visible + */ + private void showAttributeIcons(LinearLayout attribBox, int parentWidth) { + if (attributeIconsLayout == null) { + attributeIconsLayout = createAttributeIconsLayout(parentWidth); + // no matching icons found? show text + if (noAttributeIconsFound) { + showAttributeDescriptions(attribBox); + return; + } + } + attribBox.removeAllViews(); + attribBox.addView(attributeIconsLayout); + attributesShowAsIcons = true; + } + + /** + * lazy-creates the layout holding the discriptions of the chaches attributes + * and makes it visible + */ + private void showAttributeDescriptions(LinearLayout attribBox) { + if (attributeDescriptionsLayout == null) { + attributeDescriptionsLayout = createAttributeDescriptionsLayout(); + } + attribBox.removeAllViews(); + attribBox.addView(attributeDescriptionsLayout); + attributesShowAsIcons = false; + } + + /** + * toggle attribute descriptions and icons + */ + private void toggleAttributeDisplay(LinearLayout attribBox, int parentWidth) { + // Don't toggle when there are no icons to show. + if (noAttributeIconsFound) { + return; + } + + // toggle + if (attributesShowAsIcons) { + showAttributeDescriptions(attribBox); + } else { + showAttributeIcons(attribBox, parentWidth); + } + } + + private ViewGroup createAttributeIconsLayout(int parentWidth) { + LinearLayout rows = new LinearLayout(CacheDetailActivity.this); + rows.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); + rows.setOrientation(LinearLayout.VERTICAL); + + LinearLayout attributeRow = newAttributeIconsRow(); + rows.addView(attributeRow); + + noAttributeIconsFound = true; + + final String packageName = cgeoapplication.getInstance().getBaseContext().getPackageName(); + for (String attributeName : cache.getAttributes()) { + boolean strikethru = attributeName.endsWith("_no"); + // cut off _yes / _no + if (attributeName.endsWith("_no") || attributeName.endsWith("_yes")) { + attributeName = attributeName.substring(0, attributeName.lastIndexOf("_")); + } + // check if another attribute icon fits in this row + attributeRow.measure(0, 0); + int rowWidth = attributeRow.getMeasuredWidth(); + FrameLayout fl = (FrameLayout) getLayoutInflater().inflate(R.layout.attribute_image, null); + ImageView iv = (ImageView) fl.getChildAt(0); + if ((parentWidth - rowWidth) < iv.getLayoutParams().width) { + // make a new row + attributeRow = newAttributeIconsRow(); + rows.addView(attributeRow); + } + + // dynamically search icon of the attribute + Drawable d = null; + int id = res.getIdentifier("attribute_" + attributeName, "drawable", packageName); + if (id > 0) { + noAttributeIconsFound = false; + d = res.getDrawable(id); + iv.setImageDrawable(d); + // strike through? + if (strikethru) { + // generate strikethru image with same properties as attribute image + ImageView strikethruImage = new ImageView(CacheDetailActivity.this); + strikethruImage.setLayoutParams(iv.getLayoutParams()); + d = res.getDrawable(R.drawable.attribute__strikethru); + strikethruImage.setImageDrawable(d); + fl.addView(strikethruImage); + } + } else { + d = res.getDrawable(R.drawable.attribute_icon_not_found); + iv.setImageDrawable(d); + } + + attributeRow.addView(fl); + } + + return rows; + } + + private LinearLayout newAttributeIconsRow() { + LinearLayout rowLayout = new LinearLayout(CacheDetailActivity.this); + rowLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.WRAP_CONTENT)); + rowLayout.setOrientation(LinearLayout.HORIZONTAL); + return rowLayout; + } + + private ViewGroup createAttributeDescriptionsLayout() { + final LinearLayout descriptions = (LinearLayout) getLayoutInflater().inflate( + R.layout.attribute_descriptions, null); + TextView attribView = (TextView) descriptions.getChildAt(0); + + StringBuilder buffer = new StringBuilder(); + String attribute; + final String packageName = cgeoapplication.getInstance().getBaseContext().getPackageName(); + for (int i = 0; i < cache.getAttributes().size(); i++) { + attribute = cache.getAttributes().get(i); + + // dynamically search for a translation of the attribute + int id = res.getIdentifier("attribute_" + attribute, "string", packageName); + if (id > 0) { + String translated = res.getString(id); + if (StringUtils.isNotBlank(translated)) { + attribute = translated; + } + } + if (buffer.length() > 0) { + buffer.append('\n'); + } + buffer.append(attribute); + } + + if (noAttributeIconsFound) { + buffer.append("\n\n").append(res.getString(R.string.cache_attributes_no_icons)); + } + + attribView.setText(buffer); + + return descriptions; + } + } + + private class LogsViewCreator implements PageViewCreator { + + ScrollView view; + + @Override + public void notifyDataSetChanged() { + view = null; + } + + @Override + public View getView() { + if (view == null) { + view = (ScrollView) getDispatchedView(); + } + + return view; + } + + @Override + public View getDispatchedView() { + if (cache == null) { + // something is really wrong + return null; + } + + view = (ScrollView) getLayoutInflater().inflate(R.layout.cacheview_logs, null); + + TextView logCounterView = (TextView) view.findViewById(R.id.log_count); + int logCounter = 0; + if (cache != null && cache.getLogCounts() != null) { + final StringBuilder text = new StringBuilder(); + text.append(res.getString(R.string.cache_log_types)); + text.append(": "); + + // sort the log counts by type id ascending. that way the FOUND, DNF log types are the first and most visible ones + List<Entry<Integer, Integer>> sortedLogCounts = new ArrayList<Entry<Integer, Integer>>(); + sortedLogCounts.addAll(cache.getLogCounts().entrySet()); + Collections.sort(sortedLogCounts, new Comparator<Entry<Integer, Integer>>() { + + @Override + public int compare(Entry<Integer, Integer> logCountItem1, + Entry<Integer, Integer> logCountItem2) { + return logCountItem1.getKey().compareTo(logCountItem2.getKey()); + } + }); + for (Entry<Integer, Integer> pair : sortedLogCounts) { + int logTypeId = pair.getKey().intValue(); + String logTypeLabel = cgBase.logTypes1.get(logTypeId); + // it may happen that the label is unknown -> then avoid any output for this type + if (logTypeLabel != null) { + if (logCounter > 0) { + text.append(", "); + } + text.append(pair.getValue().intValue()); + text.append("× "); + text.append(logTypeLabel); + } + logCounter++; + } + logCounterView.setText(text.toString()); + } + // it may happen, that the logCounts map is available, but every log type has zero counts, + // therefore check again for the number of counted logs + if (logCounter > 0) { + logCounterView.setVisibility(View.VISIBLE); + } else { + logCounterView.setVisibility(View.GONE); + } + + // cache logs + LinearLayout logListView = (LinearLayout) view.findViewById(R.id.log_list); + + RelativeLayout rowView; + + if (cache != null && cache.getLogs() != null) { + for (cgLog log : cache.getLogs()) { + rowView = (RelativeLayout) getLayoutInflater().inflate(R.layout.log_item, null); + + if (log.date > 0) { + ((TextView) rowView.findViewById(R.id.added)).setText(cgBase.formatShortDate(log.date)); + } else { + ((TextView) rowView.findViewById(R.id.added)).setVisibility(View.GONE); + } + + if (cgBase.logTypes1.containsKey(log.type)) { + ((TextView) rowView.findViewById(R.id.type)).setText(cgBase.logTypes1.get(log.type)); + } else { + ((TextView) rowView.findViewById(R.id.type)).setText(cgBase.logTypes1.get(4)); // note if type is unknown + } + ((TextView) rowView.findViewById(R.id.author)).setText(StringEscapeUtils.unescapeHtml4(log.author)); + + if (log.found == -1) { + ((TextView) rowView.findViewById(R.id.count)).setVisibility(View.GONE); + } else if (log.found == 0) { + ((TextView) rowView.findViewById(R.id.count)).setText(res.getString(R.string.cache_count_no)); + } else if (log.found == 1) { + ((TextView) rowView.findViewById(R.id.count)).setText(res.getString(R.string.cache_count_one)); + } else { + ((TextView) rowView.findViewById(R.id.count)).setText(log.found + " " + res.getString(R.string.cache_count_more)); + } + // avoid parsing HTML if not necessary + if (BaseUtils.containsHtml(log.log)) { + ((TextView) rowView.findViewById(R.id.log)).setText(Html.fromHtml(log.log, new HtmlImage(CacheDetailActivity.this, null, false, cache.getReason(), false), null), TextView.BufferType.SPANNABLE); + } + else { + ((TextView) rowView.findViewById(R.id.log)).setText(log.log); + } + // add LogImages + LinearLayout logLayout = (LinearLayout) rowView.findViewById(R.id.log_layout); + + if (CollectionUtils.isNotEmpty(log.logImages)) { + + final ArrayList<cgImage> logImages = new ArrayList<cgImage>(log.logImages); + + final View.OnClickListener listener = new View.OnClickListener() { + @Override + public void onClick(View v) { + cgeoimages.startActivityLogImages(CacheDetailActivity.this, cache.getGeocode(), logImages); + } + }; + + ArrayList<String> titles = new ArrayList<String>(); + for (int i_img_cnt = 0; i_img_cnt < log.logImages.size(); i_img_cnt++) { + String img_title = log.logImages.get(i_img_cnt).getTitle(); + if (!StringUtils.isBlank(img_title)) { + titles.add(img_title); + } + } + if (titles.isEmpty()) { + titles.add(res.getString(R.string.cache_log_image_default_title)); + } + + LinearLayout log_imgView = (LinearLayout) getLayoutInflater().inflate(R.layout.log_img, null); + TextView log_img_title = (TextView) log_imgView.findViewById(R.id.title); + log_img_title.setText(StringUtils.join(titles.toArray(new String[titles.size()]), ", ")); + log_img_title.setOnClickListener(listener); + logLayout.addView(log_imgView); + } + + // Add colored mark + final ImageView logMark = (ImageView) rowView.findViewById(R.id.log_mark); + if (log.type == cgBase.LOG_FOUND_IT + || log.type == cgBase.LOG_WEBCAM_PHOTO_TAKEN + || log.type == cgBase.LOG_ATTENDED) { + logMark.setImageResource(R.drawable.mark_green); + } else if (log.type == cgBase.LOG_PUBLISH_LISTING + || log.type == cgBase.LOG_ENABLE_LISTING + || log.type == cgBase.LOG_OWNER_MAINTENANCE) { + logMark.setImageResource(R.drawable.mark_green_more); + } else if (log.type == cgBase.LOG_DIDNT_FIND_IT + || log.type == cgBase.LOG_NEEDS_MAINTENANCE + || log.type == cgBase.LOG_NEEDS_ARCHIVE) { + logMark.setImageResource(R.drawable.mark_red); + } else if (log.type == cgBase.LOG_TEMP_DISABLE_LISTING + || log.type == cgBase.LOG_ARCHIVE) { + logMark.setImageResource(R.drawable.mark_red_more); + } else { + logMark.setVisibility(View.GONE); + } + + ((TextView) rowView.findViewById(R.id.author)).setOnClickListener(new UserActionsClickListener()); + ((TextView) logLayout.findViewById(R.id.log)).setOnClickListener(new DecryptLogClickListener()); + + logListView.addView(rowView); + } + } + + return view; + } + + private class DecryptLogClickListener implements View.OnClickListener { + + public void onClick(View view) { + if (view == null) { + return; + } + + try { + final TextView logView = (TextView) view; + CharSequence text = logView.getText(); + if (text instanceof Spannable) { + Spannable span = (Spannable) text; + logView.setText(CryptUtils.rot13(span)); + } + else { + String string = (String) text; + logView.setText(CryptUtils.rot13(string)); + } + } catch (Exception e) { + // nothing + } + } + } + } + + private class WaypointsViewCreator implements PageViewCreator { + + ScrollView view; + + @Override + public void notifyDataSetChanged() { + view = null; + } + + @Override + public View getView() { + if (view == null) { + view = (ScrollView) getDispatchedView(); + } + + return view; + } + + @Override + public View getDispatchedView() { + if (cache == null) { + // something is really wrong + return null; + } + + view = (ScrollView) getLayoutInflater().inflate(R.layout.cacheview_waypoints, null); + + LinearLayout waypoints = (LinearLayout) view.findViewById(R.id.waypoints); + + if (CollectionUtils.isNotEmpty(cache.getWaypoints())) { + LinearLayout waypointView; + + // sort waypoints: PP, Sx, FI, OWN + List<cgWaypoint> sortedWaypoints = new ArrayList<cgWaypoint>(cache.getWaypoints()); + Collections.sort(sortedWaypoints); + + for (cgWaypoint wpt : sortedWaypoints) { + waypointView = (LinearLayout) getLayoutInflater().inflate(R.layout.waypoint_item, null); + + final List<String> infoTextList = new ArrayList<String>(3); + infoTextList.add(cgBase.waypointTypes.get(wpt.getWaypointType())); + if ("OWN".equalsIgnoreCase(wpt.getPrefix())) { + infoTextList.add(res.getString(R.string.waypoint_custom)); + } else { + if (StringUtils.isNotBlank(wpt.getPrefix())) { + infoTextList.add(wpt.getPrefix()); + } + if (StringUtils.isNotBlank(wpt.getLookup())) { + infoTextList.add(wpt.getLookup()); + } + } + ((TextView) waypointView.findViewById(R.id.info)).setText(StringUtils.join(infoTextList, " · ")); + + TextView nameView = (TextView) waypointView.findViewById(R.id.name); + if (StringUtils.isNotBlank(wpt.getName())) { + nameView.setText(StringEscapeUtils.unescapeHtml4(wpt.getName())); + } else if (null != wpt.getCoords()) { + nameView.setText(wpt.getCoords().toString()); + } else { + nameView.setText(res.getString(R.string.waypoint)); + } + + wpt.setIcon(res, nameView); + + if (StringUtils.isNotBlank(wpt.getNote())) { + TextView noteView = (TextView) waypointView.findViewById(R.id.note); + noteView.setVisibility(View.VISIBLE); + if (BaseUtils.containsHtml(wpt.getNote())) { + noteView.setText(Html.fromHtml(wpt.getNote().trim()), TextView.BufferType.SPANNABLE); + } + else { + noteView.setText(wpt.getNote().trim()); + } + } + + waypointView.setOnClickListener(new WaypointInfoClickListener(wpt.getId())); + registerForContextMenu(waypointView); + + waypoints.addView(waypointView); + } + } + + Button addWaypoint = (Button) view.findViewById(R.id.add_waypoint); + addWaypoint.setClickable(true); + addWaypoint.setOnClickListener(new AddWaypointClickListener()); + + return view; + } + + private class AddWaypointClickListener implements View.OnClickListener { + + public void onClick(View view) { + Intent addWptIntent = new Intent(CacheDetailActivity.this, cgeowaypointadd.class); + + addWptIntent.putExtra("geocode", cache.getGeocode()); + int wpCount = 0; + if (cache.getWaypoints() != null) { + wpCount = cache.getWaypoints().size(); + } + addWptIntent.putExtra("count", wpCount); + + startActivity(addWptIntent); + refreshOnResume = true; + } + } + + private class WaypointInfoClickListener implements View.OnClickListener { + private int id = -1; + + public WaypointInfoClickListener(int idIn) { + id = idIn; + } + + public void onClick(View arg0) { + Intent waypointIntent = new Intent(CacheDetailActivity.this, cgeowaypoint.class); + waypointIntent.putExtra("waypoint", id); + waypointIntent.putExtra("geocode", cache.getGeocode()); + startActivity(waypointIntent); + } + } + } + + private class InventoryViewCreator implements PageViewCreator { + + ListView view; + + @Override + public void notifyDataSetChanged() { + view = null; + } + + @Override + public View getView() { + if (view == null) { + view = (ListView) getDispatchedView(); + } + + return view; + } + + @Override + public View getDispatchedView() { + if (cache == null) { + // something is really wrong + return null; + } + + view = (ListView) getLayoutInflater().inflate(R.layout.cacheview_inventory, null); + + view.setAdapter(new ArrayAdapter<cgTrackable>(CacheDetailActivity.this, android.R.layout.simple_list_item_1, cache.getInventory())); + view.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { + Object selection = arg0.getItemAtPosition(arg2); + if (selection instanceof cgTrackable) { + cgTrackable trackable = (cgTrackable) selection; + cgeotrackable.startActivity(CacheDetailActivity.this, trackable.getGuid(), trackable.getGeocode(), trackable.getName()); + } + } + }); + + return view; + } + } +} diff --git a/main/src/cgeo/geocaching/cgCacheListAdapter.java b/main/src/cgeo/geocaching/cgCacheListAdapter.java index 1fce381..1602f84 100644 --- a/main/src/cgeo/geocaching/cgCacheListAdapter.java +++ b/main/src/cgeo/geocaching/cgCacheListAdapter.java @@ -663,7 +663,7 @@ public class cgCacheListAdapter extends ArrayAdapter<cgCache> { } // load cache details - Intent cachesIntent = new Intent(getContext(), cgeodetail.class); + Intent cachesIntent = new Intent(getContext(), CacheDetailActivity.class); cachesIntent.putExtra("geocode", geocode); cachesIntent.putExtra("name", name); getContext().startActivity(cachesIntent); diff --git a/main/src/cgeo/geocaching/cgTrackable.java b/main/src/cgeo/geocaching/cgTrackable.java index c37d411..bcf3c83 100644 --- a/main/src/cgeo/geocaching/cgTrackable.java +++ b/main/src/cgeo/geocaching/cgTrackable.java @@ -1,5 +1,7 @@ package cgeo.geocaching; +import android.text.Html; + import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -174,4 +176,16 @@ public class cgTrackable implements ILogable { this.logs = logs; } + @Override + public String toString() { + if (null != name) { + return Html.fromHtml(name).toString(); + } + + if (guid != null) { + return guid; + } + + return "???"; + } } diff --git a/main/src/cgeo/geocaching/cgeo.java b/main/src/cgeo/geocaching/cgeo.java index 0c9dc90..e24a4bb 100644 --- a/main/src/cgeo/geocaching/cgeo.java +++ b/main/src/cgeo/geocaching/cgeo.java @@ -309,7 +309,7 @@ public class cgeo extends AbstractActivity { String host = "http://coord.info/"; if (scan.toLowerCase().startsWith(host)) { String geocode = scan.substring(host.length()).trim(); - cgeodetail.startActivity(this, geocode); + CacheDetailActivity.startActivity(this, geocode); } else { showToast(res.getString(R.string.unknown_scan)); diff --git a/main/src/cgeo/geocaching/cgeoadvsearch.java b/main/src/cgeo/geocaching/cgeoadvsearch.java index e0b68f5..5fe84c4 100644 --- a/main/src/cgeo/geocaching/cgeoadvsearch.java +++ b/main/src/cgeo/geocaching/cgeoadvsearch.java @@ -118,7 +118,7 @@ public class cgeoadvsearch extends AbstractActivity { try { if (gcCodeM.find()) { // GC-code - final Intent cachesIntent = new Intent(this, cgeodetail.class); + final Intent cachesIntent = new Intent(this, CacheDetailActivity.class); cachesIntent.putExtra("geocode", query.trim().toUpperCase()); startActivity(cachesIntent); @@ -423,7 +423,7 @@ public class cgeoadvsearch extends AbstractActivity { return; } - cgeodetail.startActivity(this, geocodeText); + CacheDetailActivity.startActivity(this, geocodeText); } private class findTrackableAction implements TextView.OnEditorActionListener { diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/cgeocaches.java index 40200db..f3b20b7 100644 --- a/main/src/cgeo/geocaching/cgeocaches.java +++ b/main/src/cgeo/geocaching/cgeocaches.java @@ -1203,7 +1203,7 @@ public class cgeocaches extends AbstractListActivity { } else if (id == MENU_LOG_VISIT) { return getCacheFromAdapter(adapterInfo).logVisit(this); } else if (id == MENU_CACHE_DETAILS) { - final Intent cachesIntent = new Intent(this, cgeodetail.class); + final Intent cachesIntent = new Intent(this, CacheDetailActivity.class); final cgCache cache = getCacheFromAdapter(adapterInfo); cachesIntent.putExtra("geocode", cache.getGeocode().toUpperCase()); cachesIntent.putExtra("name", cache.getName()); diff --git a/main/src/cgeo/geocaching/cgeopopup.java b/main/src/cgeo/geocaching/cgeopopup.java index 11c8b25..2820448 100644 --- a/main/src/cgeo/geocaching/cgeopopup.java +++ b/main/src/cgeo/geocaching/cgeopopup.java @@ -389,7 +389,7 @@ public class cgeopopup extends AbstractActivity { buttonMore.setOnClickListener(new OnClickListener() { public void onClick(View arg0) { - Intent cachesIntent = new Intent(cgeopopup.this, cgeodetail.class); + Intent cachesIntent = new Intent(cgeopopup.this, CacheDetailActivity.class); cachesIntent.putExtra("geocode", geocode.toUpperCase()); startActivity(cachesIntent); diff --git a/main/src/cgeo/geocaching/cgeotrackable.java b/main/src/cgeo/geocaching/cgeotrackable.java index 6258e2b..a7a93e4 100644 --- a/main/src/cgeo/geocaching/cgeotrackable.java +++ b/main/src/cgeo/geocaching/cgeotrackable.java @@ -168,7 +168,7 @@ public class cgeotrackable extends AbstractActivity { if (cgTrackable.SPOTTED_CACHE == trackable.getSpottedType()) { itemLayout.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { - Intent cacheIntent = new Intent(cgeotrackable.this, cgeodetail.class); + Intent cacheIntent = new Intent(cgeotrackable.this, CacheDetailActivity.class); cacheIntent.putExtra("guid", trackable.getSpottedGuid()); cacheIntent.putExtra("name", trackable.getSpottedName()); startActivity(cacheIntent); @@ -506,7 +506,7 @@ public class cgeotrackable extends AbstractActivity { final String cacheName = log.cacheName; ((TextView) rowView.findViewById(R.id.location)).setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { - Intent cacheIntent = new Intent(cgeotrackable.this, cgeodetail.class); + Intent cacheIntent = new Intent(cgeotrackable.this, CacheDetailActivity.class); cacheIntent.putExtra("guid", cacheGuid); cacheIntent.putExtra("name", Html.fromHtml(cacheName).toString()); startActivity(cacheIntent); diff --git a/main/src/cgeo/geocaching/maps/OtherCachersOverlay.java b/main/src/cgeo/geocaching/maps/OtherCachersOverlay.java index 9306041..c65b4ba 100644 --- a/main/src/cgeo/geocaching/maps/OtherCachersOverlay.java +++ b/main/src/cgeo/geocaching/maps/OtherCachersOverlay.java @@ -1,8 +1,8 @@ package cgeo.geocaching.maps; +import cgeo.geocaching.CacheDetailActivity; import cgeo.geocaching.Settings; import cgeo.geocaching.cgeoapplication; -import cgeo.geocaching.cgeodetail; import cgeo.geocaching.go4cache.Go4CacheUser; import cgeo.geocaching.maps.interfaces.ItemizedOverlayImpl; import cgeo.geocaching.maps.interfaces.MapProjectionImpl; @@ -143,7 +143,7 @@ public class OtherCachersOverlay extends AbstractItemizedOverlay { public void onClick(DialogInterface dialog, int id) { if (geocode != null) { - final Intent detailIntent = new Intent(context, cgeodetail.class); + final Intent detailIntent = new Intent(context, CacheDetailActivity.class); detailIntent.putExtra("geocode", geocode); context.startActivity(detailIntent); } diff --git a/main/src/cgeo/geocaching/utils/BaseUtils.java b/main/src/cgeo/geocaching/utils/BaseUtils.java index 33a4be5..c5aea8a 100644 --- a/main/src/cgeo/geocaching/utils/BaseUtils.java +++ b/main/src/cgeo/geocaching/utils/BaseUtils.java @@ -151,4 +151,14 @@ public final class BaseUtils { return String.valueOf(chars, 0, resultSize); } + /** + * Quick and naive check for possible html-content of a string. + * + * @param str + * @return True, if <code>str</code> could contain html + */ + public static boolean containsHtml(final String str) { + return str.indexOf('<') != -1 || str.indexOf('&') != -1; + } + } |