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

Commit 2c6da220 authored by Romain Hunault's avatar Romain Hunault 💻 Committed by Jonathan Klee
Browse files

Barcode scanner

parent 4a8573a7
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ android {

    defaultConfig {
        applicationId "foundation.e.camera"
        minSdkVersion 25
        minSdkVersion 26
        targetSdkVersion 33

        renderscriptTargetApi 21
@@ -79,6 +79,9 @@ dependencies {

    implementation 'androidx.exifinterface:exifinterface:1.3.6'

    implementation 'androidx.camera:camera-core:1.2.3'
    implementation 'com.google.zxing:core:3.5.2'

    testImplementation 'junit:junit:4.13.2'

    // newer AndroidJUnit4 InstrumentedTest
+138 −75
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ 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.qr.QrImageAnalyzer;
import net.sourceforge.opencamera.remotecontrol.BluetoothRemoteControl;
import net.sourceforge.opencamera.ui.CircleImageView;
import net.sourceforge.opencamera.ui.DrawPreview;
@@ -109,6 +110,11 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import foundation.e.camera.R;
import kotlin.coroutines.CoroutineContext;
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.Dispatchers;
import kotlinx.coroutines.Job;
import kotlinx.coroutines.JobKt;

/**
 * The main Activity for Open Camera.
@@ -241,6 +247,11 @@ public class MainActivity extends AppCompatActivity {
    //public static final boolean lock_to_landscape = true;
    public static final boolean lock_to_landscape = false;

    // QRCode
    public QrImageAnalyzer qrImageAnalyzer;
    private Job job;
    private CoroutineScope coroutineScope;

    // handling for lock_to_landscape==false:

    public enum SystemOrientation {
@@ -273,6 +284,16 @@ public class MainActivity extends AppCompatActivity {
            Log.d(TAG, "activity_count: " + activity_count);
        super.onCreate(savedInstanceState);

        //QRCode
        job = JobKt.Job(null);
        coroutineScope = new CoroutineScope() {
            @Override
            public CoroutineContext getCoroutineContext() {
                return Dispatchers.getMain().plus(job);
            }
        };
        qrImageAnalyzer = new QrImageAnalyzer(this, coroutineScope);

        setContentView(R.layout.activity_main);
        PreferenceManager.setDefaultValues(this, R.xml.preferences, false); // initialise any unset preferences to their default values

@@ -2661,6 +2682,45 @@ public class MainActivity extends AppCompatActivity {
        }
    }

    /*  getBetterQRCodeCameraID()
        Returns the best camera ID, based on the fact that it's probably the first rear camera available.
        The user always has the option of selecting with the lens switcher in case the choice is wrong.
        Returns -1 if no camera available. In that case we do *NOT* trig any switch.
     */
    public int getBetterQRCodeCameraID() {
        int best_qrcode_camera = -1;
        if( MyDebug.LOG )
            Log.d(TAG, "getBetterQRCodeCameraID");
        if( !isMultiCamEnabled() ) {
            Log.e(TAG, "getBetterQRCodeCameraID switch multi camera icon shouldn't have been visible");
            return best_qrcode_camera;
        }
        if( preview.isOpeningCamera() ) {
            if( MyDebug.LOG )
                Log.d(TAG, "getBetterQRCodeCameraID already opening camera in background thread");
            return best_qrcode_camera;
        }
        if( this.preview.canSwitchCamera() ) {
            try {
                CameraManager _cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
                for (String _cameraId : _cameraManager.getCameraIdList()) {
                    CameraCharacteristics characteristics = _cameraManager.getCameraCharacteristics(_cameraId);
                    Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                    if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
                        best_qrcode_camera = Integer.parseInt(_cameraId);
                        if( MyDebug.LOG )
                            Log.d(TAG, "best_qrcode_camera ="+best_qrcode_camera);
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return best_qrcode_camera;
    }


    private void updateMultiCameraIcon() {
        Button multiCameraButton = findViewById(R.id.switch_multi_camera);

@@ -6258,7 +6318,7 @@ public class MainActivity extends AppCompatActivity {
                simple = false;
            }
        }
        else {
        else { //Camera
            if( photo_mode == MyApplicationInterface.PhotoMode.Panorama ) {
                // don't show resolution in panorama mode
                toast_string = "";
@@ -6292,6 +6352,9 @@ public class MainActivity extends AppCompatActivity {
                simple = false;
            }
        }
        if (preview.isQRCode()) {
            toast_string = "QRCode";
        } else { //Camera || Video
            if (applicationInterface.getFaceDetectionPref()) {
                // important so that the user realises why touching for focus/metering areas won't work - easy to forget that face detection has been turned on!
                toast_string += "\n" + getResources().getString(R.string.preference_face_detection);
@@ -6333,8 +6396,7 @@ public class MainActivity extends AppCompatActivity {
                    toast_string += "\n" + getResources().getString(R.string.color_effect) + ": " + mainUI.getEntryForColorEffect(color_effect);
                    simple = false;
                }
        }
        catch(RuntimeException e) {
            } catch (RuntimeException e) {
                // catch runtime error from camera_controller old API from camera.getParameters()
                e.printStackTrace();
            }
@@ -6375,6 +6437,7 @@ public class MainActivity extends AppCompatActivity {
                /*if( audio_listener != null ) {
                    toast_string += "\n" + getResources().getString(R.string.preference_audio_noise_control);
                }*/
        }

        if( MyDebug.LOG ) {
            Log.d(TAG, "toast_string: " + toast_string);
+130 −0
Original line number Diff line number Diff line
/*
 * SPDX-FileCopyrightText: 2024 The LineageOS Project
 * SPDX-License-Identifier: Apache-2.0
 */

package net.sourceforge.opencamera.ext

import android.app.RemoteAction
import android.content.Context
import android.content.Intent
import android.os.Build
import android.provider.ContactsContract
import android.view.textclassifier.TextClassification
import android.view.textclassifier.TextClassifier
import com.google.zxing.client.result.AddressBookParsedResult
import foundation.e.camera.R

fun AddressBookParsedResult.createIntent() = Intent(
    Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI
).apply {
    names.firstOrNull()?.let {
        putExtra(ContactsContract.Intents.Insert.NAME, it)
    }

    pronunciation?.let {
        putExtra(ContactsContract.Intents.Insert.PHONETIC_NAME, it)
    }

    phoneNumbers?.let { phoneNumbers ->
        val phoneTypes = phoneTypes ?: arrayOf()

        for ((key, keys) in listOf(
            listOf(
                ContactsContract.Intents.Insert.PHONE,
                ContactsContract.Intents.Insert.PHONE_TYPE,
            ),
            listOf(
                ContactsContract.Intents.Insert.SECONDARY_PHONE,
                ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE,
            ),
            listOf(
                ContactsContract.Intents.Insert.TERTIARY_PHONE,
                ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE,
            ),
        ).withIndex()) {
            phoneNumbers.getOrNull(key)?.let { phone ->
                putExtra(keys.first(), phone)
                phoneTypes.getOrNull(key)?.let {
                    putExtra(keys.last(), it)
                }
            }
        }
    }

    emails?.let { emails ->
        val emailTypes = emailTypes ?: arrayOf()

        for ((key, keys) in listOf(
            listOf(
                ContactsContract.Intents.Insert.EMAIL,
                ContactsContract.Intents.Insert.EMAIL_TYPE,
            ),
            listOf(
                ContactsContract.Intents.Insert.SECONDARY_EMAIL,
                ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE,
            ),
            listOf(
                ContactsContract.Intents.Insert.TERTIARY_EMAIL,
                ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE,
            ),
        ).withIndex()) {
            emails.getOrNull(key)?.let { phone ->
                putExtra(keys.first(), phone)
                emailTypes.getOrNull(key)?.let {
                    putExtra(keys.last(), it)
                }
            }
        }
    }

    instantMessenger?.let {
        putExtra(ContactsContract.Intents.Insert.IM_HANDLE, it)
    }

    note?.let {
        putExtra(ContactsContract.Intents.Insert.NOTES, it)
    }

    addresses?.let { emails ->
        val addressTypes = addressTypes ?: arrayOf()

        for ((key, keys) in listOf(
            listOf(
                ContactsContract.Intents.Insert.POSTAL,
                ContactsContract.Intents.Insert.POSTAL_TYPE,
            ),
        ).withIndex()) {
            emails.getOrNull(key)?.let { phone ->
                putExtra(keys.first(), phone)
                addressTypes.getOrNull(key)?.let {
                    putExtra(keys.last(), it)
                }
            }
        }
    }

    org?.let {
        putExtra(ContactsContract.Intents.Insert.COMPANY, it)
    }
}

fun AddressBookParsedResult.createTextClassification(
    context: Context
) = TextClassification.Builder()
    .setText(title ?: names.firstOrNull() ?: "")
    .setEntityType(TextClassifier.TYPE_OTHER, 1.0f)
    .apply {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            addAction(
                RemoteAction::class.build(
                    context,
                    R.drawable.ic_contact_phone,
                    R.string.qr_address_title,
                    R.string.qr_address_content_description,
                    createIntent()
                )
            )
        }
    }
    .build()
+63 −0
Original line number Diff line number Diff line
/*
 * SPDX-FileCopyrightText: 2024 The LineageOS Project
 * SPDX-License-Identifier: Apache-2.0
 */

package net.sourceforge.opencamera.ext

import android.app.RemoteAction
import android.content.Context
import android.content.Intent
import android.os.Build
import android.provider.CalendarContract
import android.view.textclassifier.TextClassification
import android.view.textclassifier.TextClassifier
import androidx.core.os.bundleOf
import com.google.zxing.client.result.CalendarParsedResult
import foundation.e.camera.R

fun CalendarParsedResult.createIntent() = Intent(
    Intent.ACTION_INSERT, CalendarContract.Events.CONTENT_URI
).apply {
    summary?.let {
        putExtra(CalendarContract.Events.TITLE, it)
    }
    description?.let {
        putExtra(CalendarContract.Events.DESCRIPTION, it)
    }
    location?.let {
        putExtra(CalendarContract.Events.EVENT_LOCATION, it)
    }
    organizer?.let {
        putExtra(CalendarContract.Events.ORGANIZER, it)
    }
    attendees?.let {
        putExtra(Intent.EXTRA_EMAIL, it.joinToString(","))
    }

    putExtras(
        bundleOf(
            CalendarContract.EXTRA_EVENT_BEGIN_TIME to startTimestamp,
            CalendarContract.EXTRA_EVENT_END_TIME to endTimestamp,
            CalendarContract.EXTRA_EVENT_ALL_DAY to (isStartAllDay && isEndAllDay),
        )
    )
}

fun CalendarParsedResult.createTextClassification(context: Context) = TextClassification.Builder()
    .setText(summary)
    .setEntityType(TextClassifier.TYPE_OTHER, 1.0f)
    .apply {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            addAction(
                RemoteAction::class.build(
                    context,
                    R.drawable.ic_calendar_add_on,
                    R.string.qr_calendar_title,
                    R.string.qr_calendar_content_description,
                    createIntent()
                )
            )
        }
    }
    .build()
+17 −0
Original line number Diff line number Diff line
/*
 * SPDX-FileCopyrightText: 2024 The LineageOS Project
 * SPDX-License-Identifier: Apache-2.0
 */

package net.sourceforge.opencamera.ext

import android.content.Context
import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt

@ColorInt
fun Context.getThemeColor(@AttrRes attribute: Int) = TypedValue().let {
    theme.resolveAttribute(attribute, it, true)
    it.data
}
Loading