/OsmAnd/src/net/osmand/plus/views/AnimateDraggingMapThread.java

https://github.com/ScottHW/Osmand · Java · 331 lines · 263 code · 51 blank · 17 comment · 31 complexity · 4c2d15521867ba47ac9b253e175632bb MD5 · raw file

  1. package net.osmand.plus.views;
  2. import net.osmand.LogUtil;
  3. import net.osmand.osm.MapUtils;
  4. import org.apache.commons.logging.Log;
  5. import android.os.SystemClock;
  6. import android.util.FloatMath;
  7. import android.view.animation.AccelerateDecelerateInterpolator;
  8. import android.view.animation.DecelerateInterpolator;
  9. import android.view.animation.LinearInterpolator;
  10. /**
  11. * Thread for animated dragging.
  12. * Defines accelerator to stop dragging screen.
  13. */
  14. public class AnimateDraggingMapThread {
  15. protected static final Log log = LogUtil.getLog(AnimateDraggingMapThread.class);
  16. private final static float DRAGGING_ANIMATION_TIME = 1900f;
  17. private final static float ZOOM_ANIMATION_TIME = 800f;
  18. private final static float ZOOM_MOVE_ANIMATION_TIME = 650f;
  19. private final static float MOVE_MOVE_ANIMATION_TIME = 2000f;
  20. private final static int DEFAULT_SLEEP_TO_REDRAW = 55;
  21. private volatile boolean stopped;
  22. private volatile Thread currentThread = null;
  23. private final OsmandMapTileView tileView;
  24. private float targetRotate = 0;
  25. private double targetLatitude = 0;
  26. private double targetLongitude = 0;
  27. private int targetZoom = 0;
  28. public AnimateDraggingMapThread(OsmandMapTileView tileView){
  29. this.tileView = tileView;
  30. }
  31. private void pendingRotateAnimation() {
  32. boolean conditionToCountinue = false;
  33. do {
  34. conditionToCountinue = false;
  35. float rotationDiff = MapUtils.unifyRotationDiff(tileView.getRotate(), targetRotate);
  36. float absDiff = Math.abs(rotationDiff);
  37. if (absDiff > 0) {
  38. try {
  39. Thread.sleep(DEFAULT_SLEEP_TO_REDRAW);
  40. } catch (InterruptedException e) {
  41. //do nothing
  42. }
  43. if (absDiff < 1) {
  44. tileView.rotateToAnimate(targetRotate);
  45. } else {
  46. conditionToCountinue = true;
  47. tileView.rotateToAnimate(rotationDiff / 5 + tileView.getRotate());
  48. }
  49. }
  50. } while (conditionToCountinue && tileView.isMapRotateEnabled());
  51. }
  52. /**
  53. * Stop dragging async
  54. */
  55. public void stopAnimating(){
  56. stopped = true;
  57. }
  58. public boolean isAnimating(){
  59. return currentThread != null && !stopped;
  60. }
  61. /**
  62. * Stop dragging sync
  63. */
  64. public void stopAnimatingSync(){
  65. // wait until current thread != null
  66. stopped = true;
  67. while(currentThread != null){
  68. try {
  69. currentThread.join();
  70. } catch (InterruptedException e) {
  71. }
  72. }
  73. }
  74. public void startThreadAnimating(final Runnable runnable){
  75. stopAnimatingSync();
  76. stopped = false;
  77. currentThread = new Thread(new Runnable() {
  78. @Override
  79. public void run() {
  80. try {
  81. runnable.run();
  82. } finally {
  83. currentThread = null;
  84. }
  85. }
  86. }, "Animating Thread");
  87. currentThread.start();
  88. }
  89. public void startMoving(final double finalLat, final double finalLon, final int endZoom, final boolean notifyListener){
  90. stopAnimatingSync();
  91. double startLat = tileView.getLatitude();
  92. double startLon = tileView.getLongitude();
  93. float rotate = tileView.getRotate();
  94. final int startZoom = tileView.getZoom();
  95. int tileSize = tileView.getSourceTileSize();
  96. int mZoom = startZoom;
  97. boolean skipAnimation = false;
  98. float mStX = (float) ((MapUtils.getTileNumberX(mZoom, startLon) - MapUtils.getTileNumberX(mZoom, finalLon)) * tileSize);
  99. float mStY = (float) ((MapUtils.getTileNumberY(mZoom, startLat) - MapUtils.getTileNumberY(mZoom, finalLat)) * tileSize);
  100. while (Math.abs(mStX) + Math.abs(mStY) > 1200) {
  101. mZoom--;
  102. if(mZoom <= 4){
  103. skipAnimation = true;
  104. }
  105. mStX = (float) ((MapUtils.getTileNumberX(mZoom, startLon) - MapUtils.getTileNumberX(mZoom, finalLon)) * tileSize);
  106. mStY = (float) ((MapUtils.getTileNumberY(mZoom, startLat) - MapUtils.getTileNumberY(mZoom, finalLat)) * tileSize);
  107. }
  108. final int moveZoom = mZoom;
  109. // check if animation needed
  110. skipAnimation = skipAnimation || (Math.abs(moveZoom - startZoom) >= 3 || Math.abs(endZoom - moveZoom) > 3);
  111. if (skipAnimation) {
  112. tileView.setLatLonAnimate(finalLat, finalLon, notifyListener);
  113. tileView.zoomToAnimate(endZoom, notifyListener);
  114. return;
  115. }
  116. float rad = (float) Math.toRadians(rotate);
  117. final float mMoveX = FloatMath.cos(rad) * mStX - FloatMath.sin(rad) * mStY;
  118. final float mMoveY = FloatMath.sin(rad) * mStX + FloatMath.cos(rad) * mStY;
  119. final float animationTime = Math.max(450, (Math.abs(mStX) + Math.abs(mStY)) / 1200f * MOVE_MOVE_ANIMATION_TIME);
  120. startThreadAnimating(new Runnable() {
  121. @Override
  122. public void run() {
  123. setTargetValues(endZoom, finalLat, finalLon);
  124. if(moveZoom != startZoom){
  125. animatingZoomInThread(startZoom, moveZoom, ZOOM_MOVE_ANIMATION_TIME, notifyListener);
  126. }
  127. if(!stopped){
  128. animatingMoveInThread(mMoveX, mMoveY, animationTime, notifyListener);
  129. }
  130. if(!stopped){
  131. tileView.setLatLonAnimate(finalLat, finalLon, notifyListener);
  132. }
  133. if (!stopped && moveZoom != endZoom) {
  134. animatingZoomInThread(moveZoom, endZoom, ZOOM_MOVE_ANIMATION_TIME, notifyListener);
  135. }
  136. pendingRotateAnimation();
  137. }
  138. });
  139. }
  140. private void animatingMoveInThread(float moveX, float moveY, float animationTime,
  141. boolean notify){
  142. AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator();
  143. float cX = 0;
  144. float cY = 0;
  145. long timeMillis = SystemClock.uptimeMillis();
  146. float normalizedTime = 0f;
  147. while(!stopped){
  148. normalizedTime = (SystemClock.uptimeMillis() - timeMillis) / animationTime;
  149. if(normalizedTime > 1f){
  150. break;
  151. }
  152. float interpolation = interpolator.getInterpolation(normalizedTime);
  153. float nX = interpolation * moveX;
  154. float nY = interpolation * moveY;
  155. tileView.dragToAnimate(cX, cY, nX, nY, notify);
  156. cX = nX;
  157. cY = nY;
  158. try {
  159. Thread.sleep(DEFAULT_SLEEP_TO_REDRAW);
  160. } catch (InterruptedException e) {
  161. stopped = true;
  162. }
  163. }
  164. }
  165. private void animatingZoomInThread(int zoomStart, int zoomEnd, float animationTime, boolean notifyListener){
  166. float curZoom = zoomStart;
  167. animationTime *= Math.abs(zoomEnd - zoomStart);
  168. // AccelerateInterpolator interpolator = new AccelerateInterpolator(1);
  169. LinearInterpolator interpolator = new LinearInterpolator();
  170. long timeMillis = SystemClock.uptimeMillis();
  171. float normalizedTime = 0f;
  172. while(!stopped){
  173. normalizedTime = (SystemClock.uptimeMillis() - timeMillis) / animationTime;
  174. if(normalizedTime > 1f){
  175. break;
  176. }
  177. float interpolation = interpolator.getInterpolation(normalizedTime);
  178. curZoom = interpolation * (zoomEnd - zoomStart) + zoomStart;
  179. tileView.zoomToAnimate(curZoom, notifyListener);
  180. try {
  181. Thread.sleep(DEFAULT_SLEEP_TO_REDRAW);
  182. } catch (InterruptedException e) {
  183. stopped = true;
  184. }
  185. }
  186. if(curZoom != ((int) Math.round(curZoom))){
  187. if(Math.abs(curZoom - zoomEnd) > 2){
  188. if(zoomStart > zoomEnd){
  189. curZoom = (float) Math.floor(curZoom);
  190. } else {
  191. curZoom = (float) Math.ceil(curZoom);
  192. }
  193. tileView.zoomToAnimate(curZoom, notifyListener);
  194. } else {
  195. tileView.zoomToAnimate(zoomEnd, notifyListener);
  196. }
  197. }
  198. }
  199. public void startZooming(final int zoomEnd, final boolean notifyListener){
  200. final float animationTime = ZOOM_ANIMATION_TIME;
  201. startThreadAnimating(new Runnable(){
  202. @Override
  203. public void run() {
  204. final int zoomStart = tileView.getZoom();
  205. setTargetValues(zoomEnd, tileView.getLatitude(), tileView.getLongitude());
  206. animatingZoomInThread(zoomStart, zoomEnd, animationTime, notifyListener);
  207. pendingRotateAnimation();
  208. }
  209. }); //$NON-NLS-1$
  210. }
  211. public void startDragging(final float velocityX, final float velocityY, float startX, float startY,
  212. final float endX, final float endY, final boolean notifyListener){
  213. final float animationTime = DRAGGING_ANIMATION_TIME;
  214. clearTargetValues();
  215. startThreadAnimating(new Runnable(){
  216. @Override
  217. public void run() {
  218. float curX = endX;
  219. float curY = endY;
  220. DecelerateInterpolator interpolator = new DecelerateInterpolator(1);
  221. long timeMillis = SystemClock.uptimeMillis();
  222. float normalizedTime = 0f;
  223. float prevNormalizedTime = 0f;
  224. while(!stopped){
  225. normalizedTime = (SystemClock.uptimeMillis() - timeMillis) / animationTime;
  226. if(normalizedTime >= 1f){
  227. break;
  228. }
  229. float interpolation = interpolator.getInterpolation(normalizedTime);
  230. float newX = velocityX * (1 - interpolation) * (normalizedTime - prevNormalizedTime) + curX;
  231. float newY = velocityY * (1 - interpolation) * (normalizedTime - prevNormalizedTime) + curY;
  232. tileView.dragToAnimate(curX, curY, newX, newY, notifyListener);
  233. curX = newX;
  234. curY = newY;
  235. prevNormalizedTime = normalizedTime;
  236. try {
  237. Thread.sleep(DEFAULT_SLEEP_TO_REDRAW);
  238. } catch (InterruptedException e) {
  239. stopped = true;
  240. }
  241. }
  242. pendingRotateAnimation();
  243. }
  244. }); //$NON-NLS-1$
  245. }
  246. private void clearTargetValues(){
  247. targetZoom = 0;
  248. }
  249. private void setTargetValues(int zoom, double lat, double lon){
  250. targetZoom = zoom;
  251. targetLatitude = lat;
  252. targetLongitude = lon;
  253. }
  254. public void startRotate(final float rotate) {
  255. if (!isAnimating()) {
  256. clearTargetValues();
  257. // stopped = false;
  258. // do we need to kill and recreate the thread? wait would be enough as now it
  259. // also handles the rotation?
  260. startThreadAnimating(new Runnable() {
  261. @Override
  262. public void run() {
  263. targetRotate = rotate;
  264. pendingRotateAnimation();
  265. }
  266. });
  267. } else {
  268. this.targetRotate = rotate;
  269. }
  270. }
  271. public int getTargetZoom() {
  272. return targetZoom;
  273. }
  274. public double getTargetLatitude() {
  275. return targetLatitude;
  276. }
  277. public double getTargetLongitude() {
  278. return targetLongitude;
  279. }
  280. }