PermissionRequest: Several updates

The app now handles runtime permissions on M and above. The runtime
permission is purposely isolated from PermissionRequest API in order to
minimize confusion.

The web app use MediaStreamTrack.stop() instead of deprecated
MediaStream.stop().

BUG: 29983168
Change-Id: I43c5f889847868e8517a5b262c724486c1f53805
(cherry picked from commit 9297fb394d8bfe007040bdb547673b3f4a9034f0)
diff --git a/content/webview/PermissionRequest/Application/src/main/assets/sample.js b/content/webview/PermissionRequest/Application/src/main/assets/sample.js
index e2806a0..b9d2fdc 100644
--- a/content/webview/PermissionRequest/Application/src/main/assets/sample.js
+++ b/content/webview/PermissionRequest/Application/src/main/assets/sample.js
@@ -20,7 +20,7 @@
     navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
     window.URL = window.URL || window.webkitURL;
 
-    window.onload = function () {
+    document.addEventListener('DOMContentLoaded', function () {
 
         var video = document.querySelector('#video'),
             toggle = document.querySelector('#toggle'),
@@ -42,7 +42,13 @@
                     console.error('Error starting camera. Denied.');
                 });
             } else {
-                stream.stop();
+                if (stream.getTracks) {
+                    stream.getTracks().forEach(function (track) {
+                        track.stop();
+                    });
+                } else if (stream.stop) {
+                    stream.stop();
+                }
                 stream = null;
                 toggle.innerText = 'Start';
                 console.log('Stopped');
@@ -51,6 +57,6 @@
 
         console.log('Page loaded');
 
-    };
+    });
 
 })();
diff --git a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/ConfirmationDialogFragment.java b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/ConfirmationDialogFragment.java
index 7dae56e..2c8a5ab 100644
--- a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/ConfirmationDialogFragment.java
+++ b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/ConfirmationDialogFragment.java
@@ -19,6 +19,7 @@
 import android.app.Dialog;
 import android.content.DialogInterface;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.v4.app.DialogFragment;
 import android.text.TextUtils;
 
@@ -32,7 +33,7 @@
     /**
      * Creates a new instance of ConfirmationDialogFragment.
      *
-     * @param resources The list of resources requested by PermissionRequeste.
+     * @param resources The list of resources requested by PermissionRequest.
      * @return A new instance.
      */
     public static ConfirmationDialogFragment newInstance(String[] resources) {
@@ -43,6 +44,7 @@
         return fragment;
     }
 
+    @NonNull
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         String[] resources = getArguments().getStringArray(ARG_RESOURCES);
@@ -69,11 +71,11 @@
     public interface Listener {
 
         /**
-         * Called when the PermissoinRequest is allowed or denied by the user.
+         * Called when the PermissionRequest is allowed or denied by the user.
          *
          * @param allowed True if the user allowed the request.
          */
-        public void onConfirmation(boolean allowed);
+        void onConfirmation(boolean allowed);
     }
 
 }
diff --git a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/MessageDialogFragment.java b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/MessageDialogFragment.java
new file mode 100644
index 0000000..7db9610
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/MessageDialogFragment.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.permissionrequest;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.support.v4.app.DialogFragment;
+
+/**
+ * Shows a dialog with a brief message.
+ */
+public class MessageDialogFragment extends DialogFragment {
+
+    private static final String ARG_MESSAGE_RES_ID = "message_res_id";
+
+    public static MessageDialogFragment newInstance(@StringRes int message) {
+        MessageDialogFragment fragment = new MessageDialogFragment();
+        Bundle args = new Bundle();
+        args.putInt(ARG_MESSAGE_RES_ID, message);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @NonNull
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        return new AlertDialog.Builder(getContext())
+                .setMessage(getArguments().getInt(ARG_MESSAGE_RES_ID))
+                .setCancelable(false)
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        ((Listener) getParentFragment()).onOkClicked();
+                    }
+                })
+                .create();
+    }
+
+    public interface Listener {
+        void onOkClicked();
+    }
+
+}
diff --git a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/PermissionRequestFragment.java b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/PermissionRequestFragment.java
index 44f1d6e..19fdc4f 100644
--- a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/PermissionRequestFragment.java
+++ b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/PermissionRequestFragment.java
@@ -16,12 +16,15 @@
 
 package com.example.android.permissionrequest;
 
+import android.Manifest;
 import android.annotation.SuppressLint;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -37,12 +40,14 @@
  * This fragment shows a {@link WebView} and loads a web app from the {@link SimpleWebServer}.
  */
 public class PermissionRequestFragment extends Fragment
-        implements ConfirmationDialogFragment.Listener {
+        implements ConfirmationDialogFragment.Listener, MessageDialogFragment.Listener {
 
     private static final String TAG = PermissionRequestFragment.class.getSimpleName();
 
     private static final String FRAGMENT_DIALOG = "dialog";
 
+    private static final int REQUEST_CAMERA_PERMISSION = 1;
+
     /**
      * We use this web server to serve HTML files in the assets folder. This is because we cannot
      * use the JavaScript method "getUserMedia" from "file:///android_assets/..." URLs.
@@ -85,7 +90,14 @@
         final int port = 8080;
         mWebServer = new SimpleWebServer(port, getResources().getAssets());
         mWebServer.start();
-        mWebView.loadUrl("http://localhost:" + port + "/sample.html");
+        // This is for runtime permission on Marshmallow and above; It is not directly related to
+        // PermissionRequest API.
+        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
+                != PackageManager.PERMISSION_GRANTED) {
+            requestCameraPermission();
+        } else {
+            mWebView.loadUrl("http://localhost:" + port + "/sample.html");
+        }
     }
 
     @Override
@@ -94,6 +106,32 @@
         super.onPause();
     }
 
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+                                           @NonNull int[] grantResults) {
+        // This is for runtime permission on Marshmallow and above; It is not directly related to
+        // PermissionRequest API.
+        if (requestCode == REQUEST_CAMERA_PERMISSION) {
+            if (permissions.length != 1 || grantResults.length != 1 ||
+                    grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+                Log.e(TAG, "Camera permission not granted.");
+            } else if (mWebView != null && mWebServer != null) {
+                mWebView.loadUrl("http://localhost:" + mWebServer.getPort() + "/sample.html");
+            }
+        } else {
+            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        }
+    }
+
+    private void requestCameraPermission() {
+        if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
+            MessageDialogFragment.newInstance(R.string.permission_message)
+                    .show(getChildFragmentManager(), FRAGMENT_DIALOG);
+        } else {
+            requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
+        }
+    }
+
     @SuppressLint("SetJavaScriptEnabled")
     private static void configureWebSettings(WebSettings settings) {
         settings.setJavaScriptEnabled(true);
@@ -155,6 +193,11 @@
     };
 
     @Override
+    public void onOkClicked() {
+        requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
+    }
+
+    @Override
     public void onConfirmation(boolean allowed) {
         if (allowed) {
             mPermissionRequest.grant(mPermissionRequest.getResources());
@@ -174,7 +217,7 @@
      * For testing.
      */
     public interface ConsoleMonitor {
-        public void onConsoleMessage(ConsoleMessage message);
+        void onConsoleMessage(ConsoleMessage message);
     }
 
 }
diff --git a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/SimpleWebServer.java b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/SimpleWebServer.java
index 36b7c46..b02275a 100644
--- a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/SimpleWebServer.java
+++ b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/SimpleWebServer.java
@@ -90,6 +90,10 @@
         }
     }
 
+    public int getPort() {
+        return mPort;
+    }
+
     @Override
     public void run() {
         try {
diff --git a/content/webview/PermissionRequest/Application/src/main/res/values/strings.xml b/content/webview/PermissionRequest/Application/src/main/res/values/strings.xml
index c3e5574..2488766 100644
--- a/content/webview/PermissionRequest/Application/src/main/res/values/strings.xml
+++ b/content/webview/PermissionRequest/Application/src/main/res/values/strings.xml
@@ -15,6 +15,7 @@
  limitations under the License.
 -->
 <resources>
+    <string name="permission_message">This sample app uses camera.</string>
     <string name="confirmation">This web page wants to use following resources:\n\n%s</string>
     <string name="allow">Allow</string>
     <string name="deny">Deny</string>