/*
* Copyright 2013 Sony Corporation
*/
package com.example.sony.cameraremote;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
/**
* SimpleCameraEventObserver.
*/
public class SimpleCameraEventObserver {
/** LiveView Statusナンバー. */
private static final int VIEW_STATUS = 3;
/** ShootMode ナンバー. */
private static final int SHOOT_MODE = 21;
/** */
private static final int TAKE_PICTURE = 5;
/**
* A listener interface to receive these changes. These methods will be
* called by UI thread.
*/
public interface ChangeListener {
/**
* Called when the list of available APIs is modified.
*
* @param apis a list of available APIs
*/
void onApiListModified(List<String> apis);
/**
* Called when the value of "Camera Status" is changed.
*
* @param status camera status (ex."IDLE")
*/
void onCameraStatusChanged(String status);
/**
* Called when the value of "Liveview Status" is changed.
*
* @param status liveview status (ex.true)
*/
void onLiveviewStatusChanged(boolean status);
/**
* Called when the value of "Shoot Mode" is changed.
*
* @param shootMode shoot mode (ex."still")
*/
void onShootModeChanged(String shootMode);
/**
* Called when the value of "zoomPosition" is changed.
*
* @param zoomPosition zoom position (ex.12)
*/
void onZoomPositionChanged(int zoomPosition);
// :
// : add methods for Event data as necessary.
/**
* 写真撮影.
* @param postImageUrl 撮影イメージのURL
*/
void onTakePicture(final String postImageUrl);
}
/** No Error Status Code.*/
private static final int STATUS_CODE_0 = 0;
/** "Ane" Error Status Code.*/
private static final int STATUS_CODE_1 = 1;
/** No such method" Error Status Code.*/
private static final int STATUS_CODE_12 = 12;
/** "Timeout" Error Status Code.*/
private static final int STATUS_CODE_2 = 2;
/** "Already polling" Error Status Code.*/
private static final int STATUS_CODE_40402 = 40402;
/** Thread sleeping msec. */
private static final int THREAD_SLEEP_MSEC = 5000;
/**
* RemoteApi.
*/
private SimpleRemoteApi mRemoteApi;
/**
* Listner.
*/
private ChangeListener mListener;
/**
* EventMonitor.
*/
private boolean mWhileEventMonitoring = false;
/**
* Current Camera Status value.
*/
private String mCameraStatus;
/**
* Current Liveview Status value.
*/
private boolean mLiveviewStatus;
/**
* Current Shoot Mode value.
*/
private String mShootMode;
/**
* Current Zoom Position value.
*/
private int mZoomPosition;
// :
// : add attributes for Event data as necessary.
/**
* Constructor.
*
* @param context context to notify the changes by UI thread.
* @param apiClient API client
*/
public SimpleCameraEventObserver(final Context context, final SimpleRemoteApi apiClient) {
if (context == null) {
throw new IllegalArgumentException("context is null.");
}
if (apiClient == null) {
throw new IllegalArgumentException("apiClient is null.");
}
mRemoteApi = apiClient;
}
/**
* Starts monitoring by continuously calling getEvent API.
*
* @return true if it successfully started, false if a monitoring is already
* started.
*/
public boolean start() {
if (mWhileEventMonitoring) {
return false;
}
mWhileEventMonitoring = true;
new Thread() {
@Override
public void run() {
// Call getEvent API continuously.
boolean firstCall = true;
MONITORLOOP: while (mWhileEventMonitoring) {
// At first, call as non-Long Polling.
boolean longPolling = firstCall ? false : true;
try {
// Call getEvent API.
JSONObject replyJson = mRemoteApi.getEvent(longPolling);
// Check error code at first.
int errorCode = findErrorCode(replyJson);
switch (errorCode) {
case STATUS_CODE_0: // no error
// Pass through.
break;
case STATUS_CODE_1: // "Any" error
case STATUS_CODE_12: // "No such method" error
break MONITORLOOP; // end monitoring.
case STATUS_CODE_2: // "Timeout" error
// Re-call immediately.
continue MONITORLOOP;
case STATUS_CODE_40402: // "Already polling" error
// Retry after 5 sec.
try {
Thread.sleep(THREAD_SLEEP_MSEC);
} catch (InterruptedException e) {
//Exceptionを受けるだけなので処理は行わない
}
continue MONITORLOOP;
default:
break MONITORLOOP; // end monitoring.
}
List<String> availableApis = findAvailableApiList(replyJson);
if (!availableApis.isEmpty()) {
fireApiListModifiedListener(availableApis);
}
// CameraStatus
String cameraStatus = findCameraStatus(replyJson);
if (cameraStatus != null
&& !cameraStatus.equals(mCameraStatus)) {
mCameraStatus = cameraStatus;
fireCameraStatusChangeListener(cameraStatus);
}
// LiveviewStatus
Boolean liveviewStatus = findLiveviewStatus(replyJson);
if (liveviewStatus != null
&& !liveviewStatus.equals(mLiveviewStatus)) {
mLiveviewStatus = liveviewStatus;
fireLiveviewStatusChangeListener(liveviewStatus);
}
// ShootMode
String shootMode = findShootMode(replyJson);
if (shootMode != null && !shootMode.equals(mShootMode)) {
mShootMode = shootMode;
fireShootModeChangeListener(shootMode);
}
// zoomPosition
int zoomPosition = findZoomInformation(replyJson);
if (zoomPosition != -1) {
mZoomPosition = zoomPosition;
fireZoomInformationChangeListener(0, 0, zoomPosition, 0);
}
// :
// : add implementation for Event data as necessary.
String imageUrl = findTakePicture(replyJson);
if (imageUrl != null) {
fireTakePictureChangeListner(imageUrl);
}
} catch (IOException e) {
// Occurs when the server is not available now.
break MONITORLOOP;
} catch (JSONException e) {
break MONITORLOOP;
}
firstCall = false;
} // MONITORLOOP end.
mWhileEventMonitoring = false;
}
} .start();
return true;
}
/**
* Requests to stop the monitoring.
*/
public void stop() {
mWhileEventMonitoring = false;
}
/**
* Checks to see whether a monitoring is already started.
*
* @return true when monitoring is started.
*/
public boolean isStarted() {
return mWhileEventMonitoring;
}
/**
* Sets a listener object.
*
* @param listener リスナー
*/
public void setEventChangeListener(final ChangeListener listener) {
mListener = listener;
}
/**
* Clears a listener object.
*/
public void clearEventChangeListener() {
mListener = null;
}
/**
* Returns the current Camera Status value.
*
* @return camera status
*/
public String getCameraStatus() {
return mCameraStatus;
}
/**
* Returns the current Camera Status value.
*
* @return camera status
*/
public boolean getLiveviewStatus() {
return mLiveviewStatus;
}
/**
* Returns the current Shoot Mode value.
*
* @return shoot mode
*/
public String getShootMode() {
return mShootMode;
}
/**
* Returns the current Zoom Position value.
*
* @return zoom position
*/
public int getZoomPosition() {
return mZoomPosition;
}
/**
* Notifies the listener of available APIs change.
* @param availableApis 利用可能APIリスト
*/
private void fireApiListModifiedListener(final List<String> availableApis) {
if (mListener != null) {
mListener.onApiListModified(availableApis);
}
}
/**
* Notifies the listener of Camera Status change.
* @param status ステータス
*/
private void fireCameraStatusChangeListener(final String status) {
if (mListener != null) {
mListener.onCameraStatusChanged(status);
}
}
/**
* Notifies the listener of Liveview Status change.
* @param status ステータス
*/
private void fireLiveviewStatusChangeListener(final boolean status) {
if (mListener != null) {
mListener.onLiveviewStatusChanged(status);
}
}
/**
* Notifies the listener of Shoot Mode change.
* @param shootMode 撮影モード
*/
private void fireShootModeChangeListener(final String shootMode) {
if (mListener != null) {
mListener.onShootModeChanged(shootMode);
}
}
/**
* Notifies the listener of Zoom Information change.
* @param zoomIndexCurrentBox
* @param zoomNumberBox
* @param zoomPosition
* @param zoomPositionCurrentBox
*/
private void fireZoomInformationChangeListener(final int zoomIndexCurrentBox,
final int zoomNumberBox, final int zoomPosition, final int zoomPositionCurrentBox) {
if (mListener != null) {
mListener.onZoomPositionChanged(zoomPosition);
}
}
/**
* .
* @param imageUrl
*/
private void fireTakePictureChangeListner(final String imageUrl) {
if (mListener != null) {
mListener.onTakePicture(imageUrl);
}
}
/**
* Finds and extracts an error code from reply JSON data.
* @param replyJson
* @return code
* @throws JSONException
*/
private static int findErrorCode(final JSONObject replyJson) throws JSONException {
int code = 0; // 0 means no error.
if (replyJson.has("error")) {
JSONArray errorObj = replyJson.getJSONArray("error");
code = errorObj.getInt(0);
}
return code;
}
/**
* Finds and extracts a list of available APIs from reply JSON data.
* As for getEvent v1.0, results[0] => "availableApiList"
* @param replyJson
* @return availavleApis
* @throws JSONException
*/
private static List<String> findAvailableApiList(final JSONObject replyJson)
throws JSONException {
List<String> availableApis = new ArrayList<String>();
int indexOfAvailableApiList = 0;
JSONArray resultsObj = replyJson.getJSONArray("result");
if (!resultsObj.isNull(indexOfAvailableApiList)) {
JSONObject availableApiListObj = resultsObj
.getJSONObject(indexOfAvailableApiList);
String type = availableApiListObj.getString("type");
if ("availableApiList".equals(type)) {
JSONArray apiArray = availableApiListObj.getJSONArray("names");
for (int i = 0; i < apiArray.length(); i++) {
availableApis.add(apiArray.getString(i));
}
}
}
return availableApis;
}
/**
* Finds and extracts a value of Camera Status from reply JSON data.
* As for getEvent v1.0, results[1] => "cameraStatus"
* @param replyJson
* @return cameraStatus
* @throws JSONException
*/
private static String findCameraStatus(final JSONObject replyJson)
throws JSONException {
String cameraStatus = null;
int indexOfCameraStatus = 1;
JSONArray resultsObj = replyJson.getJSONArray("result");
if (!resultsObj.isNull(indexOfCameraStatus)) {
JSONObject cameraStatusObj = resultsObj
.getJSONObject(indexOfCameraStatus);
String type = cameraStatusObj.getString("type");
if ("cameraStatus".equals(type)) {
cameraStatus = cameraStatusObj.getString("cameraStatus");
}
}
return cameraStatus;
}
/**
* Finds and extracts a value of Liveview Status from reply JSON data.
* As for getEvent v1.0, results[3] => "liveviewStatus"
* @param replyJson
* @return liveviewstatus
* @throws JSONException
*/
private static Boolean findLiveviewStatus(final JSONObject replyJson)
throws JSONException {
Boolean liveviewStatus = null;
int indexOfLiveviewStatus = VIEW_STATUS;
JSONArray resultsObj = replyJson.getJSONArray("result");
if (!resultsObj.isNull(indexOfLiveviewStatus)) {
JSONObject liveviewStatusObj = resultsObj
.getJSONObject(indexOfLiveviewStatus);
String type = liveviewStatusObj.getString("type");
if ("liveviewStatus".equals(type)) {
liveviewStatus = liveviewStatusObj.getBoolean("liveviewStatus");
}
}
return liveviewStatus;
}
/**
* Finds and extracts a value of Shoot Mode from reply JSON data.
* As for getEvent v1.0, results[21] => "shootMode"
* @param replyJson
* @return shootMode
* @throws JSONException
*/
private static String findShootMode(final JSONObject replyJson)
throws JSONException {
String shootMode = null;
int indexOfShootMode = SHOOT_MODE;
JSONArray resultsObj = replyJson.getJSONArray("result");
if (!resultsObj.isNull(indexOfShootMode)) {
JSONObject shootModeObj = resultsObj
.getJSONObject(indexOfShootMode);
String type = shootModeObj.getString("type");
if ("shootMode".equals(type)) {
shootMode = shootModeObj.getString("currentShootMode");
}
}
return shootMode;
}
/**
* Finds and extracts a value of Zoom Information from reply JSON data.
* As for getEvent v1.0, results[2] => "zoomInformation"
* @param replyJson
* @return zoomPosition
* @throws JSONException
*/
private static int findZoomInformation(final JSONObject replyJson)
throws JSONException {
int zoomPosition = -1;
int indexOfZoomInformation = 2;
JSONArray resultsObj = replyJson.getJSONArray("result");
if (!resultsObj.isNull(indexOfZoomInformation)) {
JSONObject zoomInformationObj = resultsObj
.getJSONObject(indexOfZoomInformation);
String type = zoomInformationObj.getString("type");
if ("zoomInformation".equals(type)) {
zoomPosition = zoomInformationObj.getInt("zoomPosition");
}
}
return zoomPosition;
}
/**
* .
* @param replyJson
* @return postImageUrl
* @throws JSONException
*/
private static String findTakePicture(final JSONObject replyJson)
throws JSONException {
String postImageUrl = null;
int indexOfTakePicture = TAKE_PICTURE;
JSONArray resultsObj = replyJson.getJSONArray("result");
if (!resultsObj.isNull(indexOfTakePicture)) {
JSONArray takePictureObj = resultsObj
.getJSONArray(indexOfTakePicture);
for (int i = 0; i < takePictureObj.length(); i++) {
JSONObject takeObj = takePictureObj.getJSONObject(i);
String type = takeObj.getString("type");
if ("takePicture".equals(type)) {
JSONArray imageUrlObj = takeObj.getJSONArray("takePictureUrl");
if (1 <= imageUrlObj.length()) {
postImageUrl = imageUrlObj.getString(0);
}
if (postImageUrl == null) {
postImageUrl = null;
}
}
}
}
return postImageUrl;
}
}