1 /* 2 * Copyright 2019 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 java.nio.charset.StandardCharsets.UTF_8; 20 21 import android.app.Activity; 22 import android.os.Bundle; 23 import android.view.KeyEvent; 24 import android.view.View; 25 import android.view.inputmethod.EditorInfo; 26 import android.webkit.WebView; 27 import android.webkit.WebViewClient; 28 import android.widget.Button; 29 import android.widget.EditText; 30 import android.widget.TextView; 31 32 import androidx.appcompat.app.AppCompatActivity; 33 import androidx.webkit.TracingConfig; 34 import androidx.webkit.TracingController; 35 import androidx.webkit.WebViewFeature; 36 37 import org.json.JSONException; 38 import org.json.JSONObject; 39 import org.jspecify.annotations.Nullable; 40 41 import java.io.BufferedReader; 42 import java.io.File; 43 import java.io.FileInputStream; 44 import java.io.FileNotFoundException; 45 import java.io.FileOutputStream; 46 import java.io.IOException; 47 import java.io.InputStreamReader; 48 import java.util.concurrent.ExecutorService; 49 import java.util.concurrent.Executors; 50 51 /** 52 * An {@link Activity} to exercise Tracing Controller functionality. 53 */ 54 public class TracingControllerActivity extends AppCompatActivity { 55 56 @Override 57 @SuppressWarnings("CatchAndPrintStackTrace") onCreate(@ullable Bundle savedInstanceState)58 protected void onCreate(@Nullable Bundle savedInstanceState) { 59 super.onCreate(savedInstanceState); 60 setContentView(R.layout.activity_tracing_controller); 61 setTitle(R.string.tracing_controller_activity_title); 62 WebkitHelpers.appendWebViewVersionToTitle(this); 63 64 final WebView webView = findViewById(R.id.tracing_controller_webview); 65 webView.setWebViewClient(new WebViewClient()); 66 67 final EditText navigationBar = findViewById(R.id.tracing_controller_edittext); 68 69 final TextView infoView = findViewById(R.id.tracing_controller_textview); 70 infoView.setVisibility(View.GONE); 71 72 final Button tracingButton = findViewById(R.id.tracing_controller_button); 73 74 if (!WebViewFeature.isFeatureSupported(WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE)) { 75 navigationBar.setVisibility(View.GONE); 76 webView.setVisibility(View.GONE); 77 tracingButton.setVisibility(View.GONE); 78 infoView.setVisibility(View.GONE); 79 WebkitHelpers.showMessageInActivity(this, R.string.webkit_api_not_available); 80 return; 81 } 82 83 final TracingController tracingController = TracingController.getInstance(); 84 85 navigationBar.setOnEditorActionListener((TextView v, int actionId, KeyEvent event) -> { 86 if (actionId == EditorInfo.IME_ACTION_NEXT) { 87 String url = navigationBar.getText().toString(); 88 if (!url.isEmpty()) { 89 if (!url.startsWith("http")) url = "http://" + url; 90 webView.loadUrl(url); 91 navigationBar.setText(""); 92 } 93 return true; 94 } 95 return false; 96 }); 97 98 tracingButton.setOnClickListener(v -> { 99 if (tracingController.isTracing()) { 100 try { 101 tracingButton.setEnabled(false); 102 final String logPath = getExternalFilesDir(null) + File.separator + "tc.json"; 103 FileOutputStream os = new VerifyingFileOutputStream(logPath, infoView, 104 tracingButton); 105 ExecutorService executor = Executors.newSingleThreadExecutor(); 106 tracingController.stop(os, executor); 107 } catch (FileNotFoundException e) { 108 109 e.printStackTrace(); 110 } 111 } else { 112 TracingConfig config = new TracingConfig.Builder().addCategories( 113 TracingConfig.CATEGORIES_ANDROID_WEBVIEW).build(); 114 tracingController.start(config); 115 tracingButton.setText(R.string.tracing_controller_stop_tracing); 116 } 117 }); 118 119 120 } 121 122 private class VerifyingFileOutputStream extends FileOutputStream { 123 124 private final String mLogPath; 125 private final TextView mInfoView; 126 private final Button mTracingButton; 127 VerifyingFileOutputStream(String logPath, TextView infoView, Button tracingButton)128 VerifyingFileOutputStream(String logPath, TextView infoView, Button tracingButton) 129 throws FileNotFoundException { 130 super(logPath); 131 mLogPath = logPath; 132 mInfoView = infoView; 133 mTracingButton = tracingButton; 134 } 135 136 @Override close()137 public void close() throws IOException { 138 super.close(); 139 runOnUiThread(() -> { 140 mInfoView.setVisibility(View.VISIBLE); 141 mTracingButton.setVisibility(View.GONE); 142 mInfoView.setText(getString(R.string.tracing_controller_log_path, mLogPath)); 143 try { 144 verifyJSON(mLogPath); 145 } catch (IOException | JSONException e) { 146 mInfoView.setText(R.string.tracing_controller_invalid_log); 147 } 148 }); 149 } 150 verifyJSON(String logPath)151 private void verifyJSON(String logPath) throws IOException, JSONException { 152 StringBuilder builder = new StringBuilder(); 153 FileInputStream fis = new FileInputStream(logPath); 154 BufferedReader br = new BufferedReader(new InputStreamReader(fis, UTF_8)); 155 String sCurrentLine; 156 while ((sCurrentLine = br.readLine()) != null) { 157 builder.append(sCurrentLine).append("\n"); 158 } 159 160 // Throw exception if JSON is incorrect 161 new JSONObject(builder.toString()); 162 } 163 164 } 165 } 166