[Change]
修改速度取值,更新行车记录仪代码。 DEMO必须依附于预览获取YUV数据,不符合需求,这里先接入直播,后续需要按照源码抽离直接通过USB获取YUV的服务 Signed-off-by: donghongyu <donghongyu@zhidaoauto.com>
This commit is contained in:
@@ -238,6 +238,7 @@ ext {
|
||||
mogo_core_function_v2x : "com.mogo.eagle.core.function.impl:v2x:${MOGO_CORE_FUNCTION_V2X_VERSION}",
|
||||
mogo_core_function_api : "com.mogo.eagle.core.function:api:${MOGO_CORE_FUNCTION_API_VERSION}",
|
||||
mogo_core_function_call : "com.mogo.eagle.core.function:call:${MOGO_CORE_FUNCTION_CALL_VERSION}",
|
||||
mogo_core_function_carcorder : "com.mogo.eagle.core.function:carcorder:${MOGO_CORE_FUNCTION_CARCORDER_VERSION}",
|
||||
mogo_core_data : "com.mogo.eagle.core:data:${MOGO_CORE_DATA_VERSION}",
|
||||
mogo_core_res : "com.mogo.eagle.core:res:${MOGO_CORE_RES_VERSION}",
|
||||
mogo_core_utils : "com.mogo.eagle.core:utils:${MOGO_CORE_UTILS_VERSION}",
|
||||
|
||||
@@ -137,7 +137,7 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
|
||||
data.putOpt("lon", stateInfo.getValues().getLon());
|
||||
data.putOpt("lat", stateInfo.getValues().getLat());
|
||||
data.putOpt("alt", stateInfo.getValues().getAlt());
|
||||
data.putOpt("speed", stateInfo.getValues().getGnss_speed());
|
||||
data.putOpt("speed", stateInfo.getValues().getVehicle_speed());
|
||||
data.putOpt("heading", stateInfo.getValues().getHeading());
|
||||
data.putOpt("acceleration", stateInfo.getValues().getAcceleration());
|
||||
data.putOpt("yawRate", stateInfo.getValues().getYaw_rate());
|
||||
|
||||
@@ -52,6 +52,8 @@ dependencies {
|
||||
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
|
||||
implementation rootProject.ext.dependencies.mogoserviceapi
|
||||
implementation rootProject.ext.dependencies.modulecommon
|
||||
|
||||
implementation project(':libraries:map-usbcamera')
|
||||
|
||||
implementation rootProject.ext.dependencies.mogo_core_utils
|
||||
implementation rootProject.ext.dependencies.mogo_core_function_api
|
||||
@@ -61,6 +63,8 @@ dependencies {
|
||||
implementation project(':services:mogo-service-api')
|
||||
implementation project(':modules:mogo-module-common')
|
||||
|
||||
implementation project(':libraries:map-usbcamera')
|
||||
|
||||
implementation project(':core:mogo-core-utils')
|
||||
implementation project(':core:mogo-core-function-api')
|
||||
implementation project(':core:mogo-core-function-call')
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
GROUP=com.mogo.eagle.core.function.impl
|
||||
POM_ARTIFACT_ID=devatools
|
||||
POM_ARTIFACT_ID=carcorder
|
||||
VERSION_CODE=1
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.eagle.core.function.carcorder">
|
||||
|
||||
<application>
|
||||
<service
|
||||
android:name=".service.CarcorderService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:process=":uvcservice">
|
||||
<intent-filter>
|
||||
<action android:name="com.mogo.launcher.action.CARCORDER_SERVICE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.mogo.eagle.core.function.carcorder.service
|
||||
|
||||
import android.content.Intent
|
||||
import android.hardware.usb.UsbDevice
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
|
||||
import com.mogo.usbcamera.UVCCameraHelper
|
||||
import com.serenegiant.usb.IFrameCallback
|
||||
import com.serenegiant.usb.USBMonitor
|
||||
import com.serenegiant.usb.USBMonitor.OnDeviceConnectListener
|
||||
import com.serenegiant.usb.USBMonitor.UsbControlBlock
|
||||
import com.serenegiant.usb.UVCCamera
|
||||
import com.serenegiant.usb.common.BaseService
|
||||
import com.serenegiant.usb.encoder.MediaVideoBufferEncoder
|
||||
|
||||
/**
|
||||
* 行车记录仪服务
|
||||
* @author donghongyu
|
||||
*/
|
||||
class CarcorderService : BaseService() {
|
||||
private val DEBUG = true
|
||||
val TAG = CarcorderService::class.java.name
|
||||
|
||||
// 挂载的USB设备集合
|
||||
private var mDeviceList: List<UsbDevice>? = null
|
||||
|
||||
// USB 设备连接工具
|
||||
private var mUSBMonitor: USBMonitor? = null
|
||||
|
||||
// 用于接入UVC摄像机
|
||||
private var mUVCCamera: UVCCamera? = null
|
||||
|
||||
// 相机控制
|
||||
private var mCtrlBlock: UsbControlBlock? = null
|
||||
|
||||
/**
|
||||
* 配置相机基本按书
|
||||
*/
|
||||
private val previewWidth = 640
|
||||
private val previewHeight = 480
|
||||
|
||||
// Default using MJPEG
|
||||
// if your device is connected,but have no images
|
||||
// please try to change it to FRAME_FORMAT_YUYV
|
||||
val FRAME_FORMAT_MJPEG: Int = UVCCamera.FRAME_FORMAT_MJPEG
|
||||
val MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS
|
||||
val MODE_CONTRAST = UVCCamera.PU_CONTRAST
|
||||
private val mFrameFormat = UVCCameraHelper.FRAME_FORMAT_MJPEG
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
if (DEBUG) {
|
||||
Logger.d(TAG, "onCreate……")
|
||||
}
|
||||
if (mUSBMonitor == null) {
|
||||
mUSBMonitor = USBMonitor(applicationContext, mOnDeviceConnectListener)
|
||||
mUSBMonitor!!.register()
|
||||
mDeviceList = mUSBMonitor!!.deviceList
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (DEBUG) Log.d(TAG, "onDestroy:")
|
||||
if (mUSBMonitor != null) {
|
||||
mUSBMonitor!!.unregister()
|
||||
mUSBMonitor = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onRebind(intent: Intent) {
|
||||
if (DEBUG) Log.d(TAG, "onRebind:$intent")
|
||||
}
|
||||
|
||||
|
||||
override fun onUnbind(intent: Intent): Boolean {
|
||||
if (DEBUG) Log.d(TAG, "onUnbind:$intent")
|
||||
|
||||
if (DEBUG) Log.d(TAG, "onUnbind:finished")
|
||||
return true
|
||||
}
|
||||
|
||||
//**********************************************************************************************************************************
|
||||
private val mSync: Any = Any()
|
||||
|
||||
private val mVideoEncoder: MediaVideoBufferEncoder? = null
|
||||
|
||||
|
||||
/**
|
||||
* USB 设备连接监听
|
||||
*/
|
||||
private val mOnDeviceConnectListener: OnDeviceConnectListener = object : OnDeviceConnectListener {
|
||||
override fun onAttach(device: UsbDevice) {
|
||||
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onAttach:${device.deviceName}---mDeviceList:${mDeviceList?.size}")
|
||||
mUSBMonitor!!.requestPermission(device)
|
||||
}
|
||||
|
||||
override fun onConnect(device: UsbDevice, ctrlBlock: UsbControlBlock, createNew: Boolean) {
|
||||
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onConnect:${device.deviceName}")
|
||||
openCamera(device, ctrlBlock, createNew)
|
||||
}
|
||||
|
||||
override fun onDisconnect(device: UsbDevice, ctrlBlock: UsbControlBlock) {
|
||||
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onDisconnect:${device.deviceName}")
|
||||
}
|
||||
|
||||
override fun onDettach(device: UsbDevice) {
|
||||
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onDettach:${device.deviceName}")
|
||||
}
|
||||
|
||||
override fun onCancel(device: UsbDevice) {
|
||||
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onCancel:${device.deviceName}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接相机
|
||||
*/
|
||||
private fun openCamera(device: UsbDevice, ctrlBlock: UsbControlBlock, createNew: Boolean) {
|
||||
if (mUVCCamera == null) {
|
||||
mUVCCamera = UVCCamera()
|
||||
mUVCCamera!!.open(ctrlBlock)
|
||||
mUVCCamera!!.setStatusCallback { statusClass, event, selector, statusAttribute, data ->
|
||||
if (DEBUG) Log.d(TAG, "IStatusCallback#onStatus(statusClass=${statusClass},event=${event},selector=${selector},statusAttribute=${statusAttribute},data=${data})")
|
||||
}
|
||||
|
||||
try {
|
||||
mUVCCamera!!.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.FRAME_FORMAT_MJPEG)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
try {
|
||||
mUVCCamera!!.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.DEFAULT_PREVIEW_MODE)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
mUVCCamera!!.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
mUVCCamera!!.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP)
|
||||
mUVCCamera!!.startPreview()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频帧回掉
|
||||
*/
|
||||
private val mIFrameCallback = IFrameCallback { frame ->
|
||||
if (DEBUG) Log.d(TAG, "IFrameCallback#onFrame:${frame}")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -160,6 +160,10 @@ class CarcorderPreviewView private constructor(
|
||||
Log.d(TAG, "onDisConnectDev")
|
||||
showShortMsg("相机断开连接")
|
||||
}
|
||||
|
||||
override fun onCancelDev(device: UsbDevice?) {
|
||||
Log.d(TAG, "onCancelDev:" + device?.deviceName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ MOGO_CORE_FUNCTION_V2X_VERSION=0.0.58.10
|
||||
MOGO_CORE_DATA_VERSION=0.0.58.10
|
||||
MOGO_CORE_FUNCTION_API_VERSION=0.0.58.10
|
||||
MOGO_CORE_FUNCTION_CALL_VERSION=0.0.58.10
|
||||
MOGO_CORE_FUNCTION_CARCORDER_VERSION=0.0.58.10
|
||||
MOGO_CORE_FUNCTION_DEVATOOLS_VERSION=0.0.58.10
|
||||
MOGO_CORE_RES_VERSION=0.0.58.10
|
||||
MOGO_CORE_UTILS_VERSION=0.0.58.10
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.mogo.usbcamera;
|
||||
|
||||
import com.serenegiant.usb.USBMonitor;
|
||||
|
||||
/**
|
||||
* @author donghongyu
|
||||
* USB摄像头方案,获取权限、获取YUV数据
|
||||
*/
|
||||
public class USBCameraHelper {
|
||||
private static USBCameraHelper mCameraHelper;
|
||||
/**
|
||||
* USB Manager
|
||||
*/
|
||||
private USBMonitor mUSBMonitor;
|
||||
|
||||
private USBCameraHelper() {
|
||||
}
|
||||
|
||||
public static USBCameraHelper getInstance() {
|
||||
if (mCameraHelper == null) {
|
||||
mCameraHelper = new USBCameraHelper();
|
||||
}
|
||||
return mCameraHelper;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void registerUSB() {
|
||||
if (mUSBMonitor != null) {
|
||||
mUSBMonitor.register();
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterUSB() {
|
||||
if (mUSBMonitor != null) {
|
||||
mUSBMonitor.unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,9 @@ public class UVCCameraHelper {
|
||||
void onConnectDev(UsbDevice device, boolean isConnected);
|
||||
|
||||
void onDisConnectDev(UsbDevice device);
|
||||
|
||||
void onCancelDev(UsbDevice device);
|
||||
|
||||
}
|
||||
|
||||
public void initUSBMonitor(Activity activity, CameraViewInterface cameraView, final OnMyDevConnectListener listener) {
|
||||
@@ -106,18 +109,15 @@ public class UVCCameraHelper {
|
||||
public void onConnect(final UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) {
|
||||
mCtrlBlock = ctrlBlock;
|
||||
openCamera(ctrlBlock);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// wait for camera created
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// start previewing
|
||||
startPreview(mCamView);
|
||||
new Thread(() -> {
|
||||
// wait for camera created
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// start previewing
|
||||
startPreview(mCamView);
|
||||
}).start();
|
||||
if (listener != null) {
|
||||
listener.onConnectDev(device, true);
|
||||
@@ -135,6 +135,9 @@ public class UVCCameraHelper {
|
||||
|
||||
@Override
|
||||
public void onCancel(UsbDevice device) {
|
||||
if (listener != null) {
|
||||
listener.onCancelDev(device);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -249,7 +252,6 @@ public class UVCCameraHelper {
|
||||
|
||||
public void capturePicture(String savePath, AbstractUVCCameraHandler.OnCaptureListener listener) {
|
||||
if (mCameraHandler != null && mCameraHandler.isOpened()) {
|
||||
|
||||
File file = new File(savePath);
|
||||
if (!Objects.requireNonNull(file.getParentFile()).exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.serenegiant.dialog;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
public abstract class DialogFragmentEx extends DialogFragment {
|
||||
protected static final String ARGS_KEY_REQUEST_CODE = "requestCode";
|
||||
protected static final String ARGS_KEY_ID_TITLE = "title";
|
||||
protected static final String ARGS_KEY_ID_MESSAGE = "message";
|
||||
protected static final String ARGS_KEY_TAG = "tag";
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
outState.putAll(args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.serenegiant.dialog;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.Fragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.serenegiant.utils.BuildCheck;
|
||||
|
||||
/**
|
||||
* パーミッション要求前に説明用のダイアログを表示するためのDialogFragment
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Deprecated
|
||||
public class MessageDialogFragment extends DialogFragment {
|
||||
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
|
||||
private static final String TAG = MessageDialogFragment.class.getSimpleName();
|
||||
|
||||
public static interface MessageDialogListener {
|
||||
public void onMessageDialogResult(final MessageDialogFragment dialog, final int requestCode, final String[] permissions, final boolean result);
|
||||
}
|
||||
|
||||
public static MessageDialogFragment showDialog(final Activity parent, final int requestCode, final int id_title, final int id_message, final String[] permissions) {
|
||||
final MessageDialogFragment dialog = newInstance(requestCode, id_title, id_message, permissions);
|
||||
dialog.show(parent.getFragmentManager(), TAG);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public static MessageDialogFragment showDialog(final Fragment parent, final int requestCode, final int id_title, final int id_message, final String[] permissions) {
|
||||
final MessageDialogFragment dialog = newInstance(requestCode, id_title, id_message, permissions);
|
||||
dialog.setTargetFragment(parent, parent.getId());
|
||||
dialog.show(parent.getFragmentManager(), TAG);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public static MessageDialogFragment newInstance(final int requestCode, final int id_title, final int id_message, final String[] permissions) {
|
||||
final MessageDialogFragment fragment = new MessageDialogFragment();
|
||||
final Bundle args = new Bundle();
|
||||
// ここでパラメータをセットする
|
||||
args.putInt("requestCode", requestCode);
|
||||
args.putInt("title", id_title);
|
||||
args.putInt("message", id_message);
|
||||
args.putStringArray("permissions", permissions != null ? permissions : new String[]{});
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private MessageDialogListener mDialogListener;
|
||||
|
||||
public MessageDialogFragment() {
|
||||
super();
|
||||
// デフォルトコンストラクタが必要
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void onAttach(final Activity activity) {
|
||||
super.onAttach(activity);
|
||||
// コールバックインターフェースを取得
|
||||
if (activity instanceof MessageDialogListener) {
|
||||
mDialogListener = (MessageDialogListener)activity;
|
||||
}
|
||||
if (mDialogListener == null) {
|
||||
final Fragment fragment = getTargetFragment();
|
||||
if (fragment instanceof MessageDialogListener) {
|
||||
mDialogListener = (MessageDialogListener)fragment;
|
||||
}
|
||||
}
|
||||
if (mDialogListener == null) {
|
||||
if (BuildCheck.isAndroid4_2()) {
|
||||
final Fragment target = getParentFragment();
|
||||
if (target instanceof MessageDialogListener) {
|
||||
mDialogListener = (MessageDialogListener)target;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mDialogListener == null) {
|
||||
// Log.w(TAG, "caller activity/fragment must implement PermissionDetailDialogFragmentListener");
|
||||
throw new ClassCastException(activity.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onCreate(final Bundle savedInstanceState) {
|
||||
// super.onCreate(savedInstanceState);
|
||||
// final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
|
||||
// }
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(final Bundle savedInstanceState) {
|
||||
final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
|
||||
final int requestCode = getArguments().getInt("requestCode");
|
||||
final int id_title = getArguments().getInt("title");
|
||||
final int id_message = getArguments().getInt("message");
|
||||
final String[] permissions = args.getStringArray("permissions");
|
||||
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTitle(id_title)
|
||||
.setMessage(id_message)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int whichButton) {
|
||||
// 本当はここでパーミッション要求をしたいだけどこのダイアログがdismissしてしまって結果を受け取れないので
|
||||
// 呼び出し側へ返してそこでパーミッション要求する。なのでこのダイアログは単にメッセージを表示するだけ
|
||||
try {
|
||||
mDialogListener.onMessageDialogResult(MessageDialogFragment.this, requestCode, permissions, true);
|
||||
} catch (final Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, int whichButton) {
|
||||
try {
|
||||
mDialogListener.onMessageDialogResult(MessageDialogFragment.this, requestCode, permissions, false);
|
||||
} catch (final Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.create();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.serenegiant.dialog;
|
||||
/*
|
||||
* libcommon
|
||||
* utility/helper classes for myself
|
||||
*
|
||||
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.serenegiant.utils.BuildCheck;
|
||||
|
||||
public class MessageDialogFragmentV4 extends DialogFragmentEx {
|
||||
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
|
||||
private static final String TAG = MessageDialogFragmentV4.class.getSimpleName();
|
||||
|
||||
private static final String ARGS_KEY_PERMISSIONS = "permissions";
|
||||
|
||||
/**
|
||||
* ダイアログの表示結果を受け取るためのコールバックリスナー
|
||||
*/
|
||||
public static interface MessageDialogListener {
|
||||
public void onMessageDialogResult(
|
||||
@NonNull final MessageDialogFragmentV4 dialog, final int requestCode,
|
||||
@NonNull final String[] permissions, final boolean result);
|
||||
}
|
||||
|
||||
/**
|
||||
* ダイアログ表示のためのヘルパーメソッド
|
||||
* @param parent
|
||||
* @param requestCode
|
||||
* @param id_title
|
||||
* @param id_message
|
||||
* @param permissions
|
||||
* @return
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public static MessageDialogFragmentV4 showDialog(
|
||||
@NonNull final FragmentActivity parent, final int requestCode,
|
||||
@StringRes final int id_title, @StringRes final int id_message,
|
||||
@NonNull final String[] permissions) throws IllegalStateException {
|
||||
|
||||
final MessageDialogFragmentV4 dialog
|
||||
= newInstance(requestCode, id_title, id_message, permissions);
|
||||
dialog.show(parent.getSupportFragmentManager(), TAG);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* ダイアログ表示のためのヘルパーメソッド
|
||||
* @param parent
|
||||
* @param requestCode
|
||||
* @param id_title
|
||||
* @param id_message
|
||||
* @param permissions
|
||||
* @return
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public static MessageDialogFragmentV4 showDialog(
|
||||
@NonNull final Fragment parent, final int requestCode,
|
||||
@StringRes final int id_title, @StringRes final int id_message,
|
||||
@NonNull final String[] permissions) throws IllegalStateException {
|
||||
|
||||
final MessageDialogFragmentV4 dialog
|
||||
= newInstance(requestCode, id_title, id_message, permissions);
|
||||
dialog.setTargetFragment(parent, parent.getId());
|
||||
dialog.show(parent.requireFragmentManager(), TAG);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* ダイアログ生成のためのヘルパーメソッド
|
||||
* ダイアログ自体を直接生成せずにこのメソッドを呼び出すこと
|
||||
* @param requestCode
|
||||
* @param id_title
|
||||
* @param id_message
|
||||
* @param permissions
|
||||
* @return
|
||||
*/
|
||||
public static MessageDialogFragmentV4 newInstance(
|
||||
final int requestCode,
|
||||
@StringRes final int id_title, @StringRes final int id_message,
|
||||
@NonNull final String[] permissions) {
|
||||
|
||||
final MessageDialogFragmentV4 fragment = new MessageDialogFragmentV4();
|
||||
final Bundle args = new Bundle();
|
||||
// ここでパラメータをセットする
|
||||
args.putInt(ARGS_KEY_REQUEST_CODE, requestCode);
|
||||
args.putInt(ARGS_KEY_ID_TITLE, id_title);
|
||||
args.putInt(ARGS_KEY_ID_MESSAGE, id_message);
|
||||
args.putStringArray(ARGS_KEY_PERMISSIONS, permissions);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private MessageDialogListener mDialogListener;
|
||||
|
||||
/**
|
||||
* コンストラクタ, 直接生成せずに#newInstanceを使うこと
|
||||
*/
|
||||
public MessageDialogFragmentV4() {
|
||||
super();
|
||||
// デフォルトコンストラクタが必要
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
// コールバックインターフェースを取得
|
||||
if (context instanceof MessageDialogListener) {
|
||||
mDialogListener = (MessageDialogListener)context;
|
||||
}
|
||||
if (mDialogListener == null) {
|
||||
final Fragment fragment = getTargetFragment();
|
||||
if (fragment instanceof MessageDialogListener) {
|
||||
mDialogListener = (MessageDialogListener)fragment;
|
||||
}
|
||||
}
|
||||
if (mDialogListener == null) {
|
||||
if (BuildCheck.isAndroid4_2()) {
|
||||
final Fragment target = getParentFragment();
|
||||
if (target instanceof MessageDialogListener) {
|
||||
mDialogListener = (MessageDialogListener)target;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mDialogListener == null) {
|
||||
// Log.w(TAG, "caller activity/fragment must implement PermissionDetailDialogFragmentListener");
|
||||
throw new ClassCastException(context.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onCreate(final Bundle savedInstanceState) {
|
||||
// super.onCreate(savedInstanceState);
|
||||
// final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
|
||||
// }
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(final Bundle savedInstanceState) {
|
||||
final Bundle args = savedInstanceState != null ? savedInstanceState : requireArguments();
|
||||
final int id_title = args.getInt(ARGS_KEY_ID_TITLE);
|
||||
final int id_message = args.getInt(ARGS_KEY_ID_MESSAGE);
|
||||
|
||||
final Activity activity = requireActivity();
|
||||
return new AlertDialog.Builder(activity)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTitle(id_title)
|
||||
.setMessage(id_message)
|
||||
.setPositiveButton(android.R.string.ok, mOnClickListener)
|
||||
.setNegativeButton(android.R.string.cancel, mOnClickListener)
|
||||
.create();
|
||||
}
|
||||
|
||||
private final DialogInterface.OnClickListener mOnClickListener
|
||||
= new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
// 本当はここでパーミッション要求をしたいだけどこのダイアログがdismissしてしまって結果を受け取れないので
|
||||
// 呼び出し側へ返してそこでパーミッション要求する。なのでこのダイアログは単にメッセージを表示するだけ
|
||||
callOnMessageDialogResult(which == DialogInterface.BUTTON_POSITIVE);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCancel(final DialogInterface dialog) {
|
||||
super.onCancel(dialog);
|
||||
callOnMessageDialogResult(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* コールバックリスナー呼び出しのためのヘルパーメソッド
|
||||
* @param result
|
||||
*/
|
||||
private void callOnMessageDialogResult(final boolean result)
|
||||
throws IllegalStateException {
|
||||
|
||||
final Bundle args = requireArguments();
|
||||
final int requestCode = args.getInt(ARGS_KEY_REQUEST_CODE);
|
||||
final String[] permissions = args.getStringArray(ARGS_KEY_PERMISSIONS);
|
||||
try {
|
||||
mDialogListener.onMessageDialogResult(
|
||||
MessageDialogFragmentV4.this,
|
||||
requestCode, permissions, result);
|
||||
} catch (final Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -24,199 +47,192 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CameraDialog extends DialogFragment {
|
||||
private static final String TAG = CameraDialog.class.getSimpleName();
|
||||
private static final String TAG = CameraDialog.class.getSimpleName();
|
||||
|
||||
public interface CameraDialogParent {
|
||||
public USBMonitor getUSBMonitor();
|
||||
public interface CameraDialogParent {
|
||||
public USBMonitor getUSBMonitor();
|
||||
public void onDialogResult(boolean canceled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method
|
||||
* @param parent FragmentActivity
|
||||
* @return
|
||||
*/
|
||||
public static CameraDialog showDialog(final Activity parent/* add parameters here if you need */) {
|
||||
CameraDialog dialog = newInstance(/* add parameters here if you need */);
|
||||
try {
|
||||
dialog.show(parent.getFragmentManager(), TAG);
|
||||
} catch (final IllegalStateException e) {
|
||||
dialog = null;
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public void onDialogResult(boolean canceled);
|
||||
}
|
||||
public static CameraDialog newInstance(/* add parameters here if you need */) {
|
||||
final CameraDialog dialog = new CameraDialog();
|
||||
final Bundle args = new Bundle();
|
||||
// add parameters here if you need
|
||||
dialog.setArguments(args);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method
|
||||
*
|
||||
* @param parent FragmentActivity
|
||||
* @return
|
||||
*/
|
||||
public static CameraDialog showDialog(final Activity parent/* add parameters here if you need */) {
|
||||
CameraDialog dialog = newInstance(/* add parameters here if you need */);
|
||||
protected USBMonitor mUSBMonitor;
|
||||
private Spinner mSpinner;
|
||||
private DeviceListAdapter mDeviceListAdapter;
|
||||
|
||||
public CameraDialog(/* no arguments */) {
|
||||
// Fragment need default constructor
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onAttach(final Activity activity) {
|
||||
super.onAttach(activity);
|
||||
if (mUSBMonitor == null)
|
||||
try {
|
||||
dialog.show(parent.getFragmentManager(), TAG);
|
||||
} catch (final IllegalStateException e) {
|
||||
dialog = null;
|
||||
mUSBMonitor = ((CameraDialogParent)activity).getUSBMonitor();
|
||||
} catch (final ClassCastException e) {
|
||||
} catch (final NullPointerException e) {
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
if (mUSBMonitor == null) {
|
||||
throw new ClassCastException(activity.toString() + " must implement CameraDialogParent#getUSBController");
|
||||
}
|
||||
}
|
||||
|
||||
public static CameraDialog newInstance(/* add parameters here if you need */) {
|
||||
final CameraDialog dialog = new CameraDialog();
|
||||
final Bundle args = new Bundle();
|
||||
// add parameters here if you need
|
||||
dialog.setArguments(args);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
protected USBMonitor mUSBMonitor;
|
||||
private Spinner mSpinner;
|
||||
private DeviceListAdapter mDeviceListAdapter;
|
||||
|
||||
public CameraDialog(/* no arguments */) {
|
||||
// Fragment need default constructor
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onAttach(final Activity activity) {
|
||||
super.onAttach(activity);
|
||||
if (mUSBMonitor == null) {
|
||||
try {
|
||||
mUSBMonitor = ((CameraDialogParent) activity).getUSBMonitor();
|
||||
} catch (final NullPointerException | ClassCastException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (mUSBMonitor == null) {
|
||||
throw new ClassCastException(activity.toString() + " must implement CameraDialogParent#getUSBController");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (savedInstanceState == null) {
|
||||
savedInstanceState = getArguments();
|
||||
}
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
if (savedInstanceState == null)
|
||||
savedInstanceState = getArguments();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(final Bundle saveInstanceState) {
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
saveInstanceState.putAll(args);
|
||||
}
|
||||
super.onSaveInstanceState(saveInstanceState);
|
||||
}
|
||||
@Override
|
||||
public void onSaveInstanceState(final Bundle saveInstanceState) {
|
||||
final Bundle args = getArguments();
|
||||
if (args != null)
|
||||
saveInstanceState.putAll(args);
|
||||
super.onSaveInstanceState(saveInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public Dialog onCreateDialog(final Bundle savedInstanceState) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setView(initView());
|
||||
builder.setTitle(R.string.select);
|
||||
builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener);
|
||||
builder.setNegativeButton(android.R.string.cancel, mOnDialogClickListener);
|
||||
builder.setNeutralButton(R.string.refresh, null);
|
||||
final Dialog dialog = builder.create();
|
||||
dialog.setCancelable(true);
|
||||
dialog.setCanceledOnTouchOutside(true);
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setView(initView());
|
||||
builder.setTitle(R.string.select);
|
||||
builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener);
|
||||
builder.setNegativeButton(android.R.string.cancel , mOnDialogClickListener);
|
||||
builder.setNeutralButton(R.string.refresh, null);
|
||||
final Dialog dialog = builder.create();
|
||||
dialog.setCancelable(true);
|
||||
dialog.setCanceledOnTouchOutside(true);
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create view that this fragment shows
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private final View initView() {
|
||||
final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null);
|
||||
mSpinner = (Spinner) rootView.findViewById(R.id.spinner1);
|
||||
final View empty = rootView.findViewById(android.R.id.empty);
|
||||
mSpinner.setEmptyView(empty);
|
||||
return rootView;
|
||||
}
|
||||
/**
|
||||
* create view that this fragment shows
|
||||
* @return
|
||||
*/
|
||||
private final View initView() {
|
||||
final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null);
|
||||
mSpinner = (Spinner)rootView.findViewById(R.id.spinner1);
|
||||
final View empty = rootView.findViewById(android.R.id.empty);
|
||||
mSpinner.setEmptyView(empty);
|
||||
return rootView;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateDevices();
|
||||
final Button button = (Button) getDialog().findViewById(android.R.id.button3);
|
||||
if (button != null) {
|
||||
button.setOnClickListener(mOnClickListener);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateDevices();
|
||||
final Button button = (Button)getDialog().findViewById(android.R.id.button3);
|
||||
if (button != null) {
|
||||
button.setOnClickListener(mOnClickListener);
|
||||
}
|
||||
}
|
||||
|
||||
private final OnClickListener mOnClickListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
switch (v.getId()) {
|
||||
case android.R.id.button3:
|
||||
updateDevices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
private final OnClickListener mOnClickListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
switch (v.getId()) {
|
||||
case android.R.id.button3:
|
||||
updateDevices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
final Object item = mSpinner.getSelectedItem();
|
||||
if (item instanceof UsbDevice) {
|
||||
mUSBMonitor.requestPermission((UsbDevice) item);
|
||||
((CameraDialogParent) getActivity()).onDialogResult(false);
|
||||
}
|
||||
break;
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
((CameraDialogParent) getActivity()).onDialogResult(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
final Object item = mSpinner.getSelectedItem();
|
||||
if (item instanceof UsbDevice) {
|
||||
mUSBMonitor.requestPermission((UsbDevice)item);
|
||||
((CameraDialogParent)getActivity()).onDialogResult(false);
|
||||
}
|
||||
break;
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
((CameraDialogParent)getActivity()).onDialogResult(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCancel(final DialogInterface dialog) {
|
||||
((CameraDialogParent) getActivity()).onDialogResult(true);
|
||||
super.onCancel(dialog);
|
||||
}
|
||||
@Override
|
||||
public void onCancel(final DialogInterface dialog) {
|
||||
((CameraDialogParent)getActivity()).onDialogResult(true);
|
||||
super.onCancel(dialog);
|
||||
}
|
||||
|
||||
public void updateDevices() {
|
||||
public void updateDevices() {
|
||||
// mUSBMonitor.dumpDevices();
|
||||
final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
|
||||
mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
|
||||
mSpinner.setAdapter(mDeviceListAdapter);
|
||||
}
|
||||
final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
|
||||
mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
|
||||
mSpinner.setAdapter(mDeviceListAdapter);
|
||||
}
|
||||
|
||||
private static final class DeviceListAdapter extends BaseAdapter {
|
||||
private static final class DeviceListAdapter extends BaseAdapter {
|
||||
|
||||
private final LayoutInflater mInflater;
|
||||
private final List<UsbDevice> mList;
|
||||
private final LayoutInflater mInflater;
|
||||
private final List<UsbDevice> mList;
|
||||
|
||||
public DeviceListAdapter(final Context context, final List<UsbDevice> list) {
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mList = list != null ? list : new ArrayList<UsbDevice>();
|
||||
}
|
||||
public DeviceListAdapter(final Context context, final List<UsbDevice>list) {
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mList = list != null ? list : new ArrayList<UsbDevice>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mList.size();
|
||||
}
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getItem(final int position) {
|
||||
if ((position >= 0) && (position < mList.size())) {
|
||||
return mList.get(position);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public UsbDevice getItem(final int position) {
|
||||
if ((position >= 0) && (position < mList.size()))
|
||||
return mList.get(position);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(final int position) {
|
||||
return position;
|
||||
}
|
||||
@Override
|
||||
public long getItemId(final int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final int position, View convertView, final ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = mInflater.inflate(R.layout.listitem_device, parent, false);
|
||||
}
|
||||
if (convertView instanceof CheckedTextView) {
|
||||
final UsbDevice device = getItem(position);
|
||||
((CheckedTextView) convertView).setText(
|
||||
String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName()));
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public View getView(final int position, View convertView, final ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = mInflater.inflate(R.layout.listitem_device, parent, false);
|
||||
}
|
||||
if (convertView instanceof CheckedTextView) {
|
||||
final UsbDevice device = getItem(position);
|
||||
((CheckedTextView)convertView).setText(
|
||||
String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName()));
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -97,20 +120,15 @@ public final class USBMonitor {
|
||||
}
|
||||
|
||||
public USBMonitor(final Context context, final OnDeviceConnectListener listener) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "USBMonitor:Constructor");
|
||||
}
|
||||
if (listener == null) {
|
||||
if (DEBUG) Log.v(TAG, "USBMonitor:Constructor");
|
||||
if (listener == null)
|
||||
throw new IllegalArgumentException("OnDeviceConnectListener should not null.");
|
||||
}
|
||||
mWeakContext = new WeakReference<Context>(context);
|
||||
mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
|
||||
mOnDeviceConnectListener = listener;
|
||||
mAsyncHandler = HandlerThreadHandler.createHandler(TAG);
|
||||
destroyed = false;
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,9 +136,7 @@ public final class USBMonitor {
|
||||
* never reuse again
|
||||
*/
|
||||
public void destroy() {
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "destroy:");
|
||||
}
|
||||
if (DEBUG) Log.i(TAG, "destroy:");
|
||||
unregister();
|
||||
if (!destroyed) {
|
||||
destroyed = true;
|
||||
@@ -153,13 +169,9 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public synchronized void register() throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
if (mPermissionIntent == null) {
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "register:");
|
||||
}
|
||||
if (DEBUG) Log.i(TAG, "register:");
|
||||
final Context context = mWeakContext.get();
|
||||
if (context != null) {
|
||||
mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
|
||||
@@ -209,9 +221,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void setDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.clear();
|
||||
mDeviceFilters.add(filter);
|
||||
}
|
||||
@@ -222,9 +232,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void addDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.add(filter);
|
||||
}
|
||||
|
||||
@@ -234,9 +242,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void removeDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.remove(filter);
|
||||
}
|
||||
|
||||
@@ -246,9 +252,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void setDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.clear();
|
||||
mDeviceFilters.addAll(filters);
|
||||
}
|
||||
@@ -259,9 +263,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void addDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.addAll(filters);
|
||||
}
|
||||
|
||||
@@ -270,9 +272,7 @@ public final class USBMonitor {
|
||||
* @param filters
|
||||
*/
|
||||
public void removeDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.removeAll(filters);
|
||||
}
|
||||
|
||||
@@ -282,9 +282,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public int getDeviceCount() throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
return getDeviceList().size();
|
||||
}
|
||||
|
||||
@@ -294,9 +292,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public List<UsbDevice> getDeviceList() throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
return getDeviceList(mDeviceFilters);
|
||||
}
|
||||
|
||||
@@ -307,9 +303,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public List<UsbDevice> getDeviceList(final List<DeviceFilter> filters) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
// get detected devices
|
||||
final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
|
||||
// store those devices info before matching filter xml file
|
||||
@@ -353,8 +347,8 @@ public final class USBMonitor {
|
||||
break;
|
||||
} else {
|
||||
// collection failed dev's class and subclass
|
||||
String devModel = android.os.Build.MODEL;
|
||||
String devSystemVersion = android.os.Build.VERSION.RELEASE;
|
||||
String devModel = Build.MODEL;
|
||||
String devSystemVersion = Build.VERSION.RELEASE;
|
||||
String devClass = String.valueOf(device.getDeviceClass());
|
||||
String subClass = String.valueOf(device.getDeviceSubclass());
|
||||
try{
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
|
||||
@@ -2,9 +2,7 @@ package com.serenegiant.usb.common;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.SurfaceTexture;
|
||||
@@ -22,7 +20,6 @@ import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.serenegiant.usb.IFrameCallback;
|
||||
import com.serenegiant.usb.ParentPreviewConstraintLayout;
|
||||
import com.serenegiant.usb.Size;
|
||||
import com.serenegiant.usb.USBMonitor;
|
||||
import com.serenegiant.usb.UVCCamera;
|
||||
@@ -52,7 +49,6 @@ import java.nio.ByteBuffer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
@@ -68,19 +64,19 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
|
||||
// 对外回调接口
|
||||
public interface CameraCallback {
|
||||
void onOpen();
|
||||
public void onOpen();
|
||||
|
||||
void onClose();
|
||||
public void onClose();
|
||||
|
||||
void onStartPreview();
|
||||
public void onStartPreview();
|
||||
|
||||
void onStopPreview();
|
||||
public void onStopPreview();
|
||||
|
||||
void onStartRecording();
|
||||
public void onStartRecording();
|
||||
|
||||
void onStopRecording();
|
||||
public void onStopRecording();
|
||||
|
||||
void onError(final Exception e);
|
||||
public void onError(final Exception e);
|
||||
}
|
||||
|
||||
public static OnEncodeResultListener mListener;
|
||||
@@ -180,16 +176,12 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "close:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "close:");
|
||||
if (isOpened()) {
|
||||
stopPreview();
|
||||
sendEmptyMessage(MSG_CLOSE);
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "close:finished");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "close:finished");
|
||||
}
|
||||
|
||||
// 切换分辨率
|
||||
@@ -214,18 +206,14 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
|
||||
// 关闭Camera预览
|
||||
public void stopPreview() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "stopPreview:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "stopPreview:");
|
||||
removeMessages(MSG_PREVIEW_START);
|
||||
if (isRecording()) {
|
||||
stopRecording();
|
||||
}
|
||||
if (isPreviewing()) {
|
||||
final CameraThread thread = mWeakThread.get();
|
||||
if (thread == null) {
|
||||
return;
|
||||
}
|
||||
if (thread == null) return;
|
||||
synchronized (thread.mSync) {
|
||||
sendEmptyMessage(MSG_PREVIEW_STOP);
|
||||
if (!isCameraThread()) {
|
||||
@@ -239,12 +227,10 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "stopPreview:finished");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "stopPreview:finished");
|
||||
}
|
||||
|
||||
public void captureStill(final String path, AbstractUVCCameraHandler.OnCaptureListener listener) {
|
||||
public void captureStill(final String path, OnCaptureListener listener) {
|
||||
AbstractUVCCameraHandler.mCaptureListener = listener;
|
||||
checkReleased();
|
||||
sendMessage(obtainMessage(MSG_CAPTURE_STILL, path));
|
||||
@@ -366,9 +352,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
@Override
|
||||
public void handleMessage(final Message msg) {
|
||||
final CameraThread thread = mWeakThread.get();
|
||||
if (thread == null) {
|
||||
return;
|
||||
}
|
||||
if (thread == null) return;
|
||||
switch (msg.what) {
|
||||
case MSG_OPEN:
|
||||
thread.handleOpen((USBMonitor.UsbControlBlock) msg.obj);
|
||||
@@ -472,17 +456,13 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public AbstractUVCCameraHandler getHandler() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "getHandler:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "getHandler:");
|
||||
synchronized (mSync) {
|
||||
if (mHandler == null) {
|
||||
if (mHandler == null)
|
||||
try {
|
||||
mSync.wait();
|
||||
} catch (final InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mHandler;
|
||||
}
|
||||
@@ -528,9 +508,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void handleOpen(final USBMonitor.UsbControlBlock ctrlBlock) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleOpen:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleOpen:");
|
||||
handleClose();
|
||||
try {
|
||||
final UVCCamera camera = new UVCCamera();
|
||||
@@ -542,15 +520,12 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
} catch (final Exception e) {
|
||||
callOnError(e);
|
||||
}
|
||||
if (DEBUG) {
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "supportedSize:" + (mUVCCamera != null ? mUVCCamera.getSupportedSize() : null));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClose() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleClose:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleClose:");
|
||||
handleStopPusher();
|
||||
final UVCCamera camera;
|
||||
synchronized (mSync) {
|
||||
@@ -565,12 +540,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void handleStartPreview(final Object surface) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleStartPreview:");
|
||||
}
|
||||
if ((mUVCCamera == null) || mIsPreviewing) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
|
||||
if ((mUVCCamera == null) || mIsPreviewing) return;
|
||||
try {
|
||||
mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, mPreviewMode, mBandwidthFactor);
|
||||
// 获取USB Camera预览数据,使用NV21颜色会失真
|
||||
@@ -603,9 +574,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void handleStopPreview() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleStopPreview:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:");
|
||||
if (mIsPreviewing) {
|
||||
if (mUVCCamera != null) {
|
||||
mUVCCamera.stopPreview();
|
||||
@@ -617,20 +586,14 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
callOnStopPreview();
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleStopPreview:finished");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:finished");
|
||||
}
|
||||
|
||||
// 捕获静态图片
|
||||
public void handleCaptureStill(final String path) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleCaptureStill:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleCaptureStill:");
|
||||
final Activity parent = mWeakParent.get();
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
if (parent == null) return;
|
||||
// mSoundPool.play(mSoundId, 0.2f, 0.2f, 0, 0, 1.0f); // play shutter sound
|
||||
try {
|
||||
final Bitmap bitmap = mWeakCameraView.get().captureStillImage(mWidth, mHeight);
|
||||
@@ -647,7 +610,6 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
os.flush();
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_MEDIA_UPDATE, outputFile.getPath()));
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
os.close();
|
||||
@@ -706,9 +668,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
private H264EncodeConsumer mH264Consumer;
|
||||
|
||||
public void handleStartPusher(RecordParams params) {
|
||||
if ((mUVCCamera == null) || (mH264Consumer != null)) {
|
||||
if ((mUVCCamera == null) || (mH264Consumer != null))
|
||||
return;
|
||||
}
|
||||
// // 获取USB Camera预览数据
|
||||
// mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21);
|
||||
|
||||
@@ -747,9 +708,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
// 停止音视频编码线程
|
||||
stopAudioRecord();
|
||||
stopVideoRecord();
|
||||
if(isSupportOverlay) {
|
||||
if(isSupportOverlay)
|
||||
TxtOverlay.getInstance().release();
|
||||
}
|
||||
// // 停止捕获视频数据
|
||||
// if (mUVCCamera != null) {
|
||||
// mUVCCamera.stopCapture();
|
||||
@@ -915,23 +875,18 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public void handleUpdateMedia(final String path) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleUpdateMedia:path=" + path);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleUpdateMedia:path=" + path);
|
||||
final Activity parent = mWeakParent.get();
|
||||
final boolean released = (mHandler == null) || mHandler.mReleased;
|
||||
if (parent != null && parent.getApplicationContext() != null) {
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "MediaScannerConnection#scanFile");
|
||||
}
|
||||
if (DEBUG) Log.i(TAG, "MediaScannerConnection#scanFile");
|
||||
MediaScannerConnection.scanFile(parent.getApplicationContext(), new String[]{path}, null, null);
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "handleUpdateMedia:", e);
|
||||
}
|
||||
if (released || parent.isDestroyed()) {
|
||||
if (released || parent.isDestroyed())
|
||||
handleRelease();
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "MainActivity already destroyed");
|
||||
// give up to add this movie to MediaStore now.
|
||||
@@ -941,71 +896,57 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void handleRelease() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleRelease:mIsRecording=" + mIsRecording);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleRelease:mIsRecording=" + mIsRecording);
|
||||
handleClose();
|
||||
mCallbacks.clear();
|
||||
if (!mIsRecording) {
|
||||
mHandler.mReleased = true;
|
||||
Looper.myLooper().quit();
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleRelease:finished");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleRelease:finished");
|
||||
}
|
||||
|
||||
// 自动对焦
|
||||
public void handleCameraFoucs() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleStartPreview:");
|
||||
}
|
||||
if ((mUVCCamera == null) || !mIsPreviewing) {
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
|
||||
if ((mUVCCamera == null) || !mIsPreviewing)
|
||||
return;
|
||||
}
|
||||
mUVCCamera.setAutoFocus(true);
|
||||
}
|
||||
|
||||
// 获取支持的分辨率
|
||||
public List<Size> getSupportedSizes() {
|
||||
if ((mUVCCamera == null) || !mIsPreviewing) {
|
||||
if ((mUVCCamera == null) || !mIsPreviewing)
|
||||
return null;
|
||||
}
|
||||
return mUVCCamera.getSupportedSizeList();
|
||||
}
|
||||
|
||||
private final MediaEncoder.MediaEncoderListener mMediaEncoderListener = new MediaEncoder.MediaEncoderListener() {
|
||||
@Override
|
||||
public void onPrepared(final MediaEncoder encoder) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "onPrepared:encoder=" + encoder);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "onPrepared:encoder=" + encoder);
|
||||
mIsRecording = true;
|
||||
if (encoder instanceof MediaVideoEncoder) {
|
||||
if (encoder instanceof MediaVideoEncoder)
|
||||
try {
|
||||
mWeakCameraView.get().setVideoEncoder((MediaVideoEncoder) encoder);
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "onPrepared:", e);
|
||||
}
|
||||
}
|
||||
if (encoder instanceof MediaSurfaceEncoder) {
|
||||
if (encoder instanceof MediaSurfaceEncoder)
|
||||
try {
|
||||
mWeakCameraView.get().setVideoEncoder((MediaSurfaceEncoder) encoder);
|
||||
mUVCCamera.startCapture(((MediaSurfaceEncoder) encoder).getInputSurface());
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "onPrepared:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
@Override
|
||||
public void onStopped(final MediaEncoder encoder) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "onStopped:encoder=" + encoder);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "onStopped:encoder=" + encoder);
|
||||
if ((encoder instanceof MediaVideoEncoder)
|
||||
|| (encoder instanceof MediaSurfaceEncoder)) {
|
||||
|| (encoder instanceof MediaSurfaceEncoder))
|
||||
try {
|
||||
mIsRecording = false;
|
||||
final Activity parent = mWeakParent.get();
|
||||
@@ -1027,7 +968,6 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "onPrepared:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
package com.serenegiant.usb.common;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.mogo.usbcamera.R;
|
||||
import com.serenegiant.dialog.MessageDialogFragmentV4;
|
||||
import com.serenegiant.utils.BuildCheck;
|
||||
import com.serenegiant.utils.HandlerThreadHandler;
|
||||
import com.serenegiant.utils.PermissionCheck;
|
||||
|
||||
/**
|
||||
* Created by saki on 2016/11/18.
|
||||
*
|
||||
*/
|
||||
public class BaseActivity extends AppCompatActivity
|
||||
implements MessageDialogFragmentV4.MessageDialogListener {
|
||||
|
||||
private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
|
||||
private static final String TAG = BaseActivity.class.getSimpleName();
|
||||
|
||||
/** UI操作のためのHandler */
|
||||
private final Handler mUIHandler = new Handler(Looper.getMainLooper());
|
||||
private final Thread mUiThread = mUIHandler.getLooper().getThread();
|
||||
/** ワーカースレッド上で処理するためのHandler */
|
||||
private Handler mWorkerHandler;
|
||||
private long mWorkerThreadID = -1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// ワーカースレッドを生成
|
||||
if (mWorkerHandler == null) {
|
||||
mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
|
||||
mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
clearToast();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void onDestroy() {
|
||||
// ワーカースレッドを破棄
|
||||
if (mWorkerHandler != null) {
|
||||
try {
|
||||
mWorkerHandler.getLooper().quit();
|
||||
} catch (final Exception e) {
|
||||
//
|
||||
}
|
||||
mWorkerHandler = null;
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
/**
|
||||
* UIスレッドでRunnableを実行するためのヘルパーメソッド
|
||||
* @param task
|
||||
* @param duration
|
||||
*/
|
||||
public final void runOnUiThread(final Runnable task, final long duration) {
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
mUIHandler.removeCallbacks(task);
|
||||
if ((duration > 0) || Thread.currentThread() != mUiThread) {
|
||||
mUIHandler.postDelayed(task, duration);
|
||||
} else {
|
||||
try {
|
||||
task.run();
|
||||
} catch (final Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
|
||||
* @param task
|
||||
*/
|
||||
public final void removeFromUiThread(final Runnable task) {
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
mUIHandler.removeCallbacks(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* ワーカースレッド上で指定したRunnableを実行する
|
||||
* 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
|
||||
* @param task
|
||||
* @param delayMillis
|
||||
*/
|
||||
protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
|
||||
if ((task == null) || (mWorkerHandler == null)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mWorkerHandler.removeCallbacks(task);
|
||||
if (delayMillis > 0) {
|
||||
mWorkerHandler.postDelayed(task, delayMillis);
|
||||
} else if (mWorkerThreadID == Thread.currentThread().getId()) {
|
||||
task.run();
|
||||
} else {
|
||||
mWorkerHandler.post(task);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
|
||||
* @param task
|
||||
*/
|
||||
protected final synchronized void removeEvent(final Runnable task) {
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mWorkerHandler.removeCallbacks(task);
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
private Toast mToast;
|
||||
/**
|
||||
* Toastでメッセージを表示
|
||||
* @param msg
|
||||
*/
|
||||
protected void showToast(@StringRes final int msg, final Object... args) {
|
||||
removeFromUiThread(mShowToastTask);
|
||||
mShowToastTask = new ShowToastTask(msg, args);
|
||||
runOnUiThread(mShowToastTask, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toastが表示されていればキャンセルする
|
||||
*/
|
||||
protected void clearToast() {
|
||||
removeFromUiThread(mShowToastTask);
|
||||
mShowToastTask = null;
|
||||
try {
|
||||
if (mToast != null) {
|
||||
mToast.cancel();
|
||||
mToast = null;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private ShowToastTask mShowToastTask;
|
||||
private final class ShowToastTask implements Runnable {
|
||||
final int msg;
|
||||
final Object args;
|
||||
private ShowToastTask(@StringRes final int msg, final Object... args) {
|
||||
this.msg = msg;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (mToast != null) {
|
||||
mToast.cancel();
|
||||
mToast = null;
|
||||
}
|
||||
final String _msg = (args != null) ? getString(msg, args) : getString(msg);
|
||||
mToast = Toast.makeText(BaseActivity.this, _msg, Toast.LENGTH_SHORT);
|
||||
mToast.show();
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
/**
|
||||
* MessageDialogFragmentメッセージダイアログからのコールバックリスナー
|
||||
* @param dialog
|
||||
* @param requestCode
|
||||
* @param permissions
|
||||
* @param result
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void onMessageDialogResult(final MessageDialogFragmentV4 dialog, final int requestCode, final String[] permissions, final boolean result) {
|
||||
if (result) {
|
||||
// メッセージダイアログでOKを押された時はパーミッション要求する
|
||||
if (BuildCheck.isMarshmallow()) {
|
||||
requestPermissions(permissions, requestCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// メッセージダイアログでキャンセルされた時とAndroid6でない時は自前でチェックして#checkPermissionResultを呼び出す
|
||||
for (final String permission: permissions) {
|
||||
checkPermissionResult(requestCode, permission, PermissionCheck.hasPermission(this, permission));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* パーミッション要求結果を受け取るためのメソッド
|
||||
* @param requestCode
|
||||
* @param permissions
|
||||
* @param grantResults
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults); // 何もしてないけど一応呼んどく
|
||||
final int n = Math.min(permissions.length, grantResults.length);
|
||||
for (int i = 0; i < n; i++) {
|
||||
checkPermissionResult(requestCode, permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* パーミッション要求の結果をチェック
|
||||
* ここではパーミッションを取得できなかった時にToastでメッセージ表示するだけ
|
||||
* @param requestCode
|
||||
* @param permission
|
||||
* @param result
|
||||
*/
|
||||
protected void checkPermissionResult(final int requestCode, final String permission, final boolean result) {
|
||||
// パーミッションがないときにはメッセージを表示する
|
||||
if (!result && (permission != null)) {
|
||||
if (Manifest.permission.RECORD_AUDIO.equals(permission)) {
|
||||
showToast(R.string.permission_audio);
|
||||
}
|
||||
if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
|
||||
showToast(R.string.permission_ext_storage);
|
||||
}
|
||||
if (Manifest.permission.INTERNET.equals(permission)) {
|
||||
showToast(R.string.permission_network);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 動的パーミッション要求時の要求コード
|
||||
protected static final int REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE = 0x12345;
|
||||
protected static final int REQUEST_PERMISSION_AUDIO_RECORDING = 0x234567;
|
||||
protected static final int REQUEST_PERMISSION_NETWORK = 0x345678;
|
||||
protected static final int REQUEST_PERMISSION_CAMERA = 0x537642;
|
||||
|
||||
/**
|
||||
* 外部ストレージへの書き込みパーミッションが有るかどうかをチェック
|
||||
* なければ説明ダイアログを表示する
|
||||
* @return true 外部ストレージへの書き込みパーミッションが有る
|
||||
*/
|
||||
protected boolean checkPermissionWriteExternalStorage() {
|
||||
if (!PermissionCheck.hasWriteExternalStorage(this)) {
|
||||
MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE,
|
||||
R.string.permission_title, R.string.permission_ext_storage_request,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 録音のパーミッションが有るかどうかをチェック
|
||||
* なければ説明ダイアログを表示する
|
||||
* @return true 録音のパーミッションが有る
|
||||
*/
|
||||
protected boolean checkPermissionAudio() {
|
||||
if (!PermissionCheck.hasAudio(this)) {
|
||||
MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_AUDIO_RECORDING,
|
||||
R.string.permission_title, R.string.permission_audio_recording_request,
|
||||
new String[]{Manifest.permission.RECORD_AUDIO});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ネットワークアクセスのパーミッションが有るかどうかをチェック
|
||||
* なければ説明ダイアログを表示する
|
||||
* @return true ネットワークアクセスのパーミッションが有る
|
||||
*/
|
||||
protected boolean checkPermissionNetwork() {
|
||||
if (!PermissionCheck.hasNetwork(this)) {
|
||||
MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_NETWORK,
|
||||
R.string.permission_title, R.string.permission_network_request,
|
||||
new String[]{Manifest.permission.INTERNET});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* カメラアクセスのパーミッションがあるかどうかをチェック
|
||||
* なければ説明ダイアログを表示する
|
||||
* @return true カメラアクセスのパーミッションが有る
|
||||
*/
|
||||
protected boolean checkPermissionCamera() {
|
||||
if (!PermissionCheck.hasCamera(this)) {
|
||||
MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_CAMERA,
|
||||
R.string.permission_title, R.string.permission_camera_request,
|
||||
new String[]{Manifest.permission.CAMERA});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
package com.serenegiant.usb.common;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Fragment;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.mogo.usbcamera.R;
|
||||
import com.serenegiant.dialog.MessageDialogFragment;
|
||||
import com.serenegiant.utils.BuildCheck;
|
||||
import com.serenegiant.utils.HandlerThreadHandler;
|
||||
import com.serenegiant.utils.PermissionCheck;
|
||||
|
||||
/**
|
||||
* Created by saki on 2016/11/19.
|
||||
*/
|
||||
public class BaseFragment extends Fragment
|
||||
implements MessageDialogFragment.MessageDialogListener {
|
||||
|
||||
private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
|
||||
private static final String TAG = BaseFragment.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* UI操作のためのHandler
|
||||
*/
|
||||
private final Handler mUIHandler = new Handler(Looper.getMainLooper());
|
||||
private final Thread mUiThread = mUIHandler.getLooper().getThread();
|
||||
/**
|
||||
* ワーカースレッド上で処理するためのHandler
|
||||
*/
|
||||
private Handler mWorkerHandler;
|
||||
private long mWorkerThreadID = -1;
|
||||
|
||||
public BaseFragment() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// ワーカースレッドを生成
|
||||
if (mWorkerHandler == null) {
|
||||
mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
|
||||
mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
clearToast();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onDestroy() {
|
||||
// ワーカースレッドを破棄
|
||||
if (mWorkerHandler != null) {
|
||||
try {
|
||||
mWorkerHandler.getLooper().quit();
|
||||
} catch (final Exception e) {
|
||||
//
|
||||
}
|
||||
mWorkerHandler = null;
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* UIスレッドでRunnableを実行するためのヘルパーメソッド
|
||||
*
|
||||
* @param task
|
||||
* @param duration
|
||||
*/
|
||||
public final void runOnUiThread(final Runnable task, final long duration) {
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
mUIHandler.removeCallbacks(task);
|
||||
if ((duration > 0) || Thread.currentThread() != mUiThread) {
|
||||
mUIHandler.postDelayed(task, duration);
|
||||
} else {
|
||||
try {
|
||||
task.run();
|
||||
} catch (final Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
|
||||
*
|
||||
* @param task
|
||||
*/
|
||||
public final void removeFromUiThread(final Runnable task) {
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
mUIHandler.removeCallbacks(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* ワーカースレッド上で指定したRunnableを実行する
|
||||
* 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
|
||||
*
|
||||
* @param task
|
||||
* @param delayMillis
|
||||
*/
|
||||
protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
|
||||
if ((task == null) || (mWorkerHandler == null)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mWorkerHandler.removeCallbacks(task);
|
||||
if (delayMillis > 0) {
|
||||
mWorkerHandler.postDelayed(task, delayMillis);
|
||||
} else if (mWorkerThreadID == Thread.currentThread().getId()) {
|
||||
task.run();
|
||||
} else {
|
||||
mWorkerHandler.post(task);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
|
||||
*
|
||||
* @param task
|
||||
*/
|
||||
protected final synchronized void removeEvent(final Runnable task) {
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mWorkerHandler.removeCallbacks(task);
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
private Toast mToast;
|
||||
|
||||
/**
|
||||
* Toastでメッセージを表示
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
protected void showToast(@StringRes final int msg, final Object... args) {
|
||||
removeFromUiThread(mShowToastTask);
|
||||
mShowToastTask = new ShowToastTask(msg, args);
|
||||
runOnUiThread(mShowToastTask, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toastが表示されていればキャンセルする
|
||||
*/
|
||||
protected void clearToast() {
|
||||
removeFromUiThread(mShowToastTask);
|
||||
mShowToastTask = null;
|
||||
try {
|
||||
if (mToast != null) {
|
||||
mToast.cancel();
|
||||
mToast = null;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private ShowToastTask mShowToastTask;
|
||||
|
||||
private final class ShowToastTask implements Runnable {
|
||||
final int msg;
|
||||
final Object args;
|
||||
|
||||
private ShowToastTask(@StringRes final int msg, final Object... args) {
|
||||
this.msg = msg;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (mToast != null) {
|
||||
mToast.cancel();
|
||||
mToast = null;
|
||||
}
|
||||
if (args != null) {
|
||||
final String _msg = getString(msg, args);
|
||||
mToast = Toast.makeText(getActivity(), _msg, Toast.LENGTH_SHORT);
|
||||
} else {
|
||||
mToast = Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT);
|
||||
}
|
||||
mToast.show();
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* MessageDialogFragmentメッセージダイアログからのコールバックリスナー
|
||||
*
|
||||
* @param dialog
|
||||
* @param requestCode
|
||||
* @param permissions
|
||||
* @param result
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void onMessageDialogResult(final MessageDialogFragment dialog, final int requestCode, final String[] permissions, final boolean result) {
|
||||
if (result) {
|
||||
// メッセージダイアログでOKを押された時はパーミッション要求する
|
||||
if (BuildCheck.isMarshmallow()) {
|
||||
requestPermissions(permissions, requestCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// メッセージダイアログでキャンセルされた時とAndroid6でない時は自前でチェックして#checkPermissionResultを呼び出す
|
||||
for (final String permission : permissions) {
|
||||
checkPermissionResult(requestCode, permission, PermissionCheck.hasPermission(getActivity(), permission));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* パーミッション要求結果を受け取るためのメソッド
|
||||
*
|
||||
* @param requestCode
|
||||
* @param permissions
|
||||
* @param grantResults
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults); // 何もしてないけど一応呼んどく
|
||||
final int n = Math.min(permissions.length, grantResults.length);
|
||||
for (int i = 0; i < n; i++) {
|
||||
checkPermissionResult(requestCode, permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* パーミッション要求の結果をチェック
|
||||
* ここではパーミッションを取得できなかった時にToastでメッセージ表示するだけ
|
||||
*
|
||||
* @param requestCode
|
||||
* @param permission
|
||||
* @param result
|
||||
*/
|
||||
protected void checkPermissionResult(final int requestCode, final String permission, final boolean result) {
|
||||
// パーミッションがないときにはメッセージを表示する
|
||||
if (!result && (permission != null)) {
|
||||
if (Manifest.permission.RECORD_AUDIO.equals(permission)) {
|
||||
showToast(R.string.permission_audio);
|
||||
}
|
||||
if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
|
||||
showToast(R.string.permission_ext_storage);
|
||||
}
|
||||
if (Manifest.permission.INTERNET.equals(permission)) {
|
||||
showToast(R.string.permission_network);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 動的パーミッション要求時の要求コード
|
||||
protected static final int REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE = 0x12345;
|
||||
protected static final int REQUEST_PERMISSION_AUDIO_RECORDING = 0x234567;
|
||||
protected static final int REQUEST_PERMISSION_NETWORK = 0x345678;
|
||||
protected static final int REQUEST_PERMISSION_CAMERA = 0x537642;
|
||||
|
||||
/**
|
||||
* 外部ストレージへの書き込みパーミッションが有るかどうかをチェック
|
||||
* なければ説明ダイアログを表示する
|
||||
*
|
||||
* @return true 外部ストレージへの書き込みパーミッションが有る
|
||||
*/
|
||||
protected boolean checkPermissionWriteExternalStorage() {
|
||||
if (!PermissionCheck.hasWriteExternalStorage(getActivity())) {
|
||||
MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE,
|
||||
R.string.permission_title, R.string.permission_ext_storage_request,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 録音のパーミッションが有るかどうかをチェック
|
||||
* なければ説明ダイアログを表示する
|
||||
*
|
||||
* @return true 録音のパーミッションが有る
|
||||
*/
|
||||
protected boolean checkPermissionAudio() {
|
||||
if (!PermissionCheck.hasAudio(getActivity())) {
|
||||
MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_AUDIO_RECORDING,
|
||||
R.string.permission_title, R.string.permission_audio_recording_request,
|
||||
new String[]{Manifest.permission.RECORD_AUDIO});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ネットワークアクセスのパーミッションが有るかどうかをチェック
|
||||
* なければ説明ダイアログを表示する
|
||||
*
|
||||
* @return true ネットワークアクセスのパーミッションが有る
|
||||
*/
|
||||
protected boolean checkPermissionNetwork() {
|
||||
if (!PermissionCheck.hasNetwork(getActivity())) {
|
||||
MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_NETWORK,
|
||||
R.string.permission_title, R.string.permission_network_request,
|
||||
new String[]{Manifest.permission.INTERNET});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* カメラアクセスのパーミッションがあるかどうかをチェック
|
||||
* なければ説明ダイアログを表示する
|
||||
*
|
||||
* @return true カメラアクセスのパーミッションが有る
|
||||
*/
|
||||
protected boolean checkPermissionCamera() {
|
||||
if (!PermissionCheck.hasCamera(getActivity())) {
|
||||
MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_CAMERA,
|
||||
R.string.permission_title, R.string.permission_camera_request,
|
||||
new String[]{Manifest.permission.CAMERA});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.serenegiant.usb.common;
|
||||
|
||||
import android.app.Service;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.serenegiant.utils.HandlerThreadHandler;
|
||||
|
||||
public abstract class BaseService extends Service {
|
||||
private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
|
||||
private static final String TAG = BaseService.class.getSimpleName();
|
||||
|
||||
/** UI操作のためのHandler */
|
||||
private final Handler mUIHandler = new Handler(Looper.getMainLooper());
|
||||
private final Thread mUiThread = mUIHandler.getLooper().getThread();
|
||||
/** ワーカースレッド上で処理するためのHandler */
|
||||
private Handler mWorkerHandler;
|
||||
private long mWorkerThreadID = -1;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// ワーカースレッドを生成
|
||||
if (mWorkerHandler == null) {
|
||||
mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
|
||||
mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onDestroy() {
|
||||
// ワーカースレッドを破棄
|
||||
if (mWorkerHandler != null) {
|
||||
try {
|
||||
mWorkerHandler.getLooper().quit();
|
||||
} catch (final Exception e) {
|
||||
//
|
||||
}
|
||||
mWorkerHandler = null;
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
/**
|
||||
* UIスレッドでRunnableを実行するためのヘルパーメソッド
|
||||
* @param task
|
||||
* @param duration
|
||||
*/
|
||||
public final void runOnUiThread(final Runnable task, final long duration) {
|
||||
if (task == null) return;
|
||||
mUIHandler.removeCallbacks(task);
|
||||
if ((duration > 0) || Thread.currentThread() != mUiThread) {
|
||||
mUIHandler.postDelayed(task, duration);
|
||||
} else {
|
||||
try {
|
||||
task.run();
|
||||
} catch (final Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
|
||||
* @param task
|
||||
*/
|
||||
public final void removeFromUiThread(final Runnable task) {
|
||||
if (task == null) return;
|
||||
mUIHandler.removeCallbacks(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* ワーカースレッド上で指定したRunnableを実行する
|
||||
* 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
|
||||
* @param task
|
||||
* @param delayMillis
|
||||
*/
|
||||
protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
|
||||
if ((task == null) || (mWorkerHandler == null)) return;
|
||||
try {
|
||||
mWorkerHandler.removeCallbacks(task);
|
||||
if (delayMillis > 0) {
|
||||
mWorkerHandler.postDelayed(task, delayMillis);
|
||||
} else if (mWorkerThreadID == Thread.currentThread().getId()) {
|
||||
task.run();
|
||||
} else {
|
||||
mWorkerHandler.post(task);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
|
||||
* @param task
|
||||
*/
|
||||
protected final synchronized void removeEvent(final Runnable task) {
|
||||
if (task == null) return;
|
||||
try {
|
||||
mWorkerHandler.removeCallbacks(task);
|
||||
} catch (final Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,30 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb.common;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import com.serenegiant.usb.ParentPreviewConstraintLayout;
|
||||
import com.serenegiant.usb.UVCCamera;
|
||||
import com.serenegiant.usb.widget.CameraViewInterface;
|
||||
|
||||
|
||||
@@ -1,10 +1,32 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb.common;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.serenegiant.glutils.RendererHolder;
|
||||
import com.serenegiant.usb.ParentPreviewConstraintLayout;
|
||||
import com.serenegiant.usb.UVCCamera;
|
||||
import com.serenegiant.usb.widget.CameraViewInterface;
|
||||
|
||||
@@ -101,7 +123,6 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler {
|
||||
mRendererHolder = new RendererHolder(thread.getWidth(), thread.getHeight(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void release() {
|
||||
if (mRendererHolder != null) {
|
||||
mRendererHolder.release();
|
||||
@@ -110,8 +131,7 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler {
|
||||
super.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void resize(final int width, final int height) {
|
||||
public synchronized void resize(final int width, final int height) {
|
||||
super.resize(width, height);
|
||||
if (mRendererHolder != null) {
|
||||
mRendererHolder.resize(width, height);
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb.encoder;
|
||||
|
||||
public interface IAudioEncoder {
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb.encoder;
|
||||
|
||||
public interface IVideoEncoder {
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb.encoder;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb.encoder;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* UVCCamera
|
||||
* library and sample to access to UVC web camera on non-rooted Android device
|
||||
*
|
||||
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* All files in the folder are under this Apache License, Version 2.0.
|
||||
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
|
||||
* may have a different license, see the respective files.
|
||||
*/
|
||||
|
||||
package com.serenegiant.usb.encoder;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.serenegiant.usb.encoder;
|
||||
|
||||
/**
|
||||
* 录制参数
|
||||
/** 录制参数
|
||||
*
|
||||
* Created by jiangdongguo on 2017/10/19.
|
||||
*/
|
||||
|
||||
public class RecordParams {
|
||||
private String recordPath;
|
||||
private int recordDuration;
|
||||
|
||||
@@ -19,10 +19,12 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
/**
|
||||
* 将PCM编码为AAC
|
||||
/**将PCM编码为AAC
|
||||
*
|
||||
* Created by jianddongguo on 2017/7/21.
|
||||
*/
|
||||
public class AACEncodeConsumer extends Thread {
|
||||
|
||||
public class AACEncodeConsumer extends Thread{
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "TMPU";
|
||||
private static final String MIME_TYPE = "audio/mp4a-latm";
|
||||
@@ -38,7 +40,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
private AudioRecord mAudioRecord; // 音频采集
|
||||
private MediaCodec mAudioEncoder; // 音频编码
|
||||
private OnAACEncodeResultListener listener;
|
||||
private int mSamplingRateIndex = 0;//ADTS
|
||||
private int mSamplingRateIndex = 0;//ADTS
|
||||
private boolean isEncoderStart = false;
|
||||
private boolean isRecMp3 = false;
|
||||
private boolean isExit = false;
|
||||
@@ -46,7 +48,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
private WeakReference<Mp4MediaMuxer> mMuxerRef;
|
||||
private MediaFormat newFormat;
|
||||
|
||||
private static final int[] AUDIO_SOURCES = new int[]{
|
||||
private static final int[] AUDIO_SOURCES = new int[] {
|
||||
MediaRecorder.AudioSource.DEFAULT,
|
||||
MediaRecorder.AudioSource.MIC,
|
||||
MediaRecorder.AudioSource.CAMCORDER,
|
||||
@@ -54,7 +56,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
/**
|
||||
* There are 13 supported frequencies by ADTS.
|
||||
**/
|
||||
public static final int[] AUDIO_SAMPLING_RATES = {96000, // 0
|
||||
public static final int[] AUDIO_SAMPLING_RATES = { 96000, // 0
|
||||
88200, // 1
|
||||
64000, // 2
|
||||
48000, // 3
|
||||
@@ -75,13 +77,13 @@ public class AACEncodeConsumer extends Thread {
|
||||
private FileOutputStream fops;
|
||||
|
||||
// 编码流结果回调接口
|
||||
public interface OnAACEncodeResultListener {
|
||||
public interface OnAACEncodeResultListener{
|
||||
void onEncodeResult(byte[] data, int offset,
|
||||
int length, long timestamp);
|
||||
}
|
||||
|
||||
public AACEncodeConsumer() {
|
||||
for (int i = 0; i < AUDIO_SAMPLING_RATES.length; i++) {
|
||||
public AACEncodeConsumer(){
|
||||
for (int i=0;i < AUDIO_SAMPLING_RATES.length; i++) {
|
||||
if (AUDIO_SAMPLING_RATES[i] == SAMPLE_RATE) {
|
||||
mSamplingRateIndex = i;
|
||||
break;
|
||||
@@ -89,16 +91,16 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnAACEncodeResultListener(OnAACEncodeResultListener listener) {
|
||||
public void setOnAACEncodeResultListener(OnAACEncodeResultListener listener){
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
public void exit(){
|
||||
isExit = true;
|
||||
}
|
||||
|
||||
public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer) {
|
||||
this.mMuxerRef = new WeakReference<>(mMuxer);
|
||||
public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer){
|
||||
this.mMuxerRef = new WeakReference<>(mMuxer);
|
||||
Mp4MediaMuxer muxer = mMuxerRef.get();
|
||||
if (muxer != null && newFormat != null) {
|
||||
muxer.addTrack(newFormat, false);
|
||||
@@ -108,7 +110,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
// 开启音频采集、编码
|
||||
if (!isEncoderStart) {
|
||||
if(! isEncoderStart){
|
||||
initAudioRecord();
|
||||
initMediaCodec();
|
||||
}
|
||||
@@ -116,16 +118,16 @@ public class AACEncodeConsumer extends Thread {
|
||||
byte[] mp3Buffer = new byte[1024];
|
||||
|
||||
// 这里有问题,当本地录制结束后,没有写入
|
||||
while (!isExit) {
|
||||
while(! isExit){
|
||||
byte[] audioBuffer = new byte[2048];
|
||||
// 采集音频
|
||||
int readBytes = mAudioRecord.read(audioBuffer, 0, BUFFER_SIZE);
|
||||
int readBytes = mAudioRecord.read(audioBuffer,0,BUFFER_SIZE);
|
||||
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "采集音频readBytes = " + readBytes);
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"采集音频readBytes = "+readBytes);
|
||||
// 编码音频
|
||||
if (readBytes > 0) {
|
||||
encodeBytes(audioBuffer, readBytes);
|
||||
if(readBytes > 0){
|
||||
encodeBytes(audioBuffer,readBytes);
|
||||
}
|
||||
}
|
||||
// 停止音频采集、编码
|
||||
@@ -140,81 +142,81 @@ public class AACEncodeConsumer extends Thread {
|
||||
ByteBuffer[] outputBuffers = mAudioEncoder.getOutputBuffers();
|
||||
//返回编码器的一个输入缓存区句柄,-1表示当前没有可用的输入缓存区
|
||||
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(TIMES_OUT);
|
||||
if (inputBufferIndex >= 0) {
|
||||
if(inputBufferIndex >= 0){
|
||||
// 绑定一个被空的、可写的输入缓存区inputBuffer到客户端
|
||||
ByteBuffer inputBuffer = null;
|
||||
if (!isLollipop()) {
|
||||
ByteBuffer inputBuffer = null;
|
||||
if(!isLollipop()){
|
||||
inputBuffer = inputBuffers[inputBufferIndex];
|
||||
} else {
|
||||
}else{
|
||||
inputBuffer = mAudioEncoder.getInputBuffer(inputBufferIndex);
|
||||
}
|
||||
// 向输入缓存区写入有效原始数据,并提交到编码器中进行编码处理
|
||||
if (audioBuf == null || readBytes <= 0) {
|
||||
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, 0, getPTSUs(), MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||
} else {
|
||||
if(audioBuf==null || readBytes<=0){
|
||||
mAudioEncoder.queueInputBuffer(inputBufferIndex,0,0,getPTSUs(),MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||
}else{
|
||||
inputBuffer.clear();
|
||||
inputBuffer.put(audioBuf);
|
||||
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, readBytes, getPTSUs(), 0);
|
||||
mAudioEncoder.queueInputBuffer(inputBufferIndex,0,readBytes,getPTSUs(),0);
|
||||
}
|
||||
}
|
||||
|
||||
// 返回一个输出缓存区句柄,当为-1时表示当前没有可用的输出缓存区
|
||||
// mBufferInfo参数包含被编码好的数据,timesOut参数为超时等待的时间
|
||||
MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
|
||||
MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
|
||||
int outputBufferIndex = -1;
|
||||
do {
|
||||
outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo, TIMES_OUT);
|
||||
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "获得编码器输出缓存区超时");
|
||||
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||
do{
|
||||
outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo,TIMES_OUT);
|
||||
if(outputBufferIndex == MediaCodec. INFO_TRY_AGAIN_LATER){
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"获得编码器输出缓存区超时");
|
||||
}else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
|
||||
// 如果API小于21,APP需要重新绑定编码器的输入缓存区;
|
||||
// 如果API大于21,则无需处理INFO_OUTPUT_BUFFERS_CHANGED
|
||||
if (!isLollipop()) {
|
||||
if(!isLollipop()){
|
||||
outputBuffers = mAudioEncoder.getOutputBuffers();
|
||||
}
|
||||
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
||||
}else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
|
||||
// 编码器输出缓存区格式改变,通常在存储数据之前且只会改变一次
|
||||
// 这里设置混合器视频轨道,如果音频已经添加则启动混合器(保证音视频同步)
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "编码器输出缓存区格式改变,添加视频轨道到混合器");
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"编码器输出缓存区格式改变,添加视频轨道到混合器");
|
||||
synchronized (AACEncodeConsumer.this) {
|
||||
newFormat = mAudioEncoder.getOutputFormat();
|
||||
if (mMuxerRef != null) {
|
||||
if(mMuxerRef != null){
|
||||
Mp4MediaMuxer muxer = mMuxerRef.get();
|
||||
if (muxer != null) {
|
||||
muxer.addTrack(newFormat, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}else{
|
||||
// 当flag属性置为BUFFER_FLAG_CODEC_CONFIG后,说明输出缓存区的数据已经被消费了
|
||||
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "编码数据被消费,BufferInfo的size属性置0");
|
||||
if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0){
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"编码数据被消费,BufferInfo的size属性置0");
|
||||
mBufferInfo.size = 0;
|
||||
}
|
||||
// 数据流结束标志,结束本次循环
|
||||
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "数据流结束,退出循环");
|
||||
if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"数据流结束,退出循环");
|
||||
break;
|
||||
}
|
||||
// 获取一个只读的输出缓存区inputBuffer ,它包含被编码好的数据
|
||||
ByteBuffer mBuffer = ByteBuffer.allocate(10240);
|
||||
ByteBuffer outputBuffer = null;
|
||||
if (!isLollipop()) {
|
||||
outputBuffer = outputBuffers[outputBufferIndex];
|
||||
} else {
|
||||
outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex);
|
||||
if(!isLollipop()){
|
||||
outputBuffer = outputBuffers[outputBufferIndex];
|
||||
}else{
|
||||
outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex);
|
||||
}
|
||||
if (mBufferInfo.size != 0) {
|
||||
if(mBufferInfo.size != 0){
|
||||
// 获取输出缓存区失败,抛出异常
|
||||
if (outputBuffer == null) {
|
||||
throw new RuntimeException("encodecOutputBuffer" + outputBufferIndex + "was null");
|
||||
if(outputBuffer == null){
|
||||
throw new RuntimeException("encodecOutputBuffer"+outputBufferIndex+"was null");
|
||||
}
|
||||
// 添加视频流到混合器
|
||||
if (mMuxerRef != null) {
|
||||
if(mMuxerRef != null){
|
||||
Mp4MediaMuxer muxer = mMuxerRef.get();
|
||||
if (muxer != null) {
|
||||
muxer.pumpStream(outputBuffer, mBufferInfo, false);
|
||||
@@ -228,25 +230,25 @@ public class AACEncodeConsumer extends Thread {
|
||||
addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7);
|
||||
mBuffer.flip();
|
||||
// 将AAC回调给MainModelImpl进行push
|
||||
if (listener != null) {
|
||||
Log.i(TAG, "----->得到aac数据流<-----");
|
||||
listener.onEncodeResult(mBuffer.array(), 0, mBufferInfo.size + 7, mBufferInfo.presentationTimeUs / 1000);
|
||||
if(listener != null){
|
||||
Log.i(TAG,"----->得到aac数据流<-----");
|
||||
listener.onEncodeResult(mBuffer.array(),0, mBufferInfo.size + 7, mBufferInfo.presentationTimeUs / 1000);
|
||||
}
|
||||
}
|
||||
// 处理结束,释放输出缓存区资源
|
||||
mAudioEncoder.releaseOutputBuffer(outputBufferIndex, false);
|
||||
mAudioEncoder.releaseOutputBuffer(outputBufferIndex,false);
|
||||
}
|
||||
} while (outputBufferIndex >= 0);
|
||||
}while (outputBufferIndex >= 0);
|
||||
}
|
||||
|
||||
private void initAudioRecord() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "AACEncodeConsumer-->开始采集音频");
|
||||
private void initAudioRecord(){
|
||||
if(DEBUG)
|
||||
Log.d(TAG,"AACEncodeConsumer-->开始采集音频");
|
||||
// 设置进程优先级
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
|
||||
int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT);
|
||||
for (final int src : AUDIO_SOURCES) {
|
||||
for (final int src: AUDIO_SOURCES) {
|
||||
try {
|
||||
mAudioRecord = new AudioRecord(src,
|
||||
SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
|
||||
@@ -266,18 +268,18 @@ public class AACEncodeConsumer extends Thread {
|
||||
mAudioRecord.startRecording();
|
||||
}
|
||||
|
||||
private void initMediaCodec() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "AACEncodeConsumer-->开始编码音频");
|
||||
private void initMediaCodec(){
|
||||
if(DEBUG)
|
||||
Log.d(TAG,"AACEncodeConsumer-->开始编码音频");
|
||||
MediaCodecInfo mCodecInfo = selectSupportCodec(MIME_TYPE);
|
||||
if (mCodecInfo == null) {
|
||||
Log.e(TAG, "编码器不支持" + MIME_TYPE + "类型");
|
||||
if(mCodecInfo == null){
|
||||
Log.e(TAG,"编码器不支持"+MIME_TYPE+"类型");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
try{
|
||||
mAudioEncoder = MediaCodec.createByCodecName(mCodecInfo.getName());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "创建编码器失败" + e.getMessage());
|
||||
}catch(IOException e){
|
||||
Log.e(TAG,"创建编码器失败"+e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
MediaFormat format = new MediaFormat();
|
||||
@@ -293,9 +295,9 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
|
||||
private void stopAudioRecord() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "AACEncodeConsumer-->停止采集音频");
|
||||
if (mAudioRecord != null) {
|
||||
if(DEBUG)
|
||||
Log.d(TAG,"AACEncodeConsumer-->停止采集音频");
|
||||
if(mAudioRecord != null){
|
||||
mAudioRecord.stop();
|
||||
mAudioRecord.release();
|
||||
mAudioRecord = null;
|
||||
@@ -303,9 +305,9 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
|
||||
private void stopMediaCodec() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "AACEncodeConsumer-->停止编码音频");
|
||||
if (mAudioEncoder != null) {
|
||||
if(DEBUG)
|
||||
Log.d(TAG,"AACEncodeConsumer-->停止编码音频");
|
||||
if(mAudioEncoder != null){
|
||||
mAudioEncoder.stop();
|
||||
mAudioEncoder.release();
|
||||
mAudioEncoder = null;
|
||||
@@ -314,28 +316,28 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
|
||||
// API>=21
|
||||
private boolean isLollipop() {
|
||||
private boolean isLollipop(){
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||
}
|
||||
|
||||
// API<=19
|
||||
private boolean isKITKAT() {
|
||||
private boolean isKITKAT(){
|
||||
return Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
|
||||
}
|
||||
|
||||
private long getPTSUs() {
|
||||
long result = System.nanoTime() / 1000;
|
||||
if (result < prevPresentationTimes) {
|
||||
result = (prevPresentationTimes - result) + result;
|
||||
private long getPTSUs(){
|
||||
long result = System.nanoTime()/1000;
|
||||
if(result < prevPresentationTimes){
|
||||
result = (prevPresentationTimes - result ) + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历所有编解码器,返回第一个与指定MIME类型匹配的编码器
|
||||
* 判断是否有支持指定mime类型的编码器
|
||||
*/
|
||||
private MediaCodecInfo selectSupportCodec(String mimeType) {
|
||||
* 判断是否有支持指定mime类型的编码器
|
||||
* */
|
||||
private MediaCodecInfo selectSupportCodec(String mimeType){
|
||||
int numCodecs = MediaCodecList.getCodecCount();
|
||||
for (int i = 0; i < numCodecs; i++) {
|
||||
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
|
||||
@@ -365,7 +367,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
|
||||
|
||||
private short[] transferByte2Short(byte[] data, int readBytes) {
|
||||
private short[] transferByte2Short(byte[] data,int readBytes){
|
||||
// byte[] 转 short[],数组长度缩减一半
|
||||
int shortLen = readBytes / 2;
|
||||
// 将byte[]数组装如ByteBuffer缓冲区
|
||||
|
||||
@@ -17,7 +17,9 @@ import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* 对YUV视频流进行编码
|
||||
* Created by jiangdongguo on 2017/5/6.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class H264EncodeConsumer extends Thread {
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@@ -10,9 +10,11 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Mp4封装混合器
|
||||
/**Mp4封装混合器
|
||||
*
|
||||
* Created by jianddongguo on 2017/7/28.
|
||||
*/
|
||||
|
||||
public class Mp4MediaMuxer {
|
||||
private static final boolean VERBOSE = false;
|
||||
private static final String TAG = Mp4MediaMuxer.class.getSimpleName();
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.serenegiant.utils;
|
||||
|
||||
import android.Manifest.permission;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PermissionGroupInfo;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class PermissionCheck {
|
||||
|
||||
public static final void dumpPermissions(@Nullable final Context context) {
|
||||
if (context == null) return;
|
||||
try {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final List<PermissionGroupInfo> list = pm.getAllPermissionGroups(PackageManager.GET_META_DATA);
|
||||
for (final PermissionGroupInfo info : list) {
|
||||
Log.d("PermissionCheck", info.name);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.w("", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* パーミッションを確認
|
||||
* @param context
|
||||
* @param permissionName
|
||||
* @return 指定したパーミッションがあればtrue
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public static boolean hasPermission(@Nullable final Context context, final String permissionName) {
|
||||
if (context == null) return false;
|
||||
boolean result = false;
|
||||
try {
|
||||
final int check;
|
||||
if (BuildCheck.isMarshmallow()) {
|
||||
check = context.checkSelfPermission(permissionName);
|
||||
} else {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
check = pm.checkPermission(permissionName, context.getPackageName());
|
||||
}
|
||||
switch (check) {
|
||||
case PackageManager.PERMISSION_DENIED:
|
||||
break;
|
||||
case PackageManager.PERMISSION_GRANTED:
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.w("", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 録音のミッションがあるかどうかを確認
|
||||
* @param context
|
||||
* @return 録音のパーミッションがあればtrue
|
||||
*/
|
||||
public static boolean hasAudio(@Nullable final Context context) {
|
||||
return hasPermission(context, permission.RECORD_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* ネットワークへのアクセスパーミッションがあるかどうかを確認
|
||||
* @param context
|
||||
* @return ネットワークへのアクセスパーミッションがあればtrue
|
||||
*/
|
||||
public static boolean hasNetwork(@Nullable final Context context) {
|
||||
return hasPermission(context, permission.INTERNET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 外部ストレージへの書き込みパーミッションがあるかどうかを確認
|
||||
* @param context
|
||||
* @return 外部ストレージへの書き込みパーミッションがあればtrue
|
||||
*/
|
||||
public static boolean hasWriteExternalStorage(@Nullable final Context context) {
|
||||
return hasPermission(context, permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 外部ストレージからの読み込みパーミッションがあるかどうかを確認
|
||||
* @param context
|
||||
* @return 外部ストレージへの読み込みパーミッションがあればtrue
|
||||
*/
|
||||
@SuppressLint("InlinedApi")
|
||||
public static boolean hasReadExternalStorage(@Nullable final Context context) {
|
||||
if (BuildCheck.isAndroid4())
|
||||
return hasPermission(context, permission.READ_EXTERNAL_STORAGE);
|
||||
else
|
||||
return hasPermission(context, permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 位置情報アクセスのパーミッションが有るかどうかを確認
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasAccessLocation(@Nullable final Context context) {
|
||||
return hasPermission(context, permission.ACCESS_COARSE_LOCATION)
|
||||
&& hasPermission(context, permission.ACCESS_FINE_LOCATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 低精度位置情報アクセスのパーミッションが有るかどうかを確認
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasAccessCoarseLocation(@Nullable final Context context) {
|
||||
return hasPermission(context, permission.ACCESS_COARSE_LOCATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 高精度位置情報アクセスのパーミッションが有るかどうかを確認
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasAccessFineLocation(@Nullable final Context context) {
|
||||
return hasPermission(context, permission.ACCESS_FINE_LOCATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* カメラへアクセス可能かどうか
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasCamera(@Nullable final Context context) {
|
||||
return hasPermission(context, permission.CAMERA);
|
||||
}
|
||||
|
||||
/**
|
||||
* アプリの詳細設定へ遷移させる(パーミッションを取得できなかった時など)
|
||||
* @param context
|
||||
*/
|
||||
public static void openSettings(@NonNull final Context context) {
|
||||
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
final Uri uri = Uri.fromParts("package", context.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* AndroidManifest.xmlに設定されているはずのパーミッションをチェックする
|
||||
* @param context
|
||||
* @param expectations
|
||||
* @return 空リストなら全てのパーミッションが入っていた,
|
||||
* @throws IllegalArgumentException
|
||||
* @throws PackageManager.NameNotFoundException
|
||||
*/
|
||||
public static List<String> missingPermissions(@NonNull final Context context, @NonNull final String[] expectations) throws IllegalArgumentException, PackageManager.NameNotFoundException {
|
||||
return missingPermissions(context, new ArrayList<String>(Arrays.asList(expectations)));
|
||||
}
|
||||
|
||||
/**
|
||||
* AndroidManifest.xmlに設定されているはずのパーミッションをチェックする
|
||||
* @param context
|
||||
* @param expectations
|
||||
* @return 空リストなら全てのパーミッションが入っていた,
|
||||
* @throws IllegalArgumentException
|
||||
* @throws PackageManager.NameNotFoundException
|
||||
*/
|
||||
public static List<String> missingPermissions(@NonNull final Context context, @NonNull final List<String> expectations) throws IllegalArgumentException, PackageManager.NameNotFoundException {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||
final String[] info = pi.requestedPermissions;
|
||||
if (info != null) {
|
||||
for (String i : info) {
|
||||
expectations.remove(i);
|
||||
}
|
||||
}
|
||||
return expectations;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="permission_title">Regarding permission</string>
|
||||
<!-- 録音のパーミッション RECORD_AUDIO -->
|
||||
<string name="permission_audio">No audio recording permission. \nAll audio function is disabled</string>
|
||||
<string name="permission_audio_recording_reason">Audio recording permission is necessary for movie capture with audio.</string>
|
||||
<string name="permission_audio_recording_request">Audio recording permission is necessary for movie capture with audio.</string>
|
||||
<string name="permission_audio_streaming_reason">Audio recording permission is necessary for streaming with audio.</string>
|
||||
<string name="permission_audio_streaming_request">Audio recording permission is necessary for streaming with audio.</string>
|
||||
<!-- 外部ストレージへの書き込みパーミッション WRITE_EXTERNAL_STORAGE -->
|
||||
<string name="permission_ext_storage">No permission of writing external storage. \nMovie/still image capturing are disabled.</string>
|
||||
<string name="permission_ext_storage_reason">Permission of writing external storage is necessary for movie/still image capturing.</string>
|
||||
<string name="permission_ext_storage_request">Permission of writing external storage is necessary for movie/still image capturing.</string>
|
||||
<string name="permission_ext_storage_finish">No writing external storage permission. \nApp will finish soon</string>
|
||||
<!-- ネットワークアクセスのパーミッション INTERNET -->
|
||||
<string name="permission_network">No network access permission.</string>
|
||||
<string name="permission_network_reason">Network access permission is necessary for streaming.</string>
|
||||
<string name="permission_network_request">Network access permission is necessary for streaming.</string>
|
||||
<string name="permission_network_finish">No network access permission. \nApp will finish soon</string>
|
||||
<!-- 位置情報のパーミッション -->
|
||||
<string name="permission_location">No location access permission</string>
|
||||
<string name="permission_location_reason">Location access permission is necessary for %s</string>
|
||||
<string name="permission_location_request">Location access permission is necessary for %s</string>
|
||||
<string name="permission_location_finish">No location access permission. \nApp will finish soon</string>
|
||||
<!-- カメラアクセスのパーミッション -->
|
||||
<string name="permission_camera">No camera access permission</string>
|
||||
<string name="permission_camera_finish">No camera access permission. \nApp will finish soon</string>
|
||||
<string name="permission_camera_reason">Camera access permission is necessary for fallback to built in camera.</string>
|
||||
<string name="permission_camera_request">Camera access permission is necessary for fallback to built in camera.</string>
|
||||
<!-- ハードウエアID・通話状態へのアクセスパーミッション -->
|
||||
<string name="permission_hardware_id_request">Permission to access phone state/hardware id necessary for calling.</string>
|
||||
</resources>
|
||||
@@ -62,6 +62,7 @@ dependencies {
|
||||
implementation rootProject.ext.dependencies.mogo_core_utils
|
||||
implementation rootProject.ext.dependencies.mogo_core_data
|
||||
implementation rootProject.ext.dependencies.mogo_core_function_call
|
||||
implementation rootProject.ext.dependencies.mogo_core_function_carcorder
|
||||
} else {
|
||||
api project(":foudations:mogo-commons")
|
||||
api project(':modules:mogo-module-common')
|
||||
@@ -72,6 +73,8 @@ dependencies {
|
||||
implementation project(':core:mogo-core-utils')
|
||||
implementation project(':core:mogo-core-data')
|
||||
implementation project(':core:mogo-core-function-call')
|
||||
implementation project(':core:function-impl:mogo-core-function-carcorder')
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.mogo.module.main;
|
||||
|
||||
import static com.mogo.module.main.MainPresenter.MOGO_PERMISSION_REQUEST_CODE;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
@@ -22,6 +24,7 @@ import com.mogo.commons.mvp.MvpActivity;
|
||||
import com.mogo.commons.mvp.MvpFragment;
|
||||
import com.mogo.eagle.core.data.constants.MoGoFragmentPaths;
|
||||
import com.mogo.eagle.core.data.map.MogoLocation;
|
||||
import com.mogo.eagle.core.function.carcorder.service.CarcorderService;
|
||||
import com.mogo.eagle.core.utilcode.mogo.AppLaunchTimeUtils;
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
|
||||
import com.mogo.eagle.core.utilcode.mogo.permissions.PermissionsDialogUtils;
|
||||
@@ -50,8 +53,6 @@ import com.zhidao.autopilot.support.api.AutopilotServiceManage;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.mogo.module.main.MainPresenter.MOGO_PERMISSION_REQUEST_CODE;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2019-12-23
|
||||
@@ -255,8 +256,12 @@ public class MainActivity extends MvpActivity<MainView, MainPresenter> implement
|
||||
}
|
||||
|
||||
private void startBaseService() {
|
||||
Intent intent = new Intent(this, MogoMainService.class);
|
||||
startService(intent);
|
||||
Intent intentMainServicee = new Intent(this, MogoMainService.class);
|
||||
startService(intentMainServicee);
|
||||
|
||||
// USB 摄像头行车记录仪进程
|
||||
Intent intentCarcorderService = new Intent(this, CarcorderService.class);
|
||||
startService(intentCarcorderService);
|
||||
}
|
||||
|
||||
protected void loadContainerModules() {
|
||||
@@ -272,7 +277,7 @@ public class MainActivity extends MvpActivity<MainView, MainPresenter> implement
|
||||
|
||||
@Override
|
||||
public void loadFunctionFragment() {
|
||||
Logger.d(TAG,"loadFunctionFragment……");
|
||||
Logger.d(TAG, "loadFunctionFragment……");
|
||||
// 加载 HMI 图层
|
||||
BaseFragment fragmentHdMap = (BaseFragment) ARouter.getInstance().build(MoGoFragmentPaths.PATH_FRAGMENT_HMI).navigation();
|
||||
addFragment(fragmentHdMap, fragmentHdMap.getTagName(), R.id.module_main_id_waring_fragment);
|
||||
|
||||
@@ -66,17 +66,17 @@ public class ApmCrashReportProvider implements ITestCrashReportProvider {
|
||||
//设置分配的appid
|
||||
builder.aid(BYTEAMP_APPID);
|
||||
//是否开启卡顿功能
|
||||
builder.blockDetect(true);
|
||||
builder.blockDetect(false);
|
||||
//是否开启严重卡顿功能
|
||||
builder.seriousBlockDetect(true);
|
||||
builder.seriousBlockDetect(false);
|
||||
//是否开启流畅性和丢帧
|
||||
builder.fpsMonitor(true);
|
||||
builder.fpsMonitor(false);
|
||||
//控制是否打开WebVeiw监控
|
||||
builder.enableWebViewMonitor(true);
|
||||
builder.enableWebViewMonitor(false);
|
||||
//控制是否打开内存监控
|
||||
builder.memoryMonitor(true);
|
||||
builder.memoryMonitor(false);
|
||||
//控制是否打开电量监控
|
||||
builder.batteryMonitor(true);
|
||||
builder.batteryMonitor(false);
|
||||
//是否打印日志,注:线上release版本要配置为false
|
||||
builder.debugMode(true);
|
||||
//支持用户自定义user_id把平台数据和自己用户关联起来,可以不配置
|
||||
|
||||
Reference in New Issue
Block a user