content: package com.gamatechno.vrlib.plugins.hotspot;
  
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.PorterDuff;
  import android.view.InputDevice;
  import android.view.MotionEvent;
  import android.view.View;
  
  import com.gamatechno.vrlib.MD360Director;
  import com.gamatechno.vrlib.MDVRLibrary;
  import com.gamatechno.vrlib.model.MDHitEvent;
  import com.gamatechno.vrlib.model.MDHitPoint;
  import com.gamatechno.vrlib.model.MDViewBuilder;
  import com.gamatechno.vrlib.texture.MD360BitmapTexture;
  import com.gamatechno.vrlib.texture.MD360Texture;
  
  import static com.gamatechno.vrlib.common.VRUtil.checkMainThread;
  import static com.gamatechno.vrlib.common.VRUtil.notNull;
  
  /**
   * Created by hzqiujiadi on 2017/4/12.
   * hzqiujiadi ashqalcn@gmail.com
   */
  
  public abstract class MDAbsView extends MDAbsHotspot {
  
      private boolean mInvalidate;
  
      private MD360Texture mTexture;
  
      private View mAttachedView;
  
      private MDLayoutParams mLayoutParams;
  
      private Canvas mCanvas;
  
      private Bitmap mBitmap;
  
      private TouchStatus mTouchStatus;
  
      private enum TouchStatus{
          NOP, DOWN
      }
  
      public MDAbsView(MDViewBuilder builder) {
          super(builder.builderDelegate);
          this.mAttachedView = builder.attachedView;
          this.mLayoutParams = builder.layoutParams;
          this.mAttachedView.setLayoutParams(this.mLayoutParams);
  
          try {
              this.mBitmap = Bitmap.createBitmap(mLayoutParams.width, mLayoutParams.height, Bitmap.Config.ARGB_8888);
              this.mCanvas = new Canvas(mBitmap);
          } catch (Exception e){
              e.printStackTrace();
          }
  
          requestLayout();
      }
  
      public void invalidate(){
          if (mBitmap == null){
              return;
          }
  
          checkMainThread("invalidate must called in main thread.");
          notNull(mLayoutParams, "layout params can't be null");
          notNull(mAttachedView, "attached view can't be null");
  
          mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
          mAttachedView.draw(mCanvas);
          mInvalidate = true;
      }
  
      public void requestLayout(){
          if (mBitmap == null){
              return;
          }
  
          checkMainThread("requestLayout must called in main thread.");
          notNull(mLayoutParams, "layout params can't be null");
          notNull(mAttachedView, "attached view can't be null");
  
          mAttachedView.measure(
                  View.MeasureSpec.makeMeasureSpec(mLayoutParams.width, View.MeasureSpec.EXACTLY),
                  View.MeasureSpec.makeMeasureSpec(mLayoutParams.height, View.MeasureSpec.EXACTLY)
          );
          mAttachedView.layout(0, 0, mAttachedView.getMeasuredWidth(), mAttachedView.getMeasuredHeight());
  
          invalidate();
      }
  
      @Override
      protected void initInGL(Context context) {
          super.initInGL(context);
  
          mTexture = new MD360BitmapTexture(new MDVRLibrary.IBitmapProvider() {
              @Override
              public void onProvideBitmap(MD360BitmapTexture.Callback callback) {
                  if (mBitmap != null){
                      callback.texture(mBitmap);
                  }
              }
          });
          mTexture.create();
      }
  
      @Override
      public void renderer(int index, int width, int height, MD360Director director) {
          if (mTexture == null || mBitmap == null){
              return;
          }
  
          if (mInvalidate){
              mInvalidate = false;
              mTexture.notifyChanged();
          }
  
          mTexture.texture(program);
  
          if (mTexture.isReady()){
              super.renderer(index, width, height, director);
          }
      }
  
      @Override
      public void onEyeHitIn(MDHitEvent hitEvent) {
          super.onEyeHitIn(hitEvent);
  
          MDHitPoint point = hitEvent.getHitPoint();
          if (point == null || mAttachedView == null) {
              return;
          }
          int action = mTouchStatus == TouchStatus.NOP ? MotionEvent.ACTION_HOVER_ENTER : MotionEvent.ACTION_HOVER_MOVE;
          float x = mAttachedView.getLeft() + mAttachedView.getWidth() * point.getU();
          float y = mAttachedView.getTop() + mAttachedView.getHeight() * point.getV();
  
          MotionEvent motionEvent = MotionEvent.obtain(hitEvent.getTimestamp(), System.currentTimeMillis(), action, x, y, 0);
          motionEvent.setSource(InputDevice.SOURCE_CLASS_POINTER);
          mAttachedView.dispatchGenericMotionEvent(motionEvent);
          motionEvent.recycle();
          mTouchStatus = TouchStatus.DOWN;
  
          invalidate();
      }
  
      @Override
      public void onEyeHitOut(long timestamp) {
          super.onEyeHitOut(timestamp);
          if (mTouchStatus == TouchStatus.DOWN){
              MotionEvent motionEvent = MotionEvent.obtain(timestamp, System.currentTimeMillis(), MotionEvent.ACTION_HOVER_EXIT, 0, 0, 0);
              motionEvent.setSource(InputDevice.SOURCE_CLASS_POINTER);
              mAttachedView.dispatchGenericMotionEvent(motionEvent);
              motionEvent.recycle();
          }
          mTouchStatus = TouchStatus.NOP;
  
          invalidate();
      }
  
      public <T extends View> T castAttachedView(Class<T> clazz){
          notNull(clazz, "param clz can't be null.");
          return clazz.cast(mAttachedView);
      }
  
      public View getAttachedView() {
          return mAttachedView;
      }
  }
  