1 /* 2 * Copyright (C) 2022 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.android.sdksandboxwebviewclient; 18 19 import static android.app.sdksandbox.SdkSandboxManager.EXTRA_DISPLAY_ID; 20 import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HEIGHT_IN_PIXELS; 21 import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HOST_TOKEN; 22 import static android.app.sdksandbox.SdkSandboxManager.EXTRA_SURFACE_PACKAGE; 23 import static android.app.sdksandbox.SdkSandboxManager.EXTRA_WIDTH_IN_PIXELS; 24 25 import android.annotation.NonNull; 26 import android.app.Activity; 27 import android.app.sdksandbox.LoadSdkException; 28 import android.app.sdksandbox.RequestSurfacePackageException; 29 import android.app.sdksandbox.SandboxedSdk; 30 import android.app.sdksandbox.SdkSandboxManager; 31 import android.app.sdksandbox.interfaces.IWebViewSdkApi; 32 import android.os.Bundle; 33 import android.os.Handler; 34 import android.os.Looper; 35 import android.os.OutcomeReceiver; 36 import android.os.RemoteException; 37 import android.util.Log; 38 import android.view.SurfaceControlViewHost.SurfacePackage; 39 import android.view.SurfaceView; 40 import android.view.View; 41 import android.widget.Button; 42 import android.widget.EditText; 43 import android.widget.LinearLayout; 44 import android.widget.Toast; 45 46 public class MainActivity extends Activity { 47 private static final String SDK_NAME = "com.android.sdksandboxcode_webview"; 48 private static final String TAG = "SdkSandboxWebViewClientMainActivity"; 49 private static final Handler sHandler = new Handler(Looper.getMainLooper()); 50 51 private boolean mSdksLoaded = false; 52 53 private SdkSandboxManager mSdkSandboxManager; 54 private Button mLoadButton; 55 private Button mRenderButton; 56 private Button mSubmitUrlButton; 57 private EditText mUrlInputTextbox; 58 private LinearLayout mUrlInputField; 59 private SurfaceView mRenderedView; 60 private SandboxedSdk mSandboxedSdk; 61 private IWebViewSdkApi mWebViewProxy; 62 63 @Override onCreate(Bundle savedInstanceState)64 public void onCreate(Bundle savedInstanceState) { 65 super.onCreate(savedInstanceState); 66 setContentView(R.layout.activity_main); 67 mSdkSandboxManager = getApplicationContext().getSystemService(SdkSandboxManager.class); 68 69 mRenderedView = findViewById(R.id.rendered_view); 70 mRenderedView.setZOrderOnTop(true); 71 mRenderedView.setVisibility(View.INVISIBLE); 72 73 mLoadButton = findViewById(R.id.load_sdk_button); 74 mRenderButton = findViewById(R.id.request_surface_button); 75 mUrlInputTextbox = findViewById(R.id.url_input_textbox); 76 mSubmitUrlButton = findViewById(R.id.submit_url_button); 77 mUrlInputField = findViewById(R.id.url_input_field); 78 79 mUrlInputField.setVisibility(View.GONE); 80 81 registerLoadSdkProviderButton(); 82 registerLoadSurfacePackageButton(); 83 registerSubmitUrlButton(); 84 } 85 registerLoadSdkProviderButton()86 private void registerLoadSdkProviderButton() { 87 mLoadButton.setOnClickListener( 88 v -> { 89 if (mSdksLoaded) { 90 resetStateForLoadSdkButton(); 91 return; 92 } 93 // Register for sandbox death event. 94 mSdkSandboxManager.addSdkSandboxProcessDeathCallback( 95 Runnable::run, () -> makeToast("Sdk Sandbox process died")); 96 97 Bundle params = new Bundle(); 98 OutcomeReceiver<SandboxedSdk, LoadSdkException> receiver = 99 new OutcomeReceiver<SandboxedSdk, LoadSdkException>() { 100 @Override 101 public void onResult(SandboxedSdk sandboxedSdk) { 102 mSdksLoaded = true; 103 mSandboxedSdk = sandboxedSdk; 104 mWebViewProxy = 105 IWebViewSdkApi.Stub.asInterface( 106 mSandboxedSdk.getInterface()); 107 makeToast("SDK Loaded successfully!"); 108 mLoadButton.setText( 109 getResources().getString(R.string.unload_sdk)); 110 } 111 112 @Override 113 public void onError(LoadSdkException error) { 114 makeToast("Failed: " + error); 115 Log.e(TAG, error.getMessage(), error); 116 } 117 }; 118 mSdkSandboxManager.loadSdk(SDK_NAME, params, Runnable::run, receiver); 119 }); 120 } 121 resetStateForLoadSdkButton()122 private void resetStateForLoadSdkButton() { 123 try { 124 mWebViewProxy.destroy(); 125 } catch (RemoteException e) { 126 Log.e(TAG, e.getMessage()); 127 } 128 mSdkSandboxManager.unloadSdk(SDK_NAME); 129 mRenderedView.setVisibility(View.INVISIBLE); 130 mLoadButton.setText(getResources().getString(R.string.load_sdk)); 131 mUrlInputField.setVisibility(View.GONE); 132 mSdksLoaded = false; 133 } 134 registerLoadSurfacePackageButton()135 private void registerLoadSurfacePackageButton() { 136 OutcomeReceiver<Bundle, RequestSurfacePackageException> receiver = 137 new RequestSurfacePackageReceiver(); 138 mRenderButton.setOnClickListener( 139 v -> { 140 if (mSdksLoaded) { 141 sHandler.post( 142 () -> { 143 mSdkSandboxManager.requestSurfacePackage( 144 SDK_NAME, 145 getRequestSurfacePackageParams(), 146 Runnable::run, 147 receiver); 148 }); 149 } else { 150 makeToast("Sdk is not loaded"); 151 } 152 }); 153 } 154 registerSubmitUrlButton()155 private void registerSubmitUrlButton() { 156 mSubmitUrlButton.setOnClickListener( 157 v -> { 158 String url = mUrlInputTextbox.getText().toString(); 159 try { 160 mWebViewProxy.loadUrl(url); 161 } catch (RemoteException e) { 162 Log.e(TAG, e.getMessage()); 163 makeToast("Cannot load URL : " + e.getMessage()); 164 } 165 }); 166 } 167 getRequestSurfacePackageParams()168 private Bundle getRequestSurfacePackageParams() { 169 Bundle params = new Bundle(); 170 params.putInt(EXTRA_WIDTH_IN_PIXELS, mRenderedView.getWidth()); 171 params.putInt(EXTRA_HEIGHT_IN_PIXELS, mRenderedView.getHeight()); 172 params.putInt(EXTRA_DISPLAY_ID, getDisplay().getDisplayId()); 173 params.putBinder(EXTRA_HOST_TOKEN, mRenderedView.getHostToken()); 174 return params; 175 } 176 makeToast(String message)177 private void makeToast(String message) { 178 runOnUiThread(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show()); 179 } 180 181 private class RequestSurfacePackageReceiver 182 implements OutcomeReceiver<Bundle, RequestSurfacePackageException> { 183 184 @Override onResult(Bundle result)185 public void onResult(Bundle result) { 186 mUrlInputField.setVisibility(View.VISIBLE); 187 sHandler.post( 188 () -> { 189 SurfacePackage surfacePackage = 190 result.getParcelable(EXTRA_SURFACE_PACKAGE, SurfacePackage.class); 191 mRenderedView.setChildSurfacePackage(surfacePackage); 192 mRenderedView.setVisibility(View.VISIBLE); 193 }); 194 makeToast("Rendered WebView"); 195 } 196 197 @Override onError(@onNull RequestSurfacePackageException error)198 public void onError(@NonNull RequestSurfacePackageException error) { 199 makeToast("Failed: " + error.getMessage()); 200 Log.e(TAG, error.getMessage(), error); 201 } 202 } 203 } 204