詹子聪 5 年 前
コミット
50594c1c1c

+ 1 - 0
app/src/main/AndroidManifest.xml

@@ -10,6 +10,7 @@
 
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-feature android:name="android.hardware.camera2.full" />
+    <uses-feature android:name="android.hardware.camera.any" />
     <uses-feature android:name="android.hardware.camera" />
     <uses-feature android:name="android.hardware.camera.autofocus" />
 

+ 1 - 0
camerax/.gitignore

@@ -0,0 +1 @@
+/build

+ 38 - 0
camerax/build.gradle

@@ -0,0 +1,38 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion versions.compileSdk
+    buildToolsVersion versions.buildTools
+
+
+    defaultConfig {
+        minSdkVersion versions.minSdk
+        targetSdkVersion versions.targetSdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles 'consumer-rules.pro'
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    implementation 'androidx.appcompat:appcompat:1.2.0'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+    def camerax_version = "1.0.0-alpha01"
+    implementation "androidx.camera:camera-core:${camerax_version}"
+    implementation "androidx.camera:camera-camera2:${camerax_version}"
+}

+ 0 - 0
camerax/consumer-rules.pro


+ 21 - 0
camerax/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 27 - 0
camerax/src/androidTest/java/com/miekir/camerax/ExampleInstrumentedTest.java

@@ -0,0 +1,27 @@
+package com.miekir.camerax;
+
+import android.content.Context;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        assertEquals("com.miekir.camerax.test", appContext.getPackageName());
+    }
+}

+ 1 - 0
camerax/src/main/AndroidManifest.xml

@@ -0,0 +1 @@
+<manifest package="com.miekir.camerax" />

+ 186 - 0
camerax/src/main/java/com/miekir/camerax/MainActivity.java

@@ -0,0 +1,186 @@
+package com.miekir.camerax;//saus: https://codelabs.developers.google.com/codelabs/camerax-getting-started/
+
+import android.content.pm.PackageManager;
+import android.graphics.Matrix;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Rational;
+import android.util.Size;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.camera.core.CameraX;
+import androidx.camera.core.ImageAnalysis;
+import androidx.camera.core.ImageAnalysisConfig;
+import androidx.camera.core.ImageCapture;
+import androidx.camera.core.ImageCaptureConfig;
+import androidx.camera.core.ImageProxy;
+import androidx.camera.core.Preview;
+import androidx.camera.core.PreviewConfig;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.lifecycle.LifecycleOwner;
+
+import java.io.File;
+public class MainActivity extends AppCompatActivity {
+
+    private int REQUEST_CODE_PERMISSIONS = 10; //arbitrary number, can be changed accordingly
+    private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA","android.permission.WRITE_EXTERNAL_STORAGE"};
+
+    TextureView txView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        txView = findViewById(R.id.view_finder);
+
+        if(allPermissionsGranted()){
+            startCamera(); //start camera if permission has been granted by user
+        } else{
+            ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
+        }
+    }
+
+    private void startCamera() {
+        //make sure there isn't another camera instance running before starting
+        CameraX.unbindAll();
+
+        /* start preview */
+        int aspRatioW = txView.getWidth(); //get width of screen
+        int aspRatioH = txView.getHeight(); //get height
+        Rational asp = new Rational (aspRatioW, aspRatioH); //aspect ratio
+        Size screen = new Size(aspRatioW, aspRatioH); //size of the screen
+
+        //config obj for preview/viewfinder thingy.
+        PreviewConfig pConfig = new PreviewConfig.Builder().setTargetAspectRatio(asp).setTargetResolution(screen).build();
+        Preview preview = new Preview(pConfig); //lets build it
+
+        preview.setOnPreviewOutputUpdateListener(
+                new Preview.OnPreviewOutputUpdateListener() {
+                    //to update the surface texture we have to destroy it first, then re-add it
+                    @Override
+                    public void onUpdated(Preview.PreviewOutput output){
+                        ViewGroup parent = (ViewGroup) txView.getParent();
+                        parent.removeView(txView);
+                        parent.addView(txView, 0);
+
+                        txView.setSurfaceTexture(output.getSurfaceTexture());
+                        updateTransform();
+                    }
+                });
+
+        /* image capture */
+
+        //config obj, selected capture mode
+        ImageCaptureConfig imgCapConfig = new ImageCaptureConfig.Builder().setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
+                .setTargetRotation(getWindowManager().getDefaultDisplay().getRotation()).build();
+        final ImageCapture imgCap = new ImageCapture(imgCapConfig);
+
+        findViewById(R.id.capture_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                File file = new File(Environment.getExternalStorageDirectory() + "/" + System.currentTimeMillis() + ".jpg");
+                imgCap.takePicture(file, new ImageCapture.OnImageSavedListener() {
+                    @Override
+                    public void onImageSaved(@NonNull File file) {
+                        String msg = "Photo capture succeeded: " + file.getAbsolutePath();
+                        Toast.makeText(getBaseContext(), msg,Toast.LENGTH_LONG).show();
+                    }
+
+                    @Override
+                    public void onError(@NonNull ImageCapture.UseCaseError useCaseError, @NonNull String message, @Nullable Throwable cause) {
+                        String msg = "Photo capture failed: " + message;
+                        Toast.makeText(getBaseContext(), msg,Toast.LENGTH_LONG).show();
+                        if(cause != null){
+                            cause.printStackTrace();
+                        }
+                    }
+                });
+            }
+        });
+
+        /* image analyser */
+
+        ImageAnalysisConfig imgAConfig = new ImageAnalysisConfig.Builder().setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE).build();
+        ImageAnalysis analysis = new ImageAnalysis(imgAConfig);
+
+        analysis.setAnalyzer(
+            new ImageAnalysis.Analyzer(){
+                @Override
+                public void analyze(ImageProxy image, int rotationDegrees){
+                    //y'all can add code to analyse stuff here idek go wild.
+                }
+            });
+
+        //bind to lifecycle:
+        CameraX.bindToLifecycle((LifecycleOwner)this, analysis, imgCap, preview);
+    }
+
+    private void updateTransform(){
+        /*
+        * compensates the changes in orientation for the viewfinder, bc the rest of the layout stays in portrait mode.
+        * methinks :thonk:
+        * imgCap does this already, this class can be commented out or be used to optimise the preview
+        */
+        Matrix mx = new Matrix();
+        float w = txView.getMeasuredWidth();
+        float h = txView.getMeasuredHeight();
+
+        float centreX = w / 2f; //calc centre of the viewfinder
+        float centreY = h / 2f;
+
+        int rotationDgr;
+        int rotation = (int)txView.getRotation(); //cast to int bc switches don't like floats
+
+        switch(rotation){ //correct output to account for display rotation
+            case Surface.ROTATION_0:
+                rotationDgr = 0;
+                break;
+            case Surface.ROTATION_90:
+                rotationDgr = 90;
+                break;
+            case Surface.ROTATION_180:
+                rotationDgr = 180;
+                break;
+            case Surface.ROTATION_270:
+                rotationDgr = 270;
+                break;
+            default:
+                return;
+        }
+
+        mx.postRotate((float)rotationDgr, centreX, centreY);
+        txView.setTransform(mx); //apply transformations to textureview
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        //start camera when permissions have been granted otherwise exit app
+        if(requestCode == REQUEST_CODE_PERMISSIONS){
+            if(allPermissionsGranted()){
+                startCamera();
+            } else{
+                Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show();
+                finish();
+            }
+        }
+    }
+
+    private boolean allPermissionsGranted(){
+        //check if req permissions have been granted
+        for(String permission : REQUIRED_PERMISSIONS){
+            if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 28 - 0
camerax/src/main/res/layout/activity_main.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <TextureView
+        android:id="@+id/view_finder"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+    <ImageButton
+        android:id="@+id/capture_button"
+        android:layout_width="72dp"
+        android:layout_height="72dp"
+        android:layout_margin="24dp"
+        app:srcCompat="@android:drawable/ic_menu_camera"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 3 - 0
camerax/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">camerax</string>
+</resources>

+ 17 - 0
camerax/src/test/java/com/miekir/camerax/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.miekir.camerax;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 1 - 1
settings.gradle

@@ -1,2 +1,2 @@
-include ':app', ':common', ':adapter', ':mvp', ':network', ':camera'
+include ':app', ':common', ':adapter', ':mvp', ':network', ':camera', ':camerax'
 rootProject.name='OCR Demo'