Loading app/build.gradle +3 −1 Original line number Diff line number Diff line apply plugin: 'com.android.application' apply plugin: 'org.jetbrains.kotlin.android' android { compileSdkVersion 31 Loading @@ -11,7 +12,7 @@ android { defaultConfig { applicationId "foundation.e.camera" minSdkVersion 21 minSdkVersion 25 targetSdkVersion 31 renderscriptTargetApi 21 Loading Loading @@ -52,6 +53,7 @@ dependencies { implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation "androidx.constraintlayout:constraintlayout:2.1.3" implementation "org.greenrobot:eventbus:3.3.1" implementation 'androidx.core:core-ktx:1.8.0' androidTestImplementation 'androidx.test.ext:junit:1.1.3' // appcompat version must be 1.4.0 or later to satisfy emoji policy! Loading app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +6 −13 Original line number Diff line number Diff line package net.sourceforge.opencamera; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.media.MediaScannerConnection; import android.os.Build; import android.os.Environment; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RSInvalidStateException; import android.renderscript.RenderScript; import android.renderscript.Script; import android.renderscript.ScriptIntrinsicHistogram; //import android.renderscript.ScriptIntrinsicResize; import android.renderscript.Type; import androidx.annotation.RequiresApi; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import android.util.Log; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class HDRProcessor { private static final String TAG = "HDRProcessor"; Loading app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +26 −28 Original line number Diff line number Diff line package net.sourceforge.opencamera; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.RawImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; Loading @@ -37,16 +13,12 @@ import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Align; //import android.location.Address; // don't use until we have info for data privacy! //import android.location.Geocoder; // don't use until we have info for data privacy! import android.location.Location; import androidx.exifinterface.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; import android.renderscript.Allocation; import androidx.annotation.RequiresApi; import android.util.Log; import android.util.TypedValue; import android.util.Xml; Loading @@ -56,9 +28,35 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import androidx.exifinterface.media.ExifInterface; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.RawImage; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import foundation.e.camera.R; /** Handles the saving (and any required processing) of photos. Loading app/src/main/java/net/sourceforge/opencamera/LocationSupplier.java +1 −1 Original line number Diff line number Diff line Loading @@ -11,10 +11,10 @@ import android.location.LocationProvider; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.Log; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import android.util.Log; /** Handles listening for GPS location (both coarse and fine). */ Loading app/src/main/java/net/sourceforge/opencamera/MainActivity.java +161 −71 Original line number Diff line number Diff line package net.sourceforge.opencamera; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.CameraControllerManager; import net.sourceforge.opencamera.cameracontroller.CameraControllerManager2; import net.sourceforge.opencamera.preview.Preview; import net.sourceforge.opencamera.preview.VideoProfile; import net.sourceforge.opencamera.remotecontrol.BluetoothRemoteControl; import net.sourceforge.opencamera.ui.FolderChooserDialog; import net.sourceforge.opencamera.ui.MainUI; import net.sourceforge.opencamera.ui.ManualSeekbars; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.List; //import java.util.Locale; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import android.Manifest; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.KeyguardManager; Loading @@ -37,12 +12,10 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; Loading @@ -58,6 +31,9 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.display.DisplayManager; import android.media.MediaMetadataRetriever; import android.net.Uri; Loading @@ -68,30 +44,8 @@ import android.os.Looper; import android.os.ParcelFileDescriptor; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.KeyguardManager; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.renderscript.RenderScript; import android.speech.tts.TextToSpeech; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.exifinterface.media.ExifInterface; import android.text.InputFilter; import android.text.InputType; import android.text.Spanned; Loading @@ -118,8 +72,15 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.exifinterface.media.ExifInterface; import net.sourceforge.opencamera.camera2.CameraFinder; import net.sourceforge.opencamera.camera2.CameraIdentifier; import net.sourceforge.opencamera.camera2.model.CameraModel; import net.sourceforge.opencamera.camera2.model.CameraType; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.CameraControllerManager; import net.sourceforge.opencamera.cameracontroller.CameraControllerManager2; Loading @@ -143,8 +104,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import foundation.e.camera.R; Loading @@ -167,6 +130,11 @@ public class MainActivity extends AppCompatActivity { // components: always non-null (after onCreate()) private BluetoothRemoteControl bluetoothRemoteControl; private PermissionHandler permissionHandler; private CameraManager cameraManager; private CameraFinder camerafinder; private CameraIdentifier cameraIdentifier; private ArrayList<CameraModel> cameraModel; private CameraCharacteristics cameraCharacteristics; private SettingsManager settingsManager; private MainUI mainUI; private ManualSeekbars manualSeekbars; Loading Loading @@ -197,6 +165,9 @@ public class MainActivity extends AppCompatActivity { private TextToSpeech textToSpeech; private boolean textToSpeechSuccess; private List<Float> frontZoomRatios = new ArrayList<>(); private List<Float> backZoomRatios = new ArrayList<>(); private AudioListener audio_listener; // may be null - created when needed //private boolean ui_placement_right = true; Loading @@ -217,6 +188,7 @@ public class MainActivity extends AppCompatActivity { private List<Integer> back_camera_ids; private List<Integer> front_camera_ids; private List<Integer> other_camera_ids; private List<Integer> logical_camera_ids; private final ToastBoxer switch_video_toast = new ToastBoxer(); private final ToastBoxer screen_locked_toast = new ToastBoxer(); Loading Loading @@ -257,6 +229,8 @@ public class MainActivity extends AppCompatActivity { private final String CHANNEL_ID = "open_camera_channel"; private final int image_saving_notification_id = 1; private final float defaultLensZoom = 1.0F; private static final float WATER_DENSITY_FRESHWATER = 1.0f; private static final float WATER_DENSITY_SALTWATER = 1.03f; private float mWaterDensity = 1.0f; Loading Loading @@ -429,6 +403,13 @@ public class MainActivity extends AppCompatActivity { getWindow().setAttributes(layout); } cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); camerafinder = new CameraFinder(cameraManager); cameraModel = camerafinder.getCameraModels(); cameraIdentifier = new CameraIdentifier(cameraModel); camerafinder.init(); cameraIdentifier.init(); // Setup multi-camera buttons (must be done after creating preview so we know which Camera API is being used, // and before initialising on-screen visibility). // We only allow the separate icon for switching cameras if: Loading @@ -439,24 +420,48 @@ public class MainActivity extends AppCompatActivity { // If there are more than two cameras, but all cameras have the same "facing, we still stick // with using the switch camera icon to iterate over all cameras. int n_cameras = preview.getCameraControllerManager().getNumberOfCameras(); if (getResources().getBoolean(R.bool.zoom_level_switch_supported)) { n_cameras = camerafinder.getAllCameraIdList().size(); } if( n_cameras > 2 ) { this.back_camera_ids = new ArrayList<>(); this.front_camera_ids = new ArrayList<>(); this.other_camera_ids = new ArrayList<>(); for(int i=0;i<n_cameras;i++) { switch( preview.getCameraControllerManager().getFacing(i) ) { case FACING_BACK: back_camera_ids.add(i); this.logical_camera_ids = new ArrayList<>(); if (!cameraModel.isEmpty()) { for (CameraModel model : cameraModel) { if (model.getCameraType() == CameraType.LOGICAL) { logical_camera_ids.add(model.getId()); } } } for(String cameraId: camerafinder.getAllCameraIdList()) { int id = Integer.parseInt(cameraId); cameraCharacteristics = camerafinder.getCameraCharacteristics(id); Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); if (!logical_camera_ids.contains(id) || getResources().getBoolean(R.bool.allow_logical_camera_ids)) { switch(facing) { case CameraMetadata.LENS_FACING_BACK: back_camera_ids.add(id); break; case FACING_FRONT: front_camera_ids.add(i); case CameraMetadata.LENS_FACING_FRONT: front_camera_ids.add(id); break; default: // we assume any unknown cameras are also external other_camera_ids.add(i); case CameraMetadata.LENS_FACING_EXTERNAL: other_camera_ids.add(id); break; } } } if( MyDebug.LOG ) { Log.d(TAG, "back_camera_ids: " + back_camera_ids); Log.d(TAG, "front_camera_ids: " + front_camera_ids); Log.d(TAG, "other_camera_ids: " + other_camera_ids); Log.d(TAG, "logical_camera_ids" + logical_camera_ids); } boolean multi_same_facing = back_camera_ids.size() >= 2 || front_camera_ids.size() >= 2 || other_camera_ids.size() >= 2; int n_facing = 0; if( back_camera_ids.size() > 0 ) Loading @@ -473,6 +478,10 @@ public class MainActivity extends AppCompatActivity { Log.d(TAG, "is_multi_cam: " + is_multi_cam); } if (getResources().getBoolean(R.bool.zoom_level_switch_supported)) { initZoomRatios(); } if( !is_multi_cam ) { this.back_camera_ids = null; this.front_camera_ids = null; Loading Loading @@ -824,15 +833,15 @@ public class MainActivity extends AppCompatActivity { int cameraId = getActualCameraId(); switch( preview.getCameraControllerManager().getFacing(cameraId) ) { case FACING_BACK: if( back_camera_ids.size() > 0 ) if( back_camera_ids.size() > 1 ) return true; break; case FACING_FRONT: if( front_camera_ids.size() > 0 ) if( front_camera_ids.size() > 1 ) return true; break; default: if( other_camera_ids.size() > 0 ) if( other_camera_ids.size() > 1 ) return true; break; } Loading Loading @@ -2277,7 +2286,7 @@ public class MainActivity extends AppCompatActivity { } } private void userSwitchToCamera(int cameraId) { private void userSwitchToCamera(int cameraId, boolean is_multi_cam) { if( MyDebug.LOG ) Log.d(TAG, "userSwitchToCamera: " + cameraId); View switchCameraButton = findViewById(R.id.switch_camera); Loading @@ -2286,13 +2295,93 @@ public class MainActivity extends AppCompatActivity { switchCameraButton.setEnabled(false); switchMultiCameraButton.setEnabled(false); applicationInterface.reset(true); if (is_multi_cam && getResources().getBoolean(R.bool.zoom_level_switch_supported)) { changeZoomFactor(); } else { this.preview.setCamera(cameraId); } switchCameraButton.setEnabled(true); switchMultiCameraButton.setEnabled(true); // no need to call mainUI.setSwitchCameraContentDescription - this will be called from Preview.cameraSetup when the // new camera is opened } private void initZoomRatios() { String[] auxZoomRatios = getResources().getStringArray(R.array.config_auxCameraZoomRatios); if (auxZoomRatios.length == 0 && !cameraModel.isEmpty()) { for (CameraModel model : cameraModel) { cameraCharacteristics = camerafinder.getCameraCharacteristics(model.getId()); Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); CameraType cameraType = model.getCameraType(); if (cameraType != CameraType.LOGICAL) { if (facing == CameraMetadata.LENS_FACING_BACK) { if (cameraType == CameraType.MAIN || cameraType == CameraType.ULTRAWIDE || cameraType == CameraType.TELE || cameraType == CameraType.MACRO) { backZoomRatios.add(model.getZoomFactor()); } } else if (facing == CameraMetadata.LENS_FACING_FRONT) { if (cameraType == CameraType.MAIN || cameraType == CameraType.ULTRAWIDE) { frontZoomRatios.add(model.getZoomFactor()); } } } } } else { for (String zoom : auxZoomRatios) { String[] separated = zoom.split(":"); float id = Integer.parseInt(separated[0]); float zoomRatio = Float.parseFloat(separated[1]); if (id == 1) { frontZoomRatios.add(zoomRatio); } else { backZoomRatios.add(zoomRatio); } } } if( MyDebug.LOG ) { Log.d(TAG, "frontZoomRatios" + frontZoomRatios); Log.d(TAG, "backZoomRatios" + backZoomRatios); } } private void changeZoomFactor() { float zoom = getNextMultiZoomRatio(backZoomRatios); if (preview.getCameraControllerManager().getFacing(getActualCameraId()) == CameraController.Facing.FACING_FRONT) { zoom = getNextMultiZoomRatio(frontZoomRatios); } preview.zoomTo(preview.findNxZoom(zoom)); updateMultiPixelIcon(backZoomRatios.indexOf(zoom)); if( MyDebug.LOG ) Log.d(TAG, "changeZoomFactor" + zoom); } public float getNextMultiZoomRatio(List<Float> zoomRations) { float currentZoom = preview.getZoomRatio(); float zoomRatio; int index = zoomRations.indexOf(currentZoom); if ( index == -1 ) { zoomRatio = zoomRations.get(0); } else { index = (index+1) % zoomRations.size(); zoomRatio = zoomRations.get(index); } if( MyDebug.LOG ) Log.d(TAG, "next zoom: " + zoomRatio); return zoomRatio; } private void updateMultiPixelIcon(int index) { Button multiCameraButton = findViewById(R.id.switch_multi_camera); if (multiCameraButton.getVisibility() != View.GONE) { String text = getString(R.string.switch_multi_camera_lens) + " " + (index + 1); multiCameraButton.setText(text); } } /** * Selects the next camera on the phone - in practice, switches between * front and back cameras Loading Loading @@ -2321,7 +2410,7 @@ public class MainActivity extends AppCompatActivity { // disappear when the user touches the screen anyway.) preview.clearActiveFakeToast(); } userSwitchToCamera(cameraId); userSwitchToCamera(cameraId, false); } } Loading @@ -2341,7 +2430,7 @@ public class MainActivity extends AppCompatActivity { if( this.preview.canSwitchCamera() ) { int cameraId = getNextMultiCameraId(); pushCameraIdToast(cameraId); userSwitchToCamera(cameraId); userSwitchToCamera(cameraId, true); } } Loading Loading @@ -3165,9 +3254,9 @@ public class MainActivity extends AppCompatActivity { View button = findViewById(R.id.switch_multi_camera); changed = changed || (button.getVisibility() != View.GONE); button.setVisibility(View.GONE); } else { updateMultiCameraIcon(); } updateMultiCameraIcon(); if( MyDebug.LOG ) Log.d(TAG, "checkDisableGUIIcons: " + changed); return changed; Loading Loading @@ -5138,6 +5227,7 @@ public class MainActivity extends AppCompatActivity { // indirectly set zoom via this method, from setting the zoom slider preview.zoomTo(progress); updateMultiCameraIcon(); } @Override Loading Loading
app/build.gradle +3 −1 Original line number Diff line number Diff line apply plugin: 'com.android.application' apply plugin: 'org.jetbrains.kotlin.android' android { compileSdkVersion 31 Loading @@ -11,7 +12,7 @@ android { defaultConfig { applicationId "foundation.e.camera" minSdkVersion 21 minSdkVersion 25 targetSdkVersion 31 renderscriptTargetApi 21 Loading Loading @@ -52,6 +53,7 @@ dependencies { implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation "androidx.constraintlayout:constraintlayout:2.1.3" implementation "org.greenrobot:eventbus:3.3.1" implementation 'androidx.core:core-ktx:1.8.0' androidTestImplementation 'androidx.test.ext:junit:1.1.3' // appcompat version must be 1.4.0 or later to satisfy emoji policy! Loading
app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +6 −13 Original line number Diff line number Diff line package net.sourceforge.opencamera; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.media.MediaScannerConnection; import android.os.Build; import android.os.Environment; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RSInvalidStateException; import android.renderscript.RenderScript; import android.renderscript.Script; import android.renderscript.ScriptIntrinsicHistogram; //import android.renderscript.ScriptIntrinsicResize; import android.renderscript.Type; import androidx.annotation.RequiresApi; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import android.util.Log; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class HDRProcessor { private static final String TAG = "HDRProcessor"; Loading
app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +26 −28 Original line number Diff line number Diff line package net.sourceforge.opencamera; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.RawImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; Loading @@ -37,16 +13,12 @@ import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Align; //import android.location.Address; // don't use until we have info for data privacy! //import android.location.Geocoder; // don't use until we have info for data privacy! import android.location.Location; import androidx.exifinterface.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; import android.renderscript.Allocation; import androidx.annotation.RequiresApi; import android.util.Log; import android.util.TypedValue; import android.util.Xml; Loading @@ -56,9 +28,35 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import androidx.exifinterface.media.ExifInterface; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.RawImage; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import foundation.e.camera.R; /** Handles the saving (and any required processing) of photos. Loading
app/src/main/java/net/sourceforge/opencamera/LocationSupplier.java +1 −1 Original line number Diff line number Diff line Loading @@ -11,10 +11,10 @@ import android.location.LocationProvider; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.Log; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import android.util.Log; /** Handles listening for GPS location (both coarse and fine). */ Loading
app/src/main/java/net/sourceforge/opencamera/MainActivity.java +161 −71 Original line number Diff line number Diff line package net.sourceforge.opencamera; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.CameraControllerManager; import net.sourceforge.opencamera.cameracontroller.CameraControllerManager2; import net.sourceforge.opencamera.preview.Preview; import net.sourceforge.opencamera.preview.VideoProfile; import net.sourceforge.opencamera.remotecontrol.BluetoothRemoteControl; import net.sourceforge.opencamera.ui.FolderChooserDialog; import net.sourceforge.opencamera.ui.MainUI; import net.sourceforge.opencamera.ui.ManualSeekbars; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.List; //import java.util.Locale; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import android.Manifest; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.KeyguardManager; Loading @@ -37,12 +12,10 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; Loading @@ -58,6 +31,9 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.display.DisplayManager; import android.media.MediaMetadataRetriever; import android.net.Uri; Loading @@ -68,30 +44,8 @@ import android.os.Looper; import android.os.ParcelFileDescriptor; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.KeyguardManager; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.renderscript.RenderScript; import android.speech.tts.TextToSpeech; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.exifinterface.media.ExifInterface; import android.text.InputFilter; import android.text.InputType; import android.text.Spanned; Loading @@ -118,8 +72,15 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.exifinterface.media.ExifInterface; import net.sourceforge.opencamera.camera2.CameraFinder; import net.sourceforge.opencamera.camera2.CameraIdentifier; import net.sourceforge.opencamera.camera2.model.CameraModel; import net.sourceforge.opencamera.camera2.model.CameraType; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.CameraControllerManager; import net.sourceforge.opencamera.cameracontroller.CameraControllerManager2; Loading @@ -143,8 +104,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import foundation.e.camera.R; Loading @@ -167,6 +130,11 @@ public class MainActivity extends AppCompatActivity { // components: always non-null (after onCreate()) private BluetoothRemoteControl bluetoothRemoteControl; private PermissionHandler permissionHandler; private CameraManager cameraManager; private CameraFinder camerafinder; private CameraIdentifier cameraIdentifier; private ArrayList<CameraModel> cameraModel; private CameraCharacteristics cameraCharacteristics; private SettingsManager settingsManager; private MainUI mainUI; private ManualSeekbars manualSeekbars; Loading Loading @@ -197,6 +165,9 @@ public class MainActivity extends AppCompatActivity { private TextToSpeech textToSpeech; private boolean textToSpeechSuccess; private List<Float> frontZoomRatios = new ArrayList<>(); private List<Float> backZoomRatios = new ArrayList<>(); private AudioListener audio_listener; // may be null - created when needed //private boolean ui_placement_right = true; Loading @@ -217,6 +188,7 @@ public class MainActivity extends AppCompatActivity { private List<Integer> back_camera_ids; private List<Integer> front_camera_ids; private List<Integer> other_camera_ids; private List<Integer> logical_camera_ids; private final ToastBoxer switch_video_toast = new ToastBoxer(); private final ToastBoxer screen_locked_toast = new ToastBoxer(); Loading Loading @@ -257,6 +229,8 @@ public class MainActivity extends AppCompatActivity { private final String CHANNEL_ID = "open_camera_channel"; private final int image_saving_notification_id = 1; private final float defaultLensZoom = 1.0F; private static final float WATER_DENSITY_FRESHWATER = 1.0f; private static final float WATER_DENSITY_SALTWATER = 1.03f; private float mWaterDensity = 1.0f; Loading Loading @@ -429,6 +403,13 @@ public class MainActivity extends AppCompatActivity { getWindow().setAttributes(layout); } cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); camerafinder = new CameraFinder(cameraManager); cameraModel = camerafinder.getCameraModels(); cameraIdentifier = new CameraIdentifier(cameraModel); camerafinder.init(); cameraIdentifier.init(); // Setup multi-camera buttons (must be done after creating preview so we know which Camera API is being used, // and before initialising on-screen visibility). // We only allow the separate icon for switching cameras if: Loading @@ -439,24 +420,48 @@ public class MainActivity extends AppCompatActivity { // If there are more than two cameras, but all cameras have the same "facing, we still stick // with using the switch camera icon to iterate over all cameras. int n_cameras = preview.getCameraControllerManager().getNumberOfCameras(); if (getResources().getBoolean(R.bool.zoom_level_switch_supported)) { n_cameras = camerafinder.getAllCameraIdList().size(); } if( n_cameras > 2 ) { this.back_camera_ids = new ArrayList<>(); this.front_camera_ids = new ArrayList<>(); this.other_camera_ids = new ArrayList<>(); for(int i=0;i<n_cameras;i++) { switch( preview.getCameraControllerManager().getFacing(i) ) { case FACING_BACK: back_camera_ids.add(i); this.logical_camera_ids = new ArrayList<>(); if (!cameraModel.isEmpty()) { for (CameraModel model : cameraModel) { if (model.getCameraType() == CameraType.LOGICAL) { logical_camera_ids.add(model.getId()); } } } for(String cameraId: camerafinder.getAllCameraIdList()) { int id = Integer.parseInt(cameraId); cameraCharacteristics = camerafinder.getCameraCharacteristics(id); Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); if (!logical_camera_ids.contains(id) || getResources().getBoolean(R.bool.allow_logical_camera_ids)) { switch(facing) { case CameraMetadata.LENS_FACING_BACK: back_camera_ids.add(id); break; case FACING_FRONT: front_camera_ids.add(i); case CameraMetadata.LENS_FACING_FRONT: front_camera_ids.add(id); break; default: // we assume any unknown cameras are also external other_camera_ids.add(i); case CameraMetadata.LENS_FACING_EXTERNAL: other_camera_ids.add(id); break; } } } if( MyDebug.LOG ) { Log.d(TAG, "back_camera_ids: " + back_camera_ids); Log.d(TAG, "front_camera_ids: " + front_camera_ids); Log.d(TAG, "other_camera_ids: " + other_camera_ids); Log.d(TAG, "logical_camera_ids" + logical_camera_ids); } boolean multi_same_facing = back_camera_ids.size() >= 2 || front_camera_ids.size() >= 2 || other_camera_ids.size() >= 2; int n_facing = 0; if( back_camera_ids.size() > 0 ) Loading @@ -473,6 +478,10 @@ public class MainActivity extends AppCompatActivity { Log.d(TAG, "is_multi_cam: " + is_multi_cam); } if (getResources().getBoolean(R.bool.zoom_level_switch_supported)) { initZoomRatios(); } if( !is_multi_cam ) { this.back_camera_ids = null; this.front_camera_ids = null; Loading Loading @@ -824,15 +833,15 @@ public class MainActivity extends AppCompatActivity { int cameraId = getActualCameraId(); switch( preview.getCameraControllerManager().getFacing(cameraId) ) { case FACING_BACK: if( back_camera_ids.size() > 0 ) if( back_camera_ids.size() > 1 ) return true; break; case FACING_FRONT: if( front_camera_ids.size() > 0 ) if( front_camera_ids.size() > 1 ) return true; break; default: if( other_camera_ids.size() > 0 ) if( other_camera_ids.size() > 1 ) return true; break; } Loading Loading @@ -2277,7 +2286,7 @@ public class MainActivity extends AppCompatActivity { } } private void userSwitchToCamera(int cameraId) { private void userSwitchToCamera(int cameraId, boolean is_multi_cam) { if( MyDebug.LOG ) Log.d(TAG, "userSwitchToCamera: " + cameraId); View switchCameraButton = findViewById(R.id.switch_camera); Loading @@ -2286,13 +2295,93 @@ public class MainActivity extends AppCompatActivity { switchCameraButton.setEnabled(false); switchMultiCameraButton.setEnabled(false); applicationInterface.reset(true); if (is_multi_cam && getResources().getBoolean(R.bool.zoom_level_switch_supported)) { changeZoomFactor(); } else { this.preview.setCamera(cameraId); } switchCameraButton.setEnabled(true); switchMultiCameraButton.setEnabled(true); // no need to call mainUI.setSwitchCameraContentDescription - this will be called from Preview.cameraSetup when the // new camera is opened } private void initZoomRatios() { String[] auxZoomRatios = getResources().getStringArray(R.array.config_auxCameraZoomRatios); if (auxZoomRatios.length == 0 && !cameraModel.isEmpty()) { for (CameraModel model : cameraModel) { cameraCharacteristics = camerafinder.getCameraCharacteristics(model.getId()); Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); CameraType cameraType = model.getCameraType(); if (cameraType != CameraType.LOGICAL) { if (facing == CameraMetadata.LENS_FACING_BACK) { if (cameraType == CameraType.MAIN || cameraType == CameraType.ULTRAWIDE || cameraType == CameraType.TELE || cameraType == CameraType.MACRO) { backZoomRatios.add(model.getZoomFactor()); } } else if (facing == CameraMetadata.LENS_FACING_FRONT) { if (cameraType == CameraType.MAIN || cameraType == CameraType.ULTRAWIDE) { frontZoomRatios.add(model.getZoomFactor()); } } } } } else { for (String zoom : auxZoomRatios) { String[] separated = zoom.split(":"); float id = Integer.parseInt(separated[0]); float zoomRatio = Float.parseFloat(separated[1]); if (id == 1) { frontZoomRatios.add(zoomRatio); } else { backZoomRatios.add(zoomRatio); } } } if( MyDebug.LOG ) { Log.d(TAG, "frontZoomRatios" + frontZoomRatios); Log.d(TAG, "backZoomRatios" + backZoomRatios); } } private void changeZoomFactor() { float zoom = getNextMultiZoomRatio(backZoomRatios); if (preview.getCameraControllerManager().getFacing(getActualCameraId()) == CameraController.Facing.FACING_FRONT) { zoom = getNextMultiZoomRatio(frontZoomRatios); } preview.zoomTo(preview.findNxZoom(zoom)); updateMultiPixelIcon(backZoomRatios.indexOf(zoom)); if( MyDebug.LOG ) Log.d(TAG, "changeZoomFactor" + zoom); } public float getNextMultiZoomRatio(List<Float> zoomRations) { float currentZoom = preview.getZoomRatio(); float zoomRatio; int index = zoomRations.indexOf(currentZoom); if ( index == -1 ) { zoomRatio = zoomRations.get(0); } else { index = (index+1) % zoomRations.size(); zoomRatio = zoomRations.get(index); } if( MyDebug.LOG ) Log.d(TAG, "next zoom: " + zoomRatio); return zoomRatio; } private void updateMultiPixelIcon(int index) { Button multiCameraButton = findViewById(R.id.switch_multi_camera); if (multiCameraButton.getVisibility() != View.GONE) { String text = getString(R.string.switch_multi_camera_lens) + " " + (index + 1); multiCameraButton.setText(text); } } /** * Selects the next camera on the phone - in practice, switches between * front and back cameras Loading Loading @@ -2321,7 +2410,7 @@ public class MainActivity extends AppCompatActivity { // disappear when the user touches the screen anyway.) preview.clearActiveFakeToast(); } userSwitchToCamera(cameraId); userSwitchToCamera(cameraId, false); } } Loading @@ -2341,7 +2430,7 @@ public class MainActivity extends AppCompatActivity { if( this.preview.canSwitchCamera() ) { int cameraId = getNextMultiCameraId(); pushCameraIdToast(cameraId); userSwitchToCamera(cameraId); userSwitchToCamera(cameraId, true); } } Loading Loading @@ -3165,9 +3254,9 @@ public class MainActivity extends AppCompatActivity { View button = findViewById(R.id.switch_multi_camera); changed = changed || (button.getVisibility() != View.GONE); button.setVisibility(View.GONE); } else { updateMultiCameraIcon(); } updateMultiCameraIcon(); if( MyDebug.LOG ) Log.d(TAG, "checkDisableGUIIcons: " + changed); return changed; Loading Loading @@ -5138,6 +5227,7 @@ public class MainActivity extends AppCompatActivity { // indirectly set zoom via this method, from setting the zoom slider preview.zoomTo(progress); updateMultiCameraIcon(); } @Override Loading