/*------------------------------------------------------------------------ * (The MIT License) * * Copyright (c) 2008-2011 Rhomobile, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * http://rhomobile.com *------------------------------------------------------------------------*/ package com.rhomobile.rhodes.mapview; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.widget.FrameLayout; import android.widget.ZoomButtonsController; import com.rhomobile.rhodes.AndroidR; import com.rhomobile.rhodes.BaseActivity; import com.rhomobile.rhodes.Logger; import com.rhomobile.rhodes.RhodesActivity; import com.rhomobile.rhodes.RhodesService; import com.rhomobile.rhodes.file.RhoFileApi; import com.rhomobile.rhodes.util.PerformOnUiThread; import com.rhomobile.rhodes.util.Utils; public class MapView extends BaseActivity implements MapTouch { private static final String TAG = MapView.class.getSimpleName(); private static final boolean ZOOM_ANIMATION_ENABLED = true; private static final int ZOOM_RESOLUTION = 50; private static final boolean ENABLE_MULTI_TOUCH = false; private static final String INTENT_EXTRA_PREFIX = RhodesService.INTENT_EXTRA_PREFIX + ".MapView"; public native void setSize(MapView javaDevice, long nativeDevice, int width, int height); public native void setPinImage(long nativeDevice, Bitmap pin); public native void setPinCalloutImage(long nativeDevice, Bitmap pin); public native void setPinCalloutLinkImage(long nativeDevice, Bitmap pin); public native void setESRILogoImage(long nativeDevice, Bitmap esriLogo); public native void setGoogleLogoImage(long nativeDevice, Bitmap googleLogo); public native void setMyLocationImage(long nativeDevice, Bitmap pin); public native int minZoom(long nativeDevice); public native int maxZoom(long nativeDevice); public native int zoom(long nativeDevice); public native void setZoom(long nativeDevice, int zoom); public native void move(long nativeDevice, int dx, int dy); public native void click(long nativeDevice, int x, int y); public native void paint(long nativeDevice, Canvas canvas); public native void destroy(long nativeDevice); private TouchHandler mTouchHandler; private static MapView mc = null; private Touch mTouchFirst; private Touch mTouchSecond; private double mDistance; private float mScale; private View mSurface; private ZoomButtonsController mZoomController; private long mNativeDevice; private static int ourDensity = Bitmap.DENSITY_NONE;//DisplayMetrics.DENSITY_DEFAULT; private TouchHandler createTouchHandler() { String className; int sdkVersion = Integer.parseInt(Build.VERSION.SDK); if (sdkVersion < Build.VERSION_CODES.ECLAIR) className = "OneTouchHandler"; else className = "MultiTouchHandler"; try { String pkgname = TouchHandler.class.getPackage().getName(); String fullName = pkgname + "." + className; Class<? extends TouchHandler> klass = Class.forName(fullName).asSubclass(TouchHandler.class); return klass.newInstance(); } catch (Exception e) { throw new IllegalStateException(e); } } public static void create(long nativeDevice) { RhodesActivity r = RhodesActivity.getInstance(); if (r == null) { Logger.E(TAG, "Can't create map view because main activity is null"); return; } Intent intent = new Intent(r, MapView.class); intent.putExtra(INTENT_EXTRA_PREFIX + ".nativeDevice", nativeDevice); r.startActivity(intent); } public static void destroy() { if (mc != null) { mc.finish(); mc = null; } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mc = this; mNativeDevice = getIntent().getLongExtra(INTENT_EXTRA_PREFIX + ".nativeDevice", 0); if (mNativeDevice == 0) throw new IllegalArgumentException(); BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inScaled = false; Bitmap pin = BitmapFactory.decodeResource(getResources(), AndroidR.drawable.marker, opt); //ourDensity = pin.getDensity(); pin.setDensity(Bitmap.DENSITY_NONE); setPinImage(mNativeDevice, pin); Bitmap pinCallout = BitmapFactory.decodeResource(getResources(), AndroidR.drawable.callout); setPinCalloutImage(mNativeDevice, pinCallout ); Bitmap pinCalloutLink = BitmapFactory.decodeResource(getResources(), AndroidR.drawable.callout_link); setPinCalloutLinkImage(mNativeDevice, pinCalloutLink ); Bitmap esriLogo = BitmapFactory.decodeResource(getResources(), AndroidR.drawable.esri); setESRILogoImage(mNativeDevice, esriLogo); Bitmap googleLogo = BitmapFactory.decodeResource(getResources(), AndroidR.drawable.google); setGoogleLogoImage(mNativeDevice, googleLogo); Bitmap pinMyLocation = BitmapFactory.decodeResource(getResources(), AndroidR.drawable.location, opt); pinMyLocation.setDensity(Bitmap.DENSITY_NONE); setMyLocationImage(mNativeDevice, pinMyLocation); mTouchHandler = createTouchHandler(); mTouchHandler.setMapTouch(this); FrameLayout pv = new FrameLayout(this); setContentView(pv); mSurface = new View(this) { @Override protected void dispatchDraw(Canvas canvas) { //super.dispatchDraw(canvas); if (mNativeDevice != 0) { paint(mNativeDevice, canvas); } } @Override protected void onSizeChanged(int w, int h, int oldW, int oldH) { super.onSizeChanged(w, h, oldW, oldH); if (w != oldW || h != oldH) setSize(MapView.this, mNativeDevice, w, h); setZoom(mNativeDevice, zoom(mNativeDevice)); } @Override protected void onDetachedFromWindow() { mZoomController.setVisible(false); } @Override public boolean onTouchEvent (MotionEvent event) { mZoomController.setVisible(true); if (mTouchHandler.handleTouch(event)) return true; return super.onTouchEvent(event); } }; pv.addView(mSurface, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); mZoomController = new ZoomButtonsController(mSurface); mZoomController.setAutoDismissed(false); mZoomController.setVisible(true); mZoomController.setOnZoomListener(new ZoomButtonsController.OnZoomListener() { @Override public void onZoom(boolean zoomIn) { zoomAnimated(zoomIn ? 1 : -1); } @Override public void onVisibilityChanged(boolean visible) {} }); } @Override protected void onStop() { mNativeDevice = 0; destroy(mNativeDevice); mc = null; finish(); super.onStop(); } @Override public void onDestroy() { super.onDestroy(); } public int zoom(int n) { int min = minZoom(mNativeDevice); int max = maxZoom(mNativeDevice); int zoom = zoom(mNativeDevice); zoom += n; if (zoom < min) zoom = min; if (zoom > max) zoom = max; mZoomController.setZoomOutEnabled(zoom > min); mZoomController.setZoomInEnabled(zoom < max); return zoom; } public void zoomAnimated(int n) { int zoom = zoom(n); if (!ZOOM_ANIMATION_ENABLED) setZoom(mNativeDevice, zoom); else { final int finalZoom = zoom; float from = 1f; float to = from * (n > 0 ? 2f : 0.5f); Animation anim = new ScaleAnimation(from, to, from, to, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); anim.setDuration(400); anim.setFillAfter(false); anim.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { setZoom(mNativeDevice, finalZoom); } }); mSurface.startAnimation(anim); } } public void drawImage(Canvas canvas, int x, int y, Bitmap bm) { Paint paint = new Paint(); canvas.drawBitmap(bm, x, y, paint); } public void drawText(Canvas canvas, int x, int y, int width, int height, String text, int color) { //Logger.I(TAG, "drawText: " + text); String [] lines = text.split("\n"); canvas.save(); Rect rcClip = new Rect( x, y, x+width, y+height); canvas.clipRect(rcClip); Paint paint = new Paint(); paint.setColor(color); paint.setAntiAlias(true); paint.setTextSize(28); for( int i = 0; i < lines.length; i++ ) { Rect rcLine = new Rect(); paint.getTextBounds( lines[i], 0, lines[i].length()-1, rcLine ); int nTextHeight = rcLine.height(); y += nTextHeight + nTextHeight/3; canvas.drawText(lines[i], x, y, paint); if (i == 0) { y += nTextHeight; } paint.setTextSize(18); } canvas.restore(); } public void redraw() { PerformOnUiThread.exec(new Runnable() { public void run() { mSurface.invalidate(); } }); } public static Bitmap createImage(String path) { //Utils.platformLog(TAG, "################## createBitmap("+path+")"); Bitmap b = BitmapFactory.decodeFile(path); if (b == null) { //Utils.platformLog(TAG, "##################2 createBitmap("+RhoFileApi.normalizePath("apps/" + path)+")"); b = BitmapFactory.decodeStream(RhoFileApi.open(RhoFileApi.normalizePath("apps/" + path))); if (b != null) { //Utils.platformLog(TAG, "################## OK !"); } else { //Utils.platformLog(TAG, "################## FALSE !"); } } else { //Utils.platformLog(TAG, "################## OK !"); } if (b != null) { b.setDensity(ourDensity); } return b; } public static Bitmap createImage(byte[] data) { return BitmapFactory.decodeByteArray(data, 0, data.length); } public static Bitmap createImageEx(byte[] data, int x, int y, int w, int h) { Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length); return Bitmap.createBitmap(b, x, y, w, h); } public static void destroyImage(Bitmap bm) { bm.recycle(); } public void destroyDevice() { mNativeDevice = 0; } @Override public void touchClick(Touch touch) { click(mNativeDevice, (int)touch.x, (int)touch.y); } @Override public void touchDown(Touch first, Touch second) { mTouchFirst = first; mTouchSecond = second; if (ENABLE_MULTI_TOUCH) { if (first != null && second != null) { mDistance = Math.sqrt(Math.pow(first.x - second.x, 2) + Math.pow(first.y - second.y, 2)); mScale = 1f; } } } @Override public void touchUp(Touch first, Touch second) { mTouchFirst = first; mTouchSecond = second; if (ENABLE_MULTI_TOUCH) { if (ZOOM_ANIMATION_ENABLED) { if (first != null && second != null) { double newDistance = Math.sqrt(Math.pow(first.x - second.x, 2) + Math.pow(first.y - second.y, 2)); int n = (int)((newDistance - mDistance)/ZOOM_RESOLUTION); int zoom = zoom(n); setZoom(mNativeDevice, zoom); } } } } @Override public void touchMove(Touch first, Touch second) { if (first == null || second == null) { // Move Touch t = first == null ? second : first; Touch prev = first == null ? mTouchSecond : mTouchFirst; int dx = (int)(prev.x - t.x); int dy = (int)(prev.y - t.y); prev.x = t.x; prev.y = t.y; if (dx != 0 || dy != 0) move(mNativeDevice, dx, dy); } if (ENABLE_MULTI_TOUCH) { if (first != null && second != null) { double oldDistance = Math.sqrt(Math.pow(mTouchFirst.x - mTouchSecond.x, 2) + Math.pow(mTouchFirst.y - mTouchSecond.y, 2)); double newDistance = Math.sqrt(Math.pow(first.x - second.x, 2) + Math.pow(first.y - second.y, 2)); if (ZOOM_ANIMATION_ENABLED) { float newScale = mScale * (float)Math.pow(2, newDistance - oldDistance); Animation anim = new ScaleAnimation(mScale, newScale, mScale, newScale, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); anim.setDuration(400); anim.setFillAfter(true); mSurface.startAnimation(anim); } else { int n = (int)((newDistance - oldDistance)/ZOOM_RESOLUTION); int zoom = zoom(n); setZoom(mNativeDevice, zoom); } mTouchFirst = first; mTouchSecond = second; } } } }