1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.permissionrequest;
18 
19 import android.Manifest;
20 import android.annotation.SuppressLint;
21 import android.content.pm.PackageManager;
22 import android.os.Bundle;
23 import android.support.annotation.NonNull;
24 import android.support.annotation.Nullable;
25 import android.support.v4.app.DialogFragment;
26 import android.support.v4.app.Fragment;
27 import android.support.v4.content.ContextCompat;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.webkit.ConsoleMessage;
32 import android.webkit.PermissionRequest;
33 import android.webkit.WebChromeClient;
34 import android.webkit.WebSettings;
35 import android.webkit.WebView;
36 
37 import com.example.android.common.logger.Log;
38 
39 /**
40  * This fragment shows a {@link WebView} and loads a web app from the {@link SimpleWebServer}.
41  */
42 public class PermissionRequestFragment extends Fragment
43         implements ConfirmationDialogFragment.Listener, MessageDialogFragment.Listener {
44 
45     private static final String TAG = PermissionRequestFragment.class.getSimpleName();
46 
47     private static final String FRAGMENT_DIALOG = "dialog";
48 
49     private static final int REQUEST_CAMERA_PERMISSION = 1;
50 
51     /**
52      * We use this web server to serve HTML files in the assets folder. This is because we cannot
53      * use the JavaScript method "getUserMedia" from "file:///android_assets/..." URLs.
54      */
55     private SimpleWebServer mWebServer;
56 
57     /**
58      * A reference to the {@link WebView}.
59      */
60     private WebView mWebView;
61 
62     /**
63      * This field stores the {@link PermissionRequest} from the web application until it is allowed
64      * or denied by user.
65      */
66     private PermissionRequest mPermissionRequest;
67 
68     /**
69      * For testing.
70      */
71     private ConsoleMonitor mConsoleMonitor;
72 
73     @Override
onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)74     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
75             @Nullable Bundle savedInstanceState) {
76         return inflater.inflate(R.layout.fragment_permission_request, container, false);
77     }
78 
79     @Override
onViewCreated(View view, @Nullable Bundle savedInstanceState)80     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
81         mWebView = (WebView) view.findViewById(R.id.web_view);
82         // Here, we use #mWebChromeClient with implementation for handling PermissionRequests.
83         mWebView.setWebChromeClient(mWebChromeClient);
84         configureWebSettings(mWebView.getSettings());
85     }
86 
87     @Override
onResume()88     public void onResume() {
89         super.onResume();
90         final int port = 8080;
91         mWebServer = new SimpleWebServer(port, getResources().getAssets());
92         mWebServer.start();
93         // This is for runtime permission on Marshmallow and above; It is not directly related to
94         // PermissionRequest API.
95         if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
96                 != PackageManager.PERMISSION_GRANTED) {
97             requestCameraPermission();
98         } else {
99             mWebView.loadUrl("http://localhost:" + port + "/sample.html");
100         }
101     }
102 
103     @Override
onPause()104     public void onPause() {
105         mWebServer.stop();
106         super.onPause();
107     }
108 
109     @Override
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)110     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
111             @NonNull int[] grantResults) {
112         // This is for runtime permission on Marshmallow and above; It is not directly related to
113         // PermissionRequest API.
114         if (requestCode == REQUEST_CAMERA_PERMISSION) {
115             if (permissions.length != 1 || grantResults.length != 1 ||
116                     grantResults[0] != PackageManager.PERMISSION_GRANTED) {
117                 Log.e(TAG, "Camera permission not granted.");
118             } else if (mWebView != null && mWebServer != null) {
119                 mWebView.loadUrl("http://localhost:" + mWebServer.getPort() + "/sample.html");
120             }
121         } else {
122             super.onRequestPermissionsResult(requestCode, permissions, grantResults);
123         }
124     }
125 
requestCameraPermission()126     private void requestCameraPermission() {
127         if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
128             MessageDialogFragment.newInstance(R.string.permission_message)
129                     .show(getChildFragmentManager(), FRAGMENT_DIALOG);
130         } else {
131             requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
132         }
133     }
134 
135     @SuppressLint("SetJavaScriptEnabled")
configureWebSettings(WebSettings settings)136     private static void configureWebSettings(WebSettings settings) {
137         settings.setJavaScriptEnabled(true);
138     }
139 
140     /**
141      * This {@link WebChromeClient} has implementation for handling {@link PermissionRequest}.
142      */
143     private WebChromeClient mWebChromeClient = new WebChromeClient() {
144 
145         // This method is called when the web content is requesting permission to access some
146         // resources.
147         @Override
148         public void onPermissionRequest(PermissionRequest request) {
149             Log.i(TAG, "onPermissionRequest");
150             mPermissionRequest = request;
151             final String[] requestedResources = request.getResources();
152             for (String r : requestedResources) {
153                 if (r.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
154                     // In this sample, we only accept video capture request.
155                     ConfirmationDialogFragment
156                             .newInstance(new String[]{PermissionRequest.RESOURCE_VIDEO_CAPTURE})
157                             .show(getChildFragmentManager(), FRAGMENT_DIALOG);
158                     break;
159                 }
160             }
161         }
162 
163         // This method is called when the permission request is canceled by the web content.
164         @Override
165         public void onPermissionRequestCanceled(PermissionRequest request) {
166             Log.i(TAG, "onPermissionRequestCanceled");
167             // We dismiss the prompt UI here as the request is no longer valid.
168             mPermissionRequest = null;
169             DialogFragment fragment = (DialogFragment) getChildFragmentManager()
170                     .findFragmentByTag(FRAGMENT_DIALOG);
171             if (null != fragment) {
172                 fragment.dismiss();
173             }
174         }
175 
176         @Override
177         public boolean onConsoleMessage(@NonNull ConsoleMessage message) {
178             switch (message.messageLevel()) {
179                 case TIP:
180                     Log.v(TAG, message.message());
181                     break;
182                 case LOG:
183                     Log.i(TAG, message.message());
184                     break;
185                 case WARNING:
186                     Log.w(TAG, message.message());
187                     break;
188                 case ERROR:
189                     Log.e(TAG, message.message());
190                     break;
191                 case DEBUG:
192                     Log.d(TAG, message.message());
193                     break;
194             }
195             if (null != mConsoleMonitor) {
196                 mConsoleMonitor.onConsoleMessage(message);
197             }
198             return true;
199         }
200 
201     };
202 
203     @Override
onOkClicked()204     public void onOkClicked() {
205         requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
206     }
207 
208     @Override
onConfirmation(boolean allowed, String[] resources)209     public void onConfirmation(boolean allowed, String[] resources) {
210         if (allowed) {
211             mPermissionRequest.grant(resources);
212             Log.d(TAG, "Permission granted.");
213         } else {
214             mPermissionRequest.deny();
215             Log.d(TAG, "Permission request denied.");
216         }
217         mPermissionRequest = null;
218     }
219 
setConsoleMonitor(ConsoleMonitor monitor)220     public void setConsoleMonitor(ConsoleMonitor monitor) {
221         mConsoleMonitor = monitor;
222     }
223 
224     /**
225      * For testing.
226      */
227     public interface ConsoleMonitor {
onConsoleMessage(ConsoleMessage message)228         void onConsoleMessage(ConsoleMessage message);
229     }
230 
231 }
232