/*
HostMediaStreamingRecordingProfile.java
Copyright (c) 2014 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.host.profile;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import org.deviceconnect.android.activity.PermissionUtility;
import org.deviceconnect.android.deviceplugin.host.HostDeviceService;
import org.deviceconnect.android.deviceplugin.host.recorder.HostDevicePhotoRecorder;
import org.deviceconnect.android.deviceplugin.host.recorder.HostDevicePreviewServer;
import org.deviceconnect.android.deviceplugin.host.recorder.HostDeviceRecorder;
import org.deviceconnect.android.deviceplugin.host.recorder.HostDeviceRecorderManager;
import org.deviceconnect.android.deviceplugin.host.recorder.HostDeviceStreamRecorder;
import org.deviceconnect.android.deviceplugin.host.recorder.util.CapabilityUtil;
import org.deviceconnect.android.deviceplugin.host.recorder.video.HostDeviceVideoRecorder;
import org.deviceconnect.android.event.Event;
import org.deviceconnect.android.event.EventError;
import org.deviceconnect.android.event.EventManager;
import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.MediaStreamRecordingProfile;
import org.deviceconnect.android.profile.api.DConnectApi;
import org.deviceconnect.android.profile.api.DeleteApi;
import org.deviceconnect.android.profile.api.GetApi;
import org.deviceconnect.android.profile.api.PostApi;
import org.deviceconnect.android.profile.api.PutApi;
import org.deviceconnect.android.provider.FileManager;
import org.deviceconnect.message.DConnectMessage;
import java.util.LinkedList;
import java.util.List;
/**
* MediaStream Recording Profile.
*
* @author NTT DOCOMO, INC.
*/
@SuppressWarnings("deprecation")
public class HostMediaStreamingRecordingProfile extends MediaStreamRecordingProfile {
private final HostDeviceRecorderManager mRecorderMgr;
private final DConnectApi mGetMediaRecorderApi = new GetApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_MEDIARECORDER;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
// ライト点灯中なら消灯処理を実施.
checkCameraLightState();
List<Bundle> recorders = new LinkedList<>();
for (HostDeviceRecorder recorder : mRecorderMgr.getRecorders()) {
Bundle info = new Bundle();
setRecorderId(info, recorder.getId());
setRecorderName(info, recorder.getName());
setRecorderMIMEType(info, recorder.getMimeType());
switch (recorder.getState()) {
case RECORDING:
setRecorderState(info, RecorderState.RECORDING);
break;
default:
setRecorderState(info, RecorderState.INACTIVE);
break;
}
if (recorder instanceof HostDevicePhotoRecorder || recorder instanceof HostDeviceVideoRecorder) {
HostDeviceRecorder.PictureSize size = recorder.getPictureSize();
setRecorderImageWidth(info, size.getWidth());
setRecorderImageHeight(info, size.getHeight());
}
if (recorder instanceof HostDevicePreviewServer) {
HostDeviceRecorder.PictureSize size = recorder.getPreviewSize();
setRecorderPreviewWidth(info, size.getWidth());
setRecorderPreviewHeight(info, size.getHeight());
setRecorderPreviewMaxFrameRate(info, recorder.getMaxFrameRate());
}
setRecorderConfig(info, "");
recorders.add(info);
}
setRecorders(response, recorders.toArray(new Bundle[recorders.size()]));
setResult(response, DConnectMessage.RESULT_OK);
sendResponse(response);
}
@Override
public void onFail(final String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mGetOptionsApi = new GetApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_OPTIONS;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
String target = getTarget(request);
final HostDeviceRecorder recorder = mRecorderMgr.getRecorder(target);
if (recorder == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return true;
}
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
// ライト点灯中なら消灯処理を実施.
checkCameraLightState();
setResult(response, DConnectMessage.RESULT_OK);
setMIMEType(response, recorder.getSupportedMimeTypes());
if (recorder instanceof HostDevicePhotoRecorder) {
setSupportedImageSizes(response, recorder.getSupportedPictureSizes());
}
if (recorder instanceof HostDevicePreviewServer) {
setSupportedPreviewSizes(response, recorder.getSupportedPreviewSizes());
}
sendResponse(response);
}
@Override
public void onFail(final String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mPutOptionsApi = new PutApi() {
private void setOptions(final Intent request, final Intent response) {
String target = getTarget(request);
String mimeType = getMIMEType(request);
Integer imageWidth = getImageWidth(request);
Integer imageHeight = getImageHeight(request);
Integer previewWidth = getPreviewWidth(request);
Integer previewHeight = getPreviewHeight(request);
Double previewMaxFrameRate = getPreviewMaxFrameRate(request);
HostDeviceRecorder recorder = mRecorderMgr.getRecorder(target);
if (recorder == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return;
}
if (!supportsMimeType(recorder, mimeType)) {
MessageUtils.setInvalidRequestParameterError(response, "MIME-Type " + mimeType + " is unsupported.");
return;
}
if (recorder.getState() != HostDeviceRecorder.RecorderState.INACTTIVE) {
MessageUtils.setInvalidRequestParameterError(response, "settings of active target cannot be changed.");
return;
}
if (imageWidth != null && imageHeight != null) {
if (!recorder.isSupportedPictureSize(imageWidth, imageHeight)) {
MessageUtils.setInvalidRequestParameterError(response, "Unsupported image size: imageWidth = "
+ imageWidth + ", imageHeight = " + imageHeight);
return;
}
HostDeviceRecorder.PictureSize newSize = new HostDeviceRecorder.PictureSize(imageWidth, imageHeight);
recorder.setPictureSize(newSize);
}
if (previewWidth != null && previewHeight != null) {
if (!recorder.isSupportedPreviewSize(previewWidth, previewHeight)) {
MessageUtils.setInvalidRequestParameterError(response, "Unsupported preview size: previewWidth = "
+ previewWidth + ", previewHeight = " + previewHeight);
return;
}
HostDeviceRecorder.PictureSize newSize = new HostDeviceRecorder.PictureSize(previewWidth, previewHeight);
recorder.setPreviewSize(newSize);
}
if (previewMaxFrameRate != null) {
if (!(recorder instanceof HostDevicePreviewServer)) {
MessageUtils.setInvalidRequestParameterError(response, "preview is unsupported.");
return;
}
recorder.setMaxFrameRate(previewMaxFrameRate);
}
setResult(response, DConnectMessage.RESULT_OK);
}
@Override
public String getAttribute() {
return ATTRIBUTE_OPTIONS;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
// ライト点灯中なら消灯処理を実施.
checkCameraLightState();
setOptions(request, response);
sendResponse(response);
}
@Override
public void onFail(final String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mPutOnPhotoApi = new PutApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_ON_PHOTO;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
EventError error = EventManager.INSTANCE.addEvent(request);
if (error == EventError.NONE) {
setResult(response, DConnectMessage.RESULT_OK);
} else {
setResult(response, DConnectMessage.RESULT_ERROR);
}
return true;
}
};
private final DConnectApi mDeleteOnPhotoApi = new DeleteApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_ON_PHOTO;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
EventError error = EventManager.INSTANCE.removeEvent(request);
if (error == EventError.NONE) {
setResult(response, DConnectMessage.RESULT_OK);
} else {
setResult(response, DConnectMessage.RESULT_ERROR);
}
return true;
}
};
private final DConnectApi mPostTakePhotoApi = new PostApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_TAKE_PHOTO;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
final String target = getTarget(request);
final String serviceId = getServiceID(request);
// ライト点灯中なら消灯処理を実施.
checkCameraLightState();
final HostDevicePhotoRecorder recorder = mRecorderMgr.getCameraRecorder(target);
if (recorder == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return true;
}
HostDeviceRecorder r = (HostDeviceRecorder) recorder;
if (r.getState() != HostDeviceRecorder.RecorderState.INACTTIVE) {
MessageUtils.setIllegalDeviceStateError(response, r.getName()
+ " is already running.");
return true;
}
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
recorder.takePhoto(new HostDevicePhotoRecorder.OnPhotoEventListener() {
@Override
public void onTakePhoto(final String uri, final String filePath) {
setResult(response, DConnectMessage.RESULT_OK);
setUri(response, uri);
setPath(response, filePath);
sendResponse(response);
List<Event> evts = EventManager.INSTANCE.getEventList(serviceId,
MediaStreamRecordingProfile.PROFILE_NAME, null,
MediaStreamRecordingProfile.ATTRIBUTE_ON_PHOTO);
Bundle photo = new Bundle();
photo.putString(MediaStreamRecordingProfile.PARAM_URI, uri);
photo.putString(MediaStreamRecordingProfile.PARAM_PATH, filePath);
photo.putString(MediaStreamRecordingProfile.PARAM_MIME_TYPE, "image/png");
for (Event evt : evts) {
Intent intent = EventManager.createEventMessage(evt);
intent.putExtra(MediaStreamRecordingProfile.PARAM_PHOTO, photo);
sendEvent(intent, evt.getAccessToken());
}
}
@Override
public void onFailedTakePhoto() {
MessageUtils.setUnknownError(response, "Failed to take a photo");
sendResponse(response);
}
});
}
@Override
public void onFail(final String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mPutPreviewApi = new PutApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_PREVIEW;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
String target = getTarget(request);
final HostDevicePreviewServer server = mRecorderMgr.getPreviewServer(target);
if (server == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return true;
}
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
// ライト点灯中なら消灯処理を実施.
checkCameraLightState();
server.startWebServer(new HostDevicePreviewServer.OnWebServerStartCallback() {
@Override
public void onStart(@NonNull String uri) {
setResult(response, DConnectMessage.RESULT_OK);
setUri(response, uri);
sendResponse(response);
}
@Override
public void onFail() {
MessageUtils.setIllegalServerStateError(response, "Failed to start web server.");
sendResponse(response);
}
});
}
@Override
public void onFail(final String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mDeletePreviewApi = new DeleteApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_PREVIEW;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
String target = getTarget(request);
final HostDevicePreviewServer server = mRecorderMgr.getPreviewServer(target);
if (server == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return true;
}
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
server.stopWebServer();
setResult(response, DConnectMessage.RESULT_OK);
sendResponse(response);
}
@Override
public void onFail(final String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mPostRecordApi = new PostApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_RECORD;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
String target = getTarget(request);
// ライト点灯中なら消灯処理を実施.
checkCameraLightState();
if (mRecorderMgr.getRecorder(target) == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return true;
}
final HostDeviceStreamRecorder recorder = mRecorderMgr.getStreamRecorder(target);
if (recorder == null) {
MessageUtils.setNotSupportAttributeError(response,
"target does not support stream recording.");
return true;
}
if (recorder.getState() != HostDeviceRecorder.RecorderState.INACTTIVE) {
MessageUtils.setIllegalDeviceStateError(response, recorder.getName()
+ " is already running.");
return true;
}
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
recorder.startRecording(new HostDeviceStreamRecorder.RecordingListener() {
@Override
public void onRecorded(final HostDeviceStreamRecorder recorder, final String fileName) {
FileManager mgr = ((HostDeviceService) getContext()).getFileManager();
setResult(response, DConnectMessage.RESULT_OK);
setPath(response, "/" + fileName);
setUri(response, mgr.getContentUri() + "/" + fileName);
sendResponse(response);
}
@Override
public void onFailed(final HostDeviceStreamRecorder recorder, final String errorMesage) {
MessageUtils.setIllegalServerStateError(response, errorMesage);
sendResponse(response);
}
}); }
@Override
public void onFail(@NonNull String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mPutStopApi = new PutApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_STOP;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
String target = getTarget(request);
if (mRecorderMgr.getRecorder(target) == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return true;
}
final HostDeviceStreamRecorder recorder = mRecorderMgr.getStreamRecorder(target);
if (recorder == null) {
MessageUtils.setNotSupportAttributeError(response,
"target does not support stream recording.");
return true;
}
if (recorder.getState() == HostDeviceRecorder.RecorderState.INACTTIVE) {
MessageUtils.setIllegalDeviceStateError(response, "recorder is stopped already.");
return true;
}
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
recorder.stopRecording();
setResult(response, DConnectMessage.RESULT_OK);
sendResponse(response);
}
@Override
public void onFail(@NonNull String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mPutPauseApi = new PutApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_PAUSE;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
String target = getTarget(request);
if (mRecorderMgr.getRecorder(target) == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return true;
}
final HostDeviceStreamRecorder recorder = mRecorderMgr.getStreamRecorder(target);
if (recorder == null) {
MessageUtils.setNotSupportAttributeError(response,
"target does not support stream recording.");
return true;
}
if (!recorder.canPauseRecording()) {
MessageUtils.setNotSupportAttributeError(response);
return true;
}
if (recorder.getState() != HostDeviceRecorder.RecorderState.RECORDING) {
MessageUtils.setIllegalDeviceStateError(response);
return true;
}
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
recorder.pauseRecording();
setResult(response, DConnectMessage.RESULT_OK);
sendResponse(response);
}
@Override
public void onFail(@NonNull String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
private final DConnectApi mPutResumeApi = new PutApi() {
@Override
public String getAttribute() {
return ATTRIBUTE_RESUME;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
String target = getTarget(request);
if (mRecorderMgr.getRecorder(target) == null) {
MessageUtils.setInvalidRequestParameterError(response, "target is invalid.");
return true;
}
final HostDeviceStreamRecorder recorder = mRecorderMgr.getStreamRecorder(target);
if (recorder == null) {
MessageUtils.setNotSupportAttributeError(response,
"target does not support stream recording.");
return true;
}
if (!recorder.canPauseRecording()) {
MessageUtils.setNotSupportAttributeError(response);
return true;
}
init(new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
recorder.resumeRecording();
setResult(response, DConnectMessage.RESULT_OK);
sendResponse(response);
}
@Override
public void onFail(@NonNull String deniedPermission) {
MessageUtils.setUnknownError(response, "Permission for camera is not granted.");
sendResponse(response);
}
});
return false;
}
};
public HostMediaStreamingRecordingProfile(final HostDeviceRecorderManager mgr) {
mRecorderMgr = mgr;
addApi(mGetMediaRecorderApi);
addApi(mGetOptionsApi);
addApi(mPutOptionsApi);
addApi(mPutOnPhotoApi);
addApi(mDeleteOnPhotoApi);
addApi(mPostTakePhotoApi);
addApi(mPutPreviewApi);
addApi(mDeletePreviewApi);
addApi(mPostRecordApi);
addApi(mPutStopApi);
addApi(mPutPauseApi);
addApi(mPutResumeApi);
}
private void init(final PermissionUtility.PermissionRequestCallback callback) {
CapabilityUtil.requestPermissions(getContext(), new PermissionUtility.PermissionRequestCallback() {
@Override
public void onSuccess() {
mRecorderMgr.initialize();
callback.onSuccess();
}
@Override
public void onFail(final String deniedPermission) {
callback.onFail(deniedPermission);
}
});
}
private static void setSupportedImageSizes(final Intent response,
final List<HostDeviceRecorder.PictureSize> sizes) {
Bundle[] array = new Bundle[sizes.size()];
int i = 0;
for (HostDeviceRecorder.PictureSize size : sizes) {
Bundle info = new Bundle();
setWidth(info, size.getWidth());
setHeight(info, size.getHeight());
array[i++] = info;
}
setImageSizes(response, array);
}
private static void setSupportedPreviewSizes(final Intent response,
final List<HostDeviceRecorder.PictureSize> sizes) {
Bundle[] array = new Bundle[sizes.size()];
int i = 0;
for (HostDeviceRecorder.PictureSize size : sizes) {
Bundle info = new Bundle();
setWidth(info, size.getWidth());
setHeight(info, size.getHeight());
array[i++] = info;
}
setPreviewSizes(response, array);
}
private boolean supportsMimeType(final HostDeviceRecorder recorder, final String mimeType) {
for (String supportedMimeType : recorder.getSupportedMimeTypes()) {
if (supportedMimeType.equals(mimeType)) {
return true;
}
}
return false;
}
/**
* MIMEタイプを設定する.
*
* @param response レスポンス
* @param mimeType MIMEタイプ
*/
public static void setMIMEType(final Intent response, final List<String> mimeType) {
response.putExtra(PARAM_MIME_TYPE, mimeType.toArray(new String[mimeType.size()]));
}
/**
* カメラライト点灯状態確認.
*/
public void checkCameraLightState() {
// ライト点灯中なら消灯処理を実施.
if (mRecorderMgr.getCameraRecorder(null).isUseFlashLight()) {
mRecorderMgr.getCameraRecorder(null).turnOffFlashLight();
}
}
}