/*
ThetaVRModeFragment
Copyright (c) 2015 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.theta.fragment;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.ToggleButton;
import org.deviceconnect.android.deviceplugin.theta.BuildConfig;
import org.deviceconnect.android.deviceplugin.theta.R;
import org.deviceconnect.android.deviceplugin.theta.ThetaDeviceApplication;
import org.deviceconnect.android.deviceplugin.theta.activity.ThetaDeviceSettingsActivity;
import org.deviceconnect.android.deviceplugin.theta.activity.ThetaFeatureActivity;
import org.deviceconnect.android.deviceplugin.theta.core.SphericalImageView;
import org.deviceconnect.android.deviceplugin.theta.core.SphericalViewApi;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaDevice;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaDeviceException;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaDeviceManager;
import org.deviceconnect.android.deviceplugin.theta.core.ThetaObject;
import org.deviceconnect.android.deviceplugin.theta.data.ThetaObjectStorage;
import org.deviceconnect.android.deviceplugin.theta.utils.DownloadThetaDataTask;
import org.deviceconnect.android.provider.FileManager;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
/**
* Fragment to display the VR mode of THETA.
*
* @author NTT DOCOMO, INC.
*/
public class ThetaVRModeFragment extends Fragment {
/** SphericalView Max fov.*/
private static final int MAX_FOV = 90;
/** SphericalView Min fov.*/
private static final int MIN_FOV = 45;
/** VR Mode Right Layout.*/
private RelativeLayout mRightLayout;
/** VR Mode change button left and right.*/
private ToggleButton[] mVRModeChangeButton = new ToggleButton[2];
/** shooting button left and right. */
private Button[] mShootingButton = new Button[2];
/** SphericalView. */
private SphericalImageView mSphereView;
/** Stereo Flag. */
private boolean mIsStereo = false;
/** Thread Manager. */
private ScheduledExecutorService mExecutorService = Executors.newSingleThreadScheduledExecutor();
/** Progress. */
private ThetaDialogFragment mProgress;
/** Task. */
private DownloadThetaDataTask mDownloadTask;
/**
* Logger.
*/
private final Logger mLogger = Logger.getLogger("theta.sampleapp");
/** Default VR. */
private int mDefaultId;
/** Is Storage. */
private boolean mIsStorage;
/** Scale Gesture.*/
private ScaleGestureDetector mScaleDetector;
/** Scale factor. */
private float mScaleFactor = 90.0f;
private LruCache<String, byte[]> mDataCache;
private ThetaObjectStorage mStorage;
/**
* Singleton.
*/
public static ThetaVRModeFragment newInstance() {
ThetaVRModeFragment fragment = new ThetaVRModeFragment();
return fragment;
}
/** VR Change toggle button's listener.*/
private CompoundButton.OnCheckedChangeListener mVRChangeToggleListener
= new CompoundButton.OnCheckedChangeListener() {
@Override
public synchronized void onCheckedChanged(final CompoundButton compoundButton, final boolean isStereo) {
mExecutorService.schedule(new Runnable() {
@Override
public void run() {
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mIsStereo != isStereo) {
for (int i = 0; i < mVRModeChangeButton.length; i++) {
mVRModeChangeButton[i].setChecked(isStereo);
}
mIsStereo = isStereo;
enableView();
}
if (mSphereView != null) {
mSphereView.setStereo(mIsStereo);
}
}
});
}
}
}, 50, TimeUnit.MILLISECONDS);
}
};
/** ScreenShot shooting button's listener.*/
private View.OnClickListener mShootingListener = new View.OnClickListener() {
@Override
public synchronized void onClick(final View view) {
if (mProgress != null) {
mProgress.dismiss();
}
mProgress = ThetaDialogFragment.newInstance(getString(R.string.theta_ssid_prefix), getString(R.string.saving));
mProgress.show(getActivity().getFragmentManager(),
"fragment_dialog");
mExecutorService.schedule(new Runnable() {
@Override
public void run() {
saveScreenShot();
}
}, 50, TimeUnit.MILLISECONDS);
}
};
@Override
public void setArguments(Bundle args) {
super.setArguments(args);
if (args != null) {
mDefaultId = args.getInt(ThetaFeatureActivity.FEATURE_DATA, -1);
mIsStorage = args.getBoolean(ThetaFeatureActivity.FEATURE_IS_STORAGE);
}
}
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
mStorage = new ThetaObjectStorage(getContext());
setRetainInstance(true);
ThetaDeviceApplication app = (ThetaDeviceApplication) getActivity().getApplication();
mDataCache = app.getCache();
View rootView = inflater.inflate(R.layout.theta_vr_mode, null);
mRightLayout = (RelativeLayout) rootView.findViewById(R.id.right_ui);
mSphereView = (SphericalImageView) rootView.findViewById(R.id.vr_view);
SphericalViewApi api = app.getSphericalViewApi();
mSphereView.setViewApi(api);
mSphereView.setOnTouchListener(new View.OnTouchListener() {
private boolean mIsEnabledLongTouch = true;
@Override
public boolean onTouch(final View view, final MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
mIsEnabledLongTouch = true;
return true;
}
if (motionEvent.getPointerCount() == 1) {
if (mIsEnabledLongTouch && motionEvent.getEventTime() - motionEvent.getDownTime() >= 300) {
mSphereView.resetCameraDirection();
}
} else {
mIsEnabledLongTouch = false;
mScaleDetector.onTouchEvent(motionEvent);
}
return true;
}
});
init3DButtons(rootView);
enableView();
mScaleDetector = new ScaleGestureDetector(getActivity(),
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(final ScaleGestureDetector detector) {
mScaleFactor /= detector.getScaleFactor();
double scale = mScaleFactor;
if (scale > MAX_FOV) {
scale = MAX_FOV;
mScaleFactor = MAX_FOV;
}
if (scale < MIN_FOV) {
scale = MIN_FOV;
mScaleFactor = MIN_FOV;
}
mSphereView.setFOV(scale);
return true;
}
});
return rootView;
}
@Override
public void onResume() {
super.onResume();
if (mDownloadTask == null) {
if (mProgress == null) {
mProgress = ThetaDialogFragment.newInstance(getString(R.string.theta_ssid_prefix), getString(R.string.loading));
mProgress.show(getActivity().getFragmentManager(),
"fragment_dialog");
}
mDownloadTask = new DownloadThetaDataTask();
ThetaMainData main = new ThetaMainData();
mDownloadTask.execute(main);
}
}
@Override
public void onPause() {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
if (mSphereView != null) {
mSphereView.stop();
mSphereView.onPause();
}
if (mDownloadTask != null) {
mDownloadTask.cancel(true);
mDownloadTask = null;
}
super.onPause();
}
public void onConfigurationChanged(final Configuration newConfig) {
super.onConfigurationChanged(newConfig);
enableView();
}
private void showReconnectionDialog() {
final Activity activity = getActivity();
if (activity != null) {
ThetaDialogFragment.showReconnectionDialog(activity,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int i) {
dialog.dismiss();
activity.finish();
showSettingsActivity();
}
},
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int i) {
dialog.dismiss();
activity.finish();
}
});
}
}
private void showDisconnectionDialog() {
final Activity activity = getActivity();
if (activity != null) {
ThetaDialogFragment.showDisconnectionDialog(activity,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int i) {
dialog.dismiss();
showSettingsActivity();
}
},
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int i) {
dialog.dismiss();
}
});
}
}
private void showSettingsActivity() {
Activity activity = getActivity();
if (activity == null) {
return;
}
Toast.makeText(activity, R.string.camera_must_connect, Toast.LENGTH_SHORT).show();
Intent intent = new Intent();
intent.setClass(activity, ThetaDeviceSettingsActivity.class);
startActivity(intent);
}
/**
* enable/disable VR buttons.
*/
private void enableView() {
if (mIsStereo) {
mRightLayout.setVisibility(View.VISIBLE);
} else {
mRightLayout.setVisibility(View.GONE);
}
}
/**
* Init VR Buttons.
* @param rootView Root XML Layout
*/
private void init3DButtons(final View rootView) {
for (int i = 0; i < mVRModeChangeButton.length; i++) {
int identifier = getResources().getIdentifier("change_vr_mode_" + i, "id", getActivity().getPackageName());
mVRModeChangeButton[i] = (ToggleButton) rootView.findViewById(identifier);
mVRModeChangeButton[i].setOnCheckedChangeListener(mVRChangeToggleListener);
identifier = getResources().getIdentifier("theta_shutter_" + i, "id", getActivity().getPackageName());
mShootingButton[i] = (Button) rootView.findViewById(identifier);
mShootingButton[i].setOnClickListener(mShootingListener);
}
}
/**
* Save ScreenShot.
*/
private void saveScreenShot() {
FileManager fileManager = new FileManager(getActivity());
fileManager.checkWritePermission(new FileManager.CheckPermissionCallback() {
@Override
public void onSuccess() {
Activity activity = getActivity();
if (activity != null && !ThetaObjectStorage.hasEnoughStorageSize()) {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
// Check Android Storage Limit
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
ThetaDialogFragment.showAlert(getActivity(),
getResources().getString(R.string.theta_ssid_prefix),
getResources().getString(R.string.theta_error_shortage_by_android), null);
}
});
return;
}
String root = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
File dir = new File(root);
if (!dir.exists()) {
dir.mkdir();
}
Date date = new Date();
SimpleDateFormat fileDate = new SimpleDateFormat("yyyyMMdd_HHmmss");
final String fileName = "theta_vr_screenshot_" + fileDate.format(date) + ".jpg";
final String filePath = root + fileName;
try {
saveFile(filePath, mSphereView.takeSnapshot());
if (BuildConfig.DEBUG) {
mLogger.severe("absolute path:" + filePath);
}
ContentValues values = new ContentValues();
ContentResolver contentResolver = getActivity().getContentResolver();
values.put(MediaStore.Images.Media.TITLE, fileName);
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.DATA, filePath);
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
ThetaDialogFragment.showAlert(getActivity(),
getResources().getString(R.string.theta_ssid_prefix),
getResources().getString(R.string.theta_save_screenshot), null);
}
});
}
} catch (IOException e) {
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
failSaveDialog();
}
});
}
} finally {
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mProgress != null) {
mProgress.dismiss();
}
}
});
}
}
}
@Override
public void onFail() {
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mProgress != null) {
mProgress.dismiss();
}
failSaveDialog();
}
});
}
}
});
}
/**
* Save File.
* @param filename absolute path
* @param data binary
* @throws IOException Failed Save
*/
private void saveFile(final String filename, final byte[] data) throws IOException {
Uri u = Uri.parse("file://" + filename);
ContentResolver contentResolver = getActivity().getContentResolver();
OutputStream out = null;
try {
out = contentResolver.openOutputStream(u, "w");
out.write(data);
out.flush();
} catch (Exception e) {
throw new IOException("Failed to save a file." + filename);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* ScreenShot failed.
*/
private void failSaveDialog() {
ThetaDialogFragment.showAlert(getActivity(),
getResources().getString(R.string.theta_ssid_prefix),
getResources().getString(R.string.theta_error_failed_save_file), null);
}
/**
* Donwload of data.
*/
private class ThetaMainData implements DownloadThetaDataTask.ThetaDownloadListener {
/** Theta Device. */
private ThetaDevice mDevice;
/** Theta Object. */
private ThetaObject mObj;
/** SphericalView byte. */
private byte[] mSphericalBinary;
/** Communication error. */
private int mError = -1;
@Override
public synchronized void doInBackground() {
ThetaDeviceApplication app = (ThetaDeviceApplication) getActivity().getApplication();
ThetaDeviceManager deviceMgr = app.getDeviceManager();
mDevice = deviceMgr.getConnectedDevice();
if (!mIsStorage && mDevice != null) {
try {
List<ThetaObject> list = mDevice.fetchAllObjectList();
mObj = list.get(mDefaultId);
mSphericalBinary = mDataCache.get(mDevice.getName() + "_" + mObj.getFileName());
if (mSphericalBinary == null) {
mObj.fetch(ThetaObject.DataType.MAIN);
mSphericalBinary = mObj.getMainData();
mObj.clear(ThetaObject.DataType.MAIN);
}
} catch (ThetaDeviceException e) {
e.printStackTrace();
mError = e.getReason();
mSphericalBinary = null;
} catch (OutOfMemoryError e) {
mError = ThetaDeviceException.OUT_OF_MEMORY;
}
} else if (mIsStorage) {
try {
List<ThetaObject> list = mStorage.geThetaObjectCaches(null);
mObj = list.get(mDefaultId);
mSphericalBinary = mDataCache.get("Android_" + mObj.getFileName());
if (mSphericalBinary == null) {
mObj.fetch(ThetaObject.DataType.MAIN);
mSphericalBinary = mObj.getMainData();
mObj.clear(ThetaObject.DataType.MAIN);
}
} catch (ThetaDeviceException e) {
e.printStackTrace();
mError = e.getReason();
mSphericalBinary = null;
} catch (OutOfMemoryError e) {
mError = ThetaDeviceException.OUT_OF_MEMORY;
}
} else {
mSphericalBinary = null;
}
}
@Override
public synchronized void onPostExecute() {
if (mProgress != null) {
mProgress.dismiss();
mProgress = null;
}
if (mSphericalBinary == null) {
if (mError == ThetaDeviceException.OUT_OF_MEMORY) {
ThetaDialogFragment.showAlert(getActivity(), getString(R.string.theta_ssid_prefix),
getString(R.string.theta_error_memory_warning),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
getActivity().finish();
}
});
} else if (mError > 0) {
// THETA device is found, but communication error occurred.
showReconnectionDialog();
} else {
// THETA device is not found.
showDisconnectionDialog();
}
} else {
if (mObj != null) {
String device = "Android";
if (mDevice != null) {
device = mDevice.getName();
}
mDataCache.put(device + "_" + mObj.getFileName(),
mSphericalBinary);
}
if (mSphereView != null) {
try {
mSphereView.onResume();
mSphereView.start(mSphericalBinary);
} catch (OutOfMemoryError e) {
ThetaDialogFragment.showAlert(getActivity(), getString(R.string.theta_ssid_prefix),
getString(R.string.theta_error_memory_warning),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
getActivity().finish();
}
});
}
}
}
}
}
}