package com.almalence.plugins.vf.gyro;
import java.util.concurrent.atomic.AtomicBoolean;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PorterDuff.Mode;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import com.almalence.plugins.capture.panoramaaugmented.AugmentedRotationListener;
import com.almalence.plugins.capture.panoramaaugmented.VfGyroSensor;
import com.almalence.ui.RotateImageView;
/* <!-- +++
import com.almalence.opencam_plus.ApplicationScreen;
import com.almalence.opencam_plus.PluginViewfinder;
import com.almalence.opencam_plus.R;
import com.almalence.opencam_plus.cameracontroller.CameraController;
+++ --> */
//<!-- -+-
import com.almalence.opencam.ApplicationScreen;
import com.almalence.opencam.PluginViewfinder;
import com.almalence.opencam.R;
import com.almalence.opencam.cameracontroller.CameraController;
//-+- -->
public class GyroVFPlugin extends PluginViewfinder
{
private static final Boolean ON = true;
private static final Boolean OFF = false;
private int mOrientation;
private Boolean mGyroState;
private SensorManager mSensorManager;
private Sensor mMagnetometer;
private Sensor mAccelerometer;
private Sensor mGyroscope;
private VfGyroSensor mVfGyroscope;
private AugmentedRotationListener mAugmentedListener;
private AugmentedSurfaceView mSurfacePreviewAugmented;
private static Handler handler = new Handler();
private float viewAngleX = 55.4f;
private float viewAngleY = 42.7f;
private int pictureWidth;
private int pictureHeight;
private int previewWidth;
private int previewHeight;
private boolean remapOrientation;
private boolean mPrefHardwareGyroscope = true;
private RelativeLayout mHorizonIndicatorContainer;
private RelativeLayout mHorizonIndicatorMarkContainer;
private RotateImageView mHorizonIndicatorMarkRotation;
private RotateImageView mHorizonIndicatorMarkHorizontal;
private RotateImageView mHorizonIndicatorMarkTopDown;
private RotateImageView mHorizonIndicatorAim;
private RotateImageView mHorizonIndicatorAimTopDown;
private View mHorizonLayout;
private Boolean flat = false;
public GyroVFPlugin()
{
super("com.almalence.plugins.gyrovf", R.xml.preferences_vf_gyro, 0, R.drawable.gui_almalence_settings_gyro,
ApplicationScreen.getAppResources().getString(R.string.Pref_TitleGyroVF));
}
@Override
public void onCameraParametersSetup()
{
this.checkCoordinatesRemapRequired();
this.pictureWidth = CameraController.getCameraImageSize().getWidth();
this.pictureHeight = CameraController.getCameraImageSize().getHeight();
this.previewWidth = ApplicationScreen.getPreviewWidth();
this.previewHeight = ApplicationScreen.getPreviewHeight();
try
{
this.viewAngleX = CameraController.getHorizontalViewAngle();
this.viewAngleY = CameraController.getVerticalViewAngle();
} catch (final Exception e)
{
// Some bugged camera drivers pop ridiculous exception here, use
// typical view angles then
this.viewAngleX = 55.4f;
this.viewAngleY = 42.7f;
}
// some devices report incorrect FOV values, use typical view angles
// then
if (this.viewAngleX >= 150)
{
this.viewAngleX = 55.4f;
this.viewAngleY = 42.7f;
}
// Some cameras report incorrect view angles
// Usually vertical view angle is incorrect, but eg Htc One report
// incorrect horizontal view angle
// If aspect ratio from FOV differs by more than 10% from aspect ratio
// from W/H
// - re-compute view angle
float HorizontalViewFromAspect = 2
* 180
* (float) Math.atan((float) this.pictureWidth / (float) this.pictureHeight
* (float) Math.tan((float) Math.PI * this.viewAngleY / (2 * 180))) / (float) Math.PI;
float VerticalViewFromAspect = 2
* 180
* (float) Math.atan((float) this.pictureHeight / (float) this.pictureWidth
* (float) Math.tan((float) Math.PI * this.viewAngleX / (2 * 180))) / (float) Math.PI;
// not expecting very narrow field of view
if ((VerticalViewFromAspect > 40.f) && (VerticalViewFromAspect < 0.9f * this.viewAngleY))
this.viewAngleY = VerticalViewFromAspect;
else if ((HorizontalViewFromAspect < 0.9f * this.viewAngleX)
|| (HorizontalViewFromAspect > 1.1f * this.viewAngleX))
this.viewAngleX = HorizontalViewFromAspect;
if(mSurfacePreviewAugmented != null)
mSurfacePreviewAugmented.reset(this.pictureHeight, this.pictureWidth, this.viewAngleY);
if (!mPrefHardwareGyroscope)
{
mVfGyroscope.SetFrameParameters(this.previewWidth, this.previewHeight, this.viewAngleX, this.viewAngleY);
}
}
private void checkCoordinatesRemapRequired()
{
final Display display = ((WindowManager) ApplicationScreen.instance.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
// This is proved way of checking it so we better use deprecated
// methods.
@SuppressWarnings("deprecation")
final int orientation = (display.getWidth() <= display.getHeight()) ? Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
final int rotation = display.getRotation();
this.remapOrientation = (orientation == Configuration.ORIENTATION_LANDSCAPE && rotation == Surface.ROTATION_0)
|| (orientation == Configuration.ORIENTATION_LANDSCAPE && rotation == Surface.ROTATION_180)
|| (orientation == Configuration.ORIENTATION_PORTRAIT && rotation == Surface.ROTATION_90)
|| (orientation == Configuration.ORIENTATION_PORTRAIT && rotation == Surface.ROTATION_270);
}
@Override
public void onResume()
{
mSensorManager = (SensorManager) ApplicationScreen.instance.getSystemService(Context.SENSOR_SERVICE);
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// to see if it helps to fix "can't load library error"
try
{
mVfGyroscope = new VfGyroSensor(null);
} catch (Exception e)
{
e.printStackTrace();
}
mSurfacePreviewAugmented = new AugmentedSurfaceView(this);
updatePreferences();
}
public boolean needPreviewFrame()
{
if (mGyroState == ON)
return true;
else
return false;
}
void updatePreferences()
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext());
mGyroState = prefs.getBoolean("PrefGyroVF", false);
if (!prefs.contains("PrefGyroTypeVF"))
{
Editor editor = prefs.edit();
if (mGyroscope != null)
{
editor.putBoolean("PrefGyroTypeVF", true);
} else
{
editor.putBoolean("PrefGyroTypeVF", false);
}
editor.commit();
}
mPrefHardwareGyroscope = prefs.getBoolean("PrefGyroTypeVF", false);
if (mGyroState == ON)
{
quickControlIconID = R.drawable.gui_almalence_settings_gyro;
if (mHorizonIndicatorContainer != null)
{
mHorizonIndicatorContainer.setVisibility(View.VISIBLE);
}
initSensors();
} else
{
quickControlIconID = R.drawable.gui_almalence_settings_gyro_off;
if (mHorizonIndicatorContainer != null)
{
mHorizonIndicatorContainer.setVisibility(View.GONE);
}
releaseSensors();
}
}
@Override
public void onPreviewFrame(byte[] data)
{
if (mGyroState == OFF)
return;
if (!this.mPrefHardwareGyroscope)
{
this.mVfGyroscope.NewData(data);
if (mSurfacePreviewAugmented != null)
{
mSurfacePreviewAugmented.onDrawFrame();
}
}
}
@Override
public void onOrientationChanged(int orientation)
{
mOrientation = orientation;
if (mHorizonIndicatorMarkHorizontal != null)
{
mHorizonIndicatorMarkHorizontal.setRotation(orientation - 90);
}
}
@Override
public void onQuickControlClick()
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext());
Editor editor = prefs.edit();
if (mGyroState == ON)
{
quickControlIconID = R.drawable.gui_almalence_settings_gyro_off;
editor.putBoolean("PrefGyroVF", false);
} else
{
quickControlIconID = R.drawable.gui_almalence_settings_gyro;
editor.putBoolean("PrefGyroVF", true);
}
editor.commit();
updatePreferences();
CameraController.checkNeedPreviewFrame();
}
@Override
public void onPause()
{
releaseSensors();
}
private void updatehHardwareGyro()
{
if (mGyroState == OFF)
return;
if (mSurfacePreviewAugmented != null)
{
mSurfacePreviewAugmented.onDrawFrame();
}
handler.postDelayed(new Runnable()
{
@Override
public void run()
{
updatehHardwareGyro();
}
}, 50);
}
private void initSensors()
{
if (mGyroState == ON)
{
if (mSensorManager == null)
{
mSensorManager = (SensorManager) ApplicationScreen.instance.getSystemService(Context.SENSOR_SERVICE);
}
if (mSensorManager == null)
{
return;
}
mSurfacePreviewAugmented.reset(this.pictureHeight, this.pictureWidth, this.viewAngleY);
mAugmentedListener = new AugmentedRotationListener(remapOrientation, !mPrefHardwareGyroscope);
if (this.mGyroscope != null)
{
if (mPrefHardwareGyroscope)
{
mSensorManager.registerListener(mAugmentedListener, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
updatehHardwareGyro();
handler.postDelayed(new Runnable()
{
@Override
public void run()
{
updatehHardwareGyro();
}
}, 500);
}
}
if (!mPrefHardwareGyroscope)
{
if (mVfGyroscope == null)
{
mVfGyroscope = new VfGyroSensor(null);
}
mVfGyroscope.open();
mVfGyroscope.SetListener(mAugmentedListener);
}
mSensorManager.registerListener(mAugmentedListener, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
mSensorManager.registerListener(mAugmentedListener, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
mAugmentedListener.setReceiver(mSurfacePreviewAugmented);
}
}
private void releaseSensors()
{
mGyroState = OFF;
if (mPrefHardwareGyroscope)
{
if (mSensorManager != null)
mSensorManager.unregisterListener(mAugmentedListener, mGyroscope);
} else
{
if (null != mVfGyroscope)
mVfGyroscope.SetListener(null);
}
if (mSensorManager != null)
{
mSensorManager.unregisterListener(mAugmentedListener, mAccelerometer);
mSensorManager.unregisterListener(mAugmentedListener, mMagnetometer);
}
}
@Override
public void onGUICreate()
{
createGyroUI();
if (mGyroState == ON)
{
if (mHorizonIndicatorContainer != null)
{
mHorizonIndicatorContainer.setVisibility(View.VISIBLE);
}
} else
{
if (mHorizonIndicatorContainer != null)
{
mHorizonIndicatorContainer.setVisibility(View.GONE);
}
}
}
private AtomicBoolean horizon_updating = new AtomicBoolean(false);
public void updateHorizonIndicator(float verticalError, float horizontalError, final float sideErrorVertical,
final float sideErrorHorizontal)
{
if (mHorizonIndicatorContainer == null || mHorizonIndicatorContainer == null
|| mHorizonIndicatorMarkTopDown == null || mHorizonIndicatorMarkRotation == null
|| mHorizonIndicatorMarkHorizontal == null || mHorizonIndicatorAim == null
|| mHorizonIndicatorAimTopDown == null || mHorizonIndicatorMarkContainer == null)
{
return;
}
if (ApplicationScreen.getGUIManager().lockControls)
{
mHorizonIndicatorContainer.setVisibility(View.GONE);
return;
}
mHorizonIndicatorContainer.setVisibility(View.VISIBLE);
if (!horizon_updating.compareAndSet(false, true))
{
return;
}
if (Math.abs(horizontalError) <= 0.01f && Math.abs(verticalError) <= 0.01f)
{
mHorizonIndicatorMarkContainer.setPadding(0, 0, 0, 0);
if (!flat)
{
rotateHorizonIndicator(sideErrorVertical, sideErrorHorizontal);
}
int color = 255;
mHorizonIndicatorMarkTopDown.setColorFilter(Color.rgb(color, color, 0), Mode.MULTIPLY);
mHorizonIndicatorMarkRotation.setColorFilter(Color.rgb(color, color, 0), Mode.MULTIPLY);
horizon_updating.set(false);
return;
}
float density = ApplicationScreen.getAppResources().getDisplayMetrics().density;
if ((Math.abs(horizontalError) > 1.0f && (mOrientation == 0 || mOrientation == 180 || flat))
|| (Math.abs(verticalError) > 1.0f && (mOrientation == 90 || mOrientation == 270 || flat)))
{
flat = true;
mHorizonIndicatorMarkRotation.setVisibility(View.GONE);
mHorizonIndicatorMarkHorizontal.setVisibility(View.GONE);
mHorizonIndicatorMarkTopDown.setVisibility(View.VISIBLE);
mHorizonIndicatorAim.setVisibility(View.GONE);
mHorizonIndicatorAimTopDown.setVisibility(View.VISIBLE);
if (Math.abs(verticalError) > 0.9f)
{
if (verticalError > 0.0f)
{
verticalError = (float) (verticalError - Math.PI / 2);
} else
{
verticalError = (float) (verticalError + Math.PI / 2);
}
}
if (Math.abs(horizontalError) > 0.9f)
{
if (horizontalError > 0.0f)
{
horizontalError = (float) (horizontalError - Math.PI / 2);
} else
{
horizontalError = (float) (horizontalError + Math.PI / 2);
}
}
final int marginVerticalValue = (int) (300.0f * Math.abs(verticalError) * density);
final int marginHorizontalValue = (int) (300.0f * Math.abs(horizontalError) * density);
int color = (255 - (marginVerticalValue + marginHorizontalValue) * 4);
if (color > 50)
{
mHorizonIndicatorMarkTopDown.setColorFilter(Color.rgb(color, color, 0), Mode.MULTIPLY);
} else
{
mHorizonIndicatorMarkTopDown.clearColorFilter();
}
if (verticalError < 0.0f)
{
if (horizontalError < 0.0f)
{
mHorizonIndicatorMarkContainer.setPadding(0, marginVerticalValue, marginHorizontalValue, 0);
} else
{
mHorizonIndicatorMarkContainer.setPadding(marginHorizontalValue, marginVerticalValue, 0, 0);
}
} else
{
if (horizontalError < 0.0f)
{
mHorizonIndicatorMarkContainer.setPadding(0, 0, marginHorizontalValue, marginVerticalValue);
} else
{
mHorizonIndicatorMarkContainer.setPadding(marginHorizontalValue, 0, 0, marginVerticalValue);
}
}
horizon_updating.set(false);
} else
{
flat = false;
mHorizonIndicatorMarkRotation.setVisibility(View.VISIBLE);
mHorizonIndicatorMarkHorizontal.setVisibility(View.VISIBLE);
mHorizonIndicatorMarkTopDown.setVisibility(View.GONE);
mHorizonIndicatorAim.setVisibility(View.VISIBLE);
mHorizonIndicatorAimTopDown.setVisibility(View.GONE);
final int marginVerticalValue = (int) (300.0f * Math.abs(verticalError) * density);
final int marginHorizontalValue = (int) (300.0f * Math.abs(horizontalError) * density);
rotateHorizonIndicator(sideErrorVertical, sideErrorHorizontal);
if (mOrientation == 90 || mOrientation == 270)
{
if (verticalError < 0.0f)
{
mHorizonIndicatorMarkContainer.setPadding(0, marginVerticalValue, 0, 0);
} else
{
mHorizonIndicatorMarkContainer.setPadding(0, 0, 0, marginVerticalValue);
}
} else
{
if (horizontalError < 0.0f)
{
mHorizonIndicatorMarkContainer.setPadding(0, 0, marginHorizontalValue, 0);
} else
{
mHorizonIndicatorMarkContainer.setPadding(marginHorizontalValue, 0, 0, 0);
}
}
horizon_updating.set(false);
}
}
private void rotateHorizonIndicator(float sideErrorVertical, float sideErrorHorizontal)
{
mHorizonIndicatorAim.setOrientation(mOrientation - 90);
int colorVert = (255 - Math.abs(((int) Math.toDegrees(sideErrorVertical) - 90) % 90) * 15);
int colorHorz = (255 - Math.abs(((int) Math.toDegrees(sideErrorHorizontal) - 90) % 90) * 15);
if (mOrientation == 90 || mOrientation == 270)
{
if (colorVert > 100)
{
mHorizonIndicatorMarkRotation.setColorFilter(Color.rgb(colorVert, colorVert, 0), Mode.MULTIPLY);
} else
{
mHorizonIndicatorMarkRotation.clearColorFilter();
}
} else
{
if (colorHorz > 100)
{
mHorizonIndicatorMarkRotation.setColorFilter(Color.rgb(colorHorz, colorHorz, 0), Mode.MULTIPLY);
} else
{
mHorizonIndicatorMarkRotation.clearColorFilter();
}
}
if (mOrientation == 90)
{
mHorizonIndicatorMarkRotation.setOrientation((int) Math.toDegrees(sideErrorVertical) + 180 + mOrientation);
}
if (mOrientation == 270)
{
mHorizonIndicatorMarkRotation.setOrientation(-(int) Math.toDegrees(sideErrorVertical) + mOrientation);
}
if (mOrientation == 180)
{
mHorizonIndicatorMarkRotation
.setOrientation((int) Math.toDegrees(sideErrorHorizontal) + 180 + mOrientation);
}
if (mOrientation == 0)
{
mHorizonIndicatorMarkRotation.setOrientation(-(int) Math.toDegrees(sideErrorHorizontal) + mOrientation);
}
}
private void createGyroUI()
{
LayoutInflater inflator = ApplicationScreen.instance.getLayoutInflater();
mHorizonLayout = inflator.inflate(R.layout.plugin_vf_gyro_layout, null, false);
mHorizonLayout.setVisibility(View.VISIBLE);
ApplicationScreen.getGUIManager().removeViews(mHorizonLayout, R.id.specialPluginsLayout);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
((RelativeLayout) ApplicationScreen.instance.findViewById(R.id.specialPluginsLayout)).addView(mHorizonLayout,
params);
mHorizonIndicatorAim = (RotateImageView) mHorizonLayout.findViewById(R.id.horizon_indicator_aim);
mHorizonIndicatorAimTopDown = (RotateImageView) mHorizonLayout
.findViewById(R.id.horizon_indicator_aim_top_down);
mHorizonIndicatorMarkRotation = (RotateImageView) mHorizonLayout
.findViewById(R.id.horizon_indicator_mark_rotation);
mHorizonIndicatorMarkHorizontal = (RotateImageView) mHorizonLayout
.findViewById(R.id.horizon_indicator_mark_horizontal);
mHorizonIndicatorMarkTopDown = (RotateImageView) mHorizonLayout
.findViewById(R.id.horizon_indicator_mark_top_down);
mHorizonIndicatorContainer = (RelativeLayout) mHorizonLayout.findViewById(R.id.horizon_indicator_container);
mHorizonIndicatorMarkContainer = (RelativeLayout) mHorizonLayout
.findViewById(R.id.horizon_indicator_mark_container);
}
}