using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.Content.PM; using Android.Graphics; using Android.Graphics.Drawables; using Android.OS; using Android.Runtime; using Android.Util; using Android.Views; using Android.Widget; using keepass2android; using KeePassLib; using KeePassLib.Utility; using Object = Java.Lang.Object; namespace keepass2android { public class ZoomableImageView : ImageView { private class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener { private ZoomableImageView parent; public ScaleListener(ZoomableImageView parent) { this.parent = parent; } public override bool OnScaleBegin(ScaleGestureDetector detector) { parent.mode = ZOOM; return true; } public override bool OnScale(ScaleGestureDetector detector) { float scaleFactor = detector.ScaleFactor; float newScale = parent.saveScale * scaleFactor; if (newScale < parent.maxScale && newScale > parent.minScale) { parent.saveScale = newScale; float width = parent.Width; float height = parent.Height; parent.right = (parent.originalBitmapWidth * parent.saveScale) - width; parent.bottom = (parent.originalBitmapHeight * parent.saveScale) - height; float scaledBitmapWidth = parent.originalBitmapWidth * parent.saveScale; float scaledBitmapHeight = parent.originalBitmapHeight * parent.saveScale; if (scaledBitmapWidth <= width || scaledBitmapHeight <= height) { parent.matrix.PostScale(scaleFactor, scaleFactor, width / 2, height / 2); } else { parent.matrix.PostScale(scaleFactor, scaleFactor, detector.FocusX, detector.FocusY); } } return true; } } const int NONE = 0; const int DRAG = 1; const int ZOOM = 2; const int CLICK = 3; private int mode = NONE; private Matrix matrix = new Matrix(); private PointF last = new PointF(); private PointF start = new PointF(); private float minScale = 1.0f; private float maxScale = 100.0f; private float[] m; private float redundantXSpace, redundantYSpace; private float saveScale = 1f; private float right, bottom, originalBitmapWidth, originalBitmapHeight; private ScaleGestureDetector mScaleDetector; protected ZoomableImageView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { } public ZoomableImageView(Context context) : base(context) { init(context); } public ZoomableImageView(Context context, IAttributeSet attrs) : base(context, attrs) { init(context); } public ZoomableImageView(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr) { init(context); } private void init(Context context) { base.Clickable = true; mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this)); m = new float[9]; ImageMatrix = matrix; SetScaleType(ScaleType.Matrix); } protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { base.OnMeasure(widthMeasureSpec, heightMeasureSpec); int bmHeight = getBmHeight(); int bmWidth = getBmWidth(); float width = MeasuredWidth; float height = MeasuredHeight; //Fit to screen. float scale = width > height ? height / bmHeight : width / bmWidth; matrix.SetScale(scale, scale); saveScale = 1f; originalBitmapWidth = scale * bmWidth; originalBitmapHeight = scale * bmHeight; // Center the image redundantYSpace = (height - originalBitmapHeight); redundantXSpace = (width - originalBitmapWidth); matrix.PostTranslate(redundantXSpace / 2, redundantYSpace / 2); ImageMatrix = matrix; } public override bool OnTouchEvent(MotionEvent event_) { mScaleDetector.OnTouchEvent(event_); matrix.GetValues(m); float x = m[Matrix.MtransX]; float y = m[Matrix.MtransY]; PointF curr = new PointF(event_.GetX(), event_.GetY()); Log.Debug("TOUCH", event_.Action.ToString(), " mode=" + mode); switch (event_.Action) { //when one finger is touching //set the mode to DRAG case MotionEventActions.Down: last.Set(event_.GetX(), event_.GetY()); start.Set(last); mode = DRAG; break; //when two fingers are touching //set the mode to ZOOM case MotionEventActions.Pointer2Down: case MotionEventActions.PointerDown: last.Set(event_.GetX(), event_.GetY()); start.Set(last); mode = ZOOM; break; //when a finger moves //If mode is applicable move image case MotionEventActions.Move: //if the mode is ZOOM or //if the mode is DRAG and already zoomed if (mode == ZOOM || (mode == DRAG && saveScale > minScale)) { float deltaX = curr.X - last.X;// x difference float deltaY = curr.Y - last.Y;// y difference float scaleWidth = (float)System.Math.Round(originalBitmapWidth * saveScale);// width after applying current scale float scaleHeight = (float)System.Math.Round(originalBitmapHeight * saveScale);// height after applying current scale bool limitX = false; bool limitY = false; //if scaleWidth is smaller than the views width //in other words if the image width fits in the view //limit left and right movement if (scaleWidth < Width && scaleHeight < Height) { // don't do anything } else if (scaleWidth < Width) { deltaX = 0; limitY = true; } //if scaleHeight is smaller than the views height //in other words if the image height fits in the view //limit up and down movement else if (scaleHeight < Height) { deltaY = 0; limitX = true; } //if the image doesnt fit in the width or height //limit both up and down and left and right else { limitX = true; limitY = true; } if (limitY) { if (y + deltaY > 0) { deltaY = -y; } else if (y + deltaY < -bottom) { deltaY = -(y + bottom); } } if (limitX) { if (x + deltaX > 0) { deltaX = -x; } else if (x + deltaX < -right) { deltaX = -(x + right); } } //move the image with the matrix matrix.PostTranslate(deltaX, deltaY); //set the last touch location to the current last.Set(curr.X, curr.Y); } break; //first finger is lifted case MotionEventActions.Up: mode = NONE; int xDiff = (int)System.Math.Abs(curr.X - start.X); int yDiff = (int)System.Math.Abs(curr.Y - start.Y); if (xDiff < CLICK && yDiff < CLICK) PerformClick(); break; // second finger is lifted case MotionEventActions.Pointer2Up: case MotionEventActions.PointerUp: mode = NONE; break; } ImageMatrix = matrix; Invalidate(); return true; } public void setMaxZoom(float x) { maxScale = x; } private int getBmWidth() { Drawable drawable = Drawable; if (drawable != null) { return drawable.IntrinsicWidth; } return 0; } private int getBmHeight() { Drawable drawable = Drawable; if (drawable != null) { return drawable.IntrinsicHeight; } return 0; } } [Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden, Theme = "@style/Kp2aTheme_ActionBar")] public class ImageViewActivity : LockCloseActivity { private ActivityDesign _activityDesign; public ImageViewActivity() { _activityDesign = new ActivityDesign(this); } protected override void OnResume() { base.OnResume(); _activityDesign.ReapplyTheme(); } protected override void OnCreate(Bundle savedInstanceState) { _activityDesign.ApplyTheme(); base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.ImageViewActivity); ElementAndDatabaseId fullId = new ElementAndDatabaseId(Intent.GetStringExtra("EntryId")); var uuid = new PwUuid(MemUtil.HexStringToByteArray(fullId.ElementIdString)); string key = Intent.GetStringExtra("EntryKey"); var binary = App.Kp2a.GetDatabase(fullId.DatabaseId).EntriesById[uuid].Binaries.Get(key); SupportActionBar.Title = key; byte[] pbdata = binary.ReadData(); var bmp = BitmapFactory.DecodeByteArray(pbdata,0,pbdata.Length); FindViewById(Resource.Id.imageView).SetImageBitmap(bmp); } } }