diff options
29 files changed, 376 insertions, 101 deletions
diff --git a/main/.project b/main/.project index 80a267b..5f0d599 100644 --- a/main/.project +++ b/main/.project @@ -27,7 +27,15 @@ </buildCommand> </buildSpec> <natures> + <nature>org.sonar.ide.eclipse.core.sonarNature</nature> <nature>com.android.ide.eclipse.adt.AndroidNature</nature> <nature>org.eclipse.jdt.core.javanature</nature> </natures> + <linkedResources> + <link> + <name>testlink</name> + <type>2</type> + <location>/home/bananeweizen/workspaces/egit</location> + </link> + </linkedResources> </projectDescription> diff --git a/main/res/values/changelog_master.xml b/main/res/values/changelog_master.xml index 501cf21..1083326 100644 --- a/main/res/values/changelog_master.xml +++ b/main/res/values/changelog_master.xml @@ -5,10 +5,15 @@ <b>Next feature release:</b>\n · New: Show also own logs on friend log page\n · New: Filter for caches which are not found\n + · New: Show \"Import from web\" also if not yet registered\n + · New: Debugging option to save memory dumps on user demand\n · Fix: Hiding own caches on opencaching\n · Fix: Webcam caches not marked as found after posting log\n · Fix: Archived caches now also hidden if hiding disabled caches is active\n · Fix: Filter invalid characters in GPX files\n + · Fix: All caches shown as owned if no username stored\n + · Fix: GPX import from mail failing on some devices\n + · Fix: Updating cache history should not ask for list to save caches\n \n \n </string> diff --git a/main/res/values/preference_keys.xml b/main/res/values/preference_keys.xml index 94a6d2a..2e3de84 100644 --- a/main/res/values/preference_keys.xml +++ b/main/res/values/preference_keys.xml @@ -169,5 +169,6 @@ <string name="pref_twitter_cache_message">twitter_cache_message</string> <string name="pref_twitter_trackable_message">twitter_trackable_message</string> <string name="pref_ec_icons">ec_icons</string> + <string name="pref_memory_dump">memory_dump</string> <string name="pref_appearance">pref_appearence</string> -</resources>
\ No newline at end of file +</resources> diff --git a/main/res/values/strings.xml b/main/res/values/strings.xml index b164070..7a9194e 100644 --- a/main/res/values/strings.xml +++ b/main/res/values/strings.xml @@ -545,6 +545,9 @@ <string name="init_maintenance">Maintenance</string> <string name="init_maintenance_directories_note">c:geo stores images, log images and other files related to a cache in a separate directory. In some cases (like importing/exporting the database) this directory may contain outdated files, which can be deleted here.</string> <string name="init_maintenance_directories">Delete orphaned files</string> + <string name="init_create_memory_dump">Create memory dump</string> + <string name="init_memory_dump">Memory dump</string> + <string name="init_memory_dumped">Memory dumped to %s</string> <string name="settings_open_website">Open website</string> <string name="settings_settings">Settings</string> <string name="settings_information">Information</string> diff --git a/main/res/xml/preferences.xml b/main/res/xml/preferences.xml index 0bb419e..124bd0d 100644 --- a/main/res/xml/preferences.xml +++ b/main/res/xml/preferences.xml @@ -740,7 +740,10 @@ android:defaultValue="false" android:key="@string/pref_debug" android:title="@string/init_debug" /> + <Preference + android:key="@string/pref_memory_dump" + android:title="@string/init_create_memory_dump" /> </PreferenceCategory> </PreferenceScreen> -</PreferenceScreen>
\ No newline at end of file +</PreferenceScreen> diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java index 1456b0e..c3125ab 100644 --- a/main/src/cgeo/geocaching/CgeoApplication.java +++ b/main/src/cgeo/geocaching/CgeoApplication.java @@ -11,12 +11,12 @@ import rx.functions.Action1; import rx.observables.ConnectableObservable; import android.app.Application; +import android.os.Environment; import android.view.ViewConfiguration; -import java.lang.reflect.Field; - import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; +import java.lang.reflect.Field; public class CgeoApplication extends Application { @@ -29,10 +29,8 @@ public class CgeoApplication extends Application { private volatile IGeoData currentGeo = null; private volatile float currentDirection = 0.0f; - private static final UncaughtExceptionHandler defaultHandler; - static { - defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + final UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @@ -43,11 +41,10 @@ public class CgeoApplication extends Application { while (exx.getCause() != null) { exx = exx.getCause(); } - if (exx.getClass().equals(OutOfMemoryError.class)) - { + if (exx.getClass().equals(OutOfMemoryError.class)) { try { Log.e("OutOfMemory"); - android.os.Debug.dumpHprofData("/sdcard/dump.hprof"); + android.os.Debug.dumpHprofData(Environment.getExternalStorageDirectory().getPath() + "/dump.hprof"); } catch (IOException e) { Log.e("Error writing dump", e); } diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 925f6f0..a38bad0 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -192,8 +192,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public boolean isOwner(final ICache cache) { - return StringUtils.equalsIgnoreCase(cache.getOwnerUserId(), Settings.getUsername()); - + final String user = Settings.getUsername(); + return StringUtils.isNotEmpty(user) && StringUtils.equalsIgnoreCase(cache.getOwnerUserId(), user); } @Override diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java index bdcd78e..3771443 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java @@ -151,7 +151,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente @Override public boolean isOwner(ICache cache) { - return StringUtils.equals(cache.getOwnerDisplayName(), getUserName()); + return StringUtils.isNotEmpty(getUserName()) && StringUtils.equals(cache.getOwnerDisplayName(), getUserName()); } @Override diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java index 08fca0b..39f4bcf 100644 --- a/main/src/cgeo/geocaching/export/GpxExport.java +++ b/main/src/cgeo/geocaching/export/GpxExport.java @@ -8,6 +8,7 @@ import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.AsyncTaskWithProgress; import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.ShareUtils; import org.apache.commons.lang3.CharEncoding; @@ -15,8 +16,6 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; import android.os.Environment; import android.view.ContextThemeWrapper; import android.view.View; @@ -168,11 +167,7 @@ class GpxExport extends AbstractExport { if (exportFile != null) { ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString()); if (Settings.getShareAfterExport()) { - final Intent shareIntent = new Intent(); - shareIntent.setAction(Intent.ACTION_SEND); - shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(exportFile)); - shareIntent.setType("application/xml"); - activity.startActivity(Intent.createChooser(shareIntent, getString(R.string.export_gpx_to))); + ShareUtils.share(activity, exportFile, "application/xml", R.string.export_gpx_to); } } else { ActivityMixin.showToast(activity, getString(R.string.export_failed)); diff --git a/main/src/cgeo/geocaching/files/FileType.java b/main/src/cgeo/geocaching/files/FileType.java new file mode 100644 index 0000000..ef62351 --- /dev/null +++ b/main/src/cgeo/geocaching/files/FileType.java @@ -0,0 +1,8 @@ +package cgeo.geocaching.files; + +public enum FileType { + UNKNOWN, + LOC, + GPX, + ZIP +} diff --git a/main/src/cgeo/geocaching/files/FileTypeDetector.java b/main/src/cgeo/geocaching/files/FileTypeDetector.java new file mode 100644 index 0000000..389b83a --- /dev/null +++ b/main/src/cgeo/geocaching/files/FileTypeDetector.java @@ -0,0 +1,77 @@ +package cgeo.geocaching.files; + +import cgeo.geocaching.utils.Log; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; + +import android.content.ContentResolver; +import android.net.Uri; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class FileTypeDetector { + + private final ContentResolver contentResolver; + private final Uri uri; + + public FileTypeDetector(Uri uri, ContentResolver contentResolver) { + this.uri = uri; + this.contentResolver = contentResolver; + } + + public @NonNull FileType getFileType() { + InputStream is = null; + BufferedReader reader = null; + FileType type = FileType.UNKNOWN; + try { + is = contentResolver.openInputStream(uri); + if (is == null) { + return FileType.UNKNOWN; + } + reader = new BufferedReader(new InputStreamReader(is)); + type = detectHeader(reader); + reader.close(); + } catch (FileNotFoundException e) { + Log.e("FileTypeDetector", e); + } catch (IOException e) { + Log.e("FileTypeDetector", e); + } finally { + IOUtils.closeQuietly(reader); + IOUtils.closeQuietly(is); + } + return type; + } + + private static FileType detectHeader(BufferedReader reader) + throws IOException { + String line = reader.readLine(); + if (isZip(line)) { + return FileType.ZIP; + } + // scan at most 5 lines of a GPX file + for (int i = 0; i < 5; i++) { + line = StringUtils.trim(line); + if (StringUtils.contains(line, "<loc")) { + return FileType.LOC; + } + if (StringUtils.contains(line, "<gpx")) { + return FileType.GPX; + } + line = reader.readLine(); + } + return FileType.UNKNOWN; + } + + private static boolean isZip(String line) { + return StringUtils.length(line) >= 4 + && StringUtils.startsWith(line, "PK") && line.charAt(2) == 3 + && line.charAt(3) == 4; + } + +} diff --git a/main/src/cgeo/geocaching/files/GPXImporter.java b/main/src/cgeo/geocaching/files/GPXImporter.java index cd2f445..f87e6b9 100644 --- a/main/src/cgeo/geocaching/files/GPXImporter.java +++ b/main/src/cgeo/geocaching/files/GPXImporter.java @@ -14,6 +14,7 @@ import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import android.app.Activity; @@ -93,46 +94,77 @@ public class GPXImporter { * * @param uri * URI of the file to import - * @param knownMimeType - * @param knownPathName + * @param mimeType + * @param pathName */ - public void importGPX(final Uri uri, final @Nullable String knownMimeType, final @Nullable String knownPathName) { + public void importGPX(final Uri uri, final @Nullable String mimeType, final @Nullable String pathName) { final ContentResolver contentResolver = fromActivity.getContentResolver(); - String mimeType = knownMimeType; - final String pathName = knownPathName != null ? knownPathName : uri.getPath(); - - // if mimetype can't be determined (e.g. for emulators email app), derive it from uri file extension - // contentResolver.getType(uri) doesn't help but throws exception for emulators email app - // Permission Denial: reading com.android.email.provider.EmailProvider uri - // Google search says: there is no solution for this problem - // Gmail doesn't work at all, see #967 - if (mimeType == null) { - if (StringUtils.endsWithIgnoreCase(pathName, GPX_FILE_EXTENSION) || StringUtils.endsWithIgnoreCase(pathName, LOC_FILE_EXTENSION)) { - mimeType = "application/xml"; - } else { - // if we can't determine a better type, default to zip import - // emulator email sends e.g. content://com.android.email.attachmentprovider/1/1/RAW, mimetype=null - mimeType = "application/zip"; - } - } Log.i("importGPX: " + uri + ", mimetype=" + mimeType); - if (GPX_MIME_TYPES.contains(mimeType)) { - if (StringUtils.endsWithIgnoreCase(pathName, LOC_FILE_EXTENSION)) { - new ImportLocAttachmentThread(uri, contentResolver, listId, importStepHandler, progressHandler).start(); - } else { - new ImportGpxAttachmentThread(uri, contentResolver, listId, importStepHandler, progressHandler).start(); - } - } else if (ZIP_MIME_TYPES.contains(mimeType)) { - new ImportGpxZipAttachmentThread(uri, contentResolver, listId, importStepHandler, progressHandler).start(); - } else { - importFinished(); + @NonNull + FileType fileType = new FileTypeDetector(uri, contentResolver) + .getFileType(); + + if (fileType == FileType.UNKNOWN) { + fileType = getFileTypeFromPathName(pathName); + } + if (fileType == FileType.UNKNOWN) { + fileType = getFileTypeFromMimeType(mimeType); + } + + ImportThread importer = getImporterFromFileType(uri, contentResolver, + fileType); + + if (importer != null) { + importer.start(); + } else { + importFinished(); + } + } + + private static @NonNull FileType getFileTypeFromPathName( + final String pathName) { + if (StringUtils.endsWithIgnoreCase(pathName, GPX_FILE_EXTENSION)) { + return FileType.GPX; } - } - /** - * Import GPX provided via intent of activity that instantiated this GPXImporter. - */ + if (StringUtils.endsWithIgnoreCase(pathName, LOC_FILE_EXTENSION)) { + return FileType.LOC; + } + return FileType.UNKNOWN; + } + + private static @NonNull FileType getFileTypeFromMimeType( + final String mimeType) { + if (GPX_MIME_TYPES.contains(mimeType)) { + return FileType.GPX; + } else if (ZIP_MIME_TYPES.contains(mimeType)) { + return FileType.ZIP; + } + return FileType.UNKNOWN; + } + + private ImportThread getImporterFromFileType(Uri uri, + ContentResolver contentResolver, FileType fileType) { + switch (fileType) { + case ZIP: + return new ImportGpxZipAttachmentThread(uri, contentResolver, + listId, importStepHandler, progressHandler); + case GPX: + return new ImportGpxAttachmentThread(uri, contentResolver, listId, + importStepHandler, progressHandler); + case LOC: + return new ImportLocAttachmentThread(uri, contentResolver, listId, + importStepHandler, progressHandler); + default: + return null; + } + } + + /** + * Import GPX provided via intent of activity that instantiated this + * GPXImporter. + */ public void importGPX() { final Intent intent = fromActivity.getIntent(); final Uri uri = intent.getData(); diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index 004081f..b4cb4b8 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -43,6 +43,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.eclipse.jdt.annotation.NonNull; + import rx.Subscription; import rx.functions.Action0; import rx.functions.Action1; @@ -484,7 +485,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } if (overlayPositionAndScale == null) { - overlayPositionAndScale = mapView.createAddPositionAndScaleOverlay(activity); + overlayPositionAndScale = mapView.createAddPositionAndScaleOverlay(); if (trailHistory != null) { overlayPositionAndScale.setHistory(trailHistory); } @@ -995,7 +996,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto if (cgeoMapRef != null) { if (cgeoMapRef.mapView != null) { if (cgeoMapRef.overlayPositionAndScale == null) { - cgeoMapRef.overlayPositionAndScale = cgeoMapRef.mapView.createAddPositionAndScaleOverlay(cgeoMapRef.activity); + cgeoMapRef.overlayPositionAndScale = cgeoMapRef.mapView.createAddPositionAndScaleOverlay(); } boolean needsRepaintForDistance = needsRepaintForDistance(); diff --git a/main/src/cgeo/geocaching/maps/PositionAndScaleOverlay.java b/main/src/cgeo/geocaching/maps/PositionAndScaleOverlay.java index 6b34b75..63fcd73 100644 --- a/main/src/cgeo/geocaching/maps/PositionAndScaleOverlay.java +++ b/main/src/cgeo/geocaching/maps/PositionAndScaleOverlay.java @@ -5,7 +5,6 @@ import cgeo.geocaching.maps.interfaces.MapProjectionImpl; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OverlayImpl; -import android.app.Activity; import android.graphics.Canvas; import android.graphics.Point; import android.location.Location; @@ -18,10 +17,10 @@ public class PositionAndScaleOverlay implements GeneralOverlay { PositionDrawer positionDrawer = null; ScaleDrawer scaleDrawer = null; - public PositionAndScaleOverlay(Activity activity, OverlayImpl ovlImpl) { + public PositionAndScaleOverlay(OverlayImpl ovlImpl) { this.ovlImpl = ovlImpl; - positionDrawer = new PositionDrawer(activity); - scaleDrawer = new ScaleDrawer(activity); + positionDrawer = new PositionDrawer(); + scaleDrawer = new ScaleDrawer(); } public void setCoordinates(Location coordinatesIn) { diff --git a/main/src/cgeo/geocaching/maps/PositionDrawer.java b/main/src/cgeo/geocaching/maps/PositionDrawer.java index 1a5dcaf..0e20e7c 100644 --- a/main/src/cgeo/geocaching/maps/PositionDrawer.java +++ b/main/src/cgeo/geocaching/maps/PositionDrawer.java @@ -1,5 +1,6 @@ package cgeo.geocaching.maps; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.maps.interfaces.GeoPointImpl; @@ -7,7 +8,6 @@ import cgeo.geocaching.maps.interfaces.MapItemFactory; import cgeo.geocaching.maps.interfaces.MapProjectionImpl; import cgeo.geocaching.settings.Settings; -import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -36,11 +36,9 @@ public class PositionDrawer { private PaintFlagsDrawFilter setfil = null; private PaintFlagsDrawFilter remfil = null; private PositionHistory positionHistory = new PositionHistory(); - private Activity activity; private MapItemFactory mapItemFactory; - public PositionDrawer(Activity activity) { - this.activity = activity; + public PositionDrawer() { this.mapItemFactory = Settings.getMapProvider().getMapItemFactory(); } @@ -144,7 +142,7 @@ public class PositionDrawer { } if (arrow == null) { - arrow = BitmapFactory.decodeResource(activity.getResources(), R.drawable.my_location_chevron); + arrow = BitmapFactory.decodeResource(CgeoApplication.getInstance().getResources(), R.drawable.my_location_chevron); widthArrowHalf = arrow.getWidth() / 2; heightArrowHalf = arrow.getHeight() / 2; } diff --git a/main/src/cgeo/geocaching/maps/ScaleDrawer.java b/main/src/cgeo/geocaching/maps/ScaleDrawer.java index fb46408..95c987d 100644 --- a/main/src/cgeo/geocaching/maps/ScaleDrawer.java +++ b/main/src/cgeo/geocaching/maps/ScaleDrawer.java @@ -1,5 +1,6 @@ package cgeo.geocaching.maps; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.maps.interfaces.GeoPointImpl; @@ -7,12 +8,13 @@ import cgeo.geocaching.maps.interfaces.MapViewImpl; import org.apache.commons.lang3.tuple.ImmutablePair; -import android.app.Activity; +import android.content.Context; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; import android.util.DisplayMetrics; +import android.view.WindowManager; public class ScaleDrawer { private static final double SCALE_WIDTH_FACTOR = 1.0 / 2.5; @@ -22,9 +24,10 @@ public class ScaleDrawer { private BlurMaskFilter blur = null; private float pixelDensity = 0; - public ScaleDrawer(Activity activity) { + public ScaleDrawer() { DisplayMetrics metrics = new DisplayMetrics(); - activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + WindowManager windowManager = (WindowManager) CgeoApplication.getInstance().getSystemService(Context.WINDOW_SERVICE); + windowManager.getDefaultDisplay().getMetrics(metrics); pixelDensity = metrics.density; } diff --git a/main/src/cgeo/geocaching/maps/google/GoogleMapView.java b/main/src/cgeo/geocaching/maps/google/GoogleMapView.java index 610dbe1..094c456 100644 --- a/main/src/cgeo/geocaching/maps/google/GoogleMapView.java +++ b/main/src/cgeo/geocaching/maps/google/GoogleMapView.java @@ -20,7 +20,6 @@ import com.google.android.maps.MapView; import org.apache.commons.lang3.reflect.MethodUtils; import org.eclipse.jdt.annotation.NonNull; -import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.drawable.Drawable; @@ -120,9 +119,9 @@ public class GoogleMapView extends MapView implements MapViewImpl { } @Override - public PositionAndScaleOverlay createAddPositionAndScaleOverlay(Activity activity) { + public PositionAndScaleOverlay createAddPositionAndScaleOverlay() { - GoogleOverlay ovl = new GoogleOverlay(activity); + GoogleOverlay ovl = new GoogleOverlay(); getOverlays().add(ovl); return (PositionAndScaleOverlay) ovl.getBase(); } diff --git a/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java b/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java index 0a5cf69..c684b9a 100644 --- a/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java +++ b/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java @@ -8,7 +8,6 @@ import cgeo.geocaching.maps.interfaces.OverlayImpl; import com.google.android.maps.MapView; import com.google.android.maps.Overlay; -import android.app.Activity; import android.graphics.Canvas; import java.util.concurrent.locks.Lock; @@ -19,8 +18,8 @@ public class GoogleOverlay extends Overlay implements OverlayImpl { private PositionAndScaleOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public GoogleOverlay(Activity activityIn) { - overlayBase = new PositionAndScaleOverlay(activityIn, this); + public GoogleOverlay() { + overlayBase = new PositionAndScaleOverlay(this); } @Override diff --git a/main/src/cgeo/geocaching/maps/interfaces/MapViewImpl.java b/main/src/cgeo/geocaching/maps/interfaces/MapViewImpl.java index 5ae8e15..4a6d733 100644 --- a/main/src/cgeo/geocaching/maps/interfaces/MapViewImpl.java +++ b/main/src/cgeo/geocaching/maps/interfaces/MapViewImpl.java @@ -6,7 +6,6 @@ import cgeo.geocaching.maps.PositionAndScaleOverlay; import org.eclipse.jdt.annotation.NonNull; -import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; @@ -47,7 +46,7 @@ public interface MapViewImpl { CachesOverlay createAddMapOverlay(Context context, Drawable drawable); - PositionAndScaleOverlay createAddPositionAndScaleOverlay(Activity activity); + PositionAndScaleOverlay createAddPositionAndScaleOverlay(); void setMapSource(); diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java index 7a5aab2..fb057a4 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java @@ -24,7 +24,6 @@ import org.mapsforge.android.maps.mapgenerator.MapGeneratorInternal; import org.mapsforge.android.maps.overlay.Overlay; import org.mapsforge.core.GeoPoint; -import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.drawable.Drawable; @@ -105,8 +104,8 @@ public class MapsforgeMapView extends MapView implements MapViewImpl { } @Override - public PositionAndScaleOverlay createAddPositionAndScaleOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity); + public PositionAndScaleOverlay createAddPositionAndScaleOverlay() { + MapsforgeOverlay ovl = new MapsforgeOverlay(); getOverlays().add(ovl); return (PositionAndScaleOverlay) ovl.getBase(); } diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java index 74a8601..3df4ab0 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java @@ -8,7 +8,6 @@ import cgeo.geocaching.maps.interfaces.OverlayImpl; import org.mapsforge.android.maps.Projection; import org.mapsforge.android.maps.overlay.Overlay; -import android.app.Activity; import android.graphics.Canvas; import android.graphics.Point; @@ -20,8 +19,8 @@ public class MapsforgeOverlay extends Overlay implements OverlayImpl { private PositionAndScaleOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public MapsforgeOverlay(Activity activityIn) { - overlayBase = new PositionAndScaleOverlay(activityIn, this); + public MapsforgeOverlay() { + overlayBase = new PositionAndScaleOverlay(this); } @Override diff --git a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java index 4fa4e02..68a03b7 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java @@ -21,7 +21,6 @@ import org.mapsforge.android.mapsold.MapViewMode; import org.mapsforge.android.mapsold.Overlay; import org.mapsforge.android.mapsold.Projection; -import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.drawable.Drawable; @@ -96,8 +95,8 @@ public class MapsforgeMapView024 extends MapView implements MapViewImpl { } @Override - public PositionAndScaleOverlay createAddPositionAndScaleOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity); + public PositionAndScaleOverlay createAddPositionAndScaleOverlay() { + MapsforgeOverlay ovl = new MapsforgeOverlay(); getOverlays().add(ovl); return (PositionAndScaleOverlay) ovl.getBase(); } diff --git a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java index 655e0b9..bfb3548 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java @@ -8,7 +8,6 @@ import cgeo.geocaching.maps.interfaces.OverlayImpl; import org.mapsforge.android.mapsold.Overlay; import org.mapsforge.android.mapsold.Projection; -import android.app.Activity; import android.graphics.Canvas; import android.graphics.Point; @@ -20,8 +19,8 @@ public class MapsforgeOverlay extends Overlay implements OverlayImpl { private PositionAndScaleOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public MapsforgeOverlay(Activity activityIn) { - overlayBase = new PositionAndScaleOverlay(activityIn, this); + public MapsforgeOverlay() { + overlayBase = new PositionAndScaleOverlay(this); } @Override diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java index c7933bd..315c73a 100644 --- a/main/src/cgeo/geocaching/settings/SettingsActivity.java +++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java @@ -14,6 +14,7 @@ import cgeo.geocaching.files.SimpleDirChooser; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.interfaces.MapSource; import cgeo.geocaching.utils.DatabaseBackupUtils; +import cgeo.geocaching.utils.DebugUtils; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; @@ -391,6 +392,15 @@ public class SettingsActivity extends PreferenceActivity { return true; } }); + Preference memoryDumpPref = getPreference(R.string.pref_memory_dump); + memoryDumpPref + .setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override public boolean onPreferenceClick( + Preference preference) { + DebugUtils.createMemoryDump(SettingsActivity.this); + return true; + } + }); } private void initDbLocationPreference() { diff --git a/main/src/cgeo/geocaching/ui/CompassView.java b/main/src/cgeo/geocaching/ui/CompassView.java index 915303b..60982a9 100644 --- a/main/src/cgeo/geocaching/ui/CompassView.java +++ b/main/src/cgeo/geocaching/ui/CompassView.java @@ -17,6 +17,7 @@ import android.graphics.PaintFlagsDrawFilter; import android.util.AttributeSet; import android.view.View; +import java.lang.ref.WeakReference; import java.util.concurrent.TimeUnit; public class CompassView extends View { @@ -55,11 +56,40 @@ public class CompassView extends View { private boolean initialDisplay; private Subscription periodicUpdate; + private static final class UpdateAction implements Action0 { + + private final WeakReference<CompassView> compassViewRef; + + private UpdateAction(CompassView view) { + this.compassViewRef = new WeakReference<CompassView>(view); + } + + @Override + public void call() { + final CompassView compassView = compassViewRef.get(); + if (compassView == null) { + return; + } + compassView.updateGraphics(); + } + } + public CompassView(Context contextIn) { super(contextIn); context = contextIn; } + public void updateGraphics() { + final float newAzimuthShown = smoothUpdate(northMeasured, azimuthShown); + final float newCacheHeadingShown = smoothUpdate(cacheHeadingMeasured, cacheHeadingShown); + if (Math.abs(AngleUtils.difference(azimuthShown, newAzimuthShown)) >= 2 || + Math.abs(AngleUtils.difference(cacheHeadingShown, newCacheHeadingShown)) >= 2) { + azimuthShown = newAzimuthShown; + cacheHeadingShown = newCacheHeadingShown; + invalidate(); + } + } + public CompassView(Context contextIn, AttributeSet attrs) { super(contextIn, attrs); context = contextIn; @@ -87,24 +117,13 @@ public class CompassView extends View { initialDisplay = true; - periodicUpdate = AndroidSchedulers.mainThread().createWorker().schedulePeriodically(new Action0() { - @Override - public void call() { - final float newAzimuthShown = smoothUpdate(northMeasured, azimuthShown); - final float newCacheHeadingShown = smoothUpdate(cacheHeadingMeasured, cacheHeadingShown); - if (Math.abs(AngleUtils.difference(azimuthShown, newAzimuthShown)) >= 2 || - Math.abs(AngleUtils.difference(cacheHeadingShown, newCacheHeadingShown)) >= 2) { - azimuthShown = newAzimuthShown; - cacheHeadingShown = newCacheHeadingShown; - invalidate(); - } - } - }, 0, 40, TimeUnit.MILLISECONDS); + periodicUpdate = AndroidSchedulers.mainThread().createWorker().schedulePeriodically(new UpdateAction(this), 0, 40, TimeUnit.MILLISECONDS); } @Override public void onDetachedFromWindow() { periodicUpdate.unsubscribe(); + super.onDetachedFromWindow(); if (compassUnderlay != null) { diff --git a/main/src/cgeo/geocaching/utils/DebugUtils.java b/main/src/cgeo/geocaching/utils/DebugUtils.java new file mode 100644 index 0000000..07aac64 --- /dev/null +++ b/main/src/cgeo/geocaching/utils/DebugUtils.java @@ -0,0 +1,37 @@ +package cgeo.geocaching.utils; + +import cgeo.geocaching.R; + +import org.eclipse.jdt.annotation.NonNull; + +import android.content.Context; +import android.os.Environment; +import android.widget.Toast; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class DebugUtils { + + private DebugUtils() { + // utility class + } + + public static void createMemoryDump(final @NonNull Context context) { + try { + final Date now = new Date(); + final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyy-MM-dd_hh-mm", Locale.US); + File file = FileUtils.getUniqueNamedFile(Environment.getExternalStorageDirectory().getPath() + + File.separatorChar + "cgeo_dump_" + fileNameDateFormat.format(now) + ".hprof"); + android.os.Debug.dumpHprofData(file.getPath()); + Toast.makeText(context, context.getString(R.string.init_memory_dumped, file.getAbsolutePath()), + Toast.LENGTH_LONG).show(); + ShareUtils.share(context, file, R.string.init_memory_dump); + } catch (IOException e) { + Log.e("createMemoryDump", e); + } + } +} diff --git a/main/src/cgeo/geocaching/utils/ShareUtils.java b/main/src/cgeo/geocaching/utils/ShareUtils.java new file mode 100644 index 0000000..bfd6838 --- /dev/null +++ b/main/src/cgeo/geocaching/utils/ShareUtils.java @@ -0,0 +1,27 @@ +package cgeo.geocaching.utils; + +import org.eclipse.jdt.annotation.NonNull; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import java.io.File; + +public class ShareUtils { + private ShareUtils() { + // utility class + } + + public static void share(final Context context, final @NonNull File file, final @NonNull String mimeType, final int titleResourceId) { + final Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file)); + shareIntent.setType(mimeType); + context.startActivity(Intent.createChooser(shareIntent, context.getString(titleResourceId))); + } + + public static void share(final Context context, final @NonNull File file, final int titleResourceId) { + share(context, file, "*/*", titleResourceId); + } +} diff --git a/tests/src/cgeo/geocaching/files/FileTypeDetectorTest.java b/tests/src/cgeo/geocaching/files/FileTypeDetectorTest.java new file mode 100644 index 0000000..5dbf3c2 --- /dev/null +++ b/tests/src/cgeo/geocaching/files/FileTypeDetectorTest.java @@ -0,0 +1,52 @@ +package cgeo.geocaching.files; + +import static org.assertj.core.api.Assertions.assertThat; + +import cgeo.geocaching.test.AbstractResourceInstrumentationTestCase; +import cgeo.geocaching.test.R; + +import org.eclipse.jdt.annotation.NonNull; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; + +public class FileTypeDetectorTest extends AbstractResourceInstrumentationTestCase { + + private static class FileContentResolver extends ContentResolver { + + public FileContentResolver(Context context) { + super(context); + } + } + + public void testUnknown() throws Exception { + assertFileType(R.raw.gc2cjpf_html, FileType.UNKNOWN); + assertFileType(R.raw.map1, FileType.UNKNOWN); + } + + public void testLoc() throws Exception { + assertFileType(R.raw.gc1bkp3_loc, FileType.LOC); + assertFileType(R.raw.oc5952_loc, FileType.LOC); + assertFileType(R.raw.waymarking_loc, FileType.LOC); + } + + public void testGpx() throws Exception { + assertFileType(R.raw.gc1bkp3_gpx100, FileType.GPX); + assertFileType(R.raw.gc1bkp3_gpx101, FileType.GPX); + assertFileType(R.raw.oc5952_gpx, FileType.GPX); + assertFileType(R.raw.renamed_waypoints_wpts, FileType.GPX); + assertFileType(R.raw.waymarking_gpx, FileType.GPX); + } + + public void testZip() throws Exception { + assertFileType(R.raw.pq_error, FileType.ZIP); + assertFileType(R.raw.pq7545915, FileType.ZIP); + } + + private void assertFileType(final int resourceId, final @NonNull FileType fileType) { + final Uri resourceURI = getResourceURI(resourceId); + final FileContentResolver contentResolver = new FileContentResolver(getInstrumentation().getContext()); + assertThat(new FileTypeDetector(resourceURI, contentResolver).getFileType()).isEqualTo(fileType); + } +} diff --git a/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java b/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java index df8dc1f..cbad794 100644 --- a/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java +++ b/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java @@ -12,7 +12,9 @@ import cgeo.geocaching.files.GPX10Parser; import cgeo.geocaching.files.ParserException; import cgeo.geocaching.list.StoredList; +import android.content.ContentResolver; import android.content.res.Resources; +import android.net.Uri; import android.test.InstrumentationTestCase; import java.io.File; @@ -102,4 +104,9 @@ public abstract class AbstractResourceInstrumentationTestCase extends Instrument instream.close(); } } + + protected Uri getResourceURI(int resId) { + Resources resources = getInstrumentation().getContext().getResources(); + return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + resources.getResourcePackageName(resId) + '/' + resources.getResourceTypeName(resId) + '/' + resources.getResourceEntryName(resId)); + } } |
