从web视图访问摄像头的android

时间:2018-04-24 08:01:54

标签: javascript android html

Android未在WebView中显示相机

我已经使用相机访问权限构建了WebView应用程序。

网址加载并且有效。但相机WebView应用程序中不起作用。

我的代码在下面我有我的问题是我做错了什么。我看到没有错误,但相机根本没有打开。

<!DOCTYPE html>
<html>
    <head>
        <title>///////</title>
        <script type="text/javascript" src="../includes/instascan.min.js"></script>
    </head>
    <body>
        <video id="preview" <?php /* style="position:fixed;right:0;bottom:0;min-width:100%;min-height:100%;" */ ?>></video>
        <script type="text/javascript">
            let opts = {
                // Whether to scan continuously for QR codes. If false, use scanner.scan() to manually scan.
                // If true, the scanner emits the "scan" event when a QR code is scanned. Default true.
                continuous: true,
                // The HTML element to use for the camera's video preview. Must be a <video> element.
                // When the camera is active, this element will have the "active" CSS class, otherwise,
                // it will have the "inactive" class. By default, an invisible element will be created to
                // host the video.
                video: document.getElementById('preview'),
                // Whether to horizontally mirror the video preview. This is helpful when trying to
                // scan a QR code with a user-facing camera. Default true.
                mirror: false,
                // Whether to include the scanned image data as part of the scan result. See the "scan" event
                // for image format details. Default false.
                captureImage: false,
                // Only applies to continuous mode. Whether to actively scan when the tab is not active.
                // When false, this reduces CPU usage when the tab is not active. Default true.
                backgroundScan: true,
                // Only applies to continuous mode. The period, in milliseconds, before the same QR code
                // will be recognized in succession. Default 5000 (5 seconds).
                refractoryPeriod: 5000,
                // Only applies to continuous mode. The period, in rendered frames, between scans. A lower scan period
                // increases CPU usage but makes scan response faster. Default 1 (i.e. analyze every frame).
                scanPeriod: 1
            };
            let scanner = new Instascan.Scanner(opts);
      scanner.addListener('scan', function (content) {
        window.location = "result.php?result="+content;
      });
      Instascan.Camera.getCameras().then(function (cameras) {
        if (cameras.length > 0) {
          if(cameras.length > 1){
            scanner.start(cameras[1]);
          }
          else{
            scanner.start(cameras[0]);
          }
        } else {
          console.error('No cameras found.');
        }
      }).catch(function (e) {
        console.error(e);
      });
        </script>
    </body>
</html>

以下是 Android 编码:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0099cc"
    tools:context=".FullscreenActivity">

    <!-- The primary full-screen view. This can be replaced with whatever view
         is needed to present your content, e.g. VideoView, SurfaceView,
         TextureView, etc. -->
    <TextView
        android:id="@+id/fullscreen_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:keepScreenOn="true"
        android:text="@string/dummy_content"
        android:textColor="#33b5e5"
        android:textSize="50sp"
        android:textStyle="bold" />

    <!-- This FrameLayout insets its children based on system windows using
         android:fitsSystemWindows. -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <WebView
            android:id="@+id/webview"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />

        <LinearLayout
            android:id="@+id/fullscreen_content_controls"
            style="?metaButtonBarStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|center_horizontal"
            android:background="@color/black_overlay"
            android:orientation="horizontal"
            tools:ignore="UselessParent">

        </LinearLayout>
    </FrameLayout>

</FrameLayout>




package //////////?????????;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.os.Build;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

/**
 * An example full-screen activity that shows and hides the system UI (i.e.
 * status bar and navigation/system bar) with user interaction.
 */
public class FullscreenActivity extends AppCompatActivity {
    /**
     * Whether or not the system UI should be auto-hidden after
     * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
     */
    private static final boolean AUTO_HIDE = true;

    /**
     * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after
     * user interaction before hiding the system UI.
     */
    private static final int AUTO_HIDE_DELAY_MILLIS = 3000;

    /**
     * Some older devices needs a small delay between UI widget updates
     * and a change of the status and navigation bar.
     */
    private static final int UI_ANIMATION_DELAY = 300;
    private final Handler mHideHandler = new Handler();
    private View mContentView;
    private final Runnable mHidePart2Runnable = new Runnable() {
        @SuppressLint("InlinedApi")
        @Override
        public void run() {
            // Delayed removal of status and navigation bar

            // Note that some of these constants are new as of API 16 (Jelly Bean)
            // and API 19 (KitKat). It is safe to use them, as they are inlined
            // at compile-time and do nothing on earlier devices.
            mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        }
    };
    private View mControlsView;
    private final Runnable mShowPart2Runnable = new Runnable() {
        @Override
        public void run() {
            // Delayed display of UI elements
            ActionBar actionBar = getSupportActionBar();
            if (actionBar != null) {
                actionBar.show();
            }
            mControlsView.setVisibility(View.VISIBLE);
        }
    };
    private boolean mVisible;
    private final Runnable mHideRunnable = new Runnable() {
        @Override
        public void run() {
            hide();
        }
    };
    /**
     * Touch listener to use for in-layout UI controls to delay hiding the
     * system UI. This is to prevent the jarring behavior of controls going away
     * while interacting with activity UI.
     */
    private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            if (AUTO_HIDE) {
                delayedHide(AUTO_HIDE_DELAY_MILLIS);
            }
            return false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_fullscreen);

        mVisible = true;
        mControlsView = findViewById(R.id.fullscreen_content_controls);
        mContentView = findViewById(R.id.fullscreen_content);


        // Set up the user interaction to manually show or hide the system UI.
        mContentView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                toggle();
            }
        });

        // Upon interacting with UI controls, delay any scheduled hide()
        // operations to prevent the jarring behavior of controls going away
        // while interacting with the UI.

        final WebView myWebView = (WebView) findViewById(R.id.webview);
        myWebView.setWebChromeClient(new WebChromeClient());
        myWebView.setWebViewClient(new WebViewClient() {
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                myWebView.setVisibility(View.GONE);
            }
            @Override
            public void onPageFinished(WebView view, String url) {
                myWebView.setVisibility(View.VISIBLE);
            }
            public void onReceivedError(WebView webview, int i, String s, String s1) {
                webview.loadUrl("");
            }
        });
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setPluginState(WebSettings.PluginState.ON);
        myWebView.loadUrl("https://www.twsb.co.za/mobile_app/rep/scan.php");

//        myWebView.setWebChromeClient(new WebChromeClient(){
//            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
//            @Override
//            public void onPermissionRequest(final PermissionRequest request) {
//                request.grant(request.getResources());
//            }
//        });

    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        // Trigger the initial hide() shortly after the activity has been
        // created, to briefly hint to the user that UI controls
        // are available.
        delayedHide(100);
    }

    private void toggle() {
        if (mVisible) {
            hide();
        } else {
            show();
        }
    }

    private void hide() {
        // Hide UI first
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.hide();
        }
        mControlsView.setVisibility(View.GONE);
        mVisible = false;

        // Schedule a runnable to remove the status and navigation bar after a delay
        mHideHandler.removeCallbacks(mShowPart2Runnable);
        mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
    }

    @SuppressLint("InlinedApi")
    private void show() {
        // Show the system bar
        mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        mVisible = true;

        // Schedule a runnable to display UI elements after a delay
        mHideHandler.removeCallbacks(mHidePart2Runnable);
        mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
    }

    /**
     * Schedules a call to hide() in delay milliseconds, canceling any
     * previously scheduled calls.
     */
    private void delayedHide(int delayMillis) {
        mHideHandler.removeCallbacks(mHideRunnable);
        mHideHandler.postDelayed(mHideRunnable, delayMillis);
    }
}


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="??????????????????">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-feature android:name="android.hardware.bluetooth" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".FullscreenActivity"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:label="@string/app_name"
            android:theme="@style/FullscreenTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>






</manifest>
  

如果我在我的互联网浏览器中单独加载网址,它可以工作,我可以   访问相机。每个人都没有通过应用程序。

1 个答案:

答案 0 :(得分:0)

实现此功能有两种方法。

  1. 通过javascript方法使用来自webview的本机调用

    了解更多信息: Binding JavaScript code to Android code

  2. 在Webview中设置WebChromeClient:

    例如:

    webView.setWebChromeClient(new WebChromeClient() {
    
        // openFileChooser for Android 3.0+
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType){  
    
            // Update message
            mUploadMessage = uploadMsg;
    
            try{    
    
                // Create AndroidExampleFolder at sdcard
    
                File imageStorageDir = new File(
                                       Environment.getExternalStoragePublicDirectory(
                                       Environment.DIRECTORY_PICTURES)
                                       , "AndroidExampleFolder");
    
                if (!imageStorageDir.exists()) {
                    // Create AndroidExampleFolder at sdcard
                    imageStorageDir.mkdirs();
                }
    
                // Create camera captured image file path and name 
                File file = new File(
                                imageStorageDir + File.separator + "IMG_"
                                + String.valueOf(System.currentTimeMillis()) 
                                + ".jpg");
    
                mCapturedImageURI = Uri.fromFile(file); 
    
                // Camera capture image intent
                final Intent captureIntent = new Intent(
                                              android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    
                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);
    
                Intent i = new Intent(Intent.ACTION_GET_CONTENT); 
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("image/*");
    
                // Create file chooser intent
                Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
    
                // Set camera intent to file chooser 
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS
                                       , new Parcelable[] { captureIntent });
    
                // On select image call onActivityResult method of activity
                startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
    
              }
             catch(Exception e){
                 Toast.makeText(getBaseContext(), "Exception:"+e, 
                            Toast.LENGTH_LONG).show();
             }
    
        }
    
        // openFileChooser for Android < 3.0
        public void openFileChooser(ValueCallback<Uri> uploadMsg){
            openFileChooser(uploadMsg, "");
        }
    
        //openFileChooser for other Android versions
        public void openFileChooser(ValueCallback<Uri> uploadMsg, 
                                   String acceptType, 
                                   String capture) {
    
            openFileChooser(uploadMsg, acceptType);
        }
    
    
    
        // The webPage has 2 filechoosers and will send a 
        // console message informing what action to perform, 
        // taking a photo or updating the file
    
        public boolean onConsoleMessage(ConsoleMessage cm) {  
    
            onConsoleMessage(cm.message(), cm.lineNumber(), cm.sourceId());
            return true;
        }
    
        public void onConsoleMessage(String message, int lineNumber, String sourceID) {
            //Log.d("androidruntime", "Show console messages, Used for debugging: " + message);
    
        }
    });   // End setWebChromeClient
    

    从相机或SD卡中选择文件时返回此处

    @覆盖     protected void onActivityResult(int requestCode,int resultCode,
                                           意图意图){

     if(requestCode==FILECHOOSER_RESULTCODE)  
     {  
    
            if (null == this.mUploadMessage) {
                return;
    
            }
    
           Uri result=null;
    
           try{
                if (resultCode != RESULT_OK) {
    
                    result = null;
    
                } else {
    
                    // retrieve from the private variable if the intent is null
                    result = intent == null ? mCapturedImageURI : intent.getData(); 
                } 
            }
            catch(Exception e)
            {
                Toast.makeText(getApplicationContext(), "activity :"+e,
                 Toast.LENGTH_LONG).show();
            }
    
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
    
     }
    
    }      
    
  3. 为API Level 25添加文件路径代码。