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