diff --git a/app/build.gradle b/app/build.gradle index 52eeee9edca1df90aaf77a3629ad9b0a9d1b5a3a..4814d5afa11bb7409e12236983123cab2144a74a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' apply plugin: 'org.jetbrains.kotlin.android' android { - compileSdkVersion 31 + compileSdkVersion 33 compileOptions.encoding = 'UTF-8' compileOptions { @@ -23,7 +23,7 @@ android { defaultConfig { applicationId "foundation.e.camera" minSdkVersion 25 - targetSdkVersion 31 + targetSdkVersion 33 renderscriptTargetApi 21 //renderscriptSupportModeEnabled true // don't use support library as it bloats the APK, and we don't need pre-4.4 support @@ -56,28 +56,31 @@ android { abortOnError false checkReleaseBuilds false } + buildFeatures { + renderScript true + buildConfig true + } + namespace 'foundation.e.camera' //useLibrary 'android.test.mock' - } dependencies { //noinspection GradleCompatible implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation "androidx.constraintlayout:constraintlayout:2.1.3" + implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "org.greenrobot:eventbus:3.3.1" - implementation 'androidx.core:core-ktx:1.8.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' // appcompat version must be 1.4.0 or later to satisfy emoji policy! - implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'androidx.exifinterface:exifinterface:1.3.3' + implementation 'androidx.exifinterface:exifinterface:1.3.7' implementation 'foundation.e:elib:0.0.1-alpha11' - testImplementation 'junit:junit:4.13.1' + testImplementation 'junit:junit:4.13.2' // newer AndroidJUnit4 InstrumentedTest - androidTestImplementation "androidx.test:runner:1.4.0" - androidTestImplementation "androidx.test:rules:1.4.0" - androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0" + androidTestImplementation "androidx.test:runner:1.5.2" + androidTestImplementation "androidx.test:rules:1.5.0" + androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0f69e855ce28a58bd16d0b14b64b2beb40ecc737..b8fdfc42aed203a11b2d0b8237d36140e4447484 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ diff --git a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java index 96e8f16934c0ae24e9b035571dd125c2bb007289..4d9cb5b060b74bec8d7c7cc12c3c0122a49f47c8 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java @@ -2435,7 +2435,7 @@ public class MyApplicationInterface extends BasicApplicationInterface { try { retriever.release(); } - catch(RuntimeException ex) { + catch(RuntimeException | IOException ex) { // ignore } try { diff --git a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java index 4fd422c427c850b078cbdc7cab43e441d3373fd4..677f69f219ad0a94625205b72d7d08d5c74ca656 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java @@ -814,18 +814,14 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared { final Preference pref = findPreference("preference_privacy_policy"); - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference arg0) { - if( pref.getKey().equals("preference_privacy_policy") ) { - if( MyDebug.LOG ) - Log.d(TAG, "user clicked privacy policy"); - MainActivity main_activity = (MainActivity)MyPreferenceFragment.this.getActivity(); - main_activity.launchOnlinePrivacyPolicy(); - return false; - } - return false; + pref.setOnPreferenceClickListener(arg0 -> { + if( pref.getKey().equals("preference_privacy_policy") ) { + if( MyDebug.LOG ) + Log.d(TAG, "user clicked privacy policy"); + MainActivity main_activity = (MainActivity)MyPreferenceFragment.this.getActivity(); + main_activity.launchOnlinePrivacyPolicy(); } + return false; }); } 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 7104a189514eb8d7136fd49d9841633e5ba999fd..92648b9d0cfa3b10ecd280a709d8422a05404c2c 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java @@ -44,6 +44,7 @@ import android.view.Display; import android.view.Surface; import android.view.SurfaceHolder; import android.view.TextureView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; @@ -65,6 +66,8 @@ import java.util.Locale; import java.util.Queue; import java.util.concurrent.Executor; +import foundation.e.camera.R; + /** Provides support using Android 5's Camera 2 API * android.hardware.camera2.*. */ @@ -100,6 +103,8 @@ public class CameraController2 extends CameraController { private long min_exposure_time; private long max_exposure_time; + private int toastCounter = 0; + private final static int tonemap_log_max_curve_points_c = 64; private final static float [] jtvideo_values_base = new float[] { 0.00f, 0.00f, @@ -180,7 +185,7 @@ public class CameraController2 extends CameraController { private boolean previewIsVideoMode; private AutoFocusCallback autofocus_cb; private long autofocus_time_ms = -1; // time we set autofocus_cb to non-null - private static final long autofocus_timeout_c = 1000; // timeout for calling autofocus_cb (applies for both auto and continuous focus) + private static final long autofocus_timeout_c = 500; // timeout for calling autofocus_cb (applies for both auto and continuous focus) private boolean capture_follows_autofocus_hint; private boolean ready_for_capture; private FaceDetectionListener face_detection_listener; @@ -265,8 +270,8 @@ public class CameraController2 extends CameraController { private static final int STATE_WAITING_FAKE_PRECAPTURE_DONE = 5; private int state = STATE_NORMAL; private long precapture_state_change_time_ms = -1; // time we changed state for precapture modes - private static final long precapture_start_timeout_c = 2000; - private static final long precapture_done_timeout_c = 3000; + private static final long precapture_start_timeout_c = 500; + private static final long precapture_done_timeout_c = 1000; private boolean use_fake_precapture; // see CameraController.setUseCamera2FakeFlash() for details - this is the user/application setting, see use_fake_precapture_mode for whether fake precapture is enabled (as we may do this for other purposes, e.g., front screen flash) private boolean use_fake_precapture_mode; // true if either use_fake_precapture is true, or we're temporarily using fake precapture mode (e.g., for front screen flash or exposure bracketing) @@ -8195,14 +8200,17 @@ public class CameraController2 extends CameraController { // need to check af_state != null, I received Google Play crash in 1.33 where it was null boolean focus_success = af_state != null && ( af_state == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || af_state == CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED ); if( MyDebug.LOG ) { - if( focus_success ) + if( focus_success ) { Log.d(TAG, "autofocus success"); - else + } else { Log.d(TAG, "autofocus failed"); - if( af_state == null ) + onFocusFailed(); + } + if( af_state == null ) { Log.e(TAG, "continuous focus mode but af_state is null"); - else + } else { Log.d(TAG, "af_state: " + af_state); + } } if( af_state == null ) { test_af_state_null_focus++; @@ -8255,10 +8263,12 @@ public class CameraController2 extends CameraController { ) { boolean focus_success = af_state == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || af_state == CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED; if( MyDebug.LOG ) { - if( focus_success ) + if( focus_success ) { Log.d(TAG, "onCaptureCompleted: autofocus success"); - else + } else { Log.d(TAG, "onCaptureCompleted: autofocus failed"); + onFocusFailed(); + } Log.d(TAG, "af_state: " + af_state); } state = STATE_NORMAL; @@ -8448,6 +8458,18 @@ public class CameraController2 extends CameraController { } } + private void onFocusFailed() { + // Show only if focus failed twice. + if (toastCounter == 1) { + Toast.makeText(context, + R.string.preference_failed_focus_mode_message, + Toast.LENGTH_SHORT).show(); + toastCounter = 0; + } else { + toastCounter++; + } + } + private void handleContinuousFocusMove(CaptureResult result) { Integer af_state = result.get(CaptureResult.CONTROL_AF_STATE); if( af_state != null && af_state == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN && af_state != last_af_state ) { 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 9d856a2ad6929c54a309135d999d14a7afb3eb12..4e4fe633ea542c9b307a2967404c9eb1daf676d1 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -8527,9 +8527,17 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu frame rate when applying the dimming effect when reopening or updating the camera (see DrawPreview.setDimPreview()) (especially for MainActivity.updateForSettings() when we pause/unpause the preview instead of reopening the camera). + Update: On more recent Android versions, this effect no longer seems to happen, and on + Android 13 (at least Pixel 6 Pro), we see the reverse (but more reasonable) behaviour + where we have fewer janky frames with a longer frame rate. Behaviour is much better at + 32ms compared to 16ms; and we shouldn't go any slower (firstly so that UI still runs + smoothly; secondly for dimming effect as noted above). */ // - if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) { + if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ) { + return 32; + } + else if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) { if( is_test_junit4 ) { // see https://stackoverflow.com/questions/29550508/espresso-freezing-on-view-with-looping-animation return 32; diff --git a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java index 7285ac7fa4cf10db8b2f2a3262da8de65c758116..42769763f549468abf10f31cbe50cdddc3d0b503 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java +++ b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java @@ -2484,7 +2484,7 @@ public class DrawPreview { if( camera_controller != null && this.thumbnail_anim && last_thumbnail != null ) { int ui_rotation = preview.getUIRotation(); long time = time_ms - this.thumbnail_anim_start_ms; - final long duration = 500; + final long duration = 400; if( time > duration ) { if( MyDebug.LOG ) Log.d(TAG, "thumbnail_anim finished"); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 609ba67528102a2c3c9a0ced87d04ff5a55a2eaf..2ab7f9bc1c91f0d7db42da7c5a16e26bcc579ea5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -783,6 +783,7 @@ Unlock exposure Low light mode: please hold the camera steady + Please hold the camera steady for better photos. Pano Panorama @@ -1027,25 +1028,25 @@ Open Camera is developed by Mark Harman. - <br/>Open Camera accesses camera sensor and microphone data to fulfil its purpose as a camera. + \nOpen Camera accesses camera sensor and microphone data to fulfil its purpose as a camera. Microphone is also used for the optional \"Audio control\". - <br/>Access to files is needed (at least for Android 9 and earlier) to save resultant files such as photos and videos to your device. - <br/>Location permission is requested in order to deliver the optional geotagging features (for photos and videos, + \nAccess to files is needed (at least for Android 9 and earlier) to save resultant files such as photos and videos to your device. + \nLocation permission is requested in order to deliver the optional geotagging features (for photos and videos, including stamp and subtitles options). When relevant option(s) are enabled, your device location will be stored in photo/video/subtitle files. - <br/>Bluetooth permissions are used to allow the optional feature to discover and connect to Bluetooth LE remote control devices; + \nBluetooth permissions are used to allow the optional feature to discover and connect to Bluetooth LE remote control devices; the Bluetooth remote control feature also requires location permission (on Android 11 or earlier) or Nearby Devices permission (on Android 12 or later). - <br/>Resultant data such as photos or videos can be shared with + \nResultant data such as photos or videos can be shared with other apps if you use the share option in Open Camera, or when Open Camera is called by another app on your device, or when you use the Storage Access Framework option to save to another app or service. - <br/>Data handling procedures, data retention and deletion policies: Open Camera + \nData handling procedures, data retention and deletion policies: Open Camera does not transmit personal or sensitive information to me. - <br/>Since Open Camera also uses operating system APIs, you should review relevant privacy policies + \nSince Open Camera also uses operating system APIs, you should review relevant privacy policies such as for your device, manufacturer, operating system and/or Google accounts. For example: - <br/>*Apps/services such as cloud services on your device may auto-upload photos and videos that are saved on your device. - <br/>If you have inquiries about my privacy policy, please contact me by email at + \n*Apps/services such as cloud services on your device may auto-upload photos and videos that are saved on your device. + \nIf you have inquiries about my privacy policy, please contact me by email at <a href=\"mailto:mark.harman.apps@gmail.com?subject=Open%20Camera%20privacy%20policy\">mark.harman.apps@gmail.com</a>. diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e0ab8a0d2790aa5a79ae60ff6243621dac74086e..4541baf86d3a007d8c40a4e2b68a26843c927e3e 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -20,5 +20,5 @@ true @null - + diff --git a/build.gradle b/build.gradle index e7b1b4e373e3ccce5d140f3f13581aa26be9fc75..45066a3a43299650ff82ca849f41e7d560123f94 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:8.2.0' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10' } } diff --git a/gradle.properties b/gradle.properties index fa694f82cb7e894a8d9e081cd38a2fc8c67c1aa7..4ff57088dee773ca551b90a3cba6e158e8a8ffbd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,3 +2,5 @@ android.useAndroidX=true android.enableJetifier=true org.gradle.jvmargs=-Xmx1400m org.gradle.parallel=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0ba64bae15d878cc28def2f3c023070bf77e6973..828c5fc5e88855a1a68fecab5bb5322048bb5e82 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Oct 13 22:07:50 BST 2020 +#Mon Dec 25 11:59:58 IST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip