Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9667ca81 authored by Mohammed Althaf T's avatar Mohammed Althaf T 😊
Browse files

Merge branch '902-master-pixel_camera' into 'master'

Support easy switch between camera lens for pixel devices

See merge request !55
parents 78df010e 62892fde
Loading
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
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!
+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";
+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;
@@ -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.
+1 −1
Original line number Diff line number Diff line
@@ -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).
 */
+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;
@@ -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> 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<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;
@@ -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();
@@ -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<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 )
@@ -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);
        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
@@ -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
Loading