/**
* カメラの接続/切断
*
- * Created by MRSa on 2017/02/28.
*/
public interface ICameraConnection
{
+ enum CameraConnectionStatus
+ {
+ UNKNOWN,
+ DISCONNECTED,
+ CONNECTING,
+ CONNECTED
+ };
+
// WIFI 接続系
void startWatchWifiStatus(@NonNull Context context);
void stopWatchWifiStatus(@NonNull Context context);
import android.Manifest;
import android.content.pm.PackageManager;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
liveView = findViewById(R.id.liveview);
}
olyAirCoordinator = new OlyCameraCoordinator(this, liveView, this, this);
- thetaCoordinator = new ThetaCameraController(this, liveView, this, this);
- currentCoordinator = olyAirCoordinator; // (connectionMethod.contains(IPreferenceCameraPropertyAccessor.CONNECTION_METHOD_THETA)) ? thetaCoordinator : olyAirCoordinator;
+ thetaCoordinator = new ThetaCameraController(this, liveView, this, this, preferences);
+ currentCoordinator = (connectionMethod.contains(IPreferenceCameraPropertyAccessor.CONNECTION_METHOD_THETA)) ? thetaCoordinator : olyAirCoordinator;
currentCoordinator.setLiveViewListener(new CameraLiveViewListenerImpl(liveView));
listener = new CameraLiveViewOnTouchListener(this, new FeatureDispatcher(this, this, currentCoordinator, preferences, liveView), this);
selectionDialog = new FavoriteSettingSelectionDialog(this, currentCoordinator.getCameraPropertyLoadSaveOperations(), this);
}
/**
+ * カメラと接続失敗
+ */
+ @Override
+ public void onCameraConnectError(@NonNull String message)
+ {
+ Log.v(TAG, "onCameraOccursException()");
+ try
+ {
+ setMessage(IShowInformation.AREA_C, Color.YELLOW, message);
+ listener.setEnableOperation(operation.ONLY_CONNECT);
+ cameraDisconnectedHappened = true;
+ }
+ catch (Exception ee)
+ {
+ ee.printStackTrace();
+ }
+ }
+
+ /**
* カメラに例外発生
*/
@Override
- public void onCameraOccursException(String message, Exception e)
+ public void onCameraOccursException(@NonNull String message, Exception e)
{
Log.v(TAG, "onCameraOccursException()");
try
else
{
// 接続方式を Theta に切り替える
- updateConnectionMethod(IPreferenceCameraPropertyAccessor.CONNECTION_METHOD_THETA, olyAirCoordinator); // thetaCoordinator
+ updateConnectionMethod(IPreferenceCameraPropertyAccessor.CONNECTION_METHOD_THETA, thetaCoordinator);
}
updateConnectionMethodMessage();
}
public void setImageData(byte[] data, Map<String, Object> metadata)
{
Bitmap bitmap;
- int rotationDegrees;
+ int rotationDegrees = 0;
- if (data != null && metadata != null)
+ if (data != null)
{
- // Create a bitmap.
try
{
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
}
- catch (OutOfMemoryError e)
+ catch (Throwable e)
{
e.printStackTrace();
return;
}
- // Acquire a rotation degree of image.
- int orientation = ExifInterface.ORIENTATION_UNDEFINED;
- try
+ if (metadata != null)
{
- if (metadata.containsKey(EXIF_ORIENTATION))
+ int orientation = ExifInterface.ORIENTATION_UNDEFINED;
+ try
{
- orientation = Integer.parseInt((String) metadata.get(EXIF_ORIENTATION));
+ if (metadata.containsKey(EXIF_ORIENTATION))
+ {
+ String degree = (String) metadata.get(EXIF_ORIENTATION);
+ if (degree != null)
+ {
+ orientation = Integer.parseInt(degree);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ switch (orientation)
+ {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ rotationDegrees = 90;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ rotationDegrees = 180;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ rotationDegrees = 270;
+ break;
+ case ExifInterface.ORIENTATION_NORMAL:
+ default:
+ rotationDegrees = 0;
+ break;
}
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- switch (orientation)
- {
- case ExifInterface.ORIENTATION_ROTATE_90:
- rotationDegrees = 90;
- break;
- case ExifInterface.ORIENTATION_ROTATE_180:
- rotationDegrees = 180;
- break;
- case ExifInterface.ORIENTATION_ROTATE_270:
- rotationDegrees = 270;
- break;
- case ExifInterface.ORIENTATION_NORMAL:
- default:
- rotationDegrees = 0;
- break;
}
imageBitmap = bitmap;
imageRotationDegrees = rotationDegrees;
*/
public class CameraLiveViewListenerImpl implements OLYCameraLiveViewListener, IImageDataReceiver
{
+ private final String TAG = toString();
private final IImageDataReceiver imageView;
/**
@Override
public void setImageData(byte[] data, Map<String, Object> metadata)
{
+ // Log.v(TAG, " setImageData len : " + data.length);
if (imageView != null)
{
- imageView.setImageData(data, metadata);
+ imageView.setImageData(data, null);
}
}
}
package jp.sfjp.gokigen.a01c.liveview;
+import androidx.annotation.NonNull;
+
/**
*
*
void onStatusNotify(String message);
void onCameraConnected();
void onCameraDisconnected();
- void onCameraOccursException(String message, Exception e);
+ void onCameraConnectError(@NonNull String message);
+ void onCameraOccursException(@NonNull String message, Exception e);
}
public interface IIndicatorControl
{
+ // 撮影状態の記録
+ enum shootingStatus
+ {
+ Unknown,
+ Starting,
+ Stopping,
+ }
+
void onAfLockUpdate(boolean isAfLocked);
+ void onShootingStatusUpdate(shootingStatus status);
+
}
}
@Override
+ public void onShootingStatusUpdate(shootingStatus status)
+ {
+ Log.v(TAG, " ShootingStatus : " + status);
+ }
+
+ @Override
public OLYCamera getOLYCamera()
{
return (camera);
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper;
+
+public interface IThetaSessionIdNotifier
+{
+ void receivedSessionId(String sessionId);
+}
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper;
+
+import androidx.annotation.NonNull;
+
+public interface IThetaSessionIdProvider
+{
+ @NonNull String getSessionId();
+}
import jp.sfjp.gokigen.a01c.liveview.IAutoFocusFrameDisplay
import jp.sfjp.gokigen.a01c.liveview.ICameraStatusReceiver
import jp.sfjp.gokigen.a01c.olycamerawrapper.ICameraRunMode
+import jp.sfjp.gokigen.a01c.olycamerawrapper.IIndicatorControl
import jp.sfjp.gokigen.a01c.olycamerawrapper.ILevelGauge
import jp.sfjp.gokigen.a01c.olycamerawrapper.IZoomLensHolder
import jp.sfjp.gokigen.a01c.olycamerawrapper.property.ICameraPropertyLoadSaveOperations
import jp.sfjp.gokigen.a01c.olycamerawrapper.property.ILoadSaveCameraProperties
import jp.sfjp.gokigen.a01c.olycamerawrapper.property.IOlyCameraPropertyProvider
+import jp.sfjp.gokigen.a01c.preference.PreferenceAccessWrapper
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.connection.ThetaCameraConnection
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.liveview.ThetaLiveViewControl
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.operation.ThetaDummyOperation
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.operation.ThetaSingleShotControl
-class ThetaCameraController(val context : AppCompatActivity, val focusFrameDisplay : IAutoFocusFrameDisplay, val showInformation : IShowInformation, val receiver : ICameraStatusReceiver) : ICameraController
+class ThetaCameraController(val context: AppCompatActivity, private val focusFrameDisplay: IAutoFocusFrameDisplay, private val showInformation: IShowInformation, private val receiver: ICameraStatusReceiver, private val preferences: PreferenceAccessWrapper) : ICameraController, IIndicatorControl
{
- private lateinit var listener : CameraLiveViewListenerImpl
+ //private lateinit var listener : CameraLiveViewListenerImpl
+ private lateinit var liveViewControl : ThetaLiveViewControl
+ private val dummyOperation = ThetaDummyOperation()
+ private val sessionIdHolder = ThetaSessionHolder()
+ private val cameraConnection = ThetaCameraConnection(context, receiver, sessionIdHolder)
+ private val singleShot = ThetaSingleShotControl(sessionIdHolder, this, this)
override fun setLiveViewListener(listener: CameraLiveViewListenerImpl)
{
- this.listener = listener
+ this.liveViewControl = ThetaLiveViewControl(sessionIdHolder, listener)
}
override fun changeLiveViewSize(size: String?)
Log.v(toString(), " changeLiveViewSize: $size")
}
- override fun startLiveView() {
- TODO("Not yet implemented")
+ override fun startLiveView()
+ {
+ if (::liveViewControl.isInitialized)
+ {
+ liveViewControl.startLiveView()
+ }
}
- override fun stopLiveView() {
- TODO("Not yet implemented")
+ override fun stopLiveView()
+ {
+ if (::liveViewControl.isInitialized)
+ {
+ liveViewControl.stopLiveView()
+ }
}
- override fun updateTakeMode() {
- TODO("Not yet implemented")
+ override fun updateTakeMode()
+ {
+ // なにもしない
}
- override fun driveAutoFocus(event: MotionEvent?): Boolean {
- TODO("Not yet implemented")
+ override fun driveAutoFocus(event: MotionEvent?): Boolean
+ {
+ return (true)
}
- override fun unlockAutoFocus() {
- TODO("Not yet implemented")
+ override fun unlockAutoFocus()
+ {
+ // なにもしない
}
- override fun isContainsAutoFocusPoint(event: MotionEvent?): Boolean {
- TODO("Not yet implemented")
+ override fun isContainsAutoFocusPoint(event: MotionEvent?): Boolean
+ {
+ return (false)
}
- override fun singleShot() {
- TODO("Not yet implemented")
+ override fun singleShot()
+ {
+ singleShot.singleShot(sessionIdHolder.isApiLevelV21())
}
- override fun movieControl() {
- TODO("Not yet implemented")
+ override fun movieControl()
+ {
+ // TODO("Not yet implemented")
}
- override fun bracketingShot(bracketingStyle: Int, bracketingCount: Int, durationSeconds: Int) {
- TODO("Not yet implemented")
+ override fun bracketingShot(bracketingStyle: Int, bracketingCount: Int, durationSeconds: Int)
+ {
+ // TODO("Not yet implemented")
}
- override fun setRecViewMode(isRecViewMode: Boolean) {
- TODO("Not yet implemented")
+ override fun setRecViewMode(isRecViewMode: Boolean)
+ {
+ // なにもしない
}
- override fun toggleAutoExposure() {
- TODO("Not yet implemented")
+ override fun toggleAutoExposure()
+ {
+ // なにもしない
}
- override fun toggleManualFocus() {
- TODO("Not yet implemented")
+ override fun toggleManualFocus()
+ {
+ // なにもしない
}
- override fun isManualFocus(): Boolean {
- TODO("Not yet implemented")
+ override fun isManualFocus(): Boolean
+ {
+ return (false)
}
- override fun isAFLock(): Boolean {
- TODO("Not yet implemented")
+ override fun isAFLock(): Boolean
+ {
+ return (false)
}
- override fun isAELock(): Boolean {
- TODO("Not yet implemented")
+ override fun isAELock(): Boolean
+ {
+ return (false)
}
- override fun updateStatusAll() {
- TODO("Not yet implemented")
+ override fun updateStatusAll()
+ {
+ // なにもしない
}
- override fun getCameraPropertyProvider(): IOlyCameraPropertyProvider {
- TODO("Not yet implemented")
+ override fun getCameraPropertyProvider(): IOlyCameraPropertyProvider
+ {
+ return (dummyOperation)
}
- override fun getCameraPropertyLoadSaveOperations(): ICameraPropertyLoadSaveOperations {
- TODO("Not yet implemented")
+ override fun getCameraPropertyLoadSaveOperations(): ICameraPropertyLoadSaveOperations
+ {
+ return (dummyOperation)
}
- override fun getLoadSaveCameraProperties(): ILoadSaveCameraProperties {
- TODO("Not yet implemented")
+ override fun getLoadSaveCameraProperties(): ILoadSaveCameraProperties
+ {
+ return (dummyOperation)
}
- override fun getChangeRunModeExecutor(): ICameraRunMode {
- TODO("Not yet implemented")
+ override fun getChangeRunModeExecutor(): ICameraRunMode
+ {
+ return (dummyOperation)
}
- override fun getConnectionInterface(): ICameraConnection {
- TODO("Not yet implemented")
+ override fun getConnectionInterface(): ICameraConnection
+ {
+ return (cameraConnection)
}
- override fun getZoomLensHolder(): IZoomLensHolder {
- TODO("Not yet implemented")
+ override fun getZoomLensHolder(): IZoomLensHolder
+ {
+ return (dummyOperation)
}
- override fun getLevelGauge(): ILevelGauge {
- TODO("Not yet implemented")
+ override fun getLevelGauge(): ILevelGauge
+ {
+ return (dummyOperation)
}
+ override fun onAfLockUpdate(isAfLocked: Boolean)
+ {
+ //TODO("Not yet implemented")
+ }
-}
\ No newline at end of file
+ override fun onShootingStatusUpdate(status: IIndicatorControl.shootingStatus?)
+ {
+ //TODO("Not yet implemented")
+ }
+}
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper
+
+class ThetaSessionHolder : IThetaSessionIdProvider, IThetaSessionIdNotifier
+{
+ private var sessionId : String = ""
+
+ override fun getSessionId(): String
+ {
+ return (sessionId)
+ }
+
+ override fun receivedSessionId(sessionId: String?)
+ {
+ if (sessionId != null)
+ {
+ this.sessionId = sessionId
+ }
+ }
+
+ fun isApiLevelV21() : Boolean
+ {
+ return (!(sessionId.isNotEmpty()))
+ }
+}
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.connection
+
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import jp.sfjp.gokigen.a01c.R
+import jp.sfjp.gokigen.a01c.liveview.ICameraStatusReceiver
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdNotifier
+import jp.sfjp.gokigen.a01c.utils.SimpleHttpClient
+import org.json.JSONObject
+
+class ThetaCameraConnectSequence(private val context: AppCompatActivity, private val cameraStatusReceiver: ICameraStatusReceiver, private val sessionIdNotifier: IThetaSessionIdNotifier) : Runnable
+{
+ private var useThetaV21 : Boolean = false
+ private val httpClient = SimpleHttpClient()
+
+ override fun run()
+ {
+ // 使用する API Levelを決める
+ useThetaV21 = decideApiLevel()
+ try
+ {
+ Log.v(TAG,"Theta API v2.1 : $useThetaV21")
+ if (useThetaV21)
+ {
+ // API Level V2.1を使用して通信する
+ connectApiV21()
+ }
+ else
+ {
+ // API Level V2 を使用して通信する
+ connectApiV2()
+ }
+ }
+ catch (e : Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ /**
+ *
+ */
+ private fun decideApiLevel() : Boolean
+ {
+ var apiLevelIsV21 = false
+ try
+ {
+ val oscInfoUrl = "http://192.168.1.1/osc/info"
+ val timeoutMs = 5000
+ val response: String = httpClient.httpGet(oscInfoUrl, timeoutMs)
+ Log.v(TAG, " $oscInfoUrl $response")
+ if (response.isNotEmpty())
+ {
+ val apiLevelArray = JSONObject(response).getJSONArray("apiLevel")
+ val size = apiLevelArray.length()
+ for (index in 0 until size)
+ {
+ val api = apiLevelArray.getInt(index)
+ if (api == 2) //if (api == 2 && useThetaV21)
+ {
+ apiLevelIsV21 = true
+ }
+ }
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ return (apiLevelIsV21)
+ }
+
+ /**
+ *
+ */
+ private fun connectApiV2()
+ {
+ val commandsExecuteUrl = "http://192.168.1.1/osc/commands/execute"
+ val startSessionData = "{\"name\":\"camera.startSession\",\"parameters\":{\"timeout\":0}}"
+ val getStateUrl = "http://192.168.1.1/osc/state"
+ val timeoutMs = 5000
+ try
+ {
+ val response: String? = httpClient.httpPostWithHeader(commandsExecuteUrl, startSessionData, null, "application/json;charset=utf-8", timeoutMs)
+ Log.v(TAG, " $commandsExecuteUrl $startSessionData $response")
+
+ val response2: String? = httpClient.httpPostWithHeader(getStateUrl, "", null, "application/json;charset=utf-8", timeoutMs)
+ Log.v(TAG, " $getStateUrl $response2")
+ if ((response2 != null)&&(response2.isNotEmpty()))
+ {
+ try
+ {
+ val jsonObject = JSONObject(response2)
+ val sessionId = jsonObject.getJSONObject("state").getString("sessionId")
+ sessionIdNotifier.receivedSessionId(sessionId)
+ onConnectNotify()
+ return
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ // 応答なし、を応答する。
+ onConnectError(context.getString(R.string.theta_connect_response_ng))
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ onConnectError(e.localizedMessage)
+ }
+ }
+
+ private fun connectApiV21() {
+ val commandsExecuteUrl = "http://192.168.1.1/osc/commands/execute"
+ val startSessionData = "{\"name\":\"camera.startSession\",\"parameters\":{\"timeout\":0}}"
+ val getStateUrl = "http://192.168.1.1/osc/state"
+ val timeoutMs = 5000
+ try {
+ val responseS: String? = httpClient.httpPostWithHeader(commandsExecuteUrl, startSessionData, null, "application/json;charset=utf-8", timeoutMs)
+ Log.v(TAG, " [ $httpClient ] $startSessionData ::: $responseS")
+ val response: String? = httpClient.httpPostWithHeader(getStateUrl, "", null, null, timeoutMs)
+ Log.v(TAG, " ($getStateUrl) $response")
+ if ((response != null)&&(response.isNotEmpty()))
+ {
+ var apiLevel = 1
+ var sessionId: String? = null
+ val `object` = JSONObject(response)
+ try {
+ apiLevel = `object`.getJSONObject("state").getInt("_apiVersion")
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ try {
+ sessionId = `object`.getJSONObject("state").getString("sessionId")
+ sessionIdNotifier.receivedSessionId(sessionId)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ if (apiLevel != 2) {
+ val setApiLevelData = "{\"name\":\"camera.setOptions\",\"parameters\":{\"sessionId\" : \"$sessionId\", \"options\":{ \"clientVersion\":2}}}"
+ val response3: String? = httpClient.httpPostWithHeader(commandsExecuteUrl, setApiLevelData, null, "application/json;charset=utf-8", timeoutMs)
+ Log.v(TAG, " $commandsExecuteUrl $setApiLevelData $response3")
+ }
+ onConnectNotify()
+ } else {
+ onConnectError(context.getString(R.string.camera_not_found))
+ }
+ } catch (e: Exception)
+ {
+ e.printStackTrace()
+ onConnectError(e.localizedMessage)
+ }
+ }
+
+ private fun onConnectNotify()
+ {
+ try
+ {
+ val thread = Thread { // カメラとの接続確立を通知する
+ cameraStatusReceiver.onStatusNotify(context.getString(R.string.connect_connected))
+ cameraStatusReceiver.onCameraConnected()
+ Log.v(TAG, "onConnectNotify()")
+ }
+ thread.start()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ private fun onConnectError(reason: String?)
+ {
+ if (reason != null)
+ {
+ cameraStatusReceiver.onCameraConnectError(reason)
+ }
+ else
+ {
+ cameraStatusReceiver.onCameraConnectError("")
+ }
+ }
+
+ companion object
+ {
+ private val TAG = ThetaCameraConnectSequence::class.java.simpleName
+ }
+}
\ No newline at end of file
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.connection
+
+import android.content.*
+import android.net.ConnectivityManager
+import android.net.wifi.WifiManager
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import jp.sfjp.gokigen.a01c.ICameraConnection
+import jp.sfjp.gokigen.a01c.R
+import jp.sfjp.gokigen.a01c.liveview.ICameraStatusReceiver
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdNotifier
+import java.util.concurrent.Executor
+import java.util.concurrent.Executors
+
+/**
+ *
+ */
+class ThetaCameraConnection(private val context: AppCompatActivity, private val statusReceiver: ICameraStatusReceiver, private val sessionIdNotifier: IThetaSessionIdNotifier) : ICameraConnection
+{
+ private val cameraExecutor: Executor = Executors.newFixedThreadPool(1)
+ private var connectionStatus: ICameraConnection.CameraConnectionStatus = ICameraConnection.CameraConnectionStatus.UNKNOWN
+ private var isNetworkStatusWatching : Boolean = false
+ private var connectionReceiver = object : BroadcastReceiver()
+ {
+ override fun onReceive(context: Context, intent: Intent)
+ {
+ onReceiveBroadcastOfConnection(context, intent)
+ }
+ }
+
+ /**
+ *
+ */
+ private fun onReceiveBroadcastOfConnection(context: Context, intent: Intent)
+ {
+ statusReceiver.onStatusNotify(context.getString(R.string.connect_check_wifi))
+ Log.v(TAG, context.getString(R.string.connect_check_wifi))
+ val action = intent.action
+ if (action == null)
+ {
+ Log.v(TAG, "intent.getAction() : null")
+ return
+ }
+ try
+ {
+ if (action == ConnectivityManager.CONNECTIVITY_ACTION)
+ {
+ Log.v(TAG, "onReceiveBroadcastOfConnection() : CONNECTIVITY_ACTION")
+ val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
+ val info = wifiManager.connectionInfo
+ if ((wifiManager.isWifiEnabled)&&(info != null))
+ {
+ connectToCamera()
+ }
+ else
+ {
+ if (info == null)
+ {
+ Log.v(TAG, "NETWORK INFO IS NULL.")
+ }
+ else
+ {
+ Log.v(TAG, "isWifiEnabled : " + wifiManager.isWifiEnabled + " NetworkId : " + info.networkId)
+ }
+ }
+ }
+ }
+ catch (e: Exception)
+ {
+ Log.w(TAG, "onReceiveBroadcastOfConnection() EXCEPTION" + e.message)
+ e.printStackTrace()
+ }
+ }
+
+ /**
+ *
+ */
+ override fun startWatchWifiStatus(context: Context)
+ {
+ Log.v(TAG, "startWatchWifiStatus()")
+ statusReceiver.onStatusNotify("prepare")
+ isNetworkStatusWatching = true
+ val filter = IntentFilter()
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION)
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
+ context.registerReceiver(connectionReceiver, filter)
+ }
+
+ /**
+ *
+ */
+ override fun stopWatchWifiStatus(context: Context)
+ {
+ Log.v(TAG, "stopWatchWifiStatus()")
+ isNetworkStatusWatching = false
+ context.unregisterReceiver(connectionReceiver)
+ disconnect(false)
+ }
+
+ /**
+ *
+ */
+ override fun isWatchWifiStatus(): Boolean
+ {
+ return (isNetworkStatusWatching)
+ }
+
+ /**
+ *
+ */
+ override fun disconnect(powerOff: Boolean)
+ {
+ Log.v(TAG, "disconnect()")
+ disconnectFromCamera(powerOff)
+ connectionStatus = ICameraConnection.CameraConnectionStatus.DISCONNECTED
+ statusReceiver.onCameraDisconnected()
+ }
+
+ /**
+ *
+ */
+ override fun connect()
+ {
+ Log.v(TAG, "connect()")
+ connectToCamera()
+ }
+
+ /**
+ * カメラとの切断処理
+ */
+ private fun disconnectFromCamera(powerOff: Boolean)
+ {
+ Log.v(TAG, "disconnectFromCamera() : $powerOff")
+ try
+ {
+ cameraExecutor.execute(ThetaCameraDisconnectSequence())
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ /**
+ * カメラとの接続処理
+ */
+ private fun connectToCamera()
+ {
+ Log.v(TAG, "connectToCamera()")
+ connectionStatus = ICameraConnection.CameraConnectionStatus.CONNECTING
+ try
+ {
+ cameraExecutor.execute(ThetaCameraConnectSequence(context, statusReceiver, sessionIdNotifier))
+ }
+ catch (e: Exception)
+ {
+ Log.v(TAG, "connectToCamera() EXCEPTION : " + e.message)
+ e.printStackTrace()
+ }
+ }
+
+ companion object
+ {
+ private val TAG = ThetaCameraConnection::class.java.simpleName
+ }
+}
\ No newline at end of file
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.connection
+
+import android.util.Log
+
+class ThetaCameraDisconnectSequence : Runnable
+{
+ override fun run()
+ {
+ // カメラをPowerOffして接続を切る
+ Log.v(ThetaCameraDisconnectSequence::class.java.simpleName, " Disconnect from THETA.")
+ }
+}
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.liveview
+
+import android.util.Log
+import jp.sfjp.gokigen.a01c.liveview.CameraLiveViewListenerImpl
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdProvider
+import jp.sfjp.gokigen.a01c.utils.SimpleLiveviewSlicer
+
+class ThetaLiveViewControl(private val sessionIdProvider: IThetaSessionIdProvider, private val liveViewListener: CameraLiveViewListenerImpl)
+{
+ private var whileFetching = false
+
+ fun startLiveView()
+ {
+ Log.v(TAG, " startLiveView()")
+ try
+ {
+ val thread = Thread {
+ try
+ {
+
+ start(!(sessionIdProvider.sessionId.isEmpty()))
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ thread.start()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ fun stopLiveView()
+ {
+ Log.v(TAG, " stopLiveView()")
+ whileFetching = false
+ }
+
+ private fun start(useOscV2 : Boolean)
+ {
+ if (whileFetching)
+ {
+ Log.v(TAG, "start() already starting.")
+ return
+ }
+ whileFetching = true
+
+ try
+ {
+ val thread = Thread {
+ Log.d(TAG, "Starting retrieving streaming data from server.")
+ val slicer = SimpleLiveviewSlicer()
+ var continuousNullDataReceived = 0
+ try
+ {
+ val streamUrl = "http://192.168.1.1/osc/commands/execute"
+ val paramData = if (useOscV2) "{\"name\":\"camera.getLivePreview\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera._getLivePreview\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.getSessionId().toString() + "\"}}"
+ Log.v(TAG, " >>>>> START THETA PREVIEW : $streamUrl $paramData")
+
+ // Create Slicer to open the stream and parse it.
+ slicer.open(streamUrl, paramData, "application/json;charset=utf-8")
+ while (whileFetching) {
+ val payload: SimpleLiveviewSlicer.Payload? = slicer.nextPayloadForMotionJpeg()
+ if (payload == null)
+ {
+ //Log.v(TAG, "Liveview Payload is null.");
+ continuousNullDataReceived++
+ if (continuousNullDataReceived > FETCH_ERROR_MAX)
+ {
+ Log.d(TAG, " FETCH ERROR MAX OVER ")
+ break
+ }
+ continue
+ }
+ liveViewListener.setImageData(payload.jpegData, null)
+ continuousNullDataReceived = 0
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ finally
+ {
+ slicer.close()
+ if (whileFetching && continuousNullDataReceived > FETCH_ERROR_MAX)
+ {
+ // 再度ライブビューのスタートをやってみる。
+ whileFetching = false
+ start(useOscV2)
+ }
+ }
+ }
+ thread.start()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ companion object
+ {
+ private val TAG = ThetaLiveViewControl::class.java.simpleName
+ private const val FETCH_ERROR_MAX = 30
+ }
+}
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.operation
+
+import android.graphics.Color
+import jp.co.olympus.camerakit.OLYCamera
+import jp.sfjp.gokigen.a01c.olycamerawrapper.ICameraHardwareStatus
+import jp.sfjp.gokigen.a01c.olycamerawrapper.ICameraRunMode
+import jp.sfjp.gokigen.a01c.olycamerawrapper.ILevelGauge
+import jp.sfjp.gokigen.a01c.olycamerawrapper.IZoomLensHolder
+import jp.sfjp.gokigen.a01c.olycamerawrapper.property.ICameraPropertyLoadSaveOperations
+import jp.sfjp.gokigen.a01c.olycamerawrapper.property.ILoadSaveCameraProperties
+import jp.sfjp.gokigen.a01c.olycamerawrapper.property.IOlyCameraPropertyProvider
+
+class ThetaDummyOperation : ILevelGauge, IZoomLensHolder, ICameraRunMode, ILoadSaveCameraProperties, ICameraPropertyLoadSaveOperations, IOlyCameraPropertyProvider, ICameraHardwareStatus
+{
+ override fun getLevel(area: ILevelGauge.LevelArea?): Float {
+ // TODO("Not yet implemented")
+ return (1.0f)
+ }
+
+ override fun getLevelColor(value: Float): Int {
+ // TODO("Not yet implemented")
+ return (Color.WHITE)
+ }
+
+ override fun checkLevelGauge(camera: OLYCamera?) {
+ // TODO("Not yet implemented")
+ }
+
+ override fun updateLevelGaugeChecking(isWatch: Boolean) {
+ //TODO("Not yet implemented")
+ }
+
+ override fun canZoom(): Boolean {
+ //TODO("Not yet implemented")
+ return (false)
+ }
+
+ override fun updateStatus() {
+ //TODO("Not yet implemented")
+ }
+
+ override fun getMaximumFocalLength(): Float {
+ // TODO("Not yet implemented")
+ return (16.0f)
+ }
+
+ override fun getActualFocalLength(): Float {
+ TODO("Not yet implemented")
+ }
+
+ override fun inquireHardwareInformation(): MutableMap<String, Any> {
+ TODO("Not yet implemented")
+ }
+
+ override fun getLensMountStatus(): String {
+ TODO("Not yet implemented")
+ }
+
+ override fun getMediaMountStatus(): String {
+ TODO("Not yet implemented")
+ }
+
+ override fun getMinimumFocalLength(): Float {
+ //TODO("Not yet implemented")
+ return (16.0f)
+ }
+
+ override fun getCurrentFocalLength(): Float {
+ //TODO("Not yet implemented")
+ return (16.0f)
+ }
+
+ override fun driveZoomLens(targetLength: Float) {
+ //TODO("Not yet implemented")
+ }
+
+ override fun driveZoomLens(direction: Int) {
+ //TODO("Not yet implemented")
+ }
+
+ override fun isDrivingZoomLens(): Boolean {
+ //TODO("Not yet implemented")
+ return (false)
+ }
+
+ override fun getCurrentDigitalZoomScale(): Float {
+ //TODO("Not yet implemented")
+ return (1.0f)
+ }
+
+ override fun magnifyLiveView(scale: Int): Boolean {
+ //TODO("Not yet implemented")
+ return (false)
+ }
+
+ override fun changeDigitalZoomScale(scale: Float, isCyclic: Boolean) {
+ // TODO("Not yet implemented")
+ }
+
+ override fun changeRunMode(isRecording: Boolean) {
+ //TODO("Not yet implemented")
+ }
+
+ override fun isRecordingMode(): Boolean {
+ //TODO("Not yet implemented")
+ return (true)
+ }
+
+ override fun loadCameraSettings(id: String?) {
+ //TODO("Not yet implemented")
+ }
+
+ override fun saveCameraSettings(id: String?, name: String?) {
+ //TODO("Not yet implemented")
+ }
+
+ override fun saveProperties(idHeader: String?, dataName: String?) {
+ //TODO("Not yet implemented")
+ }
+
+ override fun loadProperties(idHeader: String?, dataName: String?) {
+ //TODO("Not yet implemented")
+ }
+
+ override fun getCameraPropertyNames(): MutableSet<String> {
+ TODO("Not yet implemented")
+ }
+
+ override fun getCameraPropertyValue(name: String?): String {
+ //TODO("Not yet implemented")
+ return ("")
+ }
+
+ override fun getCameraPropertyValues(names: MutableSet<String>?): MutableMap<String, String> {
+ TODO("Not yet implemented")
+ }
+
+ override fun getCameraPropertyTitle(name: String?): String {
+ TODO("Not yet implemented")
+ }
+
+ override fun getCameraPropertyValueList(name: String?): MutableList<String> {
+ TODO("Not yet implemented")
+ }
+
+ override fun getCameraPropertyValueTitle(propertyValue: String?): String {
+ TODO("Not yet implemented")
+ }
+
+ override fun setCameraPropertyValue(name: String?, value: String?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun setCameraPropertyValues(values: MutableMap<String, String>?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun updateCameraPropertyUp(name: String?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun updateCameraPropertyDown(name: String?) {
+ TODO("Not yet implemented")
+ }
+
+ override fun changeCameraProperty(name: String?, direction: Int) {
+ TODO("Not yet implemented")
+ }
+
+ override fun canSetCameraProperty(name: String?): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun isConnected(): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun getHardwareStatus(): ICameraHardwareStatus {
+ TODO("Not yet implemented")
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package jp.sfjp.gokigen.a01c.thetacamerawrapper.operation
+
+import android.util.Log
+import jp.sfjp.gokigen.a01c.ICameraController
+import jp.sfjp.gokigen.a01c.olycamerawrapper.IIndicatorControl
+import jp.sfjp.gokigen.a01c.thetacamerawrapper.IThetaSessionIdProvider
+import jp.sfjp.gokigen.a01c.utils.SimpleHttpClient
+import org.json.JSONObject
+
+
+class ThetaSingleShotControl(private val sessionIdProvider: IThetaSessionIdProvider, private val indicator: IIndicatorControl, private val liveViewControl: ICameraController)
+{
+ private val httpClient = SimpleHttpClient()
+
+ /**
+ *
+ *
+ */
+ fun singleShot(useThetaV21 : Boolean)
+ {
+ Log.v(TAG, "singleShot()")
+ try
+ {
+ val thread = Thread {
+ try
+ {
+ val shootUrl = "http://192.168.1.1/osc/commands/execute"
+ val postData = if (useThetaV21) "{\"name\":\"camera.takePicture\",\"parameters\":{\"timeout\":0}}" else "{\"name\":\"camera.takePicture\",\"parameters\":{\"sessionId\": \"" + sessionIdProvider.sessionId + "\"}}"
+ val result: String? = httpClient.httpPostWithHeader(shootUrl, postData, null, "application/json;charset=utf-8", timeoutMs)
+ if ((result != null)&&(result.isNotEmpty()))
+ {
+ Log.v(TAG, " singleShot() : $result")
+ indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Starting)
+
+ // 画像処理が終わるまで待つ
+ waitChangeStatus()
+
+ // ライブビューのの再実行を指示する
+ indicator.onShootingStatusUpdate(IIndicatorControl.shootingStatus.Stopping)
+ liveViewControl.stopLiveView()
+ waitMs(300) // ちょっと待つ...
+ liveViewControl.startLiveView()
+ }
+ else
+ {
+ Log.v(TAG, "singleShot() reply is null.")
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+ thread.start()
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ /**
+ * 撮影状態が変わるまで待つ。
+ * (ただし、タイムアウト時間を超えたらライブビューを再開させる)
+ */
+ private fun waitChangeStatus()
+ {
+ val getStateUrl = "http://192.168.1.1/osc/state"
+ val maxWaitTimeoutMs = 9000 // 最大待ち時間 (単位: ms)
+ var fingerprint = ""
+ try
+ {
+ val result: String? = httpClient.httpPost(getStateUrl, "", timeoutMs)
+ if ((result != null)&&(result.isNotEmpty()))
+ {
+ val jsonObject = JSONObject(result)
+ fingerprint = jsonObject.getString("fingerprint")
+
+ // 現在の状態(ログを出す)
+ Log.v(TAG, " $getStateUrl $result ($fingerprint)")
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+
+ try
+ {
+ val firstTime = System.currentTimeMillis()
+ var currentTime = firstTime
+ while (currentTime - firstTime < maxWaitTimeoutMs)
+ {
+ // ... 状態を見て次に進める
+ val result: String? = httpClient.httpPost(getStateUrl, "", timeoutMs)
+ if ((result != null)&&(result.isNotEmpty()))
+ {
+ val jsonObject = JSONObject(result)
+ val currentFingerprint = jsonObject.getString("fingerprint")
+
+ // ログを出してみる
+ // Log.v(TAG, " " + getStateUrl + " ( " + result + " ) " + "(" + fingerprint + " " + current_fingerprint + ")");
+ if (fingerprint != currentFingerprint)
+ {
+ // fingerprintが更新された!
+ break
+ }
+ Log.v(TAG, " ----- NOW PROCESSING ----- : $fingerprint")
+ }
+ waitMs(1000)
+ currentTime = System.currentTimeMillis()
+ }
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ /**
+ *
+ *
+ */
+ private fun waitMs(waitMs: Int)
+ {
+ try
+ {
+ Thread.sleep(waitMs.toLong())
+ }
+ catch (e: Exception)
+ {
+ e.printStackTrace()
+ }
+ }
+
+ companion object
+ {
+ private val TAG = ThetaSingleShotControl::class.java.simpleName
+ private const val timeoutMs = 6000
+ }
+}
import java.net.HttpURLConnection
import java.net.URL
-class SimpleHttpClient()
+class SimpleHttpClient
{
/**
*
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
*
*
*/
- public Payload nextPayloadForMotionJpeg()
+ public @Nullable Payload nextPayloadForMotionJpeg()
{
int searchIndex = 0;
int[] endmarker = { 0xff, 0xd9 };
<string name="connection_method_opc">OPC</string>
<string name="connection_method_theta">THETA</string>
+ <string name="theta_connect_response_ng">応答がありません。</string>
+ <string name="camera_not_found">カメラ未発見</string>
+
</resources>
<string name="connection_method_opc">OPC</string>
<string name="connection_method_theta">THETA</string>
+ <string name="theta_connect_response_ng">Did not receive any response.</string>
+ <string name="camera_not_found">Not Found…</string>
+
</resources>