1 /*
2  * Copyright 2023 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.androidx.webkit;
18 
19 import static androidx.webkit.WebViewAssetLoader.AssetsPathHandler;
20 
21 import android.annotation.SuppressLint;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.webkit.WebResourceRequest;
25 import android.webkit.WebResourceResponse;
26 import android.webkit.WebSettings;
27 import android.webkit.WebView;
28 import android.webkit.WebViewClient;
29 import android.widget.RadioGroup;
30 
31 import androidx.appcompat.app.AppCompatActivity;
32 import androidx.webkit.UserAgentMetadata;
33 import androidx.webkit.WebSettingsCompat;
34 import androidx.webkit.WebViewAssetLoader;
35 import androidx.webkit.WebViewFeature;
36 
37 import org.jspecify.annotations.NonNull;
38 import org.jspecify.annotations.Nullable;
39 
40 import java.util.Collections;
41 
42 /**
43  * Demo activity to demonstrate the behaviour of overriding user-agent metadata APIs.
44  */
45 public class UserAgentMetadataActivity extends AppCompatActivity {
46 
47     private final Uri mExampleUri = new Uri.Builder()
48             .scheme("https")
49             .authority("example.com")
50             .appendPath("androidx_webkit")
51             .appendPath("example")
52             .appendPath("assets")
53             .build();
54 
55     /**
56      * A WebViewClient to intercept the request to mock HTTPS response.
57      */
58     private static class MyWebViewClient extends WebViewClient {
59         private final WebViewAssetLoader mAssetLoader;
60 
MyWebViewClient(WebViewAssetLoader loader)61         MyWebViewClient(WebViewAssetLoader loader) {
62             mAssetLoader = loader;
63         }
64 
65         @Override
shouldInterceptRequest(WebView view, WebResourceRequest request)66         public WebResourceResponse shouldInterceptRequest(WebView view,
67                 WebResourceRequest request) {
68             return mAssetLoader.shouldInterceptRequest(request.getUrl());
69         }
70 
71         @Override
72         @SuppressWarnings("deprecation") // use the old one for compatibility with all API levels.
shouldInterceptRequest(WebView view, String url)73         public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
74             return mAssetLoader.shouldInterceptRequest(Uri.parse(url));
75         }
76     }
77 
78     private WebView mWebView;
79 
80 
81     @SuppressLint("SetJavascriptEnabled")
82     @Override
onCreate(@ullable Bundle savedInstanceState)83     protected void onCreate(@Nullable Bundle savedInstanceState) {
84         super.onCreate(savedInstanceState);
85         setContentView(R.layout.activity_user_agent_metadata);
86 
87         setTitle(R.string.user_agent_metadata_activity_title);
88         WebkitHelpers.appendWebViewVersionToTitle(this);
89 
90         // Check if override user-agent metadata feature is enabled
91         if (!WebViewFeature.isFeatureSupported(WebViewFeature.USER_AGENT_METADATA)) {
92             WebkitHelpers.showMessageInActivity(this, R.string.webkit_api_not_available);
93             return;
94         }
95 
96         mWebView = findViewById(R.id.user_agent_metadata_webview);
97         mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
98         mWebView.getSettings().setJavaScriptEnabled(true);
99 
100         RadioGroup radioGroup = findViewById(R.id.user_agent_metadata_radio_group);
101         radioGroup.check(R.id.user_agent_metadata_without_override_mode);
102         radioGroup.setOnCheckedChangeListener(this::onRadioGroupChanged);
103 
104         // Initially send a request without overrides
105         refreshView(false);
106     }
107 
refreshView(boolean setOverrides)108     private void refreshView(boolean setOverrides) {
109         UserAgentMetadata overrideSetting;
110         if (setOverrides) {
111             UserAgentMetadata.BrandVersion brandVersion = new UserAgentMetadata.BrandVersion
112                     .Builder().setBrand("myBrand").setMajorVersion("1").setFullVersion("1.1.1.1")
113                     .build();
114             overrideSetting = new UserAgentMetadata.Builder()
115                     .setBrandVersionList(Collections.singletonList(brandVersion))
116                     .setFullVersion("1.1.1.1").setPlatform("myPlatform")
117                     .setPlatformVersion("2.2.2.2").setArchitecture("myArch")
118                     .setMobile(true).setModel("myModel").setBitness(32)
119                     .setWow64(false).build();
120 
121         } else {
122             overrideSetting = new UserAgentMetadata.Builder().build();
123         }
124         WebSettingsCompat.setUserAgentMetadata(mWebView.getSettings(), overrideSetting);
125 
126         // Use WebViewAssetLoader to load html page from app's assets.
127         WebViewAssetLoader assetLoader =
128                 new WebViewAssetLoader.Builder()
129                         .setDomain("example.com")
130                         .addPathHandler(mExampleUri.getPath() + "/", new AssetsPathHandler(this))
131                         .build();
132         mWebView.setWebViewClient(new MyWebViewClient(assetLoader));
133         mWebView.loadUrl(Uri.withAppendedPath(mExampleUri,
134                 "www/user_agent_metadata_main.html").toString());
135     }
136 
137     /**
138      * Handler for selecting w/o user-agent metadata mode through the radio group.
139      * @param unused Triggering radio group
140      * @param checkedId ID of checked radio button
141      */
onRadioGroupChanged(@onNull RadioGroup unused, int checkedId)142     public void onRadioGroupChanged(@NonNull RadioGroup unused, int checkedId) {
143         refreshView(checkedId == R.id.user_agent_metadata_with_override_mode);
144     }
145 }
146