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