package com.almalence.sony.cameraremote;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* A simple observer class for some status values in Camera. This class supports
* only a few of values of getEvent result, so please add implementation for the
* rest of values you want to handle.
*/
public class SimpleCameraEventObserver
{
private static final String TAG = SimpleCameraEventObserver.class.getSimpleName();
/**
* 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);
/**
* Called when the value of "storageId" is changed.
*
* @param storageId
* storageId (ex. "Memory Card 1")
*/
void onStorageIdChanged(String storageId);
/**
* Called when the value of "takePicture" is changed.
*
* @param takePictureUrl
* takePictureUrl
*/
void onPictureTaken(String takePictureUrl);
// :
// : add methods for Event data as necessary.
}
/**
* Abstract class to receive these changes. please override methods that you
* need.
*/
public abstract static class ChangeListenerTmpl implements ChangeListener
{
@Override
public void onApiListModified(List<String> apis)
{
}
@Override
public void onCameraStatusChanged(String status)
{
}
@Override
public void onLiveviewStatusChanged(boolean status)
{
}
@Override
public void onShootModeChanged(String shootMode)
{
}
@Override
public void onZoomPositionChanged(int zoomPosition)
{
}
@Override
public void onStorageIdChanged(String storageId)
{
}
@Override
public void onPictureTaken(String takePictureUrl)
{
}
}
private final Handler mUiHandler;
private SimpleRemoteApi mRemoteApi;
private ChangeListener mListener;
private boolean mWhileEventMonitoring = false;
private boolean mIsActive = 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;
// Current Storage Id value.
private String mStorageId;
// :
// : add attributes for Event data as necessary.
/**
* Constructor.
*
* @param context
* context to notify the changes by UI thread.
* @param apiClient
* API client
*/
public SimpleCameraEventObserver(Context context, SimpleRemoteApi apiClient)
{
if (context == null)
{
throw new IllegalArgumentException("context is null.");
}
if (apiClient == null)
{
throw new IllegalArgumentException("apiClient is null.");
}
mRemoteApi = apiClient;
mUiHandler = new Handler(context.getMainLooper());
}
/**
* Starts monitoring by continuously calling getEvent API.
*
* @return true if it successfully started, false if a monitoring is already
* started.
*/
public boolean start()
{
if (!mIsActive)
{
Log.w(TAG, "start() observer is not active.");
return false;
}
if (mWhileEventMonitoring)
{
Log.w(TAG, "start() already starting.");
return false;
}
mWhileEventMonitoring = true;
new Thread()
{
@Override
public void run()
{
Log.d(TAG, "start() exec.");
// Call getEvent API continuously.
boolean firstCall = true;
MONITORLOOP: while (mWhileEventMonitoring)
{
// At first, call as non-Long Polling.
boolean longPolling = !firstCall;
try
{
// Call getEvent API.
JSONObject replyJson = mRemoteApi.getEvent(longPolling);
// Check error code at first.
int errorCode = findErrorCode(replyJson);
Log.d(TAG, "getEvent errorCode: " + errorCode);
switch (errorCode)
{
case 0: // no error
// Pass through.
break;
case 1: // "Any" error
case 12: // "No such method" error
break MONITORLOOP; // end monitoring.
case 2: // "Timeout" error
// Re-call immediately.
continue MONITORLOOP;
case 40402: // "Already polling" error
// Retry after 5 sec.
try
{
Thread.sleep(5000);
} catch (InterruptedException e)
{
// do nothing.
}
continue MONITORLOOP;
default:
Log.w(TAG, "SimpleCameraEventObserver: Unexpected error: " + errorCode);
break MONITORLOOP; // end monitoring.
}
List<String> availableApis = findAvailableApiList(replyJson);
if (!availableApis.isEmpty())
{
fireApiListModifiedListener(availableApis);
}
// CameraStatus
String cameraStatus = findCameraStatus(replyJson);
Log.d(TAG, "getEvent cameraStatus: " + cameraStatus);
if (cameraStatus != null && !cameraStatus.equals(mCameraStatus))
{
mCameraStatus = cameraStatus;
fireCameraStatusChangeListener(cameraStatus);
}
// LiveviewStatus
Boolean liveviewStatus = findLiveviewStatus(replyJson);
Log.d(TAG, "getEvent liveviewStatus: " + liveviewStatus);
if (liveviewStatus != null && !liveviewStatus.equals(mLiveviewStatus))
{
mLiveviewStatus = liveviewStatus;
fireLiveviewStatusChangeListener(liveviewStatus);
}
// ShootMode
String shootMode = findShootMode(replyJson);
Log.d(TAG, "getEvent shootMode: " + shootMode);
if (shootMode != null && !shootMode.equals(mShootMode))
{
mShootMode = shootMode;
fireShootModeChangeListener(shootMode);
}
// zoomPosition
int zoomPosition = findZoomInformation(replyJson);
Log.d(TAG, "getEvent zoomPosition: " + zoomPosition);
if (zoomPosition != -1)
{
mZoomPosition = zoomPosition;
fireZoomInformationChangeListener(0, 0, zoomPosition, 0);
}
// storageId
String storageId = findStorageId(replyJson);
Log.d(TAG, "getEvent storageId:" + storageId);
if (storageId != null && !storageId.equals(mStorageId))
{
mStorageId = storageId;
fireStorageIdChangeListener(storageId);
}
// takePictureUrl
String takePictureUrl = findTakePictureUrl(replyJson);
Log.d(TAG, "getEvent takePictureUrl:" + takePictureUrl);
if (takePictureUrl != null)
{
firePictureTakenListener(takePictureUrl);
}
// :
// : add implementation for Event data as necessary.
} catch (IOException e)
{
// Occurs when the server is not available now.
Log.d(TAG, "getEvent timeout by client trigger.");
break MONITORLOOP;
} catch (JSONException e)
{
Log.w(TAG, "getEvent: JSON format error. " + e.getMessage());
break MONITORLOOP;
}
firstCall = false;
} // MONITORLOOP end.
mWhileEventMonitoring = false;
}
}.start();
return true;
}
/**
* Requests to stop the monitoring.
*/
public void stop()
{
mWhileEventMonitoring = false;
}
/**
* Requests to release resource.
*/
public void release()
{
mWhileEventMonitoring = false;
mIsActive = false;
}
public void activate()
{
mIsActive = true;
}
/**
* 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(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;
}
/**
* Returns the current Storage Id value.
*
* @return
*/
public String getStorageId()
{
return mStorageId;
}
/**
* Notify the change of available APIs
*
* @param availableApis
*/
private void fireApiListModifiedListener(final List<String> availableApis)
{
mUiHandler.post(new Runnable()
{
@Override
public void run()
{
if (mListener != null)
{
mListener.onApiListModified(availableApis);
}
}
});
}
/**
* Notify the change of Camera Status.
*
* @param status
*/
private void fireCameraStatusChangeListener(final String status)
{
mUiHandler.post(new Runnable()
{
@Override
public void run()
{
if (mListener != null)
{
mListener.onCameraStatusChanged(status);
}
}
});
}
/**
* Notify the change of Liveview Status.
*
* @param status
*/
private void fireLiveviewStatusChangeListener(final boolean status)
{
mUiHandler.post(new Runnable()
{
@Override
public void run()
{
if (mListener != null)
{
mListener.onLiveviewStatusChanged(status);
}
}
});
}
/**
* Notify the change of Shoot Mode.
*
* @param shootMode
*/
private void fireShootModeChangeListener(final String shootMode)
{
mUiHandler.post(new Runnable()
{
@Override
public void run()
{
if (mListener != null)
{
mListener.onShootModeChanged(shootMode);
}
}
});
}
/**
* Notify the change of Zoom Information
*
* @param zoomIndexCurrentBox
* @param zoomNumberBox
* @param zoomPosition
* @param zoomPositionCurrentBox
*/
private void fireZoomInformationChangeListener(final int zoomIndexCurrentBox, final int zoomNumberBox,
final int zoomPosition, final int zoomPositionCurrentBox)
{
mUiHandler.post(new Runnable()
{
@Override
public void run()
{
if (mListener != null)
{
mListener.onZoomPositionChanged(zoomPosition);
}
}
});
}
/**
* Notify the change of Storage Id.
*
* @param storageId
*/
private void fireStorageIdChangeListener(final String storageId)
{
mUiHandler.post(new Runnable()
{
@Override
public void run()
{
if (mListener != null)
{
mListener.onStorageIdChanged(storageId);
}
}
});
}
/**
* Notify the picture taken.
*
* @param storageId
*/
private void firePictureTakenListener(final String takePictureUrl)
{
mUiHandler.post(new Runnable()
{
@Override
public void run()
{
if (mListener != null)
{
mListener.onPictureTaken(takePictureUrl);
}
}
});
}
/**
* Finds and extracts an error code from reply JSON data.
*
* @param replyJson
* @return
* @throws JSONException
*/
private static int findErrorCode(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
* @throws JSONException
*/
private static List<String> findAvailableApiList(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));
}
} else
{
Log.w(TAG, "Event reply: Illegal Index (0: AvailableApiList) " + type);
}
}
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
* @throws JSONException
*/
private static String findCameraStatus(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");
} else
{
Log.w(TAG, "Event reply: Illegal Index (1: CameraStatus) " + type);
}
}
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
* @throws JSONException
*/
private static Boolean findLiveviewStatus(JSONObject replyJson) throws JSONException
{
Boolean liveviewStatus = null;
int indexOfLiveviewStatus = 3;
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");
} else
{
Log.w(TAG, "Event reply: Illegal Index (3: LiveviewStatus) " + type);
}
}
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
* @throws JSONException
*/
private static String findShootMode(JSONObject replyJson) throws JSONException
{
String shootMode = null;
int indexOfShootMode = 21;
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");
} else
{
Log.w(TAG, "Event reply: Illegal Index (21: ShootMode) " + type);
}
}
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
* @throws JSONException
*/
private static int findZoomInformation(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");
} else
{
Log.w(TAG, "Event reply: Illegal Index (2: zoomInformation) " + type);
}
}
return zoomPosition;
}
/**
* Finds and extracts value of Storage Id from reply JSON data. As for
* getEvent v1.0, results[10] => "storageInformation"
*
* @param replyJson
* @return
* @throws JSONException
*/
private static String findStorageId(JSONObject replyJson) throws JSONException
{
String storageId = null;
int indexOfStorageInfomation = 10;
JSONArray resultsObj = replyJson.getJSONArray("result");
if (!resultsObj.isNull(indexOfStorageInfomation))
{
JSONArray storageInformationArray = resultsObj.getJSONArray(indexOfStorageInfomation);
if (!storageInformationArray.isNull(0))
{
JSONObject storageInformationObj = storageInformationArray.getJSONObject(0);
String type = storageInformationObj.getString("type");
if ("storageInformation".equals(type))
{
storageId = storageInformationObj.getString("storageID");
} else
{
Log.w(TAG, "Event reply: Illegal Index (11: storageInformation) " + type);
}
}
}
return storageId;
}
/**
* Finds and extracts value of picture url from reply JSON data. As for
* getEvent v1.0, results[5] => "takePicture"
*
* @param replyJson
* @return
* @throws JSONException
*/
private static String findTakePictureUrl(JSONObject replyJson) throws JSONException
{
String takePictureUrl = null;
int indexOfTakePictureUrl = 5;
JSONArray resultsObj = replyJson.getJSONArray("result");
if (!resultsObj.isNull(indexOfTakePictureUrl))
{
JSONArray takePictureArray = resultsObj.getJSONArray(indexOfTakePictureUrl);
if (!takePictureArray.isNull(0))
{
JSONObject takePictureObj = takePictureArray.getJSONObject(0);
String type = takePictureObj.getString("type");
if ("takePicture".equals(type))
{
JSONArray takePictureUrlArray = takePictureObj.getJSONArray("takePictureUrl");
if (takePictureUrlArray.length() > 0) {
takePictureUrl = takePictureUrlArray.getString(0);
}
} else
{
Log.w(TAG, "Event reply: Illegal Index (11: takePicture) " + type);
}
}
}
return takePictureUrl;
}
}