content: /* <!-- +++
  package com.almalence.opencam_plus.cameracontroller;
  +++ --> */
  //<!-- -+-
  package com.almalence.opencam.cameracontroller;
  
  //-+- -->
  
  import java.io.BufferedInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Set;
  
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  
  import android.app.Activity;
  import android.app.ProgressDialog;
  import android.content.Context;
  import android.content.DialogInterface;
  import android.content.DialogInterface.OnCancelListener;
  import android.content.SharedPreferences;
  import android.hardware.Camera.Area;
  import android.os.Handler;
  import android.os.Looper;
  import android.preference.PreferenceManager;
  import android.util.Log;
  import android.view.View;
  
  import com.almalence.SwapHeap;
  import com.almalence.sony.cameraremote.PictureCallbackSonyRemote;
  import com.almalence.sony.cameraremote.ServerDevice;
  import com.almalence.sony.cameraremote.SimpleCameraEventObserver;
  import com.almalence.sony.cameraremote.SimpleRemoteApi;
  import com.almalence.sony.cameraremote.SimpleStreamSurfaceView;
  import com.almalence.sony.cameraremote.ZoomCallbackSonyRemote;
  import com.almalence.util.ImageConversion;
  
  /* <!-- +++
   import com.almalence.opencam_plus.ApplicationScreen;
   import com.almalence.opencam_plus.ApplicationInterface;
   import com.almalence.opencam_plus.PluginManager;
   import com.almalence.opencam_plus.PluginManagerInterface;
   import com.almalence.opencam_plus.R;
   +++ --> */
  //<!-- -+-
  import com.almalence.opencam.ApplicationScreen;
  import com.almalence.opencam.ApplicationInterface;
  import com.almalence.opencam.PluginManager;
  import com.almalence.opencam.PluginManagerInterface;
  import com.almalence.opencam.R;
  
  //-+- -->
  
  public class SonyRemoteCamera
  {
  	private static final String								TAG							= "SonyRemoteCamera";
  
  	protected static Context								mainContext					= null;
  	private static PluginManagerInterface					pluginManager				= null;
  	private static ApplicationInterface						appInterface				= null;
  	protected static Handler								messageHandler				= null;
  
  	public static ServerDevice								mTargetDevice;
  	private static SimpleRemoteApi							mRemoteApi;
  	private static Set<String>								mAvailableCameraApiSet		= new HashSet<String>();
  	private static Set<String>								mAvailableApiSet			= new HashSet<String>();
  
  	private static SimpleCameraEventObserver				mEventObserver;
  	private static SimpleCameraEventObserver.ChangeListener	mEventListener;
  
  	public static List<CameraController.Size>				mPreviewSizes				= new ArrayList<CameraController.Size>();
  	public static List<CameraController.Size>				mPictureSizes				= new ArrayList<CameraController.Size>();
  	private static int										minExpoCompensation			= 0;
  	private static int										maxExpoCompensation			= 0;
  	private static float									expoCompensationStep		= 0;
  	public static List<String>								availableWBModes			= new ArrayList<String>();
  	public static List<String>								availableFocusModes			= new ArrayList<String>();
  	public static List<String>								availableIsoModes			= new ArrayList<String>();
  	public static List<String>								availableFlashModes			= new ArrayList<String>();
  	public static boolean									isZoomAvailable				= false;
  
  	public static HashMap<Long, JSONObject>					mPictureSizeNames			= new HashMap<Long, JSONObject>();
  
  	public static int										previewWidth				= 640;
  	public static int										previewHeight				= 480;
  
  	public static String									currentWBMode				= "Auto WB";
  	public static String									currentIsoMode				= "auto";
  	public static String									currentFlashMode			= "off";
  	public static int										currentExposureCompensation	= 0;
  
  	public static Handler									UIhandler					= new Handler(
  																								Looper.getMainLooper());
  
  	public static List<Thread>								requestQueue				= new LinkedList<Thread>();
  	public static boolean									opening						= false;
  	public static ProgressDialog							progress;
  	public static ProgressDialog							progressImageDownloading;
  	public static ZoomCallbackSonyRemote					zoomCallbackSonyRemote		= null;
  
  	public static double									focusX						= 50;
  	public static double									focusY						= 50;
  
  	public static void onCreateSonyRemoteCamera(Context context, ApplicationInterface app,
  			PluginManagerInterface pluginManagerBase, Handler msgHandler)
  	{
  		mainContext = context;
  		appInterface = app;
  		pluginManager = pluginManagerBase;
  		messageHandler = msgHandler;
  
  		mEventListener = new SimpleCameraEventObserver.ChangeListenerTmpl()
  		{
  
  			@Override
  			public void onShootModeChanged(String shootMode)
  			{
  				Log.d(TAG, "onShootModeChanged() called: " + shootMode);
  				// refreshUi();
  			}
  
  			@Override
  			public void onCameraStatusChanged(String status)
  			{
  				Log.d(TAG, "onCameraStatusChanged() called: " + status);
  				// refreshUi();
  			}
  
  			@Override
  			public void onApiListModified(List<String> apis)
  			{
  				Log.d(TAG, "onApiListModified() called");
  				synchronized (mAvailableCameraApiSet)
  				{
  					mAvailableCameraApiSet.clear();
  					for (String api : apis)
  					{
  						mAvailableCameraApiSet.add(api);
  					}
  					if (!mEventObserver.getLiveviewStatus() //
  							&& isCameraApiAvailable("startLiveview"))
  					{
  						if (!CameraController.isRemoteCamera())
  							return;
  
  						if (appInterface.getSimpleStreamSurfaceView() != null
  								&& !appInterface.getSimpleStreamSurfaceView().isStarted())
  						{
  							startLiveview();
  						}
  					}
  
  					if (zoomCallbackSonyRemote != null)
  					{
  						if (isCameraApiAvailable("actZoom"))
  						{
  							isZoomAvailable = true;
  							zoomCallbackSonyRemote.onZoomAvailabelChanged(true);
  						} else
  						{
  							isZoomAvailable = false;
  							zoomCallbackSonyRemote.onZoomAvailabelChanged(false);
  						}
  					}
  				}
  			}
  
  			@Override
  			public void onZoomPositionChanged(int zoomPosition)
  			{
  				if (zoomCallbackSonyRemote != null)
  				{
  					zoomCallbackSonyRemote.onZoomPositionChanged(zoomPosition);
  				}
  			}
  
  			@Override
  			public void onLiveviewStatusChanged(boolean status)
  			{
  				Log.d(TAG, "onLiveviewStatusChanged() called = " + status);
  			}
  
  			@Override
  			public void onStorageIdChanged(String storageId)
  			{
  				Log.d(TAG, "onStorageIdChanged() called: " + storageId);
  				// refreshUi();
  			}
  
  			@Override
  			public void onPictureTaken(String takePictureUrl)
  			{
  				downloadAndProcessImage(takePictureUrl, false);
  			}
  		};
  	}
  
  	static Boolean	isBusy	= false;
  
  	public static void sendRequest()
  	{
  		synchronized (isBusy)
  		{
  			if (!isBusy && requestQueue.size() > 0)
  			{
  				isBusy = true;
  				requestQueue.get(0).start();
  				requestQueue.remove(0);
  			} else if (requestQueue.size() == 0 && opening && previewImagesCount >= 3)
  			{
  				if (progress != null)
  				{
  					progress.dismiss();
  				}
  				opening = false;
  				messageHandler.sendEmptyMessage(ApplicationInterface.MSG_SURFACE_READY);
  			}
  		}
  
  	}
  
  	public static void onRequestResult()
  	{
  		synchronized (isBusy)
  		{
  			isBusy = false;
  		}
  		CameraController.sendMessage(ApplicationInterface.MSG_BROADCAST,
  				ApplicationInterface.MSG_REMOTE_CAMERA_PARAMETR_CHANGED);
  		sendRequest();
  	}
  
  	public static void setZoomCallbackSonyRemote(ZoomCallbackSonyRemote callback)
  	{
  		zoomCallbackSonyRemote = callback;
  	}
  
  	public static void openCameraSonyRemote()
  	{
  		mRemoteApi = new SimpleRemoteApi(mTargetDevice);
  		mEventObserver = new SimpleCameraEventObserver(mainContext, mRemoteApi);
  		mEventObserver.activate();
  
  		opening = true;
  		previewImagesCount = 0;
  
  		progress = ProgressDialog.show(mainContext, mainContext.getResources().getString(R.string.title_connecting),
  				mainContext.getResources().getString(R.string.msg_connecting), true);
  		progress.setCancelable(true);
  		progress.setOnCancelListener(new OnCancelListener()
  		{
  			@Override
  			public void onCancel(DialogInterface dialog)
  			{
  				switchBackToDeviceCamera();
  			}
  		});
  
  		prepareOpenConnection();
  	}
  
  	private static void prepareOpenConnection()
  	{
  		Log.d(TAG, "prepareToOpenConection() exec");
  
  		// setProgressBarIndeterminateVisibility(true);
  
  		requestQueue.add(new Thread()
  		{
  
  			@Override
  			public void run()
  			{
  				try
  				{
  					if (mRemoteApi == null) {
  						onRequestResult();
  						return;
  					}
  					
  					// Get available API list (Camera API)
  					JSONObject replyJsonCamera = mRemoteApi.getCameraMethodTypes();
  					loadAvailableApiList(replyJsonCamera);
  
  					try
  					{
  						// Get available API list (AvContent API)
  						JSONObject replyJsonAvcontent = mRemoteApi.getAvcontentMethodTypes();
  						loadAvailableApiList(replyJsonAvcontent);
  					} catch (IOException e)
  					{
  						Log.d(TAG, "AvContent is not support.");
  					}
  
  					if (!isApiAvailable("setCameraFunction"))
  					{
  
  						// this device does not support setCameraFunction.
  						// No need to check camera status.
  
  						openConnection();
  
  					} else
  					{
  
  						// this device supports setCameraFunction.
  						// after confirmation of camera state, open connection.
  						Log.d(TAG, "this device support set camera function");
  
  						if (!isApiAvailable("getEvent"))
  						{
  							Log.e(TAG, "this device is not support getEvent");
  							openConnection();
  							return;
  						}
  
  						// confirm current camera status
  						String cameraStatus = null;
  						JSONObject replyJson = mRemoteApi.getEvent(false);
  						JSONArray resultsObj = replyJson.getJSONArray("result");
  						JSONObject cameraStatusObj = resultsObj.getJSONObject(1);
  						String type = cameraStatusObj.getString("type");
  						if ("cameraStatus".equals(type))
  						{
  							cameraStatus = cameraStatusObj.getString("cameraStatus");
  						} else
  						{
  							throw new IOException();
  						}
  
  						if (isShootingStatus(cameraStatus))
  						{
  							Log.d(TAG, "camera function is Remote Shooting.");
  							openConnection();
  						} else
  						{
  							// set Listener
  							startOpenConnectionAfterChangeCameraState();
  
  							// set Camera function to Remote Shooting
  							replyJson = mRemoteApi.setCameraFunction("Remote Shooting");
  						}
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "prepareToStartContentsListMode: IOException: " + e.getMessage());
  					// DisplayHelper.toast(getApplicationContext(),
  					// R.string.msg_error_api_calling);
  					// DisplayHelper.setProgressIndicator(SampleCameraActivity.this,
  					// false);
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "prepareToStartContentsListMode: JSONException: " + e.getMessage());
  					// DisplayHelper.toast(getApplicationContext(),
  					// R.string.msg_error_api_calling);
  					// DisplayHelper.setProgressIndicator(SampleCameraActivity.this,
  					// false);
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	/**
  	 * Open connection to the camera device to start monitoring Camera events
  	 * and showing liveview.
  	 */
  	private static void openConnection()
  	{
  
  		mEventObserver.setEventChangeListener(mEventListener);
  		requestQueue.add(new Thread()
  		{
  
  			@Override
  			public void run()
  			{
  				Log.d(TAG, "openConnection(): exec.");
  				
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  
  				try
  				{
  					
  					JSONObject replyJson = null;
  
  					// getAvailableApiList
  					replyJson = mRemoteApi.getAvailableApiList();
  					loadAvailableCameraApiList(replyJson);
  
  					// check version of the server device
  					if (isCameraApiAvailable("getApplicationInfo"))
  					{
  						Log.d(TAG, "openConnection(): getApplicationInfo()");
  						replyJson = mRemoteApi.getApplicationInfo();
  						if (!isAvailableServerVersion(replyJson))
  						{
  							return;
  						}
  					} else
  					{
  						// never happens;
  						return;
  					}
  
  					// startRecMode if necessary.
  					if (isCameraApiAvailable("startRecMode"))
  					{
  						Log.d(TAG, "openConnection(): startRecMode()");
  						replyJson = mRemoteApi.startRecMode();
  
  						// Call again.
  						replyJson = mRemoteApi.getAvailableApiList();
  						loadAvailableCameraApiList(replyJson);
  					}
  
  					String modeName = PluginManager.getInstance().getActiveModeID();
  					if (modeName.contains("video"))
  					{
  						setShootMode("movie");
  					} else
  					{
  						setShootMode("still");
  					}
  
  					// getEvent start
  					if (isCameraApiAvailable("getEvent"))
  					{
  						Log.d(TAG, "openConnection(): EventObserver.start()");
  						mEventObserver.start();
  					}
  
  					// Liveview start
  					if (isCameraApiAvailable("startLiveview"))
  					{
  						Log.d(TAG, "openConnection(): LiveviewSurface.start()");
  						startLiveview();
  					}
  
  					// Set exposure mode
  					if (isCameraApiAvailable("setExposureMode"))
  					{
  						Log.d(TAG, "openConnection(): setExposureMode");
  						setExposureMode();
  					}
  
  					if (zoomCallbackSonyRemote != null)
  					{
  						if (isCameraApiAvailable("actZoom"))
  						{
  							isZoomAvailable = true;
  							zoomCallbackSonyRemote.onZoomAvailabelChanged(true);
  						} else
  						{
  							isZoomAvailable = false;
  							zoomCallbackSonyRemote.onZoomAvailabelChanged(false);
  						}
  					}
  
  					Log.d(TAG, "openConnection(): completed.");
  				} catch (IOException e)
  				{
  					Log.w(TAG, "openConnection : IOException: " + e.getMessage());
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  
  	}
  
  	public static void onPauseSonyRemoteCamera()
  	{
  		closeConnection();
  	}
  
  	/**
  	 * Stop monitoring Camera events and close liveview connection.
  	 */
  	private static void closeConnection()
  	{
  
  		Log.d(TAG, "closeConnection(): exec.");
  		// Liveview stop
  		Log.d(TAG, "closeConnection(): LiveviewSurface.stop()");
  		stopLiveview();
  
  		// getEvent stop
  		Log.d(TAG, "closeConnection(): EventObserver.release()");
  		if (mEventObserver != null)
  		{
  			mEventObserver.release();
  		}
  
  		// stopRecMode if necessary.
  		if (isCameraApiAvailable("stopRecMode"))
  		{
  			requestQueue.add(new Thread()
  			{
  
  				@Override
  				public void run()
  				{
  					Log.d(TAG, "closeConnection(): stopRecMode()");
  					
  					if (mRemoteApi == null) {
  						onRequestResult();
  						return;
  					}
  					
  					try
  					{
  						if (mRemoteApi != null)
  						{
  							mRemoteApi.stopRecMode();
  						}
  					} catch (IOException e)
  					{
  						Log.w(TAG, "closeConnection: IOException: " + e.getMessage());
  					} finally
  					{
  						onRequestResult();
  					}
  				}
  			});
  			sendRequest();
  		}
  
  		currentWBMode = "Auto WB";
  		currentIsoMode = "auto";
  		currentFlashMode = "off";
  		currentExposureCompensation = 0;
  		requestQueue.clear();
  		mPreviewSizes.clear();
  		mPictureSizes.clear();
  		minExpoCompensation = 0;
  		maxExpoCompensation = 0;
  		expoCompensationStep = 0;
  		availableWBModes.clear();
  		availableFocusModes.clear();
  		availableIsoModes.clear();
  		availableFlashModes.clear();
  		isZoomAvailable = false;
  		mPictureSizeNames.clear();
  		previewWidth = 640;
  		previewHeight = 480;
  		opening = false;
  
  		if (progress != null)
  		{
  			progress.dismiss();
  		}
  		if (progressImageDownloading != null)
  		{
  			progressImageDownloading.dismiss();
  		}
  
  		Log.d(TAG, "closeConnection(): completed.");
  	}
  
  	private static void startOpenConnectionAfterChangeCameraState()
  	{
  		Log.d(TAG, "startOpenConectiontAfterChangeCameraState() exec");
  
  		((Activity) mainContext).runOnUiThread(new Runnable()
  		{
  
  			@Override
  			public void run()
  			{
  				mEventObserver.setEventChangeListener(new SimpleCameraEventObserver.ChangeListenerTmpl()
  				{
  
  					@Override
  					public void onCameraStatusChanged(String status)
  					{
  						Log.d(TAG, "onCameraStatusChanged:" + status);
  						if ("IDLE".equals(status))
  						{
  							openConnection();
  						}
  						// refreshUi();
  					}
  
  					@Override
  					public void onShootModeChanged(String shootMode)
  					{
  						// refreshUi();
  					}
  
  					@Override
  					public void onStorageIdChanged(String storageId)
  					{
  						// refreshUi();
  					}
  
  					@Override
  					public void onPictureTaken(String takePictureUrl)
  					{
  						downloadAndProcessImage(takePictureUrl, false);
  					}
  				});
  
  				mEventObserver.start();
  			}
  		});
  	}
  
  	private static boolean isShootingStatus(String currentStatus)
  	{
  		Set<String> shootingStatus = new HashSet<String>();
  		shootingStatus.add("IDLE");
  		shootingStatus.add("StillCapturing");
  		shootingStatus.add("StillSaving");
  		shootingStatus.add("MovieWaitRecStart");
  		shootingStatus.add("MovieRecording");
  		shootingStatus.add("MovieWaitRecStop");
  		shootingStatus.add("MovieSaving");
  		shootingStatus.add("IntervalWaitRecStart");
  		shootingStatus.add("IntervalRecording");
  		shootingStatus.add("IntervalWaitRecStop");
  		shootingStatus.add("AudioWaitRecStart");
  		shootingStatus.add("AudioRecording");
  		shootingStatus.add("AudioWaitRecStop");
  		shootingStatus.add("AudioSaving");
  
  		return shootingStatus.contains(currentStatus);
  	}
  
  	/**
  	 * Call cancelTouchAFPosition
  	 * 
  	 * @param mode
  	 */
  	public static void cancelAutoFocusSonyRemote()
  	{
  		requestQueue.add(new Thread()
  		{
  
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  				
  				try
  				{
  					if (mRemoteApi == null)
  					{
  						return;
  					}
  					JSONObject replyJson = mRemoteApi.cancelTouchAFPosition();
  					JSONArray resultsObj = replyJson.getJSONArray("result");
  					int resultCode = resultsObj.getInt(0);
  					if (resultCode != 0)
  					{
  						Log.w(TAG, "setShootMode: error: " + resultCode);
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "setShootMode: IOException: " + e.getMessage());
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "setShootMode: JSON format error.");
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	/**
  	 * Call setTouchAFPosition
  	 * 
  	 * @param mode
  	 */
  	public static boolean autoFocusSonyRemote()
  	{
  		requestQueue.add(new Thread()
  		{
  
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					CameraController.onAutoFocus(false);
  					onRequestResult();
  					return;
  				}
  				
  				boolean result = false;
  				try
  				{
  					JSONObject replyJson = mRemoteApi.setTouchAFPosition(focusX, focusY);
  					JSONArray resultsArray = replyJson.getJSONArray("result");
  					int resultCode = resultsArray.getInt(0);
  					if (resultCode != 0)
  					{
  						Log.w(TAG, "setTouchAFPosition: error: " + resultCode);
  					} else
  					{
  						JSONObject resObj = resultsArray.getJSONObject(1);
  						result = resObj.getBoolean("AFResult");
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "setTouchAFPosition: IOException: " + e.getMessage());
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "setTouchAFPosition: JSON format error.");
  				} finally
  				{
  					final boolean res = result;
  					UIhandler.post(new Runnable()
  					{
  						@Override
  						public void run()
  						{
  							CameraController.onAutoFocus(res);
  						}
  					});
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  		return true;
  	}
  
  	/**
  	 * Call setShootMode
  	 * 
  	 * @param mode
  	 */
  	public static void setShootMode(final String mode)
  	{
  		requestQueue.add(new Thread()
  		{
  
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  				
  				try
  				{
  					JSONObject replyJson = mRemoteApi.setShootMode(mode);
  					JSONArray resultsObj = replyJson.getJSONArray("result");
  					int resultCode = resultsObj.getInt(0);
  					if (resultCode != 0)
  					{
  						Log.w(TAG, "setShootMode: error: " + resultCode);
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "setShootMode: IOException: " + e.getMessage());
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "setShootMode: JSON format error.");
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	public static void setCameraFocusAreasSonyRemote(List<Area> focusAreas)
  	{
  		if (focusAreas != null && focusAreas.size() > 0)
  		{
  			Area area = focusAreas.get(0);
  			int x = area.rect.centerX();
  			int y = area.rect.centerY();
  
  			focusX = (double) (((previewWidth / 2000) * x + previewWidth / 2) / previewHeight);
  			focusY = (double) (((previewHeight / 2000) * y + previewHeight / 2) / previewHeight);
  		} else
  		{
  			focusX = 50;
  			focusY = 50;
  		}
  	}
  
  	public static void takePicture(final PictureCallbackSonyRemote pictureListener)
  	{
  		requestQueue.add(new Thread()
  		{
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  				
  				try
  				{
  					JSONObject replyJson = mRemoteApi.actTakePicture();
  					JSONArray resultsObj = replyJson.getJSONArray("result");
  					JSONArray imageUrlsObj = resultsObj.getJSONArray(0);
  					String postImageUrl = null;
  					if (1 <= imageUrlsObj.length())
  					{
  						postImageUrl = imageUrlsObj.getString(0);
  					}
  					if (postImageUrl == null)
  					{
  						Log.w(TAG, "takeAndFetchPicture: post image URL is null.");
  						return;
  					}
  
  					downloadAndProcessImage(postImageUrl, true);
  
  				} catch (IOException e)
  				{
  					Log.w(TAG, "IOException while closing slicer: " + e.getMessage());
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "JSONException while closing slicer");
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	public static void downloadAndProcessImage(final String postImageUrl, final boolean fromRequest)
  	{
  		((Activity) mainContext).runOnUiThread(new Runnable()
  		{
  			@Override
  			public void run()
  			{
  				progressImageDownloading = ProgressDialog.show(mainContext,
  						mainContext.getResources().getString(R.string.title_downloading), mainContext.getResources()
  								.getString(R.string.msg_downloading), true);
  			}
  		});
  
  		requestQueue.add(new Thread()
  		{
  			@Override
  			public void run()
  			{
  				try
  				{
  					URL url = new URL(postImageUrl);
  					InputStream istream = new BufferedInputStream(url.openStream());
  					ByteArrayOutputStream buffer = new ByteArrayOutputStream();
  					int nRead;
  					byte[] data = new byte[1024];
  
  					while ((nRead = istream.read(data, 0, data.length)) != -1)
  					{
  						buffer.write(data, 0, nRead);
  					}
  
  					buffer.flush();
  					CameraController.getInstance().onPictureTakenSonyRemote(buffer.toByteArray(), fromRequest);
  				} catch (IOException e)
  				{
  					e.printStackTrace();
  				} finally
  				{
  					((Activity) mainContext).runOnUiThread(new Runnable()
  					{
  						@Override
  						public void run()
  						{
  							if (progressImageDownloading != null)
  							{
  								progressImageDownloading.dismiss();
  							}
  						}
  					});
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	/**
  	 * Call startMovieRec
  	 */
  	public static void startMovieRec()
  	{
  		requestQueue.add(new Thread()
  		{
  
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  				
  				try
  				{
  					Log.d(TAG, "startMovieRec: exec.");
  					JSONObject replyJson = mRemoteApi.startMovieRec();
  					JSONArray resultsObj = replyJson.getJSONArray("result");
  					int resultCode = resultsObj.getInt(0);
  					if (resultCode != 0)
  					{
  						Log.w(TAG, "startMovieRec: error: " + resultCode);
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "startMovieRec: IOException: " + e.getMessage());
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "startMovieRec: JSON format error.");
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	/**
  	 * Call stopMovieRec
  	 */
  	public static void stopMovieRec()
  	{
  		requestQueue.add(new Thread()
  		{
  
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  				
  				try
  				{
  					Log.d(TAG, "stopMovieRec: exec.");
  					JSONObject replyJson = mRemoteApi.stopMovieRec();
  					JSONArray resultsObj = replyJson.getJSONArray("result");
  					String thumbnailUrl = resultsObj.getString(0);
  					if (thumbnailUrl == null)
  					{
  						Log.w(TAG, "stopMovieRec: error");
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "stopMovieRec: IOException: " + e.getMessage());
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "stopMovieRec: JSON format error.");
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	public static boolean isZoomAvailable()
  	{
  		return isZoomAvailable;
  	}
  
  	/**
  	 * Call actZoom
  	 * 
  	 * @param direction
  	 * @param movement
  	 */
  	public static void actZoom(final String direction, final String movement)
  	{
  		new Thread()
  		{
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  				
  				try
  				{
  					JSONObject replyJson = mRemoteApi.actZoom(direction, movement);
  					JSONArray resultsObj = replyJson.getJSONArray("result");
  					int resultCode = resultsObj.getInt(0);
  					if (resultCode == 0)
  					{
  						// Success, but no refresh UI at the point.
  						Log.v(TAG, "actZoom: success");
  					} else
  					{
  						Log.w(TAG, "actZoom: error: " + resultCode);
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "actZoom: IOException: " + e.getMessage());
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "actZoom: JSON format error.");
  				}
  			}
  		}.start();
  	}
  
  	private static void setExposureMode()
  	{
  		if (mRemoteApi == null) {
  			onRequestResult();
  			return;
  		}
  		
  		JSONObject replyJson = null;
  		try
  		{
  			replyJson = mRemoteApi.setExposureMode();
  
  			JSONArray resultArrayJson = replyJson.getJSONArray("result");
  			int result = resultArrayJson.getInt(0);
  		} catch (JSONException e)
  		{
  			e.printStackTrace();
  		} catch (IOException e)
  		{
  			e.printStackTrace();
  		}
  	}
  
  	private static void startLiveview()
  	{
  		if (!CameraController.isRemoteCamera())
  			return;
  
  		if (appInterface.getSimpleStreamSurfaceView() == null)
  		{
  			Log.w(TAG, "startLiveview mLiveviewSurface is null.");
  			return;
  		}
  		requestQueue.add(new Thread()
  		{
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  				
  				try
  				{
  					JSONObject replyJson = null;
  					replyJson = mRemoteApi.startLiveview();
  
  					if (!SimpleRemoteApi.isErrorReply(replyJson))
  					{
  						JSONArray resultsObj = replyJson.getJSONArray("result");
  						if (1 <= resultsObj.length())
  						{
  							initRemoteCameraFeatures();
  
  							// Obtain liveview URL from the result.
  							final String liveviewUrl = resultsObj.getString(0);
  							((Activity) mainContext).runOnUiThread(new Runnable()
  							{
  
  								@Override
  								public void run()
  								{
  									if (!CameraController.isRemoteCamera())
  										return;
  
  									appInterface.getSimpleStreamSurfaceView().start(liveviewUrl,
  											new SimpleStreamSurfaceView.StreamFrameListener()
  											{
  
  												@Override
  												public void onFrameAvailable(byte[] jpegData)
  												{
  													onPreviewFrame(jpegData);
  
  												}
  											}, new SimpleStreamSurfaceView.StreamErrorListener()
  											{
  
  												@Override
  												public void onError(StreamErrorReason reason)
  												{
  													switchBackToDeviceCamera();
  												}
  											});
  									appInterface.getSimpleStreamSurfaceView().setVisibility(View.VISIBLE);
  									appInterface.getSimpleStreamSurfaceView().bringToFront();
  								}
  							});
  						}
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "startLiveview IOException: " + e.getMessage());
  				} catch (JSONException e)
  				{
  					Log.w(TAG, "startLiveview JSONException: " + e.getMessage());
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	private static void switchBackToDeviceCamera()
  	{
  		((Activity) mainContext).runOnUiThread(new Runnable()
  		{
  			@Override
  			public void run()
  			{
  				SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen
  						.getMainContext());
  				prefs.edit().putInt(ApplicationScreen.sCameraModePref, 0).commit();
  				appInterface.relaunchCamera();
  			}
  		});
  	}
  
  	private static void stopLiveview()
  	{
  		Log.e("SonyRemoteCamera", "stopLiveview");
  		requestQueue.add(new Thread()
  		{
  			@Override
  			public void run()
  			{
  				if (mRemoteApi == null) {
  					onRequestResult();
  					return;
  				}
  				
  				try
  				{
  					if (mRemoteApi != null)
  					{
  						mRemoteApi.stopLiveview();
  					}
  				} catch (IOException e)
  				{
  					Log.w(TAG, "stopLiveview IOException: " + e.getMessage());
  				} finally
  				{
  					onRequestResult();
  				}
  			}
  		});
  		sendRequest();
  	}
  
  	static int	previewImagesCount	= 0;
  
  	public static void onPreviewFrame(byte[] jpegData)
  	{
  		previewImagesCount++;
  		if (!opening)
  		{
  			int frame = ImageConversion.JpegConvert(jpegData, previewWidth, previewHeight, false, false, 0);
  			int frameLen = previewWidth * previewHeight + 2 * ((previewWidth + 1) / 2) * ((previewHeight + 1) / 2);
  			final byte[] data = SwapHeap.SwapFromHeap(frame, frameLen);
  
  			UIhandler.post(new Runnable()
  			{
  				@Override
  				public void run()
  				{
  					pluginManager.onPreviewFrame(data);
  				}
  			});
  		}
  
  		if (requestQueue.size() == 0 && opening && previewImagesCount >= 3)
  		{
  			if (progress != null)
  			{
  				progress.dismiss();
  			}
  			opening = false;
  			messageHandler.sendEmptyMessage(ApplicationInterface.MSG_SURFACE_READY);
  		}
  	}
  
  	/**
  	 * Retrieve a list of APIs that are available at present.
  	 * 
  	 * @param replyJson
  	 */
  	private static void loadAvailableCameraApiList(JSONObject replyJson)
  	{
  		synchronized (mAvailableCameraApiSet)
  		{
  			mAvailableCameraApiSet.clear();
  			try
  			{
  				JSONArray resultArrayJson = replyJson.getJSONArray("result");
  				JSONArray apiListJson = resultArrayJson.getJSONArray(0);
  				for (int i = 0; i < apiListJson.length(); i++)
  				{
  					mAvailableCameraApiSet.add(apiListJson.getString(i));
  				}
  			} catch (JSONException e)
  			{
  				Log.w(TAG, "loadAvailableCameraApiList: JSON format error.");
  			}
  		}
  	}
  
  	/**
  	 * Retrieve a list of APIs that are available by the target device.
  	 * 
  	 * @param replyJson
  	 */
  	private static void loadAvailableApiList(JSONObject replyJson)
  	{
  		synchronized (mAvailableApiSet)
  		{
  			try
  			{
  				JSONArray resultArrayJson = replyJson.getJSONArray("results");
  				for (int i = 0; i < resultArrayJson.length(); i++)
  				{
  					mAvailableApiSet.add(resultArrayJson.getJSONArray(i).getString(0));
  				}
  			} catch (JSONException e)
  			{
  				Log.w(TAG, "loadAvailableApiList: JSON format error.");
  			}
  		}
  	}
  
  	/**
  	 * Check if the version of the server is available in this application.
  	 * 
  	 * @param replyJson
  	 * @return
  	 */
  	private static boolean isAvailableServerVersion(JSONObject replyJson)
  	{
  		try
  		{
  			JSONArray resultArrayJson = replyJson.getJSONArray("result");
  			String version = resultArrayJson.getString(1);
  			String[] separated = version.split("\\.");
  			int major = Integer.valueOf(separated[0]);
  			if (2 <= major)
  			{
  				return true;
  			}
  		} catch (JSONException e)
  		{
  			Log.w(TAG, "isAvailableServerVersion: JSON format error.");
  		} catch (NumberFormatException e)
  		{
  			Log.w(TAG, "isAvailableServerVersion: Number format error.");
  		}
  		return false;
  	}
  
  	/**
  	 * Check if the specified API is available. This is for camera and avContent
  	 * service API. The result of this method does not change dynamically.
  	 * 
  	 * @param apiName
  	 * @return
  	 */
  	private static boolean isApiAvailable(String apiName)
  	{
  		boolean isAvailable = false;
  		synchronized (mAvailableApiSet)
  		{
  			isAvailable = mAvailableApiSet.contains(apiName);
  		}
  		return isAvailable;
  	}
  
  	/**
  	 * Check if the specified API is available at present. This works correctly
  	 * only for Camera API.
  	 * 
  	 * @param apiName
  	 * @return
  	 */
  	private static boolean isCameraApiAvailable(String apiName)
  	{
  		boolean isAvailable = false;
  		synchronized (mAvailableCameraApiSet)
  		{
  			isAvailable = mAvailableCameraApiSet.contains(apiName);
  		}
  		return isAvailable;
  	}
  
  	/**
  	 * Sets a target ServerDevice object.
  	 * 
  	 * @param device
  	 */
  	public static void setTargetServerDevice(ServerDevice device)
  	{
  		mTargetDevice = device;
  	}
  
  	/**
  	 * Returns a target ServerDevice object.
  	 * 
  	 * @return return ServiceDevice
  	 */
  	public static ServerDevice getTargetServerDevice()
  	{
  		return mTargetDevice;
  	}
  
  	/**
  	 * Sets a SimpleRemoteApi object to transmit to Activity.
  	 * 
  	 * @param remoteApi
  	 */
  	public static void setRemoteApi(SimpleRemoteApi remoteApi)
  	{
  		mRemoteApi = remoteApi;
  	}
  
  	/**
  	 * Returns a SimpleRemoteApi object.
  	 * 
  	 * @return return SimpleRemoteApi
  	 */
  	public static SimpleRemoteApi getRemoteApi()
  	{
  		return mRemoteApi;
  	}
  
  	/**
  	 * Sets a List of available APIs.
  	 * 
  	 * @param apiList
  	 */
  	public static void setAvailableApiList(Set<String> apiList)
  	{
  		mAvailableApiSet = apiList;
  	}
  
  	/**
  	 * Returns a list of available APIs.
  	 * 
  	 * @return Returns a list of available APIs.
  	 */
  	public static Set<String> getAvailableApiList()
  	{
  		return mAvailableApiSet;
  	}
  
  	public static void initRemoteCameraFeatures()
  	{
  		if (isCameraApiAvailable("getAvailableLiveviewSize"))
  		{
  			initPreviewSizeList();
  		} else
  		{
  			mPreviewSizes.add(new CameraController.Size(640, 480));
  		}
  
  		mPictureSizes.add(new CameraController.Size(1920, 1080));
  		if (isCameraApiAvailable("getAvailableStillSize"))
  		{
  			initPictureSizeList();
  		}
  
  		if (isCameraApiAvailable("getAvailableExposureCompensation"))
  		{
  			initExposureCompensationAvailable();
  		}
  
  		if (isCameraApiAvailable("getAvailableFocusMode"))
  		{
  			initAvailableFocusMode();
  		}
  
  		if (isCameraApiAvailable("getAvailableIsoSpeedRate"))
  		{
  			initAvailableIsoMode();
  		}
  
  		if (isCameraApiAvailable("getAvailableWhiteBalance"))
  		{
  			initAvailableWhiteBalance();
  		}
  
  		if (isCameraApiAvailable("getAvailableFlashMode"))
  		{
  			initAvailableFlashMode();
  		}
  	}
  
  	public static List<CameraController.Size> getPreviewSizeListRemote()
  	{
  		if (mPreviewSizes == null || mPreviewSizes.size() == 0)
  		{
  			mPreviewSizes = new ArrayList<CameraController.Size>();
  			mPreviewSizes.add(new CameraController.Size(640, 480));
  		}
  		return mPreviewSizes;
  	}
  
  	public static List<CameraController.Size> initPreviewSizeList()
  	{
  		mPreviewSizes = new ArrayList<CameraController.Size>();
  		mPreviewSizes.add(new CameraController.Size(640, 480));
  
  		if (mRemoteApi == null) {
  			return mPreviewSizes;
  		}
  		
  		JSONObject replyJson = null;
  		try
  		{
  			replyJson = mRemoteApi.getAvailableLiveviewSize();
  			JSONArray resultArrayJson = replyJson.getJSONArray("result");
  			JSONArray availableLiveviewArrayJson = resultArrayJson.getJSONArray(1);
  			for (int i = 0; i < availableLiveviewArrayJson.length(); i++)
  			{
  				if (availableLiveviewArrayJson.getString(i).equals("L"))
  				{
  					CameraController.Size size = new CameraController.Size(1024, 768);
  					mPreviewSizes.add(size);
  				}
  
  				if (availableLiveviewArrayJson.getString(i).equals("M"))
  				{
  					CameraController.Size size = new CameraController.Size(640, 480);
  					mPreviewSizes.add(size);
  				}
  			}
  		} catch (JSONException e)
  		{
  			e.printStackTrace();
  		} catch (IOException e)
  		{
  			e.printStackTrace();
  		}
  
  		return mPreviewSizes;
  	}
  
  	private static CameraController.Size convertJsonToSize(JSONObject json) throws NumberFormatException, JSONException
  	{
  		CameraController.Size size = null;
  
  		long totalPixelCount = Long.valueOf(json.getString("size").replace("M", "")) * 1000000;
  
  		int horizontalRation = Integer.valueOf(json.getString("aspect").replaceAll(":.*", ""));
  		int verticalRation = Integer.valueOf(json.getString("aspect").replaceAll(".*:", ""));
  
  		double horizontalToVerticalRation = (double) ((double) horizontalRation / (double) verticalRation);
  		double verticalToHorizontalRation = (double) ((double) verticalRation / (double) horizontalRation);
  
  		int width = (int) Math.sqrt(totalPixelCount * horizontalToVerticalRation);
  		int height = (int) Math.sqrt(totalPixelCount * verticalToHorizontalRation);
  
  		size = new CameraController.Size(width, height);
  
  		mPictureSizeNames.put((long) (width * height), json);
  
  		return size;
  	}
  
  	public static void initPictureSizeList()
  	{
  		synchronized (mPictureSizes)
  		{
  
  			mPictureSizes = new ArrayList<CameraController.Size>();
  			mPictureSizes.add(new CameraController.Size(1920, 1080));
  			
  			if (mRemoteApi == null) {
  				return;
  			}
  			
  			JSONObject replyJson = null;
  			try
  			{
  				replyJson = mRemoteApi.getAvailableStillSize();
  				JSONArray resultArrayJson = replyJson.getJSONArray("result");
  				JSONArray availableStillArrayJson = resultArrayJson.getJSONArray(1);
  				if (availableStillArrayJson.length() > 0)
  				{
  					mPictureSizes.clear();
  					mPictureSizeNames.clear();
  				}
  				for (int i = 0; i < availableStillArrayJson.length(); i++)
  				{
  					CameraController.Size size = convertJsonToSize(availableStillArrayJson.getJSONObject(i));
  					if (size != null)
  					{
  						mPictureSizes.add(size);
  					}
  				}
  			} catch (JSONException e)
  			{
  				e.printStackTrace();
  			} catch (IOException e)
  			{
  				e.printStackTrace();
  			}
  		}
  	}
  
  	public static void fillPictureSizeListRemote(List<CameraController.Size> pictureSizes)
  	{
  		synchronized (mPictureSizes)
  		{
  			pictureSizes.clear();
  			pictureSizes.addAll(mPictureSizes);
  		}
  	}
  
  	public static List<CameraController.Size> getPictureSizeListRemote()
  	{
  		synchronized (mPictureSizes)
  		{
  			if (mPictureSizes.size() == 0)
  			{
  				mPictureSizes.add(new CameraController.Size(1920, 1080));
  			}
  			return mPictureSizes;
  		}
  	}
  
  	public static void initExposureCompensationAvailable()
  	{
  		if (mRemoteApi == null) {
  			return;
  		}
  		
  		JSONObject replyJson = null;
  		try
  		{
  			replyJson = mRemoteApi.getAvailableExposureCompensation();
  
  			JSONArray resultArrayJson = replyJson.getJSONArray("result");
  			maxExpoCompensation = resultArrayJson.getInt(1);
  			minExpoCompensation = resultArrayJson.getInt(2);
  
  			int step = resultArrayJson.getInt(3);
  			if (step == 1)
  			{
  				expoCompensationStep = 1f / 3f;
  			}
  			if (step == 2)
  			{
  				expoCompensationStep = 1f / 2f;
  			}
  		} catch (JSONException e)
  		{
  			e.printStackTrace();
  		} catch (IOException e)
  		{
  			e.printStackTrace();
  		}
  	}
  
  	public static boolean isExposureCompensationAvailable()
  	{
  		boolean res = false;
  
  		if (minExpoCompensation != maxExpoCompensation && expoCompensationStep != 0)
  		{
  			res = true;
  		}
  
  		return res;
  	}
  
  	public static int getMinExposureCompensationRemote()
  	{
  		return minExpoCompensation;
  	}
  
  	public static int getMaxExposureCompensationRemote()
  	{
  		return maxExpoCompensation;
  	}
  
  	public static float getExposureCompensationStepRemote()
  	{
  		return expoCompensationStep;
  	}
  
  	public static String getWhiteBalanceRemote()
  	{
  		return currentWBMode;
  	}
  
  	public static String getIsoModeRemote()
  	{
  		return currentIsoMode;
  	}
  
  	public static String getFlashModeRemote()
  	{
  		return currentFlashMode;
  	}
  
  	public static int getExposureCompensationRemote()
  	{
  		return currentExposureCompensation;
  	}
  
  	public static List<String> getAvailableWhiteBalanceRemote()
  	{
  		return availableWBModes;
  	}
  
  	public static void initAvailableWhiteBalance()
  	{
  		
  		availableWBModes = new ArrayList<String>();
  
  		if (mRemoteApi == null) {
  			return;
  		}
  		
  		JSONObject replyJson = null;
  		try
  		{
  			replyJson = mRemoteApi.getAvailableWhiteBalance();
  
  			JSONArray resultArrayJson = replyJson.getJSONArray("result");
  
  			JSONObject currentMode = resultArrayJson.getJSONObject(0);
  			currentWBMode = currentMode.getString("whiteBalanceMode");
  
  			JSONArray availableWhiteBalanceArrayJson = resultArrayJson.getJSONArray(1);
  			for (int i = 0; i < availableWhiteBalanceArrayJson.length(); i++)
  			{
  				JSONObject wb = availableWhiteBalanceArrayJson.getJSONObject(i);
  				availableWBModes.add(wb.getString("whiteBalanceMode"));
  			}
  		} catch (JSONException e)
  		{
  			e.printStackTrace();
  		} catch (IOException e)
  		{
  			e.printStackTrace();
  		}
  	}
  
  	public static List<String> getAvailableFocusModeRemote()
  	{
  		return availableFocusModes;
  	}
  
  	public static void initAvailableFocusMode()
  	{
  		availableFocusModes = new ArrayList<String>();
  
  		if (mRemoteApi == null) {
  			return;
  		}
  		
  		JSONObject replyJson = null;
  		try
  		{
  			replyJson = mRemoteApi.getAvailableFocusMode();
  
  			JSONArray resultArrayJson = replyJson.getJSONArray("result");
  			JSONArray availableFocusModeArrayJson = resultArrayJson.getJSONArray(1);
  			for (int i = 0; i < availableFocusModeArrayJson.length(); i++)
  			{
  				String focusMode = availableFocusModeArrayJson.getString(i);
  
  				if (focusMode.equals("AF-S"))
  				{
  					focusMode = "auto";
  					availableFocusModes.add(focusMode);
  				}
  
  				if (focusMode.equals("AF-C"))
  				{
  					focusMode = "continuous-picture";
  					availableFocusModes.add(focusMode);
  				}
  			}
  		} catch (JSONException e)
  		{
  			e.printStackTrace();
  		} catch (IOException e)
  		{
  			e.printStackTrace();
  		}
  	}
  
  	public static boolean isFlashAvailableRemote()
  	{
  		return (availableFlashModes != null && availableFlashModes.size() > 1);
  	}
  
  	public static List<String> getAvailableFlashModeRemote()
  	{
  		return availableFlashModes;
  	}
  
  	public static void initAvailableFlashMode()
  	{
  		availableFlashModes = new ArrayList<String>();
  
  		if (mRemoteApi == null) {
  			return;
  		}
  		
  		JSONObject replyJson = null;
  		try
  		{
  			replyJson = mRemoteApi.getAvailableFlashMode();
  
  			JSONArray resultArrayJson = replyJson.getJSONArray("result");
  			JSONArray availableFlashModeArrayJson = resultArrayJson.getJSONArray(1);
  			for (int i = 0; i < availableFlashModeArrayJson.length(); i++)
  			{
  				String flash = availableFlashModeArrayJson.getString(i);
  				availableFlashModes.add(flash);
  			}
  		} catch (JSONException e)
  		{
  			e.printStackTrace();
  		} catch (IOException e)
  		{
  			e.printStackTrace();
  		}
  	}
  
  	public static boolean isISOModeAvailableRemote()
  	{
  		return (availableIsoModes != null && availableIsoModes.size() > 1);
  	}
  
  	public static List<String> getAvailableIsoModeRemote()
  	{
  		return availableIsoModes;
  	}
  
  	public static void initAvailableIsoMode()
  	{
  		availableIsoModes = new ArrayList<String>();
  
  		if (mRemoteApi == null) {
  			return;
  		}
  		
  		JSONObject replyJson = null;
  		try
  		{
  			replyJson = mRemoteApi.getAvailableIsoSpeedRate();
  
  			JSONArray resultArrayJson = replyJson.getJSONArray("result");
  			JSONArray availableISOModeArrayJson = resultArrayJson.getJSONArray(1);
  			for (int i = 0; i < availableISOModeArrayJson.length(); i++)
  			{
  				String iso = availableISOModeArrayJson.getString(i);
  				availableIsoModes.add(iso);
  			}
  		} catch (JSONException e)
  		{
  			e.printStackTrace();
  		} catch (IOException e)
  		{
  			e.printStackTrace();
  		}
  	}
  
  	public static void setExposureCompensationRemote(final int value)
  	{
  		if (isCameraApiAvailable("setExposureCompensation"))
  		{
  			requestQueue.add(new Thread()
  			{
  				@Override
  				public void run()
  				{
  					if (mRemoteApi == null) {
  						onRequestResult();
  						return;
  					}
  					
  					try
  					{
  						mRemoteApi.setExposureCompensation(value);
  						currentExposureCompensation = value;
  					} catch (IOException e)
  					{
  						e.printStackTrace();
  					} finally
  					{
  						onRequestResult();
  					}
  				}
  			});
  			sendRequest();
  		}
  	}
  
  	public static void setFlashModeRemote(final String value)
  	{
  		if (isCameraApiAvailable("setFlashMode"))
  		{
  			requestQueue.add(new Thread()
  			{
  				@Override
  				public void run()
  				{
  					if (mRemoteApi == null) {
  						onRequestResult();
  						return;
  					}
  					
  					try
  					{
  						mRemoteApi.setFlashMode(value.toLowerCase());
  						currentFlashMode = value;
  					} catch (IOException e)
  					{
  						e.printStackTrace();
  					} finally
  					{
  						onRequestResult();
  					}
  				}
  			});
  			sendRequest();
  		}
  	}
  
  	public static void setIsoSpeedRateRemote(final String value)
  	{
  		if (isCameraApiAvailable("setIsoSpeedRate"))
  		{
  			requestQueue.add(new Thread()
  			{
  				@Override
  				public void run()
  				{
  					if (mRemoteApi == null) {
  						onRequestResult();
  						return;
  					}
  					
  					try
  					{
  						mRemoteApi.setIsoSpeedRate(value.toUpperCase());
  						currentIsoMode = value;
  					} catch (IOException e)
  					{
  						e.printStackTrace();
  					} finally
  					{
  						onRequestResult();
  					}
  				}
  			});
  			sendRequest();
  		}
  	}
  
  	public static void setWhiteBalanceRemote(final String value)
  	{
  		if (isCameraApiAvailable("setWhiteBalance"))
  		{
  			requestQueue.add(new Thread()
  			{
  				@Override
  				public void run()
  				{
  					if (mRemoteApi == null) {
  						onRequestResult();
  						return;
  					}
  					
  					try
  					{
  						mRemoteApi.setWhiteBalance(value);
  						currentWBMode = value;
  					} catch (IOException e)
  					{
  						e.printStackTrace();
  					} finally
  					{
  						onRequestResult();
  					}
  				}
  			});
  			sendRequest();
  		}
  	}
  
  	public static void setPictureSizeRemote(int width, int height)
  	{
  		final JSONObject jsonObject = mPictureSizeNames.get((long) (width * height));
  		if (jsonObject == null)
  		{
  			return;
  		}
  		if (isCameraApiAvailable("setStillSize"))
  		{
  			requestQueue.add(new Thread()
  			{
  				@Override
  				public void run()
  				{
  					if (mRemoteApi == null) {
  						onRequestResult();
  						return;
  					}
  					
  					try
  					{
  						mRemoteApi.setStillSize(jsonObject.getString("aspect"), jsonObject.getString("size"));
  						mRemoteApi.setPostviewImageSize("Original");
  					} catch (IOException e)
  					{
  						e.printStackTrace();
  					} catch (JSONException e)
  					{
  						e.printStackTrace();
  					} finally
  					{
  						onRequestResult();
  					}
  				}
  			});
  			sendRequest();
  		}
  	}
  }
  