WebViewContentsClientAdapter.java revision 245b3a6a2a7850a46536a423594286e04e702006
1/* 2 * Copyright (C) 2012 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 17package com.android.webview.chromium; 18 19import android.content.ActivityNotFoundException; 20import android.content.Context; 21import android.content.Intent; 22import android.graphics.Bitmap; 23import android.graphics.Picture; 24import android.net.http.ErrorStrings; 25import android.net.http.SslError; 26import android.os.Handler; 27import android.os.Looper; 28import android.os.Message; 29import android.provider.Browser; 30import android.util.Log; 31import android.view.KeyEvent; 32import android.view.View; 33import android.webkit.ConsoleMessage; 34import android.webkit.DownloadListener; 35import android.webkit.GeolocationPermissions; 36import android.webkit.JsDialogHelper; 37import android.webkit.JsPromptResult; 38import android.webkit.JsResult; 39import android.webkit.SslErrorHandler; 40import android.webkit.ValueCallback; 41import android.webkit.WebChromeClient; 42import android.webkit.WebChromeClient.CustomViewCallback; 43import android.webkit.WebResourceResponse; 44import android.webkit.WebView; 45import android.webkit.WebViewClient; 46 47import org.chromium.android_webview.AwContentsClient; 48import org.chromium.android_webview.AwHttpAuthHandler; 49import org.chromium.android_webview.InterceptedRequestData; 50import org.chromium.android_webview.JsPromptResultReceiver; 51import org.chromium.android_webview.JsResultReceiver; 52import org.chromium.content.browser.ContentView; 53import org.chromium.content.browser.ContentViewClient; 54import org.chromium.content.common.TraceEvent; 55 56import java.net.URISyntaxException; 57 58/** 59 * An adapter class that forwards the callbacks from {@link ContentViewClient} 60 * to the appropriate {@link WebViewClient} or {@link WebChromeClient}. 61 * 62 * An instance of this class is associated with one {@link WebViewChromium} 63 * instance. A WebViewChromium is a WebView implementation provider (that is 64 * android.webkit.WebView delegates all functionality to it) and has exactly 65 * one corresponding {@link ContentView} instance. 66 * 67 * A {@link ContentViewClient} may be shared between multiple {@link ContentView}s, 68 * and hence multiple WebViews. Many WebViewClient methods pass the source 69 * WebView as an argument. This means that we either need to pass the 70 * corresponding ContentView to the corresponding ContentViewClient methods, 71 * or use an instance of ContentViewClientAdapter per WebViewChromium, to 72 * allow the source WebView to be injected by ContentViewClientAdapter. We 73 * choose the latter, because it makes for a cleaner design. 74 */ 75public class WebViewContentsClientAdapter extends AwContentsClient { 76 private static final String TAG = "ContentViewClientAdapter"; 77 // The WebView instance that this adapter is serving. 78 private final WebView mWebView; 79 // The WebViewClient instance that was passed to WebView.setWebViewClient(). 80 private WebViewClient mWebViewClient; 81 // The WebViewClient instance that was passed to WebView.setContentViewClient(). 82 private WebChromeClient mWebChromeClient; 83 // The listener receiving find-in-page API results. 84 private WebView.FindListener mFindListener; 85 // The listener receiving notifications of screen updates. 86 private WebView.PictureListener mPictureListener; 87 88 private DownloadListener mDownloadListener; 89 90 private Handler mUiThreadHandler; 91 92 private static final int NEW_WEBVIEW_CREATED = 100; 93 94 /** 95 * Adapter constructor. 96 * 97 * @param webView the {@link WebView} instance that this adapter is serving. 98 */ 99 WebViewContentsClientAdapter(WebView webView) { 100 if (webView == null) { 101 throw new IllegalArgumentException("webView can't be null"); 102 } 103 104 mWebView = webView; 105 setWebViewClient(null); 106 setWebChromeClient(null); 107 108 mUiThreadHandler = new Handler() { 109 110 @Override 111 public void handleMessage(Message msg) { 112 switch(msg.what) { 113 case NEW_WEBVIEW_CREATED: 114 WebView.WebViewTransport t = (WebView.WebViewTransport) msg.obj; 115 WebView newWebView = t.getWebView(); 116 if (newWebView == mWebView) { 117 throw new IllegalArgumentException( 118 "Parent WebView cannot host it's own popup window. Please " + 119 "use WebSettings.setSupportMultipleWindows(false)"); 120 } 121 122 if (newWebView != null && newWebView.copyBackForwardList().getSize() != 0) { 123 throw new IllegalArgumentException( 124 "New WebView for popup window must not have been previously " + 125 "navigated."); 126 } 127 128 WebViewChromium.completeWindowCreation(mWebView, newWebView); 129 break; 130 default: 131 throw new IllegalStateException(); 132 } 133 } 134 }; 135 136 } 137 138 // WebViewClassic is coded in such a way that even if a null WebViewClient is set, 139 // certain actions take place. 140 // We choose to replicate this behavior by using a NullWebViewClient implementation (also known 141 // as the Null Object pattern) rather than duplicating the WebViewClassic approach in 142 // ContentView. 143 static class NullWebViewClient extends WebViewClient { 144 @Override 145 public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { 146 // TODO: Investigate more and add a test case. 147 // This is a copy of what Clank does. The WebViewCore key handling code and Clank key 148 // handling code differ enough that it's not trivial to figure out how keycodes are 149 // being filtered. 150 int keyCode = event.getKeyCode(); 151 if (keyCode == KeyEvent.KEYCODE_MENU || 152 keyCode == KeyEvent.KEYCODE_HOME || 153 keyCode == KeyEvent.KEYCODE_BACK || 154 keyCode == KeyEvent.KEYCODE_CALL || 155 keyCode == KeyEvent.KEYCODE_ENDCALL || 156 keyCode == KeyEvent.KEYCODE_POWER || 157 keyCode == KeyEvent.KEYCODE_HEADSETHOOK || 158 keyCode == KeyEvent.KEYCODE_CAMERA || 159 keyCode == KeyEvent.KEYCODE_FOCUS || 160 keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || 161 keyCode == KeyEvent.KEYCODE_VOLUME_MUTE || 162 keyCode == KeyEvent.KEYCODE_VOLUME_UP) { 163 return true; 164 } 165 return false; 166 } 167 168 @Override 169 public boolean shouldOverrideUrlLoading(WebView view, String url) { 170 Intent intent; 171 // Perform generic parsing of the URI to turn it into an Intent. 172 try { 173 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); 174 } catch (URISyntaxException ex) { 175 Log.w(TAG, "Bad URI " + url + ": " + ex.getMessage()); 176 return false; 177 } 178 // Sanitize the Intent, ensuring web pages can not bypass browser 179 // security (only access to BROWSABLE activities). 180 intent.addCategory(Intent.CATEGORY_BROWSABLE); 181 intent.setComponent(null); 182 // Pass the package name as application ID so that the intent from the 183 // same application can be opened in the same tab. 184 intent.putExtra(Browser.EXTRA_APPLICATION_ID, 185 view.getContext().getPackageName()); 186 try { 187 view.getContext().startActivity(intent); 188 } catch (ActivityNotFoundException ex) { 189 Log.w(TAG, "No application can handle " + url); 190 return false; 191 } 192 return true; 193 } 194 } 195 196 void setWebViewClient(WebViewClient client) { 197 if (client != null) { 198 mWebViewClient = client; 199 } else { 200 mWebViewClient = new NullWebViewClient(); 201 } 202 } 203 204 void setWebChromeClient(WebChromeClient client) { 205 if (client != null) { 206 mWebChromeClient = client; 207 } else { 208 // WebViewClassic doesn't implement any special behavior for a null WebChromeClient. 209 mWebChromeClient = new WebChromeClient(); 210 } 211 } 212 213 void setDownloadListener(DownloadListener listener) { 214 mDownloadListener = listener; 215 } 216 217 void setFindListener(WebView.FindListener listener) { 218 mFindListener = listener; 219 } 220 221 void setPictureListener(WebView.PictureListener listener) { 222 mPictureListener = listener; 223 } 224 225 //-------------------------------------------------------------------------------------------- 226 // Adapter for all the methods. 227 //-------------------------------------------------------------------------------------------- 228 229 /** 230 * @see AwContentsClient#getVisitedHistory 231 */ 232 @Override 233 public void getVisitedHistory(ValueCallback<String[]> callback) { 234 TraceEvent.begin(); 235 mWebChromeClient.getVisitedHistory(callback); 236 TraceEvent.end(); 237 } 238 239 /** 240 * @see AwContentsClient#doUpdateVisiteHistory(String, boolean) 241 */ 242 @Override 243 public void doUpdateVisitedHistory(String url, boolean isReload) { 244 TraceEvent.begin(); 245 mWebViewClient.doUpdateVisitedHistory(mWebView, url, isReload); 246 TraceEvent.end(); 247 } 248 249 /** 250 * @see AwContentsClient#onProgressChanged(int) 251 */ 252 @Override 253 public void onProgressChanged(int progress) { 254 TraceEvent.begin(); 255 mWebChromeClient.onProgressChanged(mWebView, progress); 256 TraceEvent.end(); 257 } 258 259 /** 260 * @see AwContentsClient#shouldInterceptRequest(java.lang.String) 261 */ 262 @Override 263 public InterceptedRequestData shouldInterceptRequest(String url) { 264 TraceEvent.begin(); 265 WebResourceResponse response = mWebViewClient.shouldInterceptRequest(mWebView, url); 266 TraceEvent.end(); 267 if (response == null) return null; 268 return new InterceptedRequestData( 269 response.getMimeType(), 270 response.getEncoding(), 271 response.getData()); 272 } 273 274 /** 275 * @see AwContentsClient#shouldOverrideUrlLoading(java.lang.String) 276 */ 277 @Override 278 public boolean shouldOverrideUrlLoading(String url) { 279 TraceEvent.begin(); 280 boolean result = mWebViewClient.shouldOverrideUrlLoading(mWebView, url); 281 TraceEvent.end(); 282 return result; 283 } 284 285 /** 286 * @see AwContentsClient#onUnhandledKeyEvent(android.view.KeyEvent) 287 */ 288 @Override 289 public void onUnhandledKeyEvent(KeyEvent event) { 290 TraceEvent.begin(); 291 mWebViewClient.onUnhandledKeyEvent(mWebView, event); 292 TraceEvent.end(); 293 } 294 295 /** 296 * @see AwContentsClient#onConsoleMessage(android.webkit.ConsoleMessage) 297 */ 298 @Override 299 public boolean onConsoleMessage(ConsoleMessage consoleMessage) { 300 TraceEvent.begin(); 301 boolean result = mWebChromeClient.onConsoleMessage(consoleMessage); 302 TraceEvent.end(); 303 return result; 304 } 305 306 /** 307 * @see AwContentsClient#onFindResultReceived(int,int,boolean) 308 */ 309 @Override 310 public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, 311 boolean isDoneCounting) { 312 if (mFindListener == null) return; 313 TraceEvent.begin(); 314 mFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting); 315 TraceEvent.end(); 316 } 317 318 /** 319 * @See AwContentsClient#onNewPicture(Picture) 320 */ 321 @Override 322 public void onNewPicture(Picture picture) { 323 if (mPictureListener == null) return; 324 TraceEvent.begin(); 325 mPictureListener.onNewPicture(mWebView, picture); 326 TraceEvent.end(); 327 } 328 329 @Override 330 public void onLoadResource(String url) { 331 TraceEvent.begin(); 332 mWebViewClient.onLoadResource(mWebView, url); 333 TraceEvent.end(); 334 } 335 336 @Override 337 public boolean onCreateWindow(boolean isDialog, boolean isUserGesture) { 338 Message m = mUiThreadHandler.obtainMessage( 339 NEW_WEBVIEW_CREATED, mWebView.new WebViewTransport()); 340 TraceEvent.begin(); 341 boolean result = mWebChromeClient.onCreateWindow(mWebView, isDialog, isUserGesture, m); 342 TraceEvent.end(); 343 return result; 344 } 345 346 /** 347 * @see AwContentsClient#onCloseWindow() 348 */ 349 @Override 350 public void onCloseWindow() { 351 TraceEvent.begin(); 352 mWebChromeClient.onCloseWindow(mWebView); 353 TraceEvent.end(); 354 } 355 356 /** 357 * @see AwContentsClient#onRequestFocus() 358 */ 359 @Override 360 public void onRequestFocus() { 361 TraceEvent.begin(); 362 mWebChromeClient.onRequestFocus(mWebView); 363 TraceEvent.end(); 364 } 365 366 /** 367 * @see AwContentsClient#onReceivedTouchIconUrl(String url, boolean precomposed) 368 */ 369 @Override 370 public void onReceivedTouchIconUrl(String url, boolean precomposed) { 371 TraceEvent.begin(); 372 mWebChromeClient.onReceivedTouchIconUrl(mWebView, url, precomposed); 373 TraceEvent.end(); 374 } 375 376 /** 377 * @see AwContentsClient#onReceivedIcon(Bitmap bitmap) 378 */ 379 @Override 380 public void onReceivedIcon(Bitmap bitmap) { 381 TraceEvent.begin(); 382 mWebChromeClient.onReceivedIcon(mWebView, bitmap); 383 TraceEvent.end(); 384 } 385 386 /** 387 * @see ContentViewClient#onPageStarted(String) 388 */ 389 @Override 390 public void onPageStarted(String url) { 391 TraceEvent.begin(); 392 mWebViewClient.onPageStarted(mWebView, url, mWebView.getFavicon()); 393 TraceEvent.end(); 394 } 395 396 /** 397 * @see ContentViewClient#onPageFinished(String) 398 */ 399 @Override 400 public void onPageFinished(String url) { 401 TraceEvent.begin(); 402 mWebViewClient.onPageFinished(mWebView, url); 403 TraceEvent.end(); 404 405 // See b/8208948 406 // This fakes an onNewPicture callback after onPageFinished to allow 407 // CTS tests to run in an un-flaky manner. This is required as the 408 // path for sending Picture updates in Chromium are decoupled from the 409 // page loading callbacks, i.e. the Chrome compositor may draw our 410 // content and send the Picture before onPageStarted or onPageFinished 411 // are invoked. The CTS harness discards any pictures it receives before 412 // onPageStarted is invoked, so in the case we get the Picture before that and 413 // no further updates after onPageStarted, we'll fail the test by timing 414 // out waiting for a Picture. 415 // To ensure backwards compatibility, we need to defer sending Picture updates 416 // until onPageFinished has been invoked. This work is being done 417 // upstream, and we can revert this hack when it lands. 418 if (mPictureListener != null) { 419 new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { 420 @Override 421 public void run() { 422 UnimplementedWebViewApi.invoke(); 423 if (mPictureListener != null) { 424 mPictureListener.onNewPicture(mWebView, null); 425 } 426 } 427 }, 100); 428 } 429 } 430 431 /** 432 * @see ContentViewClient#onReceivedError(int,String,String) 433 */ 434 @Override 435 public void onReceivedError(int errorCode, String description, String failingUrl) { 436 if (description == null || description.isEmpty()) { 437 // ErrorStrings is @hidden, so we can't do this in AwContents. 438 // Normally the net/ layer will set a valid description, but for synthesized callbacks 439 // (like in the case for intercepted requests) AwContents will pass in null. 440 description = ErrorStrings.getString(errorCode, mWebView.getContext()); 441 } 442 TraceEvent.begin(); 443 mWebViewClient.onReceivedError(mWebView, errorCode, description, failingUrl); 444 TraceEvent.end(); 445 } 446 447 /** 448 * @see ContentViewClient#onReceivedTitle(String) 449 */ 450 @Override 451 public void onReceivedTitle(String title) { 452 TraceEvent.begin(); 453 mWebChromeClient.onReceivedTitle(mWebView, title); 454 TraceEvent.end(); 455 } 456 457 458 /** 459 * @see ContentViewClient#shouldOverrideKeyEvent(KeyEvent) 460 */ 461 @Override 462 public boolean shouldOverrideKeyEvent(KeyEvent event) { 463 // TODO(joth): The expression here is a workaround for http://b/7697782 :- 464 // 1. The check for system key should be made in AwContents or ContentViewCore, 465 // before shouldOverrideKeyEvent() is called at all. 466 // 2. shouldOverrideKeyEvent() should be called in onKeyDown/onKeyUp, not from 467 // dispatchKeyEvent(). 468 if (event.isSystem()) return true; 469 TraceEvent.begin(); 470 boolean result = mWebViewClient.shouldOverrideKeyEvent(mWebView, event); 471 TraceEvent.end(); 472 return result; 473 } 474 475 476 /** 477 * @see ContentViewClient#onStartContentIntent(Context, String) 478 * Callback when detecting a click on a content link. 479 */ 480 // TODO: Delete this method when removed from base class. 481 public void onStartContentIntent(Context context, String contentUrl) { 482 TraceEvent.begin(); 483 mWebViewClient.shouldOverrideUrlLoading(mWebView, contentUrl); 484 TraceEvent.end(); 485 } 486 487 @Override 488 public void onGeolocationPermissionsShowPrompt(String origin, 489 GeolocationPermissions.Callback callback) { 490 TraceEvent.begin(); 491 mWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback); 492 TraceEvent.end(); 493 } 494 495 @Override 496 public void onGeolocationPermissionsHidePrompt() { 497 TraceEvent.begin(); 498 mWebChromeClient.onGeolocationPermissionsHidePrompt(); 499 TraceEvent.end(); 500 } 501 502 private static class JsPromptResultReceiverAdapter implements JsResult.ResultReceiver { 503 private JsPromptResultReceiver mChromePromptResultReceiver; 504 private JsResultReceiver mChromeResultReceiver; 505 // We hold onto the JsPromptResult here, just to avoid the need to downcast 506 // in onJsResultComplete. 507 private final JsPromptResult mPromptResult = new JsPromptResult(this); 508 509 public JsPromptResultReceiverAdapter(JsPromptResultReceiver receiver) { 510 mChromePromptResultReceiver = receiver; 511 } 512 513 public JsPromptResultReceiverAdapter(JsResultReceiver receiver) { 514 mChromeResultReceiver = receiver; 515 } 516 517 public JsPromptResult getPromptResult() { 518 return mPromptResult; 519 } 520 521 @Override 522 public void onJsResultComplete(JsResult result) { 523 if (mChromePromptResultReceiver != null) { 524 if (mPromptResult.getResult()) { 525 mChromePromptResultReceiver.confirm(mPromptResult.getStringResult()); 526 } else { 527 mChromePromptResultReceiver.cancel(); 528 } 529 } else { 530 if (mPromptResult.getResult()) { 531 mChromeResultReceiver.confirm(); 532 } else { 533 mChromeResultReceiver.cancel(); 534 } 535 } 536 } 537 } 538 539 @Override 540 public void handleJsAlert(String url, String message, JsResultReceiver receiver) { 541 TraceEvent.begin(); 542 final JsPromptResult res = new JsPromptResultReceiverAdapter(receiver).getPromptResult(); 543 if (!mWebChromeClient.onJsAlert(mWebView, url, message, res)) { 544 new JsDialogHelper(res, JsDialogHelper.ALERT, null, message, url) 545 .showDialog(mWebView.getContext()); 546 } 547 TraceEvent.end(); 548 } 549 550 @Override 551 public void handleJsBeforeUnload(String url, String message, JsResultReceiver receiver) { 552 TraceEvent.begin(); 553 final JsPromptResult res = new JsPromptResultReceiverAdapter(receiver).getPromptResult(); 554 if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, message, res)) { 555 new JsDialogHelper(res, JsDialogHelper.UNLOAD, null, message, url) 556 .showDialog(mWebView.getContext()); 557 } 558 TraceEvent.end(); 559 } 560 561 @Override 562 public void handleJsConfirm(String url, String message, JsResultReceiver receiver) { 563 TraceEvent.begin(); 564 final JsPromptResult res = new JsPromptResultReceiverAdapter(receiver).getPromptResult(); 565 if (!mWebChromeClient.onJsConfirm(mWebView, url, message, res)) { 566 new JsDialogHelper(res, JsDialogHelper.CONFIRM, null, message, url) 567 .showDialog(mWebView.getContext()); 568 } 569 TraceEvent.end(); 570 } 571 572 @Override 573 public void handleJsPrompt(String url, String message, String defaultValue, 574 JsPromptResultReceiver receiver) { 575 TraceEvent.begin(); 576 final JsPromptResult res = new JsPromptResultReceiverAdapter(receiver).getPromptResult(); 577 if (!mWebChromeClient.onJsPrompt(mWebView, url, message, defaultValue, res)) { 578 new JsDialogHelper(res, JsDialogHelper.PROMPT, defaultValue, message, url) 579 .showDialog(mWebView.getContext()); 580 } 581 TraceEvent.end(); 582 } 583 584 @Override 585 public void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) { 586 TraceEvent.begin(); 587 mWebViewClient.onReceivedHttpAuthRequest(mWebView, 588 new AwHttpAuthHandlerAdapter(handler), host, realm); 589 TraceEvent.end(); 590 } 591 592 @Override 593 public void onReceivedSslError(final ValueCallback<Boolean> callback, SslError error) { 594 SslErrorHandler handler = new SslErrorHandler() { 595 @Override 596 public void proceed() { 597 postProceed(true); 598 } 599 @Override 600 public void cancel() { 601 postProceed(false); 602 } 603 private void postProceed(final boolean proceed) { 604 post(new Runnable() { 605 @Override 606 public void run() { 607 callback.onReceiveValue(proceed); 608 } 609 }); 610 } 611 }; 612 TraceEvent.begin(); 613 mWebViewClient.onReceivedSslError(mWebView, handler, error); 614 TraceEvent.end(); 615 } 616 617 @Override 618 public void onReceivedLoginRequest(String realm, String account, String args) { 619 TraceEvent.begin(); 620 mWebViewClient.onReceivedLoginRequest(mWebView, realm, account, args); 621 TraceEvent.end(); 622 } 623 624 @Override 625 public void onFormResubmission(Message dontResend, Message resend) { 626 TraceEvent.begin(); 627 mWebViewClient.onFormResubmission(mWebView, dontResend, resend); 628 TraceEvent.end(); 629 } 630 631 @Override 632 public void onDownloadStart(String url, 633 String userAgent, 634 String contentDisposition, 635 String mimeType, 636 long contentLength) { 637 if (mDownloadListener != null) { 638 TraceEvent.begin(); 639 mDownloadListener.onDownloadStart(url, 640 userAgent, 641 contentDisposition, 642 mimeType, 643 contentLength); 644 TraceEvent.end(); 645 } 646 } 647 648 @Override 649 public void onScaleChangedScaled(float oldScale, float newScale) { 650 TraceEvent.begin(); 651 mWebViewClient.onScaleChanged(mWebView, oldScale, newScale); 652 TraceEvent.end(); 653 } 654 655 @Override 656 public void onShowCustomView(View view, CustomViewCallback cb) { 657 TraceEvent.begin(); 658 mWebChromeClient.onShowCustomView(view, cb); 659 TraceEvent.end(); 660 } 661 662 @Override 663 public void onHideCustomView() { 664 TraceEvent.begin(); 665 mWebChromeClient.onHideCustomView(); 666 TraceEvent.end(); 667 } 668 669 @Override 670 protected View getVideoLoadingProgressView() { 671 TraceEvent.begin(); 672 View result = mWebChromeClient.getVideoLoadingProgressView(); 673 TraceEvent.end(); 674 return result; 675 } 676 677 @Override 678 public Bitmap getDefaultVideoPoster() { 679 TraceEvent.begin(); 680 Bitmap result = mWebChromeClient.getDefaultVideoPoster(); 681 TraceEvent.end(); 682 return result; 683 } 684 685 private static class AwHttpAuthHandlerAdapter extends android.webkit.HttpAuthHandler { 686 private AwHttpAuthHandler mAwHandler; 687 688 public AwHttpAuthHandlerAdapter(AwHttpAuthHandler awHandler) { 689 mAwHandler = awHandler; 690 } 691 692 @Override 693 public void proceed(String username, String password) { 694 if (username == null) { 695 username = ""; 696 } 697 698 if (password == null) { 699 password = ""; 700 } 701 mAwHandler.proceed(username, password); 702 } 703 704 @Override 705 public void cancel() { 706 mAwHandler.cancel(); 707 } 708 709 @Override 710 public boolean useHttpAuthUsernamePassword() { 711 return mAwHandler.isFirstAttempt(); 712 } 713 } 714} 715