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; } }