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