- remove swrenderer

- rearchitect:
 now that MapView is a ViewGroup and MapRenderer is the GLSurfaceView.
- lock/unlock proxy tiles properly to not be removed from cache while in use
This commit is contained in:
Hannes Janetzek
2012-09-16 19:26:53 +02:00
parent caea9cd7c9
commit a1317a9de5
81 changed files with 5271 additions and 5408 deletions

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.utils;
import android.os.Build;
import android.os.Looper;
/**
* A utility class with Android-specific helper methods.
*/
public final class AndroidUtils {
/**
* Build names to detect the emulator from the Android SDK.
*/
private static final String[] EMULATOR_NAMES = { "google_sdk", "sdk" };
/**
* @return true if the application is running on the Android emulator, false otherwise.
*/
public static boolean applicationRunsOnAndroidEmulator() {
for (int i = 0, n = EMULATOR_NAMES.length; i < n; ++i) {
if (Build.PRODUCT.equals(EMULATOR_NAMES[i])) {
return true;
}
}
return false;
}
/**
* @return true if the current thread is the UI thread, false otherwise.
*/
public static boolean currentThreadIsUiThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
private AndroidUtils() {
throw new IllegalStateException();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,278 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.utils;
/**
*
*
*/
public final class GeometryUtils {
/**
* Calculates the center of the minimum bounding rectangle for the given coordinates.
*
* @param coordinates
* the coordinates for which calculation should be done.
* @return the center coordinates of the minimum bounding rectangle.
*/
static float[] calculateCenterOfBoundingBox(float[] coordinates) {
float longitudeMin = coordinates[0];
float longitudeMax = coordinates[0];
float latitudeMax = coordinates[1];
float latitudeMin = coordinates[1];
for (int i = 2; i < coordinates.length; i += 2) {
if (coordinates[i] < longitudeMin) {
longitudeMin = coordinates[i];
} else if (coordinates[i] > longitudeMax) {
longitudeMax = coordinates[i];
}
if (coordinates[i + 1] < latitudeMin) {
latitudeMin = coordinates[i + 1];
} else if (coordinates[i + 1] > latitudeMax) {
latitudeMax = coordinates[i + 1];
}
}
return new float[] { (longitudeMin + longitudeMax) / 2, (latitudeMax + latitudeMin) / 2 };
}
/**
* @param way
* the coordinates of the way.
* @return true if the given way is closed, false otherwise.
*/
static boolean isClosedWay(float[] way) {
return Float.compare(way[0], way[way.length - 2]) == 0 && Float.compare(way[1], way[way.length - 1]) == 0;
}
private GeometryUtils() {
throw new IllegalStateException();
}
static boolean linesIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
// Return false if either of the lines have zero length
if (x1 == x2 && y1 == y2 || x3 == x4 && y3 == y4) {
return false;
}
// Fastest method, based on Franklin Antonio's
// "Faster Line Segment Intersection" topic "in Graphics Gems III" book
// (http://www.graphicsgems.org/)
double ax = x2 - x1;
double ay = y2 - y1;
double bx = x3 - x4;
double by = y3 - y4;
double cx = x1 - x3;
double cy = y1 - y3;
double alphaNumerator = by * cx - bx * cy;
double commonDenominator = ay * bx - ax * by;
if (commonDenominator > 0) {
if (alphaNumerator < 0 || alphaNumerator > commonDenominator) {
return false;
}
} else if (commonDenominator < 0) {
if (alphaNumerator > 0 || alphaNumerator < commonDenominator) {
return false;
}
}
double betaNumerator = ax * cy - ay * cx;
if (commonDenominator > 0) {
if (betaNumerator < 0 || betaNumerator > commonDenominator) {
return false;
}
} else if (commonDenominator < 0) {
if (betaNumerator > 0 || betaNumerator < commonDenominator) {
return false;
}
}
if (commonDenominator == 0) {
// This code wasn't in Franklin Antonio's method. It was added by Keith
// Woodward.
// The lines are parallel.
// Check if they're collinear.
double y3LessY1 = y3 - y1;
double collinearityTestForP3 = x1 * (y2 - y3) + x2 * (y3LessY1) + x3 * (y1 - y2); // see
// http://mathworld.wolfram.com/Collinear.html
// If p3 is collinear with p1 and p2 then p4 will also be collinear,
// since p1-p2 is parallel with p3-p4
if (collinearityTestForP3 == 0) {
// The lines are collinear. Now check if they overlap.
if (x1 >= x3 && x1 <= x4 || x1 <= x3 && x1 >= x4 || x2 >= x3 && x2 <= x4 || x2 <= x3 && x2 >= x4
|| x3 >= x1 && x3 <= x2 || x3 <= x1 && x3 >= x2) {
if (y1 >= y3 && y1 <= y4 || y1 <= y3 && y1 >= y4 || y2 >= y3 && y2 <= y4 || y2 <= y3 && y2 >= y4
|| y3 >= y1 && y3 <= y2 || y3 <= y1 && y3 >= y2) {
return true;
}
}
}
return false;
}
return true;
}
static boolean doesIntersect(double l1x1, double l1y1, double l1x2, double l1y2, double l2x1, double l2y1,
double l2x2, double l2y2) {
double denom = ((l2y2 - l2y1) * (l1x2 - l1x1)) - ((l2x2 - l2x1) * (l1y2 - l1y1));
if (denom == 0.0f) {
return false;
}
double ua = (((l2x2 - l2x1) * (l1y1 - l2y1)) - ((l2y2 - l2y1) * (l1x1 - l2x1))) / denom;
double ub = (((l1x2 - l1x1) * (l1y1 - l2y1)) - ((l1y2 - l1y1) * (l1x1 - l2x1))) / denom;
return ((ua >= 0.0d) && (ua <= 1.0d) && (ub >= 0.0d) && (ub <= 1.0d));
}
/**
* @param x1
* ...
* @param y1
* ...
* @param x2
* ...
* @param y2
* ...
* @param x3
* ...
* @param y3
* ...
* @param x4
* ...
* @param y4
* ...
* @return ...
*/
public static boolean lineIntersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (denom == 0.0) { // Lines are parallel.
return false;
}
double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
double ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
if (ua >= 0.0f && ua <= 1.0f && ub >= 0.0f && ub <= 1.0f) {
// Get the intersection point.
return true;
}
return false;
}
// private static final int OUT_LEFT = 1;
// private static final int OUT_TOP = 2;
// private static final int OUT_RIGHT = 4;
// private static final int OUT_BOTTOM = 8;
//
//
// private static int outcode(double x, double y) {
// /*
// * Note on casts to double below. If the arithmetic of
// * x+w or y+h is done in float, then some bits may be
// * lost if the binary exponents of x/y and w/h are not
// * similar. By converting to double before the addition
// * we force the addition to be carried out in double to
// * avoid rounding error in the comparison.
// *
// * See bug 4320890 for problems that this inaccuracy causes.
// */
// int out = 0;
// if (this.width <= 0) {
// out |= OUT_LEFT | OUT_RIGHT;
// } else if (x < this.x) {
// out |= OUT_LEFT;
// } else if (x > this.x + (double) this.width) {
// out |= OUT_RIGHT;
// }
// if (this.height <= 0) {
// out |= OUT_TOP | OUT_BOTTOM;
// } else if (y < this.y) {
// out |= OUT_TOP;
// } else if (y > this.y + (double) this.height) {
// out |= OUT_BOTTOM;
// }
// return out;
// }
// from http://shamimkhaliq.50megs.com/Java/lineclipper.htm
// private static int outCodes(Point P)
// {
// int Code = 0;
//
// if(P.y > yTop) Code += 1; /* code for above */
// else if(P.y < yBottom) Code += 2; /* code for below */
//
// if(P.x > xRight) Code += 4; /* code for right */
// else if(P.x < xLeft) Code += 8; /* code for left */
//
// return Code;
// }
//
// private static boolean rejectCheck(int outCode1, int outCode2)
// {
// if ((outCode1 & outCode2) != 0 ) return true;
// return(false);
// }
//
//
// private static boolean acceptCheck(int outCode1, int outCode2)
// {
// if ( (outCode1 == 0) && (outCode2 == 0) ) return(true);
// return(false);
// }
//
// static boolean CohenSutherland2DClipper(Point P0,Point P1)
// {
// int outCode0,outCode1;
// while(true)
// {
// outCode0 = outCodes(P0);
// outCode1 = outCodes(P1);
// if( rejectCheck(outCode0,outCode1) ) return(false);
// if( acceptCheck(outCode0,outCode1) ) return(true);
// if(outCode0 == 0)
// {
// double tempCoord; int tempCode;
// tempCoord = P0.x; P0.x= P1.x; P1.x = tempCoord;
// tempCoord = P0.y; P0.y= P1.y; P1.y = tempCoord;
// tempCode = outCode0; outCode0 = outCode1; outCode1 = tempCode;
// }
// if( (outCode0 & 1) != 0 )
// {
// P0.x += (P1.x - P0.x)*(yTop - P0.y)/(P1.y - P0.y);
// P0.y = yTop;
// }
// else
// if( (outCode0 & 2) != 0 )
// {
// P0.x += (P1.x - P0.x)*(yBottom - P0.y)/(P1.y - P0.y);
// P0.y = yBottom;
// }
// else
// if( (outCode0 & 4) != 0 )
// {
// P0.y += (P1.y - P0.y)*(xRight - P0.x)/(P1.x - P0.x);
// P0.x = xRight;
// }
// else
// if( (outCode0 & 8) != 0 )
// {
// P0.y += (P1.y - P0.y)*(xLeft - P0.x)/(P1.x - P0.x);
// P0.x = xLeft;
// }
// }
// }
}

View File

@@ -0,0 +1,158 @@
package org.oscim.utils;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import android.opengl.GLSurfaceView;
import android.util.Log;
/**
*
*
*/
public class GlConfigChooser implements GLSurfaceView.EGLConfigChooser {
static private final String TAG = "ConfigChooser";
/**
*
*/
public static int stencilSize = 0;
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
mValue = new int[1];
// Try to find a normal multisample configuration first.
int[] configSpec = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 16,
// Requires that setEGLContextClientVersion(2) is called on the view.
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
EGL10.EGL_STENCIL_SIZE, 8,
EGL10.EGL_NONE };
if (!egl.eglChooseConfig(display, configSpec, null, 0, mValue)) {
throw new IllegalArgumentException("eglChooseConfig failed");
}
int numConfigs = mValue[0];
if (numConfigs <= 0) {
stencilSize = 4;
configSpec = new int[] {
// EGL10.EGL_RENDERABLE_TYPE, 4, EGL10.EGL_NONE };
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
EGL10.EGL_STENCIL_SIZE, 8,
EGL10.EGL_NONE };
if (!egl.eglChooseConfig(display, configSpec, null, 0, mValue)) {
throw new IllegalArgumentException("eglChooseConfig failed");
}
numConfigs = mValue[0];
if (numConfigs <= 0) {
throw new IllegalArgumentException("No configs match configSpec");
}
} else {
stencilSize = 8;
}
// Get all matching configurations.
EGLConfig[] configs = new EGLConfig[numConfigs];
if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, mValue)) {
throw new IllegalArgumentException("data eglChooseConfig failed");
}
// CAUTION! eglChooseConfigs returns configs with higher bit depth
// first: Even though we asked for rgb565 configurations, rgb888
// configurations are considered to be "better" and returned first.
// You need to explicitly filter the data returned by eglChooseConfig!
// for (int i = 0; i < configs.length; ++i) {
// Log.i(TAG, printConfig(egl, display, configs[i]));
// }
// int index = -1;
// for (int i = 0; i < configs.length; ++i) {
// // if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE, 0) == 8
// // &&
// // findConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE, 0) == 0) {
// // index = i;
// // break;
// // }
// // else
// if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE, 0) == 8
// &&
// findConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE, 0) == 0
// &&
// findConfigAttrib(egl, display, configs[i], EGL10.EGL_DEPTH_SIZE, 0) == 24) {
// index = i;
// break;
// }
// }
// if (index == -1) {
// Log.w(TAG, "Did not find sane config, using first");
// index = 0;
// }
int index = 0;
Log.i(TAG, "using: " + printConfig(egl, display, configs[index]));
EGLConfig config = configs.length > 0 ? configs[index] : null;
if (config == null) {
throw new IllegalArgumentException("No config chosen");
}
return config;
}
// from quake2android
private String printConfig(EGL10 egl, EGLDisplay display,
EGLConfig config) {
int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
/*
* EGL_CONFIG_CAVEAT value #define EGL_NONE 0x3038 #define EGL_SLOW_CONFIG 0x3050 #define
* EGL_NON_CONFORMANT_CONFIG 0x3051
*/
return String.format("EGLConfig rgba=%d%d%d%d depth=%d stencil=%d",
Integer.valueOf(r), Integer.valueOf(g),
Integer.valueOf(b), Integer.valueOf(a), Integer.valueOf(d),
Integer.valueOf(s))
+ " native="
+ findConfigAttrib(egl, display, config, EGL10.EGL_NATIVE_RENDERABLE, 0)
+ " buffer="
+ findConfigAttrib(egl, display, config, EGL10.EGL_BUFFER_SIZE, 0)
+ String.format(
" caveat=0x%04x",
Integer.valueOf(findConfigAttrib(egl, display, config,
EGL10.EGL_CONFIG_CAVEAT, 0)));
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
int attribute, int defaultValue) {
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
return mValue[0];
}
return defaultValue;
}
private int[] mValue;
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.utils;
import static android.opengl.GLES20.glUniform4f;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;
/**
* Utility functions
*/
public class GlUtils {
private static String TAG = "GlUtils";
/**
* @param bitmap
* ...
* @return gl identifier
*/
public static int loadTextures(Bitmap bitmap) {
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
int textureID = textures[0];
// Log.i(TAG, "new texture " + textureID + " " + textureCnt++);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
return textureID;
}
/**
* @param shaderType
* shader type
* @param source
* shader code
* @return gl identifier
*/
public static int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "Could not compile shader " + shaderType + ":");
Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
/**
* @param vertexSource
* ...
* @param fragmentSource
* ...
* @return gl identifier
*/
public static int createProgram(String vertexSource, String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
int program = GLES20.glCreateProgram();
if (program != 0) {
checkGlError("glCreateProgram");
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e(TAG, "Could not link program: ");
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
/**
* @param op
* ...
*/
public static void checkGlError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, op + ": glError " + error);
// throw new RuntimeException(op + ": glError " + error);
}
}
public static boolean checkGlOutOfMemory(String op) {
int error;
boolean oom = false;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, op + ": glError " + error);
// throw new RuntimeException(op + ": glError " + error);
if (error == 1285)
oom = true;
}
return oom;
}
public static void setBlendColors(int handle, float[] c1, float[] c2, float alpha) {
glUniform4f(handle,
c1[0] * (1 - alpha) + c2[0] * alpha,
c1[1] * (1 - alpha) + c2[1] * alpha,
c1[2] * (1 - alpha) + c2[2] * alpha, 1);
}
public static void setColor(int handle, float[] c, float alpha) {
if (alpha >= 1)
GLES20.glUniform4fv(handle, 1, c, 0);
else
glUniform4f(handle, c[0] * alpha, c[1] * alpha, c[2] * alpha, alpha);
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.utils;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* An LRUCache with a fixed size and an access-order policy. Old mappings are automatically removed from the cache when
* new mappings are added. This implementation uses an {@link LinkedHashMap} internally.
*
* @param <K>
* the type of the map key, see {@link Map}.
* @param <V>
* the type of the map value, see {@link Map}.
*/
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private static final float LOAD_FACTOR = 0.6f;
private static final long serialVersionUID = 1L;
private static int calculateInitialCapacity(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("capacity must not be negative: " + capacity);
}
return (int) (capacity / LOAD_FACTOR) + 2;
}
private final int capacity;
/**
* @param capacity
* the maximum capacity of this cache.
* @throws IllegalArgumentException
* if the capacity is negative.
*/
public LRUCache(int capacity) {
super(calculateInitialCapacity(capacity), LOAD_FACTOR, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > this.capacity;
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.utils;
/**
* An abstract base class for threads which support pausing and resuming.
*/
public abstract class PausableThread extends Thread {
private boolean mPausing;
private boolean mShouldPause;
/**
* Causes the current thread to wait until this thread is pausing.
*/
public final void awaitPausing() {
synchronized (this) {
while (!isInterrupted() && !isPausing()) {
try {
wait(100);
} catch (InterruptedException e) {
// restore the interrupted status
Thread.currentThread().interrupt();
}
}
}
}
@Override
public void interrupt() {
// first acquire the monitor which is used to call wait()
synchronized (this) {
super.interrupt();
}
}
/**
* @return true if this thread is currently pausing, false otherwise.
*/
public final synchronized boolean isPausing() {
return mPausing;
}
/**
* The thread should stop its work temporarily.
*/
public final synchronized void pause() {
if (!mShouldPause) {
mShouldPause = true;
takeabreak();
notify();
}
}
/**
* The paused thread should continue with its work.
*/
public final synchronized void proceed() {
if (mShouldPause) {
mShouldPause = false;
mPausing = false;
afterPause();
notify();
}
}
@Override
public final void run() {
setName(getThreadName());
setPriority(getThreadPriority());
while (!isInterrupted()) {
synchronized (this) {
while (!isInterrupted() && (mShouldPause || !hasWork())) {
try {
if (mShouldPause) {
mPausing = true;
}
wait();
} catch (InterruptedException e) {
// restore the interrupted status
interrupt();
}
}
}
if (isInterrupted()) {
break;
}
try {
doWork();
} catch (InterruptedException e) {
// restore the interrupted status
interrupt();
}
}
afterRun();
}
/**
* Called once when this thread continues to work after a pause. The default implementation is empty.
*/
protected void afterPause() {
// do nothing
}
protected void takeabreak() {
// do nothing
}
/**
* Called once at the end of the {@link #run()} method. The default implementation is empty.
*/
protected void afterRun() {
// do nothing
}
/**
* Called when this thread is not paused and should do its work.
*
* @throws InterruptedException
* if the thread has been interrupted.
*/
protected abstract void doWork() throws InterruptedException;
/**
* @return the name of this thread.
*/
protected abstract String getThreadName();
/**
* @return the priority of this thread. The default value is {@link Thread#NORM_PRIORITY}.
*/
protected int getThreadPriority() {
return Thread.NORM_PRIORITY;
}
/**
* @return true if this thread has some work to do, false otherwise.
*/
protected abstract boolean hasWork();
}