aboutsummaryrefslogtreecommitdiffstats
path: root/main/src/cgeo/geocaching/utils
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/cgeo/geocaching/utils')
-rw-r--r--main/src/cgeo/geocaching/utils/AngleUtils.java4
-rw-r--r--main/src/cgeo/geocaching/utils/AsyncTaskWithProgress.java139
-rw-r--r--main/src/cgeo/geocaching/utils/ClipboardUtils.java8
-rw-r--r--main/src/cgeo/geocaching/utils/CryptUtils.java62
-rw-r--r--main/src/cgeo/geocaching/utils/DateUtils.java7
-rw-r--r--main/src/cgeo/geocaching/utils/EditUtils.java4
-rw-r--r--main/src/cgeo/geocaching/utils/FileUtils.java8
-rw-r--r--main/src/cgeo/geocaching/utils/GeoDirHandler.java24
-rw-r--r--main/src/cgeo/geocaching/utils/HtmlUtils.java9
-rw-r--r--main/src/cgeo/geocaching/utils/ImageUtils.java (renamed from main/src/cgeo/geocaching/utils/ImageHelper.java)44
-rw-r--r--main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java6
-rw-r--r--main/src/cgeo/geocaching/utils/Log.java13
-rw-r--r--main/src/cgeo/geocaching/utils/LogTemplateProvider.java4
-rw-r--r--main/src/cgeo/geocaching/utils/PeriodicHandler.java33
-rw-r--r--main/src/cgeo/geocaching/utils/ProcessUtils.java47
-rw-r--r--main/src/cgeo/geocaching/utils/TextUtils.java (renamed from main/src/cgeo/geocaching/utils/BaseUtils.java)21
-rw-r--r--main/src/cgeo/geocaching/utils/TranslationUtils.java6
-rw-r--r--main/src/cgeo/geocaching/utils/XmlUtils.java2
18 files changed, 358 insertions, 83 deletions
diff --git a/main/src/cgeo/geocaching/utils/AngleUtils.java b/main/src/cgeo/geocaching/utils/AngleUtils.java
index 6e59a91..fdd9a9d 100644
--- a/main/src/cgeo/geocaching/utils/AngleUtils.java
+++ b/main/src/cgeo/geocaching/utils/AngleUtils.java
@@ -1,6 +1,6 @@
package cgeo.geocaching.utils;
-public class AngleUtils {
+public final class AngleUtils {
private AngleUtils() {
// Do not instantiate
@@ -8,7 +8,7 @@ public class AngleUtils {
/**
* Return the angle to turn of to go from an angle to the other
- *
+ *
* @param from
* the origin angle in degrees
* @param to
diff --git a/main/src/cgeo/geocaching/utils/AsyncTaskWithProgress.java b/main/src/cgeo/geocaching/utils/AsyncTaskWithProgress.java
new file mode 100644
index 0000000..7526d92
--- /dev/null
+++ b/main/src/cgeo/geocaching/utils/AsyncTaskWithProgress.java
@@ -0,0 +1,139 @@
+package cgeo.geocaching.utils;
+
+import cgeo.geocaching.activity.Progress;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.os.AsyncTask;
+
+/**
+ * AsyncTask which automatically shows a progress dialog. Use it like the {@code AsyncTask} class, but leave away the
+ * middle template parameter. Override {@link #doInBackgroundInternal(Object[])} and related methods.
+ * <p>
+ * If no style is given, the progress dialog uses "determinate" style with known maximum. The progress maximum is
+ * automatically derived from the number of {@code Params} given to the task in {@link #execute(Object...)}.
+ * </p>
+ *
+ * @param <Params>
+ * @param <Result>
+ */
+public abstract class AsyncTaskWithProgress<Params, Result> extends AsyncTask<Params, Integer, Result> {
+
+ private final Progress progress = new Progress();
+ private final Activity activity;
+ private final String progressTitle;
+ private final String progressMessage;
+ private boolean indeterminate = false;
+
+ /**
+ * Creates an AsyncTask with progress dialog.
+ *
+ * @param activity
+ * @param progressTitle
+ * @param progressMessage
+ */
+ public AsyncTaskWithProgress(final Activity activity, final String progressTitle, final String progressMessage) {
+ this(activity, progressTitle, progressMessage, false);
+ }
+
+ /**
+ * Creates an AsyncTask with progress dialog.
+ *
+ * @param activity
+ * @param progressTitle
+ */
+ public AsyncTaskWithProgress(final Activity activity, final String progressTitle) {
+ this(activity, progressTitle, null);
+ }
+
+ /**
+ * Creates an AsyncTask with progress dialog.
+ *
+ * @param activity
+ * @param progressTitle
+ * @param progressMessage
+ */
+ public AsyncTaskWithProgress(final Activity activity, final String progressTitle, final String progressMessage, boolean indeterminate) {
+ this.activity = activity;
+ this.progressTitle = progressTitle;
+ this.progressMessage = progressMessage;
+ this.indeterminate = indeterminate;
+ }
+
+ /**
+ * Creates an AsyncTask with progress dialog.
+ *
+ * @param activity
+ * @param progressTitle
+ */
+ public AsyncTaskWithProgress(final Activity activity, final String progressTitle, boolean indeterminate) {
+ this(activity, progressTitle, null, indeterminate);
+ }
+
+ @Override
+ protected final void onPreExecute() {
+ if (null != activity) {
+ if (indeterminate) {
+ progress.show(activity, progressTitle, progressMessage, true, null);
+ }
+ else {
+ progress.show(activity, progressTitle, progressMessage, ProgressDialog.STYLE_HORIZONTAL, null);
+ }
+ }
+ onPreExecuteInternal();
+ }
+
+ /**
+ * This method should typically be overridden by sub classes instead of {@link #onPreExecute()}.
+ */
+ protected void onPreExecuteInternal() {
+ // empty by default
+ }
+
+ @Override
+ protected final void onPostExecute(Result result) {
+ onPostExecuteInternal(result);
+ if (null != activity) {
+ progress.dismiss();
+ }
+ }
+
+ /**
+ * This method should typically be overridden by sub classes instead of {@link #onPostExecute(Object)}.
+ *
+ * @param result
+ */
+ protected void onPostExecuteInternal(Result result) {
+ // empty by default
+ }
+
+ @Override
+ protected final void onProgressUpdate(Integer... status) {
+ final int progressValue = status[0];
+ if (null != activity && progressValue >= 0) {
+ progress.setProgress(progressValue);
+ }
+ onProgressUpdateInternal(progressValue);
+ }
+
+ /**
+ * This method should by overridden by sub classes instead of {@link #onProgressUpdate(Integer...)}.
+ */
+ protected void onProgressUpdateInternal(@SuppressWarnings("unused") int progress) {
+ // empty by default
+ }
+
+ protected void setMessage(final String message) {
+ progress.setMessage(message);
+ }
+
+ @Override
+ protected final Result doInBackground(Params... params) {
+ if (params != null) {
+ progress.setMaxProgressAndReset(params.length);
+ }
+ return doInBackgroundInternal(params);
+ }
+
+ protected abstract Result doInBackgroundInternal(Params[] params);
+}
diff --git a/main/src/cgeo/geocaching/utils/ClipboardUtils.java b/main/src/cgeo/geocaching/utils/ClipboardUtils.java
index e6779ad..67069b2 100644
--- a/main/src/cgeo/geocaching/utils/ClipboardUtils.java
+++ b/main/src/cgeo/geocaching/utils/ClipboardUtils.java
@@ -3,7 +3,6 @@ package cgeo.geocaching.utils;
import cgeo.geocaching.cgeoapplication;
import android.content.Context;
-import android.text.ClipboardManager;
/**
* Clipboard Utilities. Functions to copy data to the Android clipboard.
@@ -13,6 +12,10 @@ import android.text.ClipboardManager;
@SuppressWarnings("deprecation")
public final class ClipboardUtils {
+ private ClipboardUtils() {
+ // utility class
+ }
+
/**
* Places the text passed in onto the clipboard as text
*
@@ -20,7 +23,8 @@ public final class ClipboardUtils {
* The text to place in the clipboard.
*/
public static void copyToClipboard(final CharSequence text) {
- final ClipboardManager clipboard = (ClipboardManager) cgeoapplication.getInstance().getSystemService(Context.CLIPBOARD_SERVICE);
+ // fully qualified name used here to avoid buggy deprecation warning (of javac) on the import statement
+ final android.text.ClipboardManager clipboard = (android.text.ClipboardManager) cgeoapplication.getInstance().getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(text);
}
diff --git a/main/src/cgeo/geocaching/utils/CryptUtils.java b/main/src/cgeo/geocaching/utils/CryptUtils.java
index df2baa0..18a337d 100644
--- a/main/src/cgeo/geocaching/utils/CryptUtils.java
+++ b/main/src/cgeo/geocaching/utils/CryptUtils.java
@@ -12,6 +12,10 @@ import javax.crypto.spec.SecretKeySpec;
public final class CryptUtils {
+ private CryptUtils() {
+ // utility class
+ }
+
private static char[] base64map1 = new char[64];
private static byte[] base64map2 = new byte[128];
@@ -37,28 +41,36 @@ public final class CryptUtils {
}
}
+ private static class Rot13Encryption {
+ private boolean plaintext = false;
+
+ char getNextEncryptedCharacter(final char c) {
+ int result = c;
+ if (result == '[') {
+ plaintext = true;
+ } else if (result == ']') {
+ plaintext = false;
+ } else if (!plaintext) {
+ int capitalized = result & 32;
+ result &= ~capitalized;
+ result = ((result >= 'A') && (result <= 'Z') ? ((result - 'A' + 13) % 26 + 'A') : result)
+ | capitalized;
+ }
+ return (char) result;
+ }
+ }
+
public static String rot13(String text) {
if (text == null) {
return "";
}
final StringBuilder result = new StringBuilder();
- // plaintext flag (do not convert)
- boolean plaintext = false;
+ Rot13Encryption rot13 = new Rot13Encryption();
final int length = text.length();
for (int index = 0; index < length; index++) {
- int c = text.charAt(index);
- if (c == '[') {
- plaintext = true;
- } else if (c == ']') {
- plaintext = false;
- } else if (!plaintext) {
- int capitalized = c & 32;
- c &= ~capitalized;
- c = ((c >= 'A') && (c <= 'Z') ? ((c - 'A' + 13) % 26 + 'A') : c)
- | capitalized;
- }
- result.append((char) c);
+ char c = text.charAt(index);
+ result.append(rot13.getNextEncryptedCharacter(c));
}
return result.toString();
}
@@ -71,7 +83,7 @@ public final class CryptUtils {
digest.update(text.getBytes(), 0, text.length());
hashed = new BigInteger(1, digest.digest()).toString(16);
} catch (Exception e) {
- Log.e("cgBase.md5", e);
+ Log.e("CryptUtils.md5", e);
}
return hashed;
@@ -85,7 +97,7 @@ public final class CryptUtils {
digest.update(text.getBytes(), 0, text.length());
hashed = new BigInteger(1, digest.digest()).toString(16);
} catch (Exception e) {
- Log.e("cgBase.sha1", e);
+ Log.e("CryptUtils.sha1", e);
}
return hashed;
@@ -100,7 +112,7 @@ public final class CryptUtils {
mac.init(secretKeySpec);
macBytes = mac.doFinal(text.getBytes());
} catch (Exception e) {
- Log.e("cgBase.hashHmac", e);
+ Log.e("CryptUtils.hashHmac", e);
}
return macBytes;
@@ -111,22 +123,12 @@ public final class CryptUtils {
// a SpannableStringBuilder instead of the pure text and we must replace each character inline.
// Otherwise we loose all the images, colors and so on...
final SpannableStringBuilder buffer = new SpannableStringBuilder(span);
- boolean plaintext = false;
+ Rot13Encryption rot13 = new Rot13Encryption();
final int length = span.length();
for (int index = 0; index < length; index++) {
- int c = span.charAt(index);
- if (c == '[') {
- plaintext = true;
- } else if (c == ']') {
- plaintext = false;
- } else if (!plaintext) {
- int capitalized = c & 32;
- c &= ~capitalized;
- c = ((c >= 'A') && (c <= 'Z') ? ((c - 'A' + 13) % 26 + 'A') : c)
- | capitalized;
- }
- buffer.replace(index, index + 1, String.valueOf((char) c));
+ char c = span.charAt(index);
+ buffer.replace(index, index + 1, String.valueOf(rot13.getNextEncryptedCharacter(c)));
}
return buffer;
}
diff --git a/main/src/cgeo/geocaching/utils/DateUtils.java b/main/src/cgeo/geocaching/utils/DateUtils.java
index fd5ef4a..b148979 100644
--- a/main/src/cgeo/geocaching/utils/DateUtils.java
+++ b/main/src/cgeo/geocaching/utils/DateUtils.java
@@ -2,7 +2,12 @@ package cgeo.geocaching.utils;
import java.util.Calendar;
-public class DateUtils {
+public final class DateUtils {
+
+ private DateUtils() {
+ // utility class
+ }
+
public static int daysSince(long date) {
final Calendar logDate = Calendar.getInstance();
logDate.setTimeInMillis(date);
diff --git a/main/src/cgeo/geocaching/utils/EditUtils.java b/main/src/cgeo/geocaching/utils/EditUtils.java
index f2f89e7..d975df9 100644
--- a/main/src/cgeo/geocaching/utils/EditUtils.java
+++ b/main/src/cgeo/geocaching/utils/EditUtils.java
@@ -8,6 +8,10 @@ import android.widget.TextView;
public final class EditUtils {
+ private EditUtils() {
+ // utility class
+ }
+
public static void setActionListener(final EditText editText, final Runnable runnable) {
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
diff --git a/main/src/cgeo/geocaching/utils/FileUtils.java b/main/src/cgeo/geocaching/utils/FileUtils.java
index 6fefc02..5ab8fcc 100644
--- a/main/src/cgeo/geocaching/utils/FileUtils.java
+++ b/main/src/cgeo/geocaching/utils/FileUtils.java
@@ -9,12 +9,16 @@ import java.io.File;
import java.util.List;
/**
- * Utiliy class for files
+ * Utility class for files
*
* @author rsudev
*
*/
-public class FileUtils {
+public final class FileUtils {
+
+ private FileUtils() {
+ // utility class
+ }
public static void listDir(List<File> result, File directory, FileSelector chooser, Handler feedBackHandler) {
diff --git a/main/src/cgeo/geocaching/utils/GeoDirHandler.java b/main/src/cgeo/geocaching/utils/GeoDirHandler.java
index 21b2562..98a2287 100644
--- a/main/src/cgeo/geocaching/utils/GeoDirHandler.java
+++ b/main/src/cgeo/geocaching/utils/GeoDirHandler.java
@@ -1,7 +1,7 @@
package cgeo.geocaching.utils;
import cgeo.geocaching.IGeoData;
-import cgeo.geocaching.Settings;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.cgeoapplication;
import android.os.Handler;
@@ -9,10 +9,19 @@ import android.os.Message;
/**
* GeoData and Direction handler. Manipulating geodata and direction information
- * through a GeoDirHandler ensures that all listeners are registered from a
- * {@link android.os.Looper} thread.
+ * through a GeoDirHandler ensures that all listeners are registered from a {@link android.os.Looper} thread.
+ * <p>
+ * To use this class, override at least one of {@link #updateDirection(float)} or {@link #updateGeoData(IGeoData)}. You
+ * need to start the handler using one of
+ * <ul>
+ * <li>{@link #startDir()}</li>
+ * <li>{@link #startGeo()}</li>
+ * <li>{@link #startGeoAndDir()}</li>
+ * </ul>
+ * A good place might be the {@code onResume} method of the Activity. Stop the Handler accordingly in {@code onPause}.
+ * </p>
*/
-public class GeoDirHandler extends Handler implements IObserver<Object> {
+public abstract class GeoDirHandler extends Handler implements IObserver<Object> {
private static final int OBSERVABLE = 1 << 1;
private static final int START_GEO = 1 << 2;
@@ -57,7 +66,8 @@ public class GeoDirHandler extends Handler implements IObserver<Object> {
/**
* Update method called when new IGeoData is available.
*
- * @param data the new data
+ * @param data
+ * the new data
*/
protected void updateGeoData(final IGeoData data) {
// Override this in children
@@ -66,7 +76,8 @@ public class GeoDirHandler extends Handler implements IObserver<Object> {
/**
* Update method called when new direction data is available.
*
- * @param direction the new direction
+ * @param direction
+ * the new direction
*/
protected void updateDirection(final float direction) {
// Override this in children
@@ -118,4 +129,3 @@ public class GeoDirHandler extends Handler implements IObserver<Object> {
sendEmptyMessage(STOP_GEO | STOP_DIR);
}
}
-
diff --git a/main/src/cgeo/geocaching/utils/HtmlUtils.java b/main/src/cgeo/geocaching/utils/HtmlUtils.java
index 8d4eed1..5717a37 100644
--- a/main/src/cgeo/geocaching/utils/HtmlUtils.java
+++ b/main/src/cgeo/geocaching/utils/HtmlUtils.java
@@ -10,7 +10,11 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-public class HtmlUtils {
+public final class HtmlUtils {
+
+ private HtmlUtils() {
+ // utility class
+ }
/**
* Extract the text from a HTML based string. This is similar to what HTML.fromHtml(...) does, but this method also
@@ -20,6 +24,9 @@ public class HtmlUtils {
* @return
*/
public static String extractText(CharSequence html) {
+ if (StringUtils.isBlank(html)) {
+ return StringUtils.EMPTY;
+ }
String result = html.toString();
// recognize images in textview HTML contents
diff --git a/main/src/cgeo/geocaching/utils/ImageHelper.java b/main/src/cgeo/geocaching/utils/ImageUtils.java
index 98cad64..ea7d3ff 100644
--- a/main/src/cgeo/geocaching/utils/ImageHelper.java
+++ b/main/src/cgeo/geocaching/utils/ImageUtils.java
@@ -8,10 +8,13 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
-public class ImageHelper {
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
- // Do not let this class be instantiated, this is a utility class.
- private ImageHelper() {
+public final class ImageUtils {
+
+ private ImageUtils() {
+ // Do not let this class be instantiated, this is a utility class.
}
/**
@@ -22,11 +25,21 @@ public class ImageHelper {
* @return BitmapDrawable The scaled image
*/
public static BitmapDrawable scaleBitmapToFitDisplay(final Bitmap image) {
- final cgeoapplication app = cgeoapplication.getInstance();
Point displaySize = Compatibility.getDisplaySize();
final int maxWidth = displaySize.x - 25;
final int maxHeight = displaySize.y - 25;
+ return scaleBitmapTo(image, maxWidth, maxHeight);
+ }
+ /**
+ * Scales a bitmap to the given bounds if it is larger, otherwise returns the original bitmap.
+ *
+ * @param image
+ * The bitmap to scale
+ * @return BitmapDrawable The scaled image
+ */
+ public static BitmapDrawable scaleBitmapTo(final Bitmap image, final int maxWidth, final int maxHeight) {
+ final cgeoapplication app = cgeoapplication.getInstance();
Bitmap result = image;
int width = image.getWidth();
int height = image.getHeight();
@@ -43,4 +56,27 @@ public class ImageHelper {
return resultDrawable;
}
+ /**
+ * Store a bitmap to file.
+ *
+ * @param bitmap
+ * The bitmap to store
+ * @param format
+ * The image format
+ * @param quality
+ * The image quality
+ * @param pathOfOutputImage
+ * Path to store to
+ */
+ public static void storeBitmap(final Bitmap bitmap, final Bitmap.CompressFormat format, final int quality, final String pathOfOutputImage) {
+ try {
+ FileOutputStream out = new FileOutputStream(pathOfOutputImage);
+ BufferedOutputStream bos = new BufferedOutputStream(out);
+ bitmap.compress(format, quality, bos);
+ bos.flush();
+ bos.close();
+ } catch (Exception e) {
+ Log.e("ImageHelper.storeBitmap", e);
+ }
+ }
}
diff --git a/main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java b/main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java
index 698d38a..708dff0 100644
--- a/main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java
+++ b/main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java
@@ -10,13 +10,13 @@ import java.util.List;
/**
* Synchronized set wrapper for the LeastRecentlyUsedMap.
*
- * This code is heavily based on the HashSet code that represent Map as a Set.
+ * This code is heavily based on the HashSet code that represents Map as a Set.
* Unfortunately HashSet does not allow to use a custom Map as its Storage.
* Therefore overriding removeEldestEntry() is impossible for a normal LinkedHashSet.
*
* Synchronization is added to guard against concurrent modification. Iterator
* access has to be guarded externally or the synchronized getAsList method can be used
- * to get a clone for iteration
+ * to get a clone for iteration.
*/
public class LeastRecentlyUsedSet<E> extends AbstractSet<E>
implements Cloneable, java.io.Serializable {
@@ -28,7 +28,7 @@ public class LeastRecentlyUsedSet<E> extends AbstractSet<E>
public LeastRecentlyUsedSet(int maxEntries, int initialCapacity, float loadFactor) {
// because we don't use any Map.get() methods from the Set, BOUNDED and LRU_CACHE have the exact same Behaviour
- // So we useLRU_CACHE mode because it should perform a bit better (as it doesn't re-add explicitly)
+ // So we use LRU_CACHE mode because it should perform a bit better (as it doesn't re-add explicitly)
map = new LeastRecentlyUsedMap.LruCache<E, Object>(maxEntries, initialCapacity, loadFactor);
}
diff --git a/main/src/cgeo/geocaching/utils/Log.java b/main/src/cgeo/geocaching/utils/Log.java
index 6d57b75..f7f33d9 100644
--- a/main/src/cgeo/geocaching/utils/Log.java
+++ b/main/src/cgeo/geocaching/utils/Log.java
@@ -2,6 +2,7 @@ package cgeo.geocaching.utils;
import android.os.Environment;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@@ -22,11 +23,11 @@ final public class Log {
}
/**
- * make a non persisted copy of the debug flag from the settings for performance reasons
- *
+ * save a copy of the debug flag from the settings for performance reasons
+ *
* @param isDebug
*/
- public static void setDebugUnsaved(boolean isDebug) {
+ public static void setDebug(boolean isDebug) {
Log.isDebug = isDebug;
}
@@ -100,12 +101,14 @@ final public class Log {
first = false;
file.delete();
}
+ Writer writer = null;
try {
- final Writer writer = new FileWriter(file, true);
+ writer = new BufferedWriter(new FileWriter(file, true));
writer.write(msg);
- writer.close();
} catch (final IOException e) {
Log.e("logToFile: cannot write to " + file, e);
+ } finally {
+ IOUtils.closeQuietly(writer);
}
}
}
diff --git a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java
index 0a8e547..6d5f130 100644
--- a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java
+++ b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java
@@ -1,7 +1,7 @@
package cgeo.geocaching.utils;
import cgeo.geocaching.R;
-import cgeo.geocaching.Settings;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.Trackable;
import cgeo.geocaching.Geocache;
import cgeo.geocaching.connector.gc.GCConstants;
@@ -188,7 +188,7 @@ public class LogTemplateProvider {
}
try {
- return Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""));
+ return Integer.parseInt(TextUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""));
} catch (NumberFormatException e) {
Log.e("parseFindCount", e);
return -1;
diff --git a/main/src/cgeo/geocaching/utils/PeriodicHandler.java b/main/src/cgeo/geocaching/utils/PeriodicHandler.java
index 2759580..288bbb0 100644
--- a/main/src/cgeo/geocaching/utils/PeriodicHandler.java
+++ b/main/src/cgeo/geocaching/utils/PeriodicHandler.java
@@ -3,16 +3,26 @@ package cgeo.geocaching.utils;
import android.os.Handler;
import android.os.Message;
+import java.lang.ref.WeakReference;
+
/**
* A PeriodicHandler class helps with the implementation of a periodic
* action embedded in a thread with a looper such as the UI thread.
- * The act() method will be called periodically. The clock may drift
- * as the implementation does not target real-time actions.
+ * The onPeriodic() method of the listener will be called periodically.
+ * The clock may drift as the implementation does not target real-time
+ * actions.
*
* The handler will be interrupted if the device goes to sleep.
*
+ * The handler only keeps a weak reference to the listener. If the listener
+ * is garbage-collected without having stopped the timer, the handler will
+ * stop itself.
*/
-abstract public class PeriodicHandler extends Handler {
+final public class PeriodicHandler extends Handler {
+
+ public static interface PeriodicHandlerListener {
+ public void onPeriodic();
+ }
final static private int START = 0;
final static private int STOP = 1;
@@ -20,21 +30,19 @@ abstract public class PeriodicHandler extends Handler {
final private long period;
+ final private WeakReference<PeriodicHandlerListener> listenerRef;
+
/**
* Create a new PeriodicHandler object.
*
* @param period
* The period in milliseconds.
*/
- protected PeriodicHandler(final long period) {
+ public PeriodicHandler(final long period, final PeriodicHandlerListener listener) {
this.period = period;
+ listenerRef = new WeakReference<PeriodicHandlerListener>(listener);
}
- /**
- * Subclasses of PeriodicHandler must implement this method.
- */
- abstract public void act();
-
@Override
public void handleMessage(final Message msg) {
switch (msg.what) {
@@ -46,8 +54,11 @@ abstract public class PeriodicHandler extends Handler {
removeMessages(ACT);
break;
case ACT:
- sendEmptyMessageDelayed(ACT, period);
- act();
+ final PeriodicHandlerListener listener = listenerRef.get();
+ if (listener != null) {
+ sendEmptyMessageDelayed(ACT, period);
+ listener.onPeriodic();
+ }
break;
default:
throw new UnsupportedOperationException();
diff --git a/main/src/cgeo/geocaching/utils/ProcessUtils.java b/main/src/cgeo/geocaching/utils/ProcessUtils.java
index 53991fb..85cedc5 100644
--- a/main/src/cgeo/geocaching/utils/ProcessUtils.java
+++ b/main/src/cgeo/geocaching/utils/ProcessUtils.java
@@ -3,14 +3,55 @@ package cgeo.geocaching.utils;
import cgeo.geocaching.cgeoapplication;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-public class ProcessUtils {
+import java.util.List;
- public static boolean isInstalled(final String packageName) {
+public final class ProcessUtils {
+
+ private ProcessUtils() {
+ // utility class
+ }
+
+ /**
+ * Preferred method to detect the availability of an external app
+ *
+ * @param packageName
+ * @return
+ */
+ public static boolean isLaunchable(final String packageName) {
return getLaunchIntent(packageName) != null;
}
+ /**
+ * Checks whether a launch intent is available or if the package is just installed
+ * This function is relatively costly, so if you know that the package in question has
+ * a launch intent, use isLaunchable() instead.
+ *
+ * @param packageName
+ * @return
+ */
+ public static boolean isInstalled(final String packageName) {
+ return isLaunchable(packageName) || hasPackageInstalled(packageName);
+ }
+
+ /**
+ * This will find installed applications even without launch intent (e.g. the streetview plugin).
+ */
+ private static boolean hasPackageInstalled(final String packageName) {
+ final List<PackageInfo> packs = cgeoapplication.getInstance().getPackageManager().getInstalledPackages(0);
+ for (final PackageInfo packageInfo : packs) {
+ if (packageName.equals(packageInfo.packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This will find applications, which can be launched.
+ */
public static Intent getLaunchIntent(final String packageName) {
if (packageName == null) {
return null;
@@ -20,7 +61,7 @@ public class ProcessUtils {
// This can throw an exception where the exception type is only defined on API Level > 3
// therefore surround with try-catch
return packageManager.getLaunchIntentForPackage(packageName);
- } catch (Exception e) {
+ } catch (final Exception e) {
return null;
}
}
diff --git a/main/src/cgeo/geocaching/utils/BaseUtils.java b/main/src/cgeo/geocaching/utils/TextUtils.java
index 82e48cb..c9d4958 100644
--- a/main/src/cgeo/geocaching/utils/BaseUtils.java
+++ b/main/src/cgeo/geocaching/utils/TextUtils.java
@@ -9,10 +9,14 @@ import java.util.regex.Pattern;
/**
* Misc. utils. All methods don't use Android specific stuff to use these methods in plain JUnit tests.
*/
-public final class BaseUtils {
+public final class TextUtils {
private static final Pattern PATTERN_REMOVE_NONPRINTABLE = Pattern.compile("\\p{Cntrl}");
+ private TextUtils() {
+ // utility class
+ }
+
/**
* Searches for the pattern p in the data. If the pattern is not found defaultValue is returned
*
@@ -69,7 +73,7 @@ public final class BaseUtils {
* @return defaultValue or the first group if the pattern matches (trimmed if wanted)
*/
public static String getMatch(final String data, final Pattern p, final boolean trim, final String defaultValue) {
- return BaseUtils.getMatch(data, p, trim, 1, defaultValue, false);
+ return TextUtils.getMatch(data, p, trim, 1, defaultValue, false);
}
/**
@@ -84,7 +88,7 @@ public final class BaseUtils {
* @return defaultValue or the first group if the pattern matches (trimmed)
*/
public static String getMatch(final String data, final Pattern p, final String defaultValue) {
- return BaseUtils.getMatch(data, p, true, 1, defaultValue, false);
+ return TextUtils.getMatch(data, p, true, 1, defaultValue, false);
}
/**
@@ -104,13 +108,13 @@ public final class BaseUtils {
}
/**
- * Replaces every \n, \r and \t with a single space. Afterwards multiples spaces
+ * Replaces every \n, \r and \t with a single space. Afterwards multiple spaces
* are merged into a single space. Finally leading spaces are deleted.
*
* This method must be fast, but may not lead to the shortest replacement String.
*
* You are only allowed to change this code if you can prove it became faster on a device.
- * see cgeo.geocaching.test.WhiteSpaceTest#replaceWhitespaceManually in the test project
+ * see cgeo.geocaching.test.WhiteSpaceTest#replaceWhitespaceManually in the test project.
*
* @param data
* complete HTML page
@@ -137,10 +141,11 @@ public final class BaseUtils {
}
/**
- * Quick and naive check for possible html-content of a string.
+ * Quick and naive check for possible rich HTML content in a string.
*
- * @param str
- * @return True, if <code>str</code> could contain html
+ * @param str A string containing HTML code.
+ * @return <tt>true</tt> if <tt>str</tt> contains HTML code that needs to go through a HTML renderer before
+ * being displayed, <tt>false</tt> if it can be displayed as-is without any loss
*/
public static boolean containsHtml(final String str) {
return str.indexOf('<') != -1 || str.indexOf('&') != -1;
diff --git a/main/src/cgeo/geocaching/utils/TranslationUtils.java b/main/src/cgeo/geocaching/utils/TranslationUtils.java
index 4d318f0..1224f7e 100644
--- a/main/src/cgeo/geocaching/utils/TranslationUtils.java
+++ b/main/src/cgeo/geocaching/utils/TranslationUtils.java
@@ -19,6 +19,10 @@ public final class TranslationUtils {
public static final int translationTextLengthToWarn = 500;
private static final String TRANSLATION_APP = "com.google.android.apps.translate";
+ private TranslationUtils() {
+ // utility class
+ }
+
/**
* Build a URI for Google Translate
*
@@ -31,7 +35,7 @@ public final class TranslationUtils {
private static String buildTranslationURI(final String toLang, final String text) {
String content = Network.encode(text);
// the app works better without the "+", the website works better with "+", therefore assume using the app if installed
- if (ProcessUtils.isInstalled(TRANSLATION_APP)) {
+ if (ProcessUtils.isLaunchable(TRANSLATION_APP)) {
content = content.replace("+", "%20");
}
return translationWebsite + translationForceClassicMode + translationAutoSelect + translationFieldSeparator + toLang + translationFieldSeparator + content;
diff --git a/main/src/cgeo/geocaching/utils/XmlUtils.java b/main/src/cgeo/geocaching/utils/XmlUtils.java
index 4e08f42..2d85950 100644
--- a/main/src/cgeo/geocaching/utils/XmlUtils.java
+++ b/main/src/cgeo/geocaching/utils/XmlUtils.java
@@ -4,7 +4,7 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
-public class XmlUtils {
+public final class XmlUtils {
private XmlUtils() {
// Do not instantiate