package org.deviceconnect.android.deviceplugin.theta.profile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.PixelFormat; import android.graphics.Point; import android.os.Handler; import android.os.Looper; import android.util.DisplayMetrics; import android.view.Display; import android.view.View; import android.view.WindowManager; import org.deviceconnect.android.deviceplugin.theta.core.SphericalViewParam; class OverlayProjector extends AbstractProjector { private static final long MAX_INTERVAL = 100; private final Context mContext; private final WindowManager mWinMgr; private final Handler mHandler; private Thread mThread; private boolean mIsRequestedToStop; private OverlayView mPreview; private boolean mIsAttachedView; private EventListener mEventListener; private byte[] mImageCache; private final BroadcastReceiver mOrientReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { if (mPreview == null) { return; } String action = intent.getAction(); if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { updatePosition(mPreview); } } }; public OverlayProjector(final Context context) { mContext = context; mWinMgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mHandler = new Handler(Looper.getMainLooper()); } @Override public synchronized boolean start() { if (isShow()) { return false; } mHandler.post(new Runnable() { @Override public void run() { int w = mRenderer.getScreenWidth(); int h = mRenderer.getScreenHeight(); show(w, h, mRenderer.isStereo()); } }); if (mScreen != null) { startProjectionThread(); } return true; } private void startProjectionThread() { mThread = new Thread(new Runnable() { @Override public void run() { try { mScreen.onStart(OverlayProjector.this); while (!mIsRequestedToStop) { long start = System.currentTimeMillis(); byte[] frame = mRenderer.takeSnapshot(); mImageCache = frame; mScreen.onProjected(OverlayProjector.this, frame); long end = System.currentTimeMillis(); long interval = MAX_INTERVAL - (end - start); if (interval > 0) { Thread.sleep(interval); } } } catch (InterruptedException e) { // Nothing to do. } finally { mIsRequestedToStop = false; mThread = null; mScreen.onStop(OverlayProjector.this); } } }); mThread.start(); } @Override public synchronized boolean stop() { if (!isShow()) { return false; } mHandler.post(new Runnable() { @Override public void run() { hide(); } }); if (mThread != null) { mIsRequestedToStop = true; } return true; } @Override public void setParameter(final SphericalViewParam param) { updateViewSize(param.getWidth(), param.getHeight(), param.isStereo()); super.setParameter(param); } @Override public byte[] getImageCache() { return mImageCache; } private boolean isShow() { return mIsAttachedView; } private void show(final int width , final int height, final boolean isStereo) { Point size = getDisplaySize(); int x = size.x / 2; int y = size.y / 2; show(x, y, width, height, isStereo); } private void show(final int x, final int y, final int width , final int height, final boolean isStereo) { mPreview = new OverlayView(mContext); mPreview.setRenderer(getRenderer()); mPreview.setOnClickListener(new View.OnClickListener() { private int i = 0; @Override public void onClick(final View v) { i++; Handler handler = new Handler(); Runnable r = new Runnable() { @Override public void run() { i = 0; } }; if (i == 1) { handler.postDelayed(r, 300); } else if (i == 2) { i = 0; if (mEventListener != null) { mEventListener.onClick(); } } } }); mPreview.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(final View v) { if (mEventListener != null) { mEventListener.onClose(); } hide(); return true; } }); final int w = isStereo ? width * 2 : width; final WindowManager.LayoutParams l = new WindowManager.LayoutParams( w, height, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT); l.x = (int) (x * getScaledDensity()); l.y = (int) (y * getScaledDensity()); mWinMgr.addView(mPreview, l); mIsAttachedView = true; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); mContext.registerReceiver(mOrientReceiver, filter); } private void hide() { if (mPreview != null) { mWinMgr.removeView(mPreview); mPreview = null; } mIsAttachedView = false; mContext.unregisterReceiver(mOrientReceiver); } private void updateViewSize(final int width, final int height, final boolean isStereo) { final int w = isStereo ? width * 2 : width; mHandler.post(new Runnable() { @Override public void run() { final WindowManager.LayoutParams l = new WindowManager.LayoutParams( w, height, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT); Point size = getDisplaySize(); l.x = (int) ((size.x / 2) * getScaledDensity()); l.y = (int) ((size.y / 2) * getScaledDensity()); mWinMgr.updateViewLayout(mPreview, l); } }); } private float getScaledDensity() { DisplayMetrics metrics = new DisplayMetrics(); mWinMgr.getDefaultDisplay().getMetrics(metrics); return metrics.scaledDensity; } private Point getDisplaySize() { Display display = mWinMgr.getDefaultDisplay(); Point size = new Point(); display.getSize(size); return size; } private void updatePosition(final View view) { if (view == null) { return; } WindowManager.LayoutParams lp = (WindowManager.LayoutParams) view.getLayoutParams(); Point size = getDisplaySize(); lp.x = (int) ((size.x / 2) * getScaledDensity()); lp.y = (int) ((size.y / 2) * getScaledDensity()); mWinMgr.updateViewLayout(view, lp); } public void setEventListener(final EventListener l) { mEventListener = l; } public interface EventListener { void onClose(); void onClick(); } }