From 5c93d275d462832a6061e88bd3c846ba516e25ab Mon Sep 17 00:00:00 2001 From: Dave Brand Date: Mon, 16 Dec 2024 18:56:09 -0500 Subject: [PATCH 1/2] Add button to flip to rear camera, still in mirror mode. --- app/build.gradle | 20 ++++++------- .../com/polar/mirror/FreezeController.java | 22 ++++++++++++-- .../java/com/polar/mirror/MainActivity.java | 29 +++++++++++++++++-- app/src/main/res/layout/action_panel.xml | 24 +++++++++++++++ app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 7 ++++- 9 files changed, 90 insertions(+), 17 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 260c533..bee2cf8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,15 +34,15 @@ android { } dependencies { - implementation 'androidx.camera:camera-view:1.3.1' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.11.0' - implementation 'androidx.camera:camera-lifecycle:1.3.1' - implementation "androidx.camera:camera-camera2:1.3.1" - implementation 'com.google.android.material:material:1.11.0' - implementation 'androidx.compose.material3:material3:1.1.2' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.camera:camera-view:1.4.1' + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'com.google.android.material:material:1.12.0' + implementation 'androidx.camera:camera-lifecycle:1.4.1' + implementation 'androidx.camera:camera-camera2:1.4.1' + implementation 'com.google.android.material:material:1.12.0' + implementation 'androidx.compose.material3:material3:1.3.1' + implementation 'androidx.constraintlayout:constraintlayout:2.2.0' testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' } \ No newline at end of file diff --git a/app/src/main/java/com/polar/mirror/FreezeController.java b/app/src/main/java/com/polar/mirror/FreezeController.java index d059df5..60f0709 100644 --- a/app/src/main/java/com/polar/mirror/FreezeController.java +++ b/app/src/main/java/com/polar/mirror/FreezeController.java @@ -38,6 +38,7 @@ public class FreezeController { private final Context mContext; private boolean mCameraFrozen = false; private final FloatingActionButton mFreezeButton; + private int selectedCamera = CameraSelector.LENS_FACING_FRONT; FreezeController(Context context, FloatingActionButton freezeButton, PreviewView cameraView, ImageView freezeView){ @@ -57,14 +58,14 @@ public class FreezeController { */ public void onCameraInitialized(@NonNull ProcessCameraProvider provider, LifecycleOwner lcOwner){ CameraSelector cameraSelector = new CameraSelector.Builder() - .requireLensFacing(CameraSelector.LENS_FACING_FRONT) + .requireLensFacing(selectedCamera) .build(); provider.bindToLifecycle(lcOwner, cameraSelector, mImageCapture); Log.d(TAG, "completed onCameraInitialized"); } private int getRotationAngleFromOrientation(int orientation){ - int angle = 270; + int angle; switch (orientation) { case Surface.ROTATION_90: angle = 0; @@ -76,8 +77,12 @@ private int getRotationAngleFromOrientation(int orientation){ angle = 180; break; default: + angle = 270; break; } + if (getSelectedCamera() == CameraSelector.LENS_FACING_BACK) { + angle = (angle + 180) % 360; + } return angle; } @@ -151,4 +156,17 @@ public void toggleFreeze(){ mCameraFrozen = true; } } + + public void toggleSelectedCamera() { + if (selectedCamera == CameraSelector.LENS_FACING_FRONT) { + selectedCamera = CameraSelector.LENS_FACING_BACK; + } else { + selectedCamera = CameraSelector.LENS_FACING_FRONT; + } + } + + public int getSelectedCamera() { + return selectedCamera; + } + } diff --git a/app/src/main/java/com/polar/mirror/MainActivity.java b/app/src/main/java/com/polar/mirror/MainActivity.java index a40593f..64897bf 100644 --- a/app/src/main/java/com/polar/mirror/MainActivity.java +++ b/app/src/main/java/com/polar/mirror/MainActivity.java @@ -4,6 +4,7 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.camera.core.CameraSelector; +import androidx.camera.core.MirrorMode; import androidx.camera.core.Preview; import androidx.camera.view.PreviewView; import androidx.camera.lifecycle.ProcessCameraProvider; @@ -33,6 +34,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe private final static String TAG = "MainActivity"; private Preview mPreview = null; private static final int CAMERA_PERMISSION_CODE = 858; + ProcessCameraProvider cameraProvider = null; @Override protected void onCreate(Bundle savedInstanceState) { @@ -125,12 +127,15 @@ private void setupFloatingButtons(){ FloatingActionButton exitButton = findViewById(R.id.exit_button); FloatingActionButton freezeButton = findViewById(R.id.freeze_button); FloatingActionButton lowLightButton = findViewById(R.id.low_light_button); + FloatingActionButton cameraChooserButton = findViewById(R.id.camera_chooser_button); exitButton.setClickable(true); exitButton.setOnClickListener(this); freezeButton.setClickable(true); freezeButton.setOnClickListener(this); lowLightButton.setClickable(true); lowLightButton.setOnClickListener(this); + cameraChooserButton.setClickable(true); + cameraChooserButton.setOnClickListener(this); } /** @@ -147,13 +152,16 @@ private void hideSystemUi(){ * @throws ExecutionException in case of task errors * @throws InterruptedException in case of thread interruption */ + @androidx.annotation.OptIn(markerClass = androidx.camera.core.ExperimentalMirrorMode.class) private void startCamera() throws ExecutionException, InterruptedException { - mPreview = new Preview.Builder().build(); + mPreview = new Preview.Builder() + .setMirrorMode(MirrorMode.MIRROR_MODE_ON) + .build(); mPreview.setSurfaceProvider(mCameraView.getSurfaceProvider()); - ProcessCameraProvider cameraProvider = ProcessCameraProvider.getInstance(this).get(); + cameraProvider = ProcessCameraProvider.getInstance(this).get(); try { CameraSelector cameraSelector = new CameraSelector.Builder() - .requireLensFacing(CameraSelector.LENS_FACING_FRONT) + .requireLensFacing(mFreezeController.getSelectedCamera()) .build(); cameraProvider.unbindAll(); cameraProvider.bindToLifecycle(this, cameraSelector, mPreview); @@ -178,6 +186,19 @@ private void toggleLowLightMode(){ mLowLightController.toggleLowLightMode(); } + /** + * Toggles front/rear camera + */ + private void toggleSelectedCamera(){ + mFreezeController.toggleSelectedCamera(); + CameraSelector cameraSelector = new CameraSelector.Builder() + .requireLensFacing(mFreezeController.getSelectedCamera()) + .build(); + cameraProvider.unbindAll(); + cameraProvider.bindToLifecycle(this, cameraSelector, mPreview); + mFreezeController.onCameraInitialized(cameraProvider, this); + } + @Override public void onClick(@NonNull View v) { int viewId = v.getId(); @@ -188,6 +209,8 @@ public void onClick(@NonNull View v) { toggleCameraFreeze(); } else if(viewId == R.id.low_light_button){ toggleLowLightMode(); + } else if(viewId == R.id.camera_chooser_button){ + toggleSelectedCamera(); } else { Log.w(TAG, "Unknown id of view: " + viewId); } diff --git a/app/src/main/res/layout/action_panel.xml b/app/src/main/res/layout/action_panel.xml index 3e70c76..fc08ded 100644 --- a/app/src/main/res/layout/action_panel.xml +++ b/app/src/main/res/layout/action_panel.xml @@ -85,6 +85,30 @@ + + + + + + + + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 76f4cc3..597697e 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -9,4 +9,5 @@ Dostęp do kamery nie został przyznany. Użyj aplikacji Ustawienia, aby pryznać dostęp Tryb ciemności Overlay trybu ciemności + Obrót \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8f7e655..09f7bbd 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -8,4 +8,5 @@ Доступ до камери не надано. Скористуйтесь застосунком Налаштування, аби надати доступ вручну Режим пітьми Оверлей режим пітьми + Фліп \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8c2affd..49e228f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,4 +8,5 @@ Camera permissions was not granted. Use Settings app to grant it. Dark mode Low light mode overlay + Flip \ No newline at end of file diff --git a/build.gradle b/build.gradle index 11bab8c..f1187a3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { -id 'com.android.application' version '8.3.1' apply false +id 'com.android.application' version '8.7.3' apply false } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 828358b..a2e5fd7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,11 @@ #Tue Oct 24 12:02:08 CEST 2023 +# suppress inspection "Annotator" distributionBase=GRADLE_USER_HOME +# suppress inspection "Annotator" distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +# suppress inspection "Annotator" +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +# suppress inspection "Annotator" zipStoreBase=GRADLE_USER_HOME +# suppress inspection "Annotator" zipStorePath=wrapper/dists From 9764a5909b4146027858b3c501eaaee63df22cb4 Mon Sep 17 00:00:00 2001 From: Dave Brand Date: Wed, 18 Dec 2024 15:23:10 -0500 Subject: [PATCH 2/2] Fix rear camera landscape view --- .../com/polar/mirror/FreezeController.java | 8 ++-- .../java/com/polar/mirror/MainActivity.java | 44 +++++++++++++++---- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/polar/mirror/FreezeController.java b/app/src/main/java/com/polar/mirror/FreezeController.java index 60f0709..9509b21 100644 --- a/app/src/main/java/com/polar/mirror/FreezeController.java +++ b/app/src/main/java/com/polar/mirror/FreezeController.java @@ -66,23 +66,21 @@ public void onCameraInitialized(@NonNull ProcessCameraProvider provider, Lifecyc private int getRotationAngleFromOrientation(int orientation){ int angle; + boolean rearCamera = getSelectedCamera() == CameraSelector.LENS_FACING_BACK; switch (orientation) { case Surface.ROTATION_90: angle = 0; break; case Surface.ROTATION_180: - angle = 90; + angle = rearCamera ? 270 : 90; break; case Surface.ROTATION_270: angle = 180; break; default: - angle = 270; + angle = rearCamera ? 90 : 270; break; } - if (getSelectedCamera() == CameraSelector.LENS_FACING_BACK) { - angle = (angle + 180) % 360; - } return angle; } diff --git a/app/src/main/java/com/polar/mirror/MainActivity.java b/app/src/main/java/com/polar/mirror/MainActivity.java index 64897bf..784ec0e 100644 --- a/app/src/main/java/com/polar/mirror/MainActivity.java +++ b/app/src/main/java/com/polar/mirror/MainActivity.java @@ -15,6 +15,7 @@ import android.os.Build; import android.os.Bundle; import android.util.Log; +import android.view.Surface; import android.view.View; import android.widget.ImageView; import android.widget.Toast; @@ -34,7 +35,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe private final static String TAG = "MainActivity"; private Preview mPreview = null; private static final int CAMERA_PERMISSION_CODE = 858; - ProcessCameraProvider cameraProvider = null; + private ProcessCameraProvider mCameraProvider = null; @Override protected void onCreate(Bundle savedInstanceState) { @@ -154,18 +155,20 @@ private void hideSystemUi(){ */ @androidx.annotation.OptIn(markerClass = androidx.camera.core.ExperimentalMirrorMode.class) private void startCamera() throws ExecutionException, InterruptedException { + mCameraView.setImplementationMode(PreviewView.ImplementationMode.COMPATIBLE); + mCameraView.addOnLayoutChangeListener(this::onLayoutChange); mPreview = new Preview.Builder() .setMirrorMode(MirrorMode.MIRROR_MODE_ON) .build(); mPreview.setSurfaceProvider(mCameraView.getSurfaceProvider()); - cameraProvider = ProcessCameraProvider.getInstance(this).get(); + mCameraProvider = ProcessCameraProvider.getInstance(this).get(); try { CameraSelector cameraSelector = new CameraSelector.Builder() .requireLensFacing(mFreezeController.getSelectedCamera()) .build(); - cameraProvider.unbindAll(); - cameraProvider.bindToLifecycle(this, cameraSelector, mPreview); - mFreezeController.onCameraInitialized(cameraProvider, this); + mCameraProvider.unbindAll(); + mCameraProvider.bindToLifecycle(this, cameraSelector, mPreview); + mFreezeController.onCameraInitialized(mCameraProvider, this); } catch (Exception e) { e.printStackTrace(); } @@ -194,9 +197,9 @@ private void toggleSelectedCamera(){ CameraSelector cameraSelector = new CameraSelector.Builder() .requireLensFacing(mFreezeController.getSelectedCamera()) .build(); - cameraProvider.unbindAll(); - cameraProvider.bindToLifecycle(this, cameraSelector, mPreview); - mFreezeController.onCameraInitialized(cameraProvider, this); + mCameraProvider.unbindAll(); + mCameraProvider.bindToLifecycle(this, cameraSelector, mPreview); + mFreezeController.onCameraInitialized(mCameraProvider, this); } @Override @@ -241,5 +244,28 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis } } } -} + private void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) { + int orientation = mCameraView.getDisplay().getRotation(); + if (mFreezeController.getSelectedCamera() == CameraSelector.LENS_FACING_BACK) { + int o; + switch (orientation) { + case Surface.ROTATION_0: + o = Surface.ROTATION_0; + break; + case Surface.ROTATION_90: + o = Surface.ROTATION_270; + break; + case Surface.ROTATION_180: + o = Surface.ROTATION_180; + break; + default: // Surface.ROTATION_270: + o = Surface.ROTATION_90; + break; + } + mPreview.setTargetRotation(o); + } else { + mPreview.setTargetRotation(orientation); + } + } +}