public void OnTouchEvent(global::Android.Views.MotionEvent e, CanvasContent content)
        {
            if (content == null)
            {
                throw new ArgumentNullException("content");
            }
            if (e == null)
            {
                throw new ArgumentNullException("e");
            }

            var began     = new List <CanvasTouch> ();
            var moved     = new List <CanvasTouch> ();
            var ended     = new List <CanvasTouch> ();
            var cancelled = new List <CanvasTouch> ();

            var pointerCount = e.PointerCount;
            var actionMasked = e.Action & global::Android.Views.MotionEventActions.Mask;
            var actionIndex  = (int)(e.Action & global::Android.Views.MotionEventActions.PointerIdMask) >> (int)global::Android.Views.MotionEventActions.PointerIdShift;
            var actionId     = e.GetPointerId(actionIndex);

            //Log.WriteLine("a = {0}, index = {1}, id = {2}, c = {3}", actionMasked, actionIndex, actionId, pointerCount);

            switch (actionMasked)
            {
            case global::Android.Views.MotionEventActions.Move:
                for (var index = 0; index < pointerCount; index++)
                {
                    var id = e.GetPointerId(index);
                    if (id < MaxTouchId)
                    {
                        var t = _activeTouches[id];
                        if (t != null)
                        {
                            var curSuperLoc = t.SuperCanvasLocation;
                            var newSuperLoc = new System.Drawing.PointF(e.GetX(index), e.GetY(index));
                            var dx          = curSuperLoc.X - newSuperLoc.X;
                            var dy          = curSuperLoc.Y - newSuperLoc.Y;
                            if (t.IsMoving ||
                                (Math.Abs(dx) > _initialMoveResolution ||
                                 Math.Abs(dy) > _initialMoveResolution) ||
                                NumActiveTouches > 1)
                            {
                                t.SuperCanvasPreviousLocation = t.SuperCanvasLocation;
                                t.CanvasPreviousLocation      = t.CanvasLocation;
                                t.PreviousTime = t.Time;
                                t.IsMoving     = true;

                                t.SuperCanvasLocation = newSuperLoc;
                                t.CanvasLocation      = LocationFromView(t.SuperCanvasLocation);
                                t.Time = DateTime.UtcNow;

                                moved.Add(t);

                                _longClickToken = -1;
                            }
                        }
                    }
                }
                break;

            case global::Android.Views.MotionEventActions.Down:
            case global::Android.Views.MotionEventActions.PointerDown:
                if (actionId < MaxTouchId)
                {
                    var t = new AndroidTouch {
                        Handle = new IntPtr(actionId + 1),                          // +1 because IntPtr=0 is special
                        SuperCanvasLocation = new System.Drawing.PointF(e.GetX(actionIndex), e.GetY(actionIndex)),
                        Time     = DateTime.UtcNow,
                        TapCount = 1,
                    };
                    t.CanvasLocation              = LocationFromView(t.SuperCanvasLocation);
                    t.CanvasPreviousLocation      = t.CanvasLocation;
                    t.SuperCanvasPreviousLocation = t.SuperCanvasLocation;
                    t.PreviousTime = t.Time;
                    began.Add(t);
                    _activeTouches[actionId] = t;

                    //
                    // Detect double tap
                    //
                    if ((t.Time - _lastBeganTime).TotalMilliseconds <= _doubleTapTimeoutMillis &&
                        Math.Abs(_lastBeganLocation.X - t.SuperCanvasLocation.X) <= _doubleTapResolution &&
                        Math.Abs(_lastBeganLocation.Y - t.SuperCanvasLocation.Y) <= _doubleTapResolution)
                    {
                        t.TapCount = 2;
                    }
                    else
                    {
                        //
                        // Detect long press
                        //
                        if (NumActiveTouches == 1)
                        {
                            var tok = _longClickTokenGenerator.Next();
                            _handler.PostDelayed(() => { HandleLongClick(tok, content); }, _longClickTimeoutMillis);
                            _longClickToken = tok;
                        }
                        else
                        {
                            _longClickToken = -1;
                        }
                    }

                    _lastBeganTime     = t.Time;
                    _lastBeganLocation = t.SuperCanvasLocation;
                }
                break;

            case global::Android.Views.MotionEventActions.Up:
            case global::Android.Views.MotionEventActions.PointerUp:
                if (actionId < MaxTouchId)
                {
                    var t = _activeTouches[actionId];
                    if (t != null)
                    {
                        t.Time = DateTime.UtcNow;
                        _activeTouches[actionId] = null;
                        ended.Add(t);
                    }
                    _longClickToken = -1;
                }
                break;
            }

            if (began.Count > 0)
            {
                content.TouchesBegan(began.ToArray(), CanvasKeys.None);
            }
            if (moved.Count > 0)
            {
                content.TouchesMoved(moved.ToArray());
            }
            if (ended.Count > 0)
            {
                content.TouchesEnded(ended.ToArray());
            }
            if (cancelled.Count > 0)
            {
                content.TouchesCancelled(cancelled.ToArray());
            }
        }
        public override void Draw(global::Android.Graphics.Canvas canvas)
        {
            //
            // Start drawing
            //
            var del = Content;

            if (del == null)
            {
                return;
            }

            var startT = DateTime.Now;

            var _graphics = new AndroidGraphics(canvas);

            _graphics.SaveState();
            _graphics.Scale(Zoom, Zoom);


            //
            // Draw
            //
            del.Frame = new RectangleF(0, 0, Width / Zoom, Height / Zoom);
            try {
                del.Draw(_graphics);
            }
            catch (Exception) {
            }

            _graphics.RestoreState();

            var endT = DateTime.Now;

            _drawTime += (endT - startT).TotalSeconds;
            _drawCount++;

            //
            // Throttle
            //
            if (_running && _drawCount > 2 && (DateTime.Now - _lastThrottleTime) >= ThrottleInterval)
            {
                _lastThrottleTime = DateTime.Now;

                var maxfps = 1.0 / (_drawTime / _drawCount);
                _drawTime  = 0;
                _drawCount = 0;

                var fps = ClampUpdateFreq((int)(maxfps * CpuUtilization));

                if (Math.Abs(fps - _fps) > 1)
                {
                    _fps = fps;
                    Start();
                }
            }

            //
            // Notify
            //
            var df = DrewFrame;

            if (df != null)
            {
                df(this, EventArgs.Empty);
            }

            if (_running)
            {
                _handler.PostDelayed(HandleDrawTimerElapsed, 1000 / _fps);
            }
        }