diff --git a/app/build.gradle b/app/build.gradle index 769c0773b66b6ce5f66bb60a447197c05ec0a2d8..64413aafa05be4577654548cd7638c1494aca2b4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'org.jetbrains.kotlin.android' android { compileSdkVersion 31 @@ -11,7 +12,7 @@ android { defaultConfig { applicationId "foundation.e.camera" - minSdkVersion 21 + minSdkVersion 25 targetSdkVersion 31 renderscriptTargetApi 21 @@ -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! diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 7dec0c26096beaa991ca6af9d022526ad06b4e39..6fa6ede472b8e14ea6ac013101fe7a28742b694d 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1,32 +1,25 @@ 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"; diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index dbe2b33801fe81e85ac1020a372a470482849a7b..886801fbfffd7ba6e9cab8e0ba11a7693a9a2b10 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -1,29 +1,5 @@ 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; @@ -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; @@ -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. diff --git a/app/src/main/java/net/sourceforge/opencamera/LocationSupplier.java b/app/src/main/java/net/sourceforge/opencamera/LocationSupplier.java index f998ef7afc2b29ba2a4e2838c369ca0841768048..c948c2b3b7ed58d8f8fcdd5a9b10a7f3d3bcd6f0 100644 --- a/app/src/main/java/net/sourceforge/opencamera/LocationSupplier.java +++ b/app/src/main/java/net/sourceforge/opencamera/LocationSupplier.java @@ -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). */ diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java index cac48b2de926f0866db137cb36252bcd2ebf5db0..b40d0f96c4357d0eaeadeb233e99fcd06f6a41a7 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java @@ -1,35 +1,10 @@ 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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; + private CameraCharacteristics cameraCharacteristics; private SettingsManager settingsManager; private MainUI mainUI; private ManualSeekbars manualSeekbars; @@ -197,6 +165,9 @@ public class MainActivity extends AppCompatActivity { private TextToSpeech textToSpeech; private boolean textToSpeechSuccess; + private List frontZoomRatios = new ArrayList<>(); + private List backZoomRatios = new ArrayList<>(); + private AudioListener audio_listener; // may be null - created when needed //private boolean ui_placement_right = true; @@ -217,6 +188,7 @@ public class MainActivity extends AppCompatActivity { private List back_camera_ids; private List front_camera_ids; private List other_camera_ids; + private List logical_camera_ids; private final ToastBoxer switch_video_toast = new ToastBoxer(); private final ToastBoxer screen_locked_toast = new ToastBoxer(); @@ -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; @@ -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: @@ -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(); + + 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 CameraMetadata.LENS_FACING_FRONT: + front_camera_ids.add(id); + break; + 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 ) @@ -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; @@ -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; } @@ -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); @@ -2286,13 +2295,93 @@ public class MainActivity extends AppCompatActivity { switchCameraButton.setEnabled(false); switchMultiCameraButton.setEnabled(false); applicationInterface.reset(true); - this.preview.setCamera(cameraId); + 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 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 @@ -2321,7 +2410,7 @@ public class MainActivity extends AppCompatActivity { // disappear when the user touches the screen anyway.) preview.clearActiveFakeToast(); } - userSwitchToCamera(cameraId); + userSwitchToCamera(cameraId, false); } } @@ -2341,7 +2430,7 @@ public class MainActivity extends AppCompatActivity { if( this.preview.canSwitchCamera() ) { int cameraId = getNextMultiCameraId(); pushCameraIdToast(cameraId); - userSwitchToCamera(cameraId); + userSwitchToCamera(cameraId, true); } } @@ -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; @@ -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 diff --git a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java index 22454e4351ae37f48748ffef2348368a33df2c42..a6345f470ccdf887879568f47363b4ea17e9f5e4 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java @@ -11,8 +11,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; -//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.hardware.camera2.CameraExtensionCharacteristics; import android.location.Location; import android.media.MediaMetadataRetriever; @@ -55,7 +53,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.Timer; import java.util.TimerTask; diff --git a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java index b43d8249bd49a9974c664c86e2c6eecd02dc9f6e..c1f4d144d179e500d9d963dbbfd15bbe0ec4b09a 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java @@ -1,11 +1,5 @@ package net.sourceforge.opencamera; -import net.sourceforge.opencamera.cameracontroller.CameraController; -import net.sourceforge.opencamera.preview.Preview; -import net.sourceforge.opencamera.ui.ArraySeekBarPreference; -import net.sourceforge.opencamera.ui.FolderChooserDialog; -import net.sourceforge.opencamera.ui.MyEditTextPreference; - import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityManager; @@ -16,7 +10,6 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; -import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.pm.PackageInfo; @@ -24,7 +17,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Point; -import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.preference.EditTextPreference; @@ -36,8 +28,6 @@ import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.preference.PreferenceManager; import android.preference.TwoStatePreference; -import androidx.annotation.Nullable; -import android.text.Html; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; @@ -53,6 +43,14 @@ import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.Nullable; + +import net.sourceforge.opencamera.cameracontroller.CameraController; +import net.sourceforge.opencamera.preview.Preview; +import net.sourceforge.opencamera.ui.ArraySeekBarPreference; +import net.sourceforge.opencamera.ui.FolderChooserDialog; +import net.sourceforge.opencamera.ui.MyEditTextPreference; + import java.io.File; import java.io.IOException; import java.util.ArrayList; diff --git a/app/src/main/java/net/sourceforge/opencamera/MyTileService.java b/app/src/main/java/net/sourceforge/opencamera/MyTileService.java index 5a01174b8dab7dfbe65bff723ebb95412b9083b2..11e3eb428cda0db2c02e07f119777e1a8754d971 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyTileService.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyTileService.java @@ -3,9 +3,10 @@ package net.sourceforge.opencamera; import android.content.Intent; import android.os.Build; import android.service.quicksettings.TileService; -import androidx.annotation.RequiresApi; import android.util.Log; +import androidx.annotation.RequiresApi; + /** Provides service for quick settings tile. */ @RequiresApi(api = Build.VERSION_CODES.N) diff --git a/app/src/main/java/net/sourceforge/opencamera/MyTileServiceFrontCamera.java b/app/src/main/java/net/sourceforge/opencamera/MyTileServiceFrontCamera.java index 010a7fd08bea9a8fca57c2a9724681ad6ef38a63..ca92cb3e24eed82d2319b237ac7318464255511d 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyTileServiceFrontCamera.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyTileServiceFrontCamera.java @@ -3,9 +3,10 @@ package net.sourceforge.opencamera; import android.content.Intent; import android.os.Build; import android.service.quicksettings.TileService; -import androidx.annotation.RequiresApi; import android.util.Log; +import androidx.annotation.RequiresApi; + /** Provides service for quick settings tile. */ @RequiresApi(api = Build.VERSION_CODES.N) diff --git a/app/src/main/java/net/sourceforge/opencamera/MyTileServiceVideo.java b/app/src/main/java/net/sourceforge/opencamera/MyTileServiceVideo.java index 3db1694acf6ab1c642a8150c58be1aae65ac1b7e..b7a6789f490da7a1746ee760c8baa3ed660a6217 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyTileServiceVideo.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyTileServiceVideo.java @@ -3,9 +3,10 @@ package net.sourceforge.opencamera; import android.content.Intent; import android.os.Build; import android.service.quicksettings.TileService; -import androidx.annotation.RequiresApi; import android.util.Log; +import androidx.annotation.RequiresApi; + /** Provides service for quick settings tile. */ @RequiresApi(api = Build.VERSION_CODES.N) diff --git a/app/src/main/java/net/sourceforge/opencamera/MyWidgetProvider.java b/app/src/main/java/net/sourceforge/opencamera/MyWidgetProvider.java index eeb9e59655174c241a4b1d9511230540eaed1ccf..e816a89b87bff08b06c318096c02721f63ff90c4 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyWidgetProvider.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyWidgetProvider.java @@ -1,9 +1,5 @@ package net.sourceforge.opencamera; -import net.sourceforge.opencamera.MyDebug; -import net.sourceforge.opencamera.MainActivity; -import foundation.e.camera.R; - import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; @@ -13,6 +9,8 @@ import android.os.Build; import android.util.Log; import android.widget.RemoteViews; +import foundation.e.camera.R; + /** Handles the Open Camera lock screen widget. Lock screen widgets are no * longer supported in Android 5 onwards (instead Open Camera can be launched * from the lock screen using the standard camera icon), but this is kept here diff --git a/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java b/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java index ed2f74045256ffb602a566a80636e2a8fbdde31f..2734b607b1f4954714db7f3a7b18e818f39f0b9c 100644 --- a/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java @@ -1,13 +1,5 @@ package net.sourceforge.opencamera; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -25,11 +17,19 @@ import android.renderscript.Element; import android.renderscript.RSInvalidStateException; import android.renderscript.RenderScript; import android.renderscript.Script; -//import android.renderscript.ScriptIntrinsicResize; import android.renderscript.Type; -import androidx.annotation.RequiresApi; import android.util.Log; +import androidx.annotation.RequiresApi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + public class PanoramaProcessor { private static final String TAG = "PanoramaProcessor"; diff --git a/app/src/main/java/net/sourceforge/opencamera/PermissionHandler.java b/app/src/main/java/net/sourceforge/opencamera/PermissionHandler.java index a7a1ee36255678c62326719ff9bf6ff3e43685e3..95fa1ca3b0a13e5f173c51a1c4c3e9ea679314b6 100644 --- a/app/src/main/java/net/sourceforge/opencamera/PermissionHandler.java +++ b/app/src/main/java/net/sourceforge/opencamera/PermissionHandler.java @@ -8,9 +8,10 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Build; import android.preference.PreferenceManager; +import android.util.Log; + import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; -import android.util.Log; import foundation.e.camera.R; diff --git a/app/src/main/java/net/sourceforge/opencamera/SaveLocationHistory.java b/app/src/main/java/net/sourceforge/opencamera/SaveLocationHistory.java index 0ce4172cfd1f0be01ee066b1c24ce421510ea98e..6afa4f9502a62ba4cd51e7739c6050754228a309 100644 --- a/app/src/main/java/net/sourceforge/opencamera/SaveLocationHistory.java +++ b/app/src/main/java/net/sourceforge/opencamera/SaveLocationHistory.java @@ -1,11 +1,11 @@ package net.sourceforge.opencamera; -import java.util.ArrayList; - import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; +import java.util.ArrayList; + /** Handles a history of save locations. */ public class SaveLocationHistory { diff --git a/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java b/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java index 64f8bf9563faa15776187bd25a6e90d9fe2a86fd..35c34f3babd8913fc1f32bd644f91d066ac9680a 100644 --- a/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java +++ b/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java @@ -1,25 +1,15 @@ package net.sourceforge.opencamera; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.content.ContentUris; -//import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.database.Cursor; -//import android.location.Location; import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; @@ -30,17 +20,25 @@ import android.preference.PreferenceManager; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.provider.MediaStore.Images; -import android.provider.MediaStore.Video; import android.provider.MediaStore.Images.ImageColumns; +import android.provider.MediaStore.Video; import android.provider.MediaStore.Video.VideoColumns; import android.provider.OpenableColumns; -import androidx.annotation.RequiresApi; -import androidx.core.content.ContextCompat; - import android.system.Os; import android.system.StructStatVfs; import android.util.Log; +import androidx.annotation.RequiresApi; +import androidx.core.content.ContextCompat; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + /** Provides access to the filesystem. Supports both standard and Storage * Access Framework. */ diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/CameraFinder.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraFinder.kt new file mode 100644 index 0000000000000000000000000000000000000000..48c5d9a6798a8d8513166bb053012b322705c973 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraFinder.kt @@ -0,0 +1,90 @@ +package net.sourceforge.opencamera.camera2 + +import android.graphics.ImageFormat +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraManager +import android.hardware.camera2.CameraMetadata +import android.os.Build +import net.sourceforge.opencamera.camera2.model.Camera2ApiProperties +import net.sourceforge.opencamera.camera2.model.CameraModel +import net.sourceforge.opencamera.camera2.model.DerivedProperties +import java.util.function.Consumer + +class CameraFinder(cameraManager: CameraManager) : + CameraFinderAbstract>(cameraManager) { + init { + cameraModels = ArrayList() + } + + public override fun createModels() { + validCameraIds.forEach(Consumer { cameraModels.add(CameraModel(it.toInt())) }) + cameraModels.forEach(Consumer { + it.camera2ApiProperties = findProperties(it.id, getCameraCharacteristics(it.id)!!) + }) + cameraModels.forEach(Consumer { + it.derivedProperties = deriveProperties(it.id, it.camera2ApiProperties) + }) + } + + public override fun findProperties( + cameraId: Int, characteristics: CameraCharacteristics): Camera2ApiProperties { + return Camera2ApiProperties(cameraId).apply { + facing = characteristics.get(CameraCharacteristics.LENS_FACING)!! + focalLength = + characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)!![0] + aperture = + characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)!![0] + aeModes = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES)!! + isFlashSupported = characteristics.get( + CameraCharacteristics.FLASH_INFO_AVAILABLE)!! + val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) + if (configMap?.getOutputSizes(ImageFormat.RAW_SENSOR) != null) { + rawSensorSizes = configMap.getOutputSizes(ImageFormat.RAW_SENSOR) + } + sensorSize = characteristics.get( + CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)!! + pixelArraySize = characteristics.get( + CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)!! + supportedHardwareLevel = characteristics.get( + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)!! + supportedHardwareLevelString = reflectionProvider.getResultFieldName( + CameraMetadata::class.java, + "INFO_SUPPORTED_HARDWARE_LEVEL_", + supportedHardwareLevel + ) + physicalIds = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + characteristics.physicalCameraIds + } else { + setOf() + } + } + } + + public override fun deriveProperties( + cameraId: Int, + camera2ApiProperties: Camera2ApiProperties + ): DerivedProperties { + return DerivedProperties(cameraId).apply { + facing = reflectionProvider.getResultFieldName( + CameraMetadata::class.java, + "LENS_FACING_", + camera2ApiProperties.facing + ) + isLogical = camera2ApiProperties.physicalIds.isNotEmpty() + angleOfView = CameraUtil.calculateAngleOfView( + camera2ApiProperties.focalLength, + camera2ApiProperties.sensorSize, + camera2ApiProperties.pixelArraySize + ) + pixelSize = CameraUtil.calculatePixelSize( + camera2ApiProperties.pixelArraySize.width, + camera2ApiProperties.sensorSize.width + ) + mm35FocalLength = + CameraUtil.calculate35mmeqv( + camera2ApiProperties.focalLength, + camera2ApiProperties.sensorSize + ) + } + } +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/CameraFinderAbstract.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraFinderAbstract.kt new file mode 100644 index 0000000000000000000000000000000000000000..860dd367af725ad5b8253a28abf012341ed24c94 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraFinderAbstract.kt @@ -0,0 +1,68 @@ +package net.sourceforge.opencamera.camera2 + +import android.hardware.camera2.CameraAccessException +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraManager +import net.sourceforge.opencamera.camera2.model.Camera2ApiProperties +import net.sourceforge.opencamera.camera2.model.CameraModel +import net.sourceforge.opencamera.camera2.model.DerivedProperties +import java.util.* +import java.util.stream.Collectors + +abstract class CameraFinderAbstract>( + private val cameraManager: CameraManager +) : CameraIDs.Finder { + protected val validCameraIds: MutableList = ArrayList() + protected val reflectionProvider = ReflectionProvider() + override lateinit var cameraModels: T + protected set + + override fun init() { + scanCameras(cameraManager) + createModels() + } + + protected abstract fun createModels() + protected abstract fun findProperties( + cameraId: Int, + characteristics: CameraCharacteristics + ): Camera2ApiProperties + + protected abstract fun deriveProperties( + cameraId: Int, + camera2ApiProperties: Camera2ApiProperties + ): DerivedProperties + + private fun scanCameras(cameraManager: CameraManager) { + for (id in 0..511) { + try { + cameraManager.getCameraCharacteristics(id.toString()) + validCameraIds.add(id.toString()) + } catch (ignored: IllegalArgumentException) { + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + override fun getCameraCharacteristics(cameraId: Int): CameraCharacteristics? { + try { + return cameraManager.getCameraCharacteristics(cameraId.toString()) + } catch (e: CameraAccessException) { + e.printStackTrace() + } + return null + } + + override val apiCameraIdList: List + get() { + try { + return Arrays.stream(cameraManager.cameraIdList).collect(Collectors.toList()) + } catch (e: CameraAccessException) { + e.printStackTrace() + } + return ArrayList() + } + override val allCameraIdList: List + get() = validCameraIds +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIDs.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIDs.kt new file mode 100644 index 0000000000000000000000000000000000000000..059eb1a638634a7c2c027da217e47ca2034f90bc --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIDs.kt @@ -0,0 +1,19 @@ +package net.sourceforge.opencamera.camera2 + +import android.hardware.camera2.CameraCharacteristics +import net.sourceforge.opencamera.camera2.model.CameraModel + +interface CameraIDs { + interface Finder> { + fun init() + fun getCameraCharacteristics(cameraId: Int): CameraCharacteristics? + val cameraModels: T + val apiCameraIdList: List + val allCameraIdList: List + } + + interface Identifier> { + fun init() + fun identifyCamera(cameraModels: T) + } +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIdentifier.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIdentifier.kt new file mode 100644 index 0000000000000000000000000000000000000000..f2727e59ed5240c275b554fb47e032e60fca7eb7 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIdentifier.kt @@ -0,0 +1,102 @@ +package net.sourceforge.opencamera.camera2 + +import android.hardware.camera2.CameraCharacteristics +import net.sourceforge.opencamera.camera2.model.Camera2ApiProperties +import net.sourceforge.opencamera.camera2.model.CameraModel +import net.sourceforge.opencamera.camera2.model.CameraType +import java.util.* +import java.util.function.Consumer + +class CameraIdentifier(cameraModels: ArrayList) : + CameraIdentifierAbstract>(cameraModels) { + private val frontCameraModels = ArrayList() + private val backCameraModels = ArrayList() + private val widerThanMain = TreeSet(SORT_BY_ANGLE_OF_VIEW) + private val narrowerThanMain = ArrayList() + private val propertiesArrayList = ArrayList() + override fun init() { + cameraModels.forEach(Consumer { + propertiesArrayList.add(it.camera2ApiProperties) + }) + frontOrBack(cameraModels) + identifyCamera(frontCameraModels) + identifyCamera(backCameraModels) + } + + private fun frontOrBack(cameraModels: Collection) { + for (model in cameraModels) { + if (model.camera2ApiProperties.facing == CameraCharacteristics.LENS_FACING_BACK) { + backCameraModels.add(model) + } + if (model.camera2ApiProperties.facing == CameraCharacteristics.LENS_FACING_FRONT) { + frontCameraModels.add(model) + } + } + } + + override fun identifyCamera(cameraModels: ArrayList) { + if (cameraModels.isNotEmpty()) { + val main = cameraModels[0] + main.cameraType = CameraType.MAIN + main.zoomFactor = 1f + cameraModels.removeAt(0) + + //Determine whether camera is logical + cameraModels.forEach(Consumer { model: CameraModel -> + if (model.derivedProperties.isLogical || getBit(6, model.id)) { + model.cameraType = CameraType.LOGICAL + } + propertiesArrayList.forEach(Consumer { + if (model.id != it.id && model.camera2ApiProperties == it) { + model.cameraType = CameraType.LOGICAL + } + }) + }) + cameraModels.removeIf { it.isTypeSet } + cameraModels.sortWith(SORT_BY_ANGLE_OF_VIEW) + cameraModels.forEach(Consumer { + val zoom = + it.derivedProperties.mm35FocalLength / main.derivedProperties.mm35FocalLength + it.zoomFactor = zoom + + //Determine whether camera is Depth or Other + it.cameraType = CameraType.OTHER + if (!it.camera2ApiProperties.isFlashSupported) { + it.cameraType = CameraType.DEPTH + } else { + if (it.derivedProperties.angleOfView > main.derivedProperties.angleOfView) { + widerThanMain.add(it) + } else { + narrowerThanMain.add(it) + } + } + }) + + //Determine whether camera is Ultrawide or Macro + widerThanMain.forEach(Consumer { + it.cameraType = if ( + it.derivedProperties.angleOfView == + widerThanMain.last().derivedProperties.angleOfView + ) { + CameraType.ULTRAWIDE + } else { + CameraType.MACRO + } + }) + + // Determine whether camera is Tele + narrowerThanMain.forEach(Consumer { cameraModel: CameraModel -> + cameraModel.cameraType = CameraType.TELE + }) + } + } + + private fun getBit(num: Int, `val`: Int): Boolean { + return `val` shr num - 1 and 1 == 1 + } + + companion object { + private val SORT_BY_ANGLE_OF_VIEW = + Comparator.comparingDouble { cameraModel: CameraModel -> cameraModel.derivedProperties.angleOfView } + } +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIdentifierAbstract.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIdentifierAbstract.kt new file mode 100644 index 0000000000000000000000000000000000000000..9f2e1d8a82d0e19a22da30ebbcef91cb4e5e0bff --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraIdentifierAbstract.kt @@ -0,0 +1,7 @@ +package net.sourceforge.opencamera.camera2 + +import net.sourceforge.opencamera.camera2.model.CameraModel + +abstract class CameraIdentifierAbstract>( + protected val cameraModels: T +) : CameraIDs.Identifier diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/CameraUtil.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraUtil.kt new file mode 100644 index 0000000000000000000000000000000000000000..cfef5db5906bc8a456074eac8719fa7f484c63f3 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/CameraUtil.kt @@ -0,0 +1,26 @@ +package net.sourceforge.opencamera.camera2 + +import android.util.Size +import android.util.SizeF +import kotlin.math.atan +import kotlin.math.pow +import kotlin.math.sqrt + +object CameraUtil { + fun calculatePixelSize(pixelArrayWidth: Int, sensorWidth: Float): Float { + return sensorWidth / pixelArrayWidth.toFloat() * 1000.0f + } + + fun calculateAngleOfView( + focalLength: Float, sensorSize: SizeF, pixelArraySize: Size + ): Double { + val pixelSize = calculatePixelSize(pixelArraySize.width, sensorSize.width) + return Math.toDegrees(atan(sqrt((sensorSize.width * pixelSize).toDouble().pow(2.0) + + (sensorSize.height * pixelSize).toDouble().pow(2.0) + ) / (2.0f * focalLength).toDouble()) * 2.0) + } + + fun calculate35mmeqv(focalLength: Float, sensorSize: SizeF): Float { + return 36.0f / sensorSize.width * focalLength + } +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/ReflectionApi.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/ReflectionApi.kt new file mode 100644 index 0000000000000000000000000000000000000000..73240f6a4a5b48acc2e35f0aaababf08906446f9 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/ReflectionApi.kt @@ -0,0 +1,9 @@ +package net.sourceforge.opencamera.camera2 + +import java.lang.reflect.Field +import java.lang.reflect.Method + +interface ReflectionApi { + fun getFields(aClass: Class<*>): Array + fun getMethods(aClass: Class<*>): Array +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/ReflectionProvider.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/ReflectionProvider.kt new file mode 100644 index 0000000000000000000000000000000000000000..16400bed9149f9dbe4122378d3d312982b78ea51 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/ReflectionProvider.kt @@ -0,0 +1,25 @@ +package net.sourceforge.opencamera.camera2 + +import java.lang.reflect.Field +import java.lang.reflect.Method + +class ReflectionProvider : ReflectionApi { + override fun getFields(aClass: Class<*>): Array { + return aClass.declaredFields + } + + override fun getMethods(aClass: Class<*>): Array { + return aClass.declaredMethods + } + + fun getResultFieldName(aClass: Class<*>, prefix: String, value: Int): String { + for (f in getFields(aClass)) if (f.name.startsWith(prefix)) { + try { + if (f.getInt(f) == value) return f.name.replace(prefix, "") + } catch (e: IllegalAccessException) { + e.printStackTrace() + } + } + return "" + } +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/model/Camera2ApiProperties.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/model/Camera2ApiProperties.kt new file mode 100644 index 0000000000000000000000000000000000000000..7b0db7cb8430094cb58d14cb00c04c2b74c449f9 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/model/Camera2ApiProperties.kt @@ -0,0 +1,35 @@ +package net.sourceforge.opencamera.camera2.model + +import android.util.Size +import android.util.SizeF +import java.util.* + +class Camera2ApiProperties(val id: Int) { + var facing = 0 + var focalLength = 0f + var aperture = 0f + lateinit var aeModes: IntArray + lateinit var rawSensorSizes: Array + lateinit var sensorSize: SizeF + lateinit var pixelArraySize: Size + var isFlashSupported = false + var supportedHardwareLevel = 0 + lateinit var supportedHardwareLevelString: String + lateinit var physicalIds: Set + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || javaClass != other.javaClass) return false + val that = other as Camera2ApiProperties + return facing == that.facing && that.focalLength.compareTo(focalLength) == 0 && that.aperture.compareTo( + aperture + ) == 0 && isFlashSupported == that.isFlashSupported && + aeModes.contentEquals(that.aeModes) && sensorSize == that.sensorSize + } + + override fun hashCode(): Int { + var result = Objects.hash(facing, focalLength, aperture, sensorSize, isFlashSupported) + result = 31 * result + aeModes.contentHashCode() + return result + } +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/model/CameraModel.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/model/CameraModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..3dc78c704898bed047ebd30051d6abbb2af9ec97 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/model/CameraModel.kt @@ -0,0 +1,48 @@ +package net.sourceforge.opencamera.camera2.model + +import java.util.* +import kotlin.math.roundToInt + +class CameraModel(val id: Int) { + var cameraType: CameraType? = null + var zoomFactor = 0f + lateinit var camera2ApiProperties: Camera2ApiProperties + lateinit var derivedProperties: DerivedProperties + + val isTypeSet: Boolean + get() = cameraType != null + + fun cameraFullModel(): MutableMap { + return mutableMapOf().apply { + this["CameraID"] = "[$id]" + if (cameraType != CameraType.LOGICAL) { + this["CameraID"] += " \u2605" + } else if (camera2ApiProperties.physicalIds.isNotEmpty()) { + this["CameraID"] += + " = ${camera2ApiProperties.physicalIds.toString().replace(", ", "+")}" + } + + this["Facing"] = "${derivedProperties.facing}" + + if (cameraType != CameraType.LOGICAL) { + this["Zoom"] = String.format(Locale.ROOT, "%.2fx", zoomFactor) + .replace(".00", "") + } + + this["Type"] = "$cameraType" + this["FocalLength"] = String.format( + Locale.ROOT, "%.2fmm", camera2ApiProperties.focalLength) + this["35mm eqv FocalLength"] = String.format( + Locale.ROOT, "%.2fmm", derivedProperties.mm35FocalLength) + this["Aperture"] = "${camera2ApiProperties.aperture}" + this["SensorSize"] = "${camera2ApiProperties.sensorSize}" + this["PixelArray"] = "${camera2ApiProperties.pixelArraySize}" + this["PixelSize"] = String.format(Locale.ROOT, "%.2fµm", derivedProperties.pixelSize) + this["AngleOfView(Diagonal)"] = "${derivedProperties.angleOfView.roundToInt()}°" + this["AEModes"] = camera2ApiProperties.aeModes.contentToString() + this["FlashSupported"] = "${camera2ApiProperties.isFlashSupported}" + this["RAW_SENSOR sizes"] = camera2ApiProperties.rawSensorSizes.contentDeepToString() + this["SupportedHardwareLevel"] = camera2ApiProperties.supportedHardwareLevelString + } + } +} diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/model/CameraType.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/model/CameraType.kt new file mode 100644 index 0000000000000000000000000000000000000000..601886465efac1069f44b45ba50c5d0db0a136e2 --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/model/CameraType.kt @@ -0,0 +1,11 @@ +package net.sourceforge.opencamera.camera2.model + +enum class CameraType { + MAIN, + ULTRAWIDE, + TELE, + MACRO, + DEPTH, + LOGICAL, + OTHER +} \ No newline at end of file diff --git a/app/src/main/java/net/sourceforge/opencamera/camera2/model/DerivedProperties.kt b/app/src/main/java/net/sourceforge/opencamera/camera2/model/DerivedProperties.kt new file mode 100644 index 0000000000000000000000000000000000000000..266a9349cdbd62bc036b4a03767711b77243fa8c --- /dev/null +++ b/app/src/main/java/net/sourceforge/opencamera/camera2/model/DerivedProperties.kt @@ -0,0 +1,9 @@ +package net.sourceforge.opencamera.camera2.model + +class DerivedProperties(val id: Int) { + var isLogical = false + var facing: String? = null + var angleOfView = 0.0 + var mm35FocalLength = 0f + var pixelSize = 0f +} diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java index c8f6e12c746b2080985e9a3d2cea7092b23af364..1b475ca8906d026e97f75f4c686a52d9ff619538 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java @@ -1,13 +1,5 @@ package net.sourceforge.opencamera.cameracontroller; -import net.sourceforge.opencamera.MyDebug; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - import android.graphics.Rect; import android.location.Location; import android.media.MediaRecorder; @@ -17,6 +9,14 @@ import android.view.TextureView; import androidx.annotation.NonNull; +import net.sourceforge.opencamera.MyDebug; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + /** CameraController is an abstract class that wraps up the access/control to * the Android camera, so that the rest of the application doesn't have to * deal directly with the Android camera API. It also allows us to support diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java index 379da2c2b1e741d510b25a56130d606069af5bbe..f8acf03925ab42d257c4c14b2e581c02df44e7d8 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java @@ -1,13 +1,5 @@ package net.sourceforge.opencamera.cameracontroller; -import net.sourceforge.opencamera.MyDebug; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; - import android.hardware.Camera; import android.hardware.Camera.AutoFocusMoveCallback; import android.location.Location; @@ -18,6 +10,14 @@ import android.util.Log; import android.view.SurfaceHolder; import android.view.TextureView; +import net.sourceforge.opencamera.MyDebug; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + /** Provides support using Android's original camera API * android.hardware.Camera. */ diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java index cc1cc7958cf0e8845e8d3fda26d6953eb9d35013..dbc3ffe287fac21bcce22d896ff84bb6c3db2f88 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java @@ -1,19 +1,5 @@ package net.sourceforge.opencamera.cameracontroller; -import net.sourceforge.opencamera.ContinuousBurstImageRunningAction; -import net.sourceforge.opencamera.HDRProcessor; -import net.sourceforge.opencamera.MyDebug; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Queue; -import java.util.concurrent.Executor; - import android.app.Activity; import android.content.Context; import android.graphics.ImageFormat; @@ -43,7 +29,6 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.params.TonemapCurve; import android.location.Location; import android.media.AudioManager; -import androidx.exifinterface.media.ExifInterface; import android.media.Image; import android.media.ImageReader; import android.media.MediaActionSound; @@ -51,8 +36,6 @@ import android.media.MediaRecorder; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; import android.util.Log; import android.util.Pair; import android.util.Range; @@ -62,8 +45,26 @@ import android.view.Surface; import android.view.SurfaceHolder; import android.view.TextureView; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.exifinterface.media.ExifInterface; + +import net.sourceforge.opencamera.ContinuousBurstImageRunningAction; +import net.sourceforge.opencamera.HDRProcessor; +import net.sourceforge.opencamera.MyDebug; + import org.greenrobot.eventbus.EventBus; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Queue; +import java.util.concurrent.Executor; + /** Provides support using Android 5's Camera 2 API * android.hardware.camera2.*. */ diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraControllerManager1.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraControllerManager1.java index 0a08b6fb48e284ba4e321f807bf22d5be0e48777..25ccbea7a6812ddf8b9b0e0b468cfdda7b4aa0db 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraControllerManager1.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraControllerManager1.java @@ -1,11 +1,11 @@ package net.sourceforge.opencamera.cameracontroller; -import foundation.e.camera.R; - import android.content.Context; import android.hardware.Camera; import android.util.Log; +import foundation.e.camera.R; + /** Provides support using Android's original camera API * android.hardware.Camera. */ diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraControllerManager2.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraControllerManager2.java index f2d17cf7664b3810af09976fcebffdcf263a3b9c..02081e43f10b5981f737b3b1c2a0164b914a3ee3 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraControllerManager2.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraControllerManager2.java @@ -1,8 +1,5 @@ package net.sourceforge.opencamera.cameracontroller; -import net.sourceforge.opencamera.MyDebug; -import foundation.e.camera.R; - import android.annotation.TargetApi; import android.content.Context; import android.graphics.Rect; @@ -13,6 +10,10 @@ import android.os.Build; import android.util.Log; import android.util.SizeF; +import net.sourceforge.opencamera.MyDebug; + +import foundation.e.camera.R; + /** Provides support using Android 5's Camera 2 API * android.hardware.camera2.*. */ diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/ApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/preview/ApplicationInterface.java index a367b441a9fe3f22cfb1cdd5f2a00c45b99c99f6..bfe4760ae699a328198f365f877e8fd17c3499a1 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/ApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/ApplicationInterface.java @@ -1,10 +1,5 @@ package net.sourceforge.opencamera.preview; -import java.io.File; -import java.io.IOException; -import java.util.Date; -import java.util.List; - import android.content.Context; import android.graphics.Canvas; import android.location.Location; @@ -20,6 +15,11 @@ import net.sourceforge.opencamera.MyDebug; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.RawImage; +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.List; + /** Provides communication between the Preview and the rest of the application * - so in theory one can drop the Preview/ (and CameraController/) classes * into a new application, by providing an appropriate implementation of this diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/BasicApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/preview/BasicApplicationInterface.java index b12b7b144bf9088846448167efdaf058397728f7..b9875eafbffb8be32a4a8b3e31d6890d046ce86c 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/BasicApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/BasicApplicationInterface.java @@ -1,8 +1,5 @@ package net.sourceforge.opencamera.preview; -import java.util.Date; -import java.util.List; - import android.app.Activity; import android.graphics.Canvas; import android.location.Location; @@ -16,6 +13,9 @@ import androidx.annotation.RequiresApi; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.RawImage; +import java.util.Date; +import java.util.List; + /** A partial implementation of ApplicationInterface that provides "default" implementations. So * sub-classing this is easier than implementing ApplicationInterface directly - you only have to * provide the unimplemented methods to get started, and can later override diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/CanvasView.java b/app/src/main/java/net/sourceforge/opencamera/preview/CanvasView.java index ec6f13bb500103223df86ff74e920a4f44d62dc2..446d5b759ac94809ed3d34ac3665a8d313b4a7dd 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/CanvasView.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/CanvasView.java @@ -1,12 +1,13 @@ package net.sourceforge.opencamera.preview; -import net.sourceforge.opencamera.MyDebug; import android.content.Context; import android.graphics.Canvas; import android.os.Handler; import android.util.Log; import android.view.View; +import net.sourceforge.opencamera.MyDebug; + /** Overlay for the Preview - this just redirects to Preview.onDraw to do the * work. Only used if using a MyTextureView (if using MySurfaceView, then that * class can handle the onDraw()). TextureViews can't be used for both a diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index dcccc4e00bb646add60acb72a94e7a0fd64fdf3c..bb44d61dcddc869711c9efcae27c8ab0da3f8a89 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -1,44 +1,5 @@ package net.sourceforge.opencamera.preview; -import net.sourceforge.opencamera.MainActivity; -import net.sourceforge.opencamera.PropertyUtility; -import net.sourceforge.opencamera.cameracontroller.RawImage; -import net.sourceforge.opencamera.MyDebug; - -import foundation.e.camera.R; - -import net.sourceforge.opencamera.ScriptC_histogram_compute; -import net.sourceforge.opencamera.TakePhoto; -import net.sourceforge.opencamera.ToastBoxer; -import net.sourceforge.opencamera.cameracontroller.CameraController; -import net.sourceforge.opencamera.cameracontroller.CameraController1; -import net.sourceforge.opencamera.cameracontroller.CameraController2; -import net.sourceforge.opencamera.cameracontroller.CameraControllerException; -import net.sourceforge.opencamera.cameracontroller.CameraControllerManager; -import net.sourceforge.opencamera.cameracontroller.CameraControllerManager1; -import net.sourceforge.opencamera.cameracontroller.CameraControllerManager2; -import net.sourceforge.opencamera.preview.ApplicationInterface.NoFreeStorageException; -import net.sourceforge.opencamera.preview.camerasurface.CameraSurface; -import net.sourceforge.opencamera.preview.camerasurface.MySurfaceView; -import net.sourceforge.opencamera.preview.camerasurface.MyTextureView; - -import java.io.File; -//import java.io.FileOutputStream; -import java.io.IOException; -//import java.io.OutputStream; -import java.lang.ref.WeakReference; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -68,7 +29,6 @@ import android.os.AsyncTask; import android.os.BatteryManager; import android.os.Build; import android.os.Bundle; -//import android.os.Environment; import android.os.Handler; import android.os.ParcelFileDescriptor; import android.renderscript.Allocation; @@ -76,10 +36,6 @@ import android.renderscript.Element; import android.renderscript.RSInvalidStateException; import android.renderscript.RenderScript; import android.renderscript.Type; -import androidx.annotation.RequiresApi; -import androidx.core.content.ContextCompat; -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; import android.util.Log; import android.util.Pair; import android.view.Display; @@ -93,14 +49,54 @@ import android.view.Surface; import android.view.SurfaceHolder; import android.view.TextureView; import android.view.View; +import android.view.View.MeasureSpec; import android.view.ViewGroup; import android.view.ViewParent; import android.view.WindowManager; -import android.view.View.MeasureSpec; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.core.content.ContextCompat; + +import net.sourceforge.opencamera.MainActivity; +import net.sourceforge.opencamera.MyDebug; +import net.sourceforge.opencamera.PropertyUtility; +import net.sourceforge.opencamera.ScriptC_histogram_compute; +import net.sourceforge.opencamera.TakePhoto; +import net.sourceforge.opencamera.ToastBoxer; +import net.sourceforge.opencamera.cameracontroller.CameraController; +import net.sourceforge.opencamera.cameracontroller.CameraController1; +import net.sourceforge.opencamera.cameracontroller.CameraController2; +import net.sourceforge.opencamera.cameracontroller.CameraControllerException; +import net.sourceforge.opencamera.cameracontroller.CameraControllerManager; +import net.sourceforge.opencamera.cameracontroller.CameraControllerManager1; +import net.sourceforge.opencamera.cameracontroller.CameraControllerManager2; +import net.sourceforge.opencamera.cameracontroller.RawImage; +import net.sourceforge.opencamera.preview.ApplicationInterface.NoFreeStorageException; +import net.sourceforge.opencamera.preview.camerasurface.CameraSurface; +import net.sourceforge.opencamera.preview.camerasurface.MySurfaceView; +import net.sourceforge.opencamera.preview.camerasurface.MyTextureView; + +import java.io.File; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import foundation.e.camera.R; + /** This class was originally named due to encapsulating the camera preview, * but in practice it's grown to more than this, and includes most of the * operation of the camera. It exists at a higher level than CameraController @@ -2214,9 +2210,13 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu } } - private int find1xZoom() { + public int find1xZoom() { + return findNxZoom(1.0f); + } + + public int findNxZoom(float zoom) { for(int i=0;i + + false + false + + + + + + diff --git a/app/src/test/java/net/sourceforge/opencamera/test/UnitTest.java b/app/src/test/java/net/sourceforge/opencamera/test/UnitTest.java index e7a0d401cf98dc2df66c45abcd5401c3989015e8..211ba2db63bec9f9d79130bdfd8ddbeec95b6a18 100644 --- a/app/src/test/java/net/sourceforge/opencamera/test/UnitTest.java +++ b/app/src/test/java/net/sourceforge/opencamera/test/UnitTest.java @@ -1,17 +1,21 @@ package net.sourceforge.opencamera.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import android.media.CamcorderProfile; +import net.sourceforge.opencamera.HDRProcessor; +import net.sourceforge.opencamera.ImageSaver; +import net.sourceforge.opencamera.LocationSupplier; import net.sourceforge.opencamera.MainActivity; import net.sourceforge.opencamera.MyApplicationInterface; +import net.sourceforge.opencamera.TextFormatter; import net.sourceforge.opencamera.cameracontroller.CameraController; import net.sourceforge.opencamera.cameracontroller.CameraController2; -import net.sourceforge.opencamera.HDRProcessor; -import net.sourceforge.opencamera.ImageSaver; -import net.sourceforge.opencamera.LocationSupplier; import net.sourceforge.opencamera.preview.Preview; import net.sourceforge.opencamera.preview.VideoQualityHandler; -import net.sourceforge.opencamera.TextFormatter; import net.sourceforge.opencamera.ui.DrawPreview; import net.sourceforge.opencamera.ui.MainUI; import net.sourceforge.opencamera.ui.PopupView; @@ -26,8 +30,6 @@ import java.util.Date; import java.util.List; import java.util.Locale; -import static org.junit.Assert.*; - class Log { public static void d(String tag, String text) { System.out.println(tag + ": " + text); diff --git a/build.gradle b/build.gradle index f3b60a1fabe3f2a429f053e80ce80cb9c3037b7b..e7b1b4e373e3ccce5d140f3f13581aa26be9fc75 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10' } }