From 6988ae803bb283f2bcf37589e7771ebc72ea6002 Mon Sep 17 00:00:00 2001 From: John Zhao Date: Sun, 27 Jan 2019 23:34:47 +0800 Subject: [PATCH] feat(android): view mynteye datas --- .../slightech/mynteye/demo/MyApplication.java | 2 + .../mynteye/demo/camera/Mynteye.java | 123 ++++++++++++++++++ .../mynteye/demo/ui/MainActivity.java | 101 +++++++++++++- .../app/src/main/res/layout/activity_main.xml | 30 ++++- .../src/main/cpp/mynteye/cpp/device.hpp | 4 +- .../src/main/cpp/mynteye/impl/device_impl.cpp | 21 ++- .../src/main/cpp/mynteye/impl/device_impl.hpp | 4 +- .../cpp/mynteye/impl/motion_data_impl.hpp | 4 +- .../cpp/mynteye/impl/stream_data_impl.hpp | 8 +- .../src/main/cpp/mynteye/jni/NativeDevice.cpp | 4 +- .../java/com/slightech/mynteye/Device.java | 10 +- .../android/mynteye/scripts/mynteye.djinni | 4 +- 12 files changed, 288 insertions(+), 27 deletions(-) create mode 100644 wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/camera/Mynteye.java diff --git a/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/MyApplication.java b/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/MyApplication.java index b72b43d..7eed9ec 100644 --- a/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/MyApplication.java +++ b/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/MyApplication.java @@ -1,6 +1,7 @@ package com.slightech.mynteye.demo; import android.app.Application; +//import com.stericson.RootShell.RootShell; import timber.log.Timber; public class MyApplication extends Application { @@ -16,6 +17,7 @@ public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Timber.plant(new Timber.DebugTree()); + //RootShell.debugMode = true; } @Override public void onLowMemory() { diff --git a/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/camera/Mynteye.java b/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/camera/Mynteye.java new file mode 100644 index 0000000..7c3031d --- /dev/null +++ b/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/camera/Mynteye.java @@ -0,0 +1,123 @@ +package com.slightech.mynteye.demo.camera; + +import android.os.Handler; +import android.os.HandlerThread; +import com.slightech.mynteye.Device; +import com.slightech.mynteye.DeviceUsbInfo; +import com.slightech.mynteye.MotionData; +import com.slightech.mynteye.Source; +import com.slightech.mynteye.Stream; +import com.slightech.mynteye.StreamData; +import com.slightech.mynteye.StreamRequest; +import java.util.ArrayList; + +public final class Mynteye implements Runnable { + + private Device mDevice; + + private HandlerThread mBackgroundThread; + private Handler mBackgroundHandler; + + private boolean mOpened; + + public interface OnStreamDataReceiveListener { + void onStreamDataReceive(Stream stream, StreamData data, Handler handler); + void onStreamLeftReceive(StreamData data, Handler handler); + void onStreamRightReceive(StreamData data, Handler handler); + } + + public interface OnMotionDataReceiveListener { + void onMotionDataReceive(ArrayList datas, Handler handler); + } + + private OnStreamDataReceiveListener mOnStreamDataReceiveListener; + private OnMotionDataReceiveListener mOnMotionDataReceiveListener; + + public Mynteye(DeviceUsbInfo info) { + mDevice = Device.create(info); + mOpened = false; + } + + public void setOnStreamDataReceiveListener(OnStreamDataReceiveListener l) { + mOnStreamDataReceiveListener = l; + } + + public void setOnMotionDataReceiveListener(OnMotionDataReceiveListener l) { + mOnMotionDataReceiveListener = l; + } + + public ArrayList getStreamRequests() { + return mDevice.getStreamRequests(); + } + + public void open(StreamRequest request) { + if (mOpened) return; + mOpened = true; + startBackgroundThread(); + + mDevice.configStreamRequest(request); + mDevice.enableMotionDatas(Integer.MAX_VALUE); + mDevice.start(Source.ALL); + + mBackgroundHandler.post(this); + } + + public void close() { + if (!mOpened) return; + mOpened = false; + stopBackgroundThread(); + mDevice.stop(Source.ALL); + } + + @Override + public void run() { + //Timber.i("wait streams"); + mDevice.waitForStreams(); + + //Timber.i("get streams"); + { + StreamData data = mDevice.getStreamData(Stream.LEFT); + if (mOnStreamDataReceiveListener != null) { + mOnStreamDataReceiveListener.onStreamDataReceive(Stream.LEFT, data, mBackgroundHandler); + mOnStreamDataReceiveListener.onStreamLeftReceive(data, mBackgroundHandler); + } + } + { + StreamData data = mDevice.getStreamData(Stream.RIGHT); + if (mOnStreamDataReceiveListener != null) { + mOnStreamDataReceiveListener.onStreamDataReceive(Stream.RIGHT, data, mBackgroundHandler); + mOnStreamDataReceiveListener.onStreamRightReceive(data, mBackgroundHandler); + } + } + + //Timber.i("get motions"); + { + ArrayList datas = mDevice.getMotionDatas(); + if (mOnMotionDataReceiveListener != null) { + mOnMotionDataReceiveListener.onMotionDataReceive(datas, mBackgroundHandler); + } + } + + if (mOpened) mBackgroundHandler.post(this); + } + + private void startBackgroundThread() { + mBackgroundThread = new HandlerThread("MynteyeBackground"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + } + + private void stopBackgroundThread() { + mBackgroundThread.quitSafely(); + //mBackgroundThread.interrupt(); + try { + mBackgroundHandler.removeCallbacksAndMessages(null); + mBackgroundThread.join(); + mBackgroundThread = null; + mBackgroundHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/ui/MainActivity.java b/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/ui/MainActivity.java index 2eec261..55c422c 100644 --- a/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/ui/MainActivity.java +++ b/wrappers/android/mynteye/app/src/main/java/com/slightech/mynteye/demo/ui/MainActivity.java @@ -1,21 +1,43 @@ package com.slightech.mynteye.demo.ui; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.os.Bundle; +import android.os.Handler; import android.view.Menu; import android.view.MenuItem; import android.widget.ArrayAdapter; +import android.widget.ImageView; import android.widget.ListView; +import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; +import butterknife.BindView; import butterknife.ButterKnife; import com.slightech.mynteye.Device; import com.slightech.mynteye.DeviceUsbInfo; +import com.slightech.mynteye.Frame; +import com.slightech.mynteye.MotionData; +import com.slightech.mynteye.Stream; +import com.slightech.mynteye.StreamData; +import com.slightech.mynteye.StreamRequest; import com.slightech.mynteye.demo.R; +import com.slightech.mynteye.demo.camera.Mynteye; import com.slightech.mynteye.demo.util.RootUtils; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Locale; +import timber.log.Timber; -public class MainActivity extends BaseActivity { +public class MainActivity extends BaseActivity implements Mynteye.OnStreamDataReceiveListener, + Mynteye.OnMotionDataReceiveListener{ + + @BindView(R.id.text) TextView mTextView; + @BindView(R.id.image_left) ImageView mLeftImageView; + @BindView(R.id.image_right) ImageView mRightImageView; + + private Mynteye mMynteye; + private Bitmap mLeftBitmap, mRightBitmap; @Override protected void onCreate(Bundle savedInstanceState) { @@ -90,6 +112,83 @@ public class MainActivity extends BaseActivity { } private void openDevice(DeviceUsbInfo info) { + mMynteye = new Mynteye(info); + ArrayList requests = mMynteye.getStreamRequests(); + if (requests.isEmpty()) { + alert("Warning", "There are no streams to request :("); + } else { + ArrayList items = new ArrayList<>(); + for (StreamRequest req : requests) { + items.add(req.toString()); + } + + AlertDialog dialog = new AlertDialog.Builder(this) + .setTitle("StreamRequests") + .create(); + ListView listView = new ListView(this); + listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items)); + listView.setOnItemClickListener((parent, view, position, id) -> { + dialog.dismiss(); + mMynteye.setOnStreamDataReceiveListener(this); + mMynteye.setOnMotionDataReceiveListener(this); + mMynteye.open(requests.get(position)); + }); + dialog.setView(listView); + dialog.show(); + } + } + + @Override + public void onStreamDataReceive(Stream stream, StreamData data, Handler handler) { + } + + @Override + public void onStreamLeftReceive(StreamData data, Handler handler) { + //Timber.i("onStreamLeftReceive"); + Frame frame = data.frame(); + if (mLeftBitmap == null) { + Bitmap.Config config; + switch (frame.format()) { + case GREY: config = Bitmap.Config.ALPHA_8; break; + case RGB888: config = Bitmap.Config.ARGB_8888; break; + default: Timber.e("Unaccepted stream format"); return; + } + mLeftBitmap = Bitmap.createBitmap(frame.width(), frame.height(), config); + } + mLeftBitmap.copyPixelsFromBuffer(ByteBuffer.wrap(frame.data())); + mLeftImageView.post(() -> mLeftImageView.setImageBitmap(mLeftBitmap)); + } + + @Override + public void onStreamRightReceive(StreamData data, Handler handler) { + //Timber.i("onStreamRightReceive"); + Frame frame = data.frame(); + if (mRightBitmap == null) { + Bitmap.Config config; + switch (frame.format()) { + case GREY: config = Bitmap.Config.ALPHA_8; break; + case RGB888: config = Bitmap.Config.ARGB_8888; break; + default: Timber.e("Unaccepted stream format"); return; + } + mRightBitmap = Bitmap.createBitmap(frame.width(), frame.height(), config); + } + mRightBitmap.copyPixelsFromBuffer(ByteBuffer.wrap(frame.data())); + mRightImageView.post(() -> mRightImageView.setImageBitmap(mRightBitmap)); + } + + @Override + public void onMotionDataReceive(ArrayList datas, Handler handler) { + if (datas.isEmpty()) return; + mTextView.post(() -> mTextView.setText(datas.get(0).imu().toString())); + } + + @Override + protected void onDestroy() { + if (mMynteye != null) { + mMynteye.close(); + mMynteye = null; + } + super.onDestroy(); } private void toast(CharSequence text) { diff --git a/wrappers/android/mynteye/app/src/main/res/layout/activity_main.xml b/wrappers/android/mynteye/app/src/main/res/layout/activity_main.xml index 32dae9a..ecf855f 100644 --- a/wrappers/android/mynteye/app/src/main/res/layout/activity_main.xml +++ b/wrappers/android/mynteye/app/src/main/res/layout/activity_main.xml @@ -12,11 +12,35 @@ android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" android:text="Hello World!" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" + app:layout_constraintBottom_toTopOf="@+id/image_left" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + \ No newline at end of file diff --git a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/cpp/device.hpp b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/cpp/device.hpp index bc620a6..63fb43e 100644 --- a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/cpp/device.hpp +++ b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/cpp/device.hpp @@ -47,8 +47,8 @@ public: /** Get the datas of stream */ virtual std::vector> GetStreamDatas(::mynteye_jni::Stream stream) = 0; - /** Enable cache motion datas */ - virtual void EnableCacheMotionDatas(int32_t max_size) = 0; + /** Enable cache motion datas until get them, otherwise using callback instead */ + virtual void EnableMotionDatas(int32_t max_size) = 0; /** Get the motion datas */ virtual std::vector> GetMotionDatas() = 0; diff --git a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/device_impl.cpp b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/device_impl.cpp index a35561b..b00e49e 100644 --- a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/device_impl.cpp +++ b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/device_impl.cpp @@ -81,30 +81,43 @@ void DeviceImpl::ConfigStreamRequest( } void DeviceImpl::Start(::mynteye_jni::Source source) { + device_->Start(from_jni(source)); } void DeviceImpl::Stop(::mynteye_jni::Source source) { + device_->Stop(from_jni(source)); } void DeviceImpl::WaitForStreams() { + device_->WaitForStreams(); } std::shared_ptr<::mynteye_jni::StreamData> DeviceImpl::GetStreamData( ::mynteye_jni::Stream stream) { - return nullptr; + auto&& data = device_->GetStreamData(from_jni(stream)); + return std::make_shared(data); } std::vector> DeviceImpl::GetStreamDatas(::mynteye_jni::Stream stream) { - return {}; + std::vector> datas; + for (auto&& data : device_->GetStreamDatas(from_jni(stream))) { + datas.push_back(std::make_shared(data)); + } + return datas; } -void DeviceImpl::EnableCacheMotionDatas(int32_t max_size) { +void DeviceImpl::EnableMotionDatas(int32_t max_size) { + device_->EnableMotionDatas(max_size); } std::vector> DeviceImpl::GetMotionDatas() { - return {}; + std::vector> datas; + for (auto&& data : device_->GetMotionDatas()) { + datas.push_back(std::make_shared(data)); + } + return datas; } } // namespace mynteye_jni diff --git a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/device_impl.hpp b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/device_impl.hpp index 1a555bb..3068633 100644 --- a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/device_impl.hpp +++ b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/device_impl.hpp @@ -37,8 +37,8 @@ class DeviceImpl : public Device { /** Get the datas of stream */ std::vector> GetStreamDatas(::mynteye_jni::Stream stream) override; - /** Enable cache motion datas */ - void EnableCacheMotionDatas(int32_t max_size) override; + /** Enable cache motion datas until get them, otherwise using callback instead */ + void EnableMotionDatas(int32_t max_size) override; /** Get the motion datas */ std::vector> GetMotionDatas() override; diff --git a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/motion_data_impl.hpp b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/motion_data_impl.hpp index 45db7aa..2508f5e 100644 --- a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/motion_data_impl.hpp +++ b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/motion_data_impl.hpp @@ -9,13 +9,13 @@ namespace mynteye_jni { class MotionDataImpl : public MotionData { public: - using motion_data_t = std::shared_ptr; + using motion_data_t = MYNTEYE_NAMESPACE::device::MotionData; explicit MotionDataImpl(const motion_data_t& data) : data_(data) {} ~MotionDataImpl() {} ImuData Imu() override { - auto&& imu = data_->imu; + auto&& imu = data_.imu; return { imu->frame_id, imu->flag, diff --git a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/stream_data_impl.hpp b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/stream_data_impl.hpp index 8e3d5db..7b21ee7 100644 --- a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/stream_data_impl.hpp +++ b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/impl/stream_data_impl.hpp @@ -10,13 +10,13 @@ namespace mynteye_jni { class StreamDataImpl : public StreamData { public: - using stream_data_t = std::shared_ptr; + using stream_data_t = MYNTEYE_NAMESPACE::device::StreamData; explicit StreamDataImpl(const stream_data_t& data) : data_(data) {} ~StreamDataImpl() {} ImgData Img() override { - auto&& img = data_->img; + auto&& img = data_.img; return { img->frame_id, static_cast(img->timestamp), @@ -25,11 +25,11 @@ class StreamDataImpl : public StreamData { } std::shared_ptr<::mynteye_jni::Frame> Frame() override { - return std::make_shared<::mynteye_jni::FrameImpl>(data_->frame); + return std::make_shared<::mynteye_jni::FrameImpl>(data_.frame); } int64_t FrameId() override { - return data_->frame_id; + return data_.frame_id; } private: diff --git a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/jni/NativeDevice.cpp b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/jni/NativeDevice.cpp index 1673a2a..bc12a52 100644 --- a/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/jni/NativeDevice.cpp +++ b/wrappers/android/mynteye/libmynteye/src/main/cpp/mynteye/jni/NativeDevice.cpp @@ -109,12 +109,12 @@ CJNIEXPORT jobject JNICALL Java_com_slightech_mynteye_Device_00024CppProxy_nativ } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } -CJNIEXPORT void JNICALL Java_com_slightech_mynteye_Device_00024CppProxy_native_1enableCacheMotionDatas(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jint j_maxSize) +CJNIEXPORT void JNICALL Java_com_slightech_mynteye_Device_00024CppProxy_native_1enableMotionDatas(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jint j_maxSize) { try { DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef); const auto& ref = ::djinni::objectFromHandleAddress<::mynteye_jni::Device>(nativeRef); - ref->EnableCacheMotionDatas(::djinni::I32::toCpp(jniEnv, j_maxSize)); + ref->EnableMotionDatas(::djinni::I32::toCpp(jniEnv, j_maxSize)); } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ) } diff --git a/wrappers/android/mynteye/libmynteye/src/main/java/com/slightech/mynteye/Device.java b/wrappers/android/mynteye/libmynteye/src/main/java/com/slightech/mynteye/Device.java index 7b90aef..0095978 100644 --- a/wrappers/android/mynteye/libmynteye/src/main/java/com/slightech/mynteye/Device.java +++ b/wrappers/android/mynteye/libmynteye/src/main/java/com/slightech/mynteye/Device.java @@ -34,8 +34,8 @@ public interface Device { @NonNull public ArrayList getStreamDatas(@NonNull com.slightech.mynteye.Stream stream); - /** Enable cache motion datas */ - public void enableCacheMotionDatas(int maxSize); + /** Enable cache motion datas until get them, otherwise using callback instead */ + public void enableMotionDatas(int maxSize); /** Get the motion datas */ @NonNull @@ -135,12 +135,12 @@ public interface Device { private native ArrayList native_getStreamDatas(long _nativeRef, com.slightech.mynteye.Stream stream); @Override - public void enableCacheMotionDatas(int maxSize) + public void enableMotionDatas(int maxSize) { assert !this.destroyed.get() : "trying to use a destroyed object"; - native_enableCacheMotionDatas(this.nativeRef, maxSize); + native_enableMotionDatas(this.nativeRef, maxSize); } - private native void native_enableCacheMotionDatas(long _nativeRef, int maxSize); + private native void native_enableMotionDatas(long _nativeRef, int maxSize); @Override public ArrayList getMotionDatas() diff --git a/wrappers/android/mynteye/scripts/mynteye.djinni b/wrappers/android/mynteye/scripts/mynteye.djinni index e77db0c..473d68a 100644 --- a/wrappers/android/mynteye/scripts/mynteye.djinni +++ b/wrappers/android/mynteye/scripts/mynteye.djinni @@ -26,8 +26,8 @@ device = interface +c { # Get the datas of stream get_stream_datas(stream: stream): list; - # Enable cache motion datas - enable_cache_motion_datas(max_size: i32); + # Enable cache motion datas until get them, otherwise using callback instead + enable_motion_datas(max_size: i32); # Get the motion datas get_motion_datas(): list; }