From 8dd4c6d56a5c6ae061be4c0976480e020df0de71 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 18 Jun 2023 20:57:48 +0200 Subject: [PATCH] Added Video detection to project --- app/build.gradle | 6 + app/src/main/AndroidManifest.xml | 3 + .../com/example/greenwatch/MainActivity.java | 12 +- .../greenwatch/VideodetectionActivity.java | 71 ++++++++++ .../greenwatch/sensors/CameraSensor.java | 126 +++++++++++++++++ .../viewmodels/AccelerometerViewModel.java | 9 +- .../viewmodels/MainActivityViewModel.java | 21 ++- .../viewmodels/VideodetectionViewModel.java | 131 ++++++++++++++++++ .../viewmodels/ViewModelInterface.java | 1 + .../res/layout/activity_videodetection.xml | 23 +++ 10 files changed, 394 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/example/greenwatch/sensors/CameraSensor.java create mode 100644 app/src/main/java/com/example/greenwatch/viewmodels/VideodetectionViewModel.java diff --git a/app/build.gradle b/app/build.gradle index e5afc91..a919618 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,4 +35,10 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + + def camerax_version = "1.2.3" + implementation "androidx.camera:camera-camera2:${camerax_version}" + implementation "androidx.camera:camera-lifecycle:${camerax_version}" + implementation "androidx.camera:camera-view:${camerax_version}" + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 803c5b5..e348435 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,9 @@ package="com.example.greenwatch"> + + + >() { @Override public void onChanged(List devices) { @@ -82,10 +83,13 @@ public class MainActivity extends AppCompatActivity { videodetectionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - //openVideodetectionActivity(); - mMainActivityViewModel.setSensorType(mMainActivityViewModel.getLocalDeviceUUID(), "Video"); - mMainActivityViewModel.setTimeStamp(mMainActivityViewModel.getLocalDeviceUUID(), "12:50"); - mMainActivityViewModel.setDeviceID(mMainActivityViewModel.getLocalDeviceUUID(), "12345"); + if (mMainActivityViewModel.isCameraAccessAllowed(MainActivity.this)) { + openVideodetectionActivity(); + } + else { + mMainActivityViewModel.accessRequestCamera(MainActivity.this); + } + } }); accelerometerButton.setOnClickListener(new View.OnClickListener() { diff --git a/app/src/main/java/com/example/greenwatch/VideodetectionActivity.java b/app/src/main/java/com/example/greenwatch/VideodetectionActivity.java index 2547bde..cd119a2 100644 --- a/app/src/main/java/com/example/greenwatch/VideodetectionActivity.java +++ b/app/src/main/java/com/example/greenwatch/VideodetectionActivity.java @@ -1,14 +1,32 @@ package com.example.greenwatch; import androidx.appcompat.app.AppCompatActivity; +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.core.content.ContextCompat; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.os.Bundle; import android.view.View; import android.widget.Button; +import android.widget.Toast; + +import com.example.greenwatch.adapters.AlarmHistoryListAdapter; +import com.example.greenwatch.adapters.DeviceListAdapter; +import com.example.greenwatch.models.Device; +import com.example.greenwatch.viewmodels.AccelerometerViewModel; +import com.example.greenwatch.viewmodels.VideodetectionViewModel; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.List; +import java.util.concurrent.ExecutionException; public class VideodetectionActivity extends AppCompatActivity { private Button backToMainActivity; + private VideodetectionViewModel mVideoDetectionViewModel; @Override protected void onCreate(Bundle savedInstanceState) { @@ -17,11 +35,64 @@ public class VideodetectionActivity extends AppCompatActivity { backToMainActivity = (Button) findViewById(R.id.videodetectorBackToMainActivity); + RecyclerView recyclerView = findViewById(R.id.deviceListRecyclerView); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setHasFixedSize(true); + + final DeviceListAdapter deviceListAdapter = new DeviceListAdapter(); + recyclerView.setAdapter(deviceListAdapter); + + RecyclerView alarmHistoryListRecyclerView = findViewById(R.id.alarmHistoryListRecyclerView); + alarmHistoryListRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + alarmHistoryListRecyclerView.setHasFixedSize(true); + + final AlarmHistoryListAdapter alarmHistoryListAdapter = new AlarmHistoryListAdapter(); + alarmHistoryListRecyclerView.setAdapter(alarmHistoryListAdapter); + backToMainActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); + + mVideoDetectionViewModel = new ViewModelProvider(this).get(VideodetectionViewModel.class); + mVideoDetectionViewModel.init(); + mVideoDetectionViewModel.getConnectedDeviceList().observe(this, new Observer>() { + @Override + public void onChanged(List devices) { + deviceListAdapter.setDevices(devices); + Toast.makeText(VideodetectionActivity.this, "onChanged", Toast.LENGTH_LONG).show(); + } + }); + + mVideoDetectionViewModel.getAlarmHistoryList().observe(this, new Observer>() { + @Override + public void onChanged(List devices) { + alarmHistoryListAdapter.setAlarmHistoryList(devices); + } + }); + + mVideoDetectionViewModel.getVideoAlarmDetectedValue().observe(this, new Observer() { + @Override + public void onChanged(Boolean aBoolean) { + if (aBoolean) { + mVideoDetectionViewModel.updateDevice(mVideoDetectionViewModel.getLocalDeviceUUID(), mVideoDetectionViewModel.getSystemTimeStamp(), true, "Video", 10); + } + else { + mVideoDetectionViewModel.updateDevice(mVideoDetectionViewModel.getLocalDeviceUUID(), mVideoDetectionViewModel.getSystemTimeStamp(), false, "Video", 0); + } + + } + }); + + final ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(this); + cameraProviderFuture.addListener(() -> { + try { + mVideoDetectionViewModel.bindImageAnalysis(cameraProviderFuture.get(), this, this); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + }, ContextCompat.getMainExecutor(this)); } } \ No newline at end of file diff --git a/app/src/main/java/com/example/greenwatch/sensors/CameraSensor.java b/app/src/main/java/com/example/greenwatch/sensors/CameraSensor.java new file mode 100644 index 0000000..7933115 --- /dev/null +++ b/app/src/main/java/com/example/greenwatch/sensors/CameraSensor.java @@ -0,0 +1,126 @@ +package com.example.greenwatch.sensors; + +import android.content.Context; +import android.graphics.ImageFormat; +import android.media.Image; +import android.util.Size; + +import androidx.camera.core.CameraSelector; +import androidx.camera.core.ImageAnalysis; +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.core.content.ContextCompat; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.MutableLiveData; + +import java.nio.ByteBuffer; + +public class CameraSensor { + private final MutableLiveData mVideoAlarmDetected = new MutableLiveData<>(); + private static CameraSensor cameraSensorInstance; + private boolean videoAlarmDetected; + private ByteBuffer previousBuffer; + private int previousWidth; + private int previousHeight; + + private CameraSensor() { + videoAlarmDetected = false; + } + + public static synchronized CameraSensor getInstance() { + if (cameraSensorInstance == null){ + cameraSensorInstance = new CameraSensor(); + } + return cameraSensorInstance; + } + + public MutableLiveData getVideoAlarmDetectedValue() { + setMutableLiveDataVideoMovementDetected(); + return mVideoAlarmDetected; + } + + private void setMutableLiveDataVideoMovementDetected() { + mVideoAlarmDetected.setValue(videoAlarmDetected); + } + + public void bindImageAnalysis(ProcessCameraProvider cameraProvider, LifecycleOwner lifecycleOwner, Context context) { + ImageAnalysis.Builder builder = new ImageAnalysis.Builder(); + builder.setTargetResolution(new Size(640, 480)); + builder.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST); + ImageAnalysis imageAnalysis = builder.build(); + imageAnalysis.setAnalyzer( + ContextCompat.getMainExecutor(context), + imageProxy -> { + + int imageFormat = imageProxy.getFormat(); + + if (imageFormat == ImageFormat.YUV_420_888) { + + Image currentImage = imageProxy.getImage(); + + if (previousHeight != 0) { + assert currentImage != null; + videoAlarmDetected = compareFrames(currentImage); + } + + assert currentImage != null; + Image.Plane[] planes = currentImage.getPlanes(); + ByteBuffer buffer = planes[0].getBuffer().duplicate(); + previousBuffer = ByteBuffer.allocateDirect(buffer.remaining()); + previousBuffer.put(buffer); + previousWidth = currentImage.getWidth(); + previousHeight = currentImage.getHeight(); + + currentImage.close(); + + checkAlarmCondition(); + + } + + imageProxy.close(); + }); + + CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build(); + + cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis); + } + + private boolean compareFrames(Image currentImage) { + + ByteBuffer currentBuffer = currentImage.getPlanes()[0].getBuffer(); + + int currentWidth = currentImage.getWidth(); + int currentHeight = currentImage.getHeight(); + + if (previousWidth != currentWidth || previousHeight != currentHeight) { + return false; + } + + for (int row = 0; row < previousHeight; row++) { + for (int col = 0; col < previousWidth; col++) { + + int previousIndex = row * previousWidth + col; + int currentIndex = row * currentWidth + col; + + int previousPixel = previousBuffer.get(previousIndex) & 0xFF; + int currentPixel = currentBuffer.get(currentIndex) & 0xFF; + + int pixelDifference = Math.abs(previousPixel - currentPixel); + int threshold = 120; + + if (pixelDifference > threshold) { + return true; + } + } + } + return false; + } + + public void checkAlarmCondition() { + if (videoAlarmDetected && !mVideoAlarmDetected.getValue()) { + setMutableLiveDataVideoMovementDetected(); + } + else if (!videoAlarmDetected && mVideoAlarmDetected.getValue()){ + setMutableLiveDataVideoMovementDetected(); + } + } +} diff --git a/app/src/main/java/com/example/greenwatch/viewmodels/AccelerometerViewModel.java b/app/src/main/java/com/example/greenwatch/viewmodels/AccelerometerViewModel.java index 14bec64..bd767ab 100644 --- a/app/src/main/java/com/example/greenwatch/viewmodels/AccelerometerViewModel.java +++ b/app/src/main/java/com/example/greenwatch/viewmodels/AccelerometerViewModel.java @@ -82,10 +82,6 @@ public class AccelerometerViewModel extends ViewModel implements ViewModelInterf } } - public String getSystemTimeStamp() { - return mDeviceRepository.getSystemTimeStamp(); - } - public LiveData getMovementDetectedValue() { return mMovementDetected; } @@ -159,4 +155,9 @@ public class AccelerometerViewModel extends ViewModel implements ViewModelInterf public String getLocalDeviceUUID() { return mDeviceRepository.getLocalDeviceUUID(); } + + @Override + public String getSystemTimeStamp() { + return mDeviceRepository.getSystemTimeStamp(); + } } diff --git a/app/src/main/java/com/example/greenwatch/viewmodels/MainActivityViewModel.java b/app/src/main/java/com/example/greenwatch/viewmodels/MainActivityViewModel.java index 0405d93..cce6ee0 100644 --- a/app/src/main/java/com/example/greenwatch/viewmodels/MainActivityViewModel.java +++ b/app/src/main/java/com/example/greenwatch/viewmodels/MainActivityViewModel.java @@ -1,5 +1,11 @@ package com.example.greenwatch.viewmodels; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; @@ -15,6 +21,7 @@ public class MainActivityViewModel extends ViewModel implements ViewModelInterfa private MutableLiveData> mDeviceList; private MutableLiveData> mAlarmHistoryList; private DeviceRepository mDeviceRepository; + private static final int RECHTEANFORDERUNG_KAMERA = 10; @Override public void init() { @@ -28,11 +35,18 @@ public class MainActivityViewModel extends ViewModel implements ViewModelInterfa mDeviceRepository.setWiFiCommunication(mWiFiCommunication); mWiFiCommunication.setDeviceRepository(mDeviceRepository); mDeviceRepository.createNewDevice(mDeviceRepository.getSystemTimeStamp(), mDeviceRepository.getLocalDeviceUUID(), false, "No Sensor selected", 0); - mDeviceRepository.createNewDevice("00:00", "1234", true, "Test", 21); mDeviceList = mDeviceRepository.getConnectedDeviceList(); mAlarmHistoryList = mDeviceRepository.getAlarmHistoryDeviceList(); } + public boolean isCameraAccessAllowed(Context context) { + return ContextCompat.checkSelfPermission(context, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; + } + + public void accessRequestCamera(Activity activity) { + ActivityCompat.requestPermissions(activity, new String[]{android.Manifest.permission.CAMERA}, RECHTEANFORDERUNG_KAMERA); + } + @Override public LiveData> getConnectedDeviceList() { return mDeviceList; @@ -102,4 +116,9 @@ public class MainActivityViewModel extends ViewModel implements ViewModelInterfa public String getLocalDeviceUUID() { return mDeviceRepository.getLocalDeviceUUID(); } + + @Override + public String getSystemTimeStamp() { + return null; + } } diff --git a/app/src/main/java/com/example/greenwatch/viewmodels/VideodetectionViewModel.java b/app/src/main/java/com/example/greenwatch/viewmodels/VideodetectionViewModel.java new file mode 100644 index 0000000..1fa0018 --- /dev/null +++ b/app/src/main/java/com/example/greenwatch/viewmodels/VideodetectionViewModel.java @@ -0,0 +1,131 @@ +package com.example.greenwatch.viewmodels; + +import android.content.Context; + +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import com.example.greenwatch.models.Device; +import com.example.greenwatch.repositories.DeviceRepository; +import com.example.greenwatch.sensors.CameraSensor; + +import java.util.List; + +public class VideodetectionViewModel extends ViewModel implements ViewModelInterface { + + private MutableLiveData> mDeviceList; + private MutableLiveData mVideoAlarmDetected; + private MutableLiveData> mAlarmHistoryList; + private DeviceRepository mDeviceRepository; + private CameraSensor mCameraSensor; + + + public LiveData getVideoMovementDetectedValue() { + return mVideoAlarmDetected; + } + + @Override + public void init() { + if (mDeviceRepository == null) { + mDeviceRepository = DeviceRepository.getInstance(); + } + if (mCameraSensor == null) { + mCameraSensor = CameraSensor.getInstance(); + } + if (mDeviceList == null) { + mDeviceList = mDeviceRepository.getConnectedDeviceList(); + } + if (mAlarmHistoryList == null) { + mAlarmHistoryList = mDeviceRepository.getAlarmHistoryDeviceList(); + } + if (mVideoAlarmDetected == null) { + mVideoAlarmDetected = mCameraSensor.getVideoAlarmDetectedValue(); + } + } + + public void bindImageAnalysis(ProcessCameraProvider cameraProvider, LifecycleOwner lifecycleOwner, Context context) { + mCameraSensor.bindImageAnalysis(cameraProvider, lifecycleOwner, context); + } + + public LiveData getVideoAlarmDetectedValue() { + return mVideoAlarmDetected; + } + + @Override + public LiveData> getConnectedDeviceList() { + return mDeviceList; + } + + @Override + public LiveData> getAlarmHistoryList() { + return mAlarmHistoryList; + } + + @Override + public void updateDevice(String deviceID, String timeStamp, boolean sensorStatus, String sensorType, int sensorMassage) { + mDeviceRepository.updateDevice(deviceID, timeStamp, sensorStatus, sensorType, sensorMassage); + } + + @Override + public void setTimeStamp(String deviceID, String timeStamp) { + mDeviceRepository.setTimeStamp(deviceID, timeStamp); + } + + @Override + public String getTimeStamp(String deviceID) { + return mDeviceRepository.getTimeStamp(deviceID); + } + + @Override + public void setDeviceID(String deviceID, String newDeviceID) { + mDeviceRepository.setDeviceID(deviceID, newDeviceID); + } + + @Override + public String getDeviceID(String deviceID) { + return mDeviceRepository.getDeviceID(deviceID); + } + + @Override + public void setSensorStatus(String deviceID, boolean sensorStatus) { + mDeviceRepository.setSensorStatus(deviceID, sensorStatus); + } + + @Override + public boolean getSensorStatus(String deviceID) { + return mDeviceRepository.getSensorStatus(deviceID); + } + + @Override + public void setSensorType(String deviceID, String sensorType) { + mDeviceRepository.setSensorType(deviceID, sensorType); + } + + @Override + public String getSensorType(String deviceID) { + return mDeviceRepository.getSensorType(deviceID); + } + + @Override + public void setSensorMassage(String deviceID, int sensorMessage) { + mDeviceRepository.setSensorMassage(deviceID, sensorMessage); + } + + @Override + public int getSensorMassage(String deviceID) { + return mDeviceRepository.getSensorMassage(deviceID); + } + + @Override + public String getLocalDeviceUUID() { + return mDeviceRepository.getLocalDeviceUUID(); + } + + @Override + public String getSystemTimeStamp() { + return mDeviceRepository.getSystemTimeStamp(); + } +} diff --git a/app/src/main/java/com/example/greenwatch/viewmodels/ViewModelInterface.java b/app/src/main/java/com/example/greenwatch/viewmodels/ViewModelInterface.java index 45c6839..35372c3 100644 --- a/app/src/main/java/com/example/greenwatch/viewmodels/ViewModelInterface.java +++ b/app/src/main/java/com/example/greenwatch/viewmodels/ViewModelInterface.java @@ -22,4 +22,5 @@ public interface ViewModelInterface { void setSensorMassage(String deviceID, int sensorMessage); int getSensorMassage(String deviceID); String getLocalDeviceUUID(); + String getSystemTimeStamp(); } diff --git a/app/src/main/res/layout/activity_videodetection.xml b/app/src/main/res/layout/activity_videodetection.xml index d68362e..d7bfb3c 100644 --- a/app/src/main/res/layout/activity_videodetection.xml +++ b/app/src/main/res/layout/activity_videodetection.xml @@ -22,4 +22,27 @@ android:text="Back to MainActivity"> + + + + + + + + + + \ No newline at end of file