/* * Copyright (C) 2012 Google Inc. * Licensed to The Android Open Source Project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.mail.browse; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.MotionEvent; import com.android.mail.R; import com.android.mail.utils.LogTag; import com.android.mail.utils.LogUtils; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; public class ConversationWebView extends MailWebView implements ScrollNotifier { /** The initial delay when rendering in hardware layer. */ private final int mWebviewInitialDelay; private Bitmap mBitmap; private Canvas mCanvas; private boolean mUseSoftwareLayer; /** * Whether this view is user-visible; we don't bother doing supplemental software drawing * if the view is off-screen. */ private boolean mVisible; /** {@link Runnable} to be run when the page is rendered in hardware layer. */ private final Runnable mNotifyPageRenderedInHardwareLayer = new Runnable() { @Override public void run() { // Switch to hardware layer. mUseSoftwareLayer = false; destroyBitmap(); invalidate(); } }; @Override public void onDraw(Canvas canvas) { // Always render in hardware layer to avoid flicker when switch. super.onDraw(canvas); // Render in software layer on top if needed, and we're visible (i.e. it's worthwhile to // do all this) if (mUseSoftwareLayer && mVisible && getWidth() > 0 && getHeight() > 0) { if (mBitmap == null) { try { // Create an offscreen bitmap. mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565); mCanvas = new Canvas(mBitmap); } catch (OutOfMemoryError e) { // just give up mBitmap = null; mCanvas = null; mUseSoftwareLayer = false; } } if (mBitmap != null) { final int x = getScrollX(); final int y = getScrollY(); mCanvas.save(); mCanvas.translate(-x, -y); super.onDraw(mCanvas); mCanvas.restore(); canvas.drawBitmap(mBitmap, x, y, null /* paint */); } } } @Override public void destroy() { destroyBitmap(); removeCallbacks(mNotifyPageRenderedInHardwareLayer); super.destroy(); } /** * Destroys the {@link Bitmap} used for software layer. */ private void destroyBitmap() { if (mBitmap != null) { mBitmap = null; mCanvas = null; } } /** * Enable this WebView to also draw to an internal software canvas until * {@link #onRenderComplete()} is called. The software draw will happen every time * a normal {@link #onDraw(Canvas)} happens, and will overwrite whatever is normally drawn * (i.e. drawn in hardware) with the results of software rendering. *
* This is useful when you know that the WebView draws sooner to a software layer than it does
* to its normal hardware layer.
*/
public void setUseSoftwareLayer(boolean useSoftware) {
mUseSoftwareLayer = useSoftware;
}
/**
* Notifies the {@link ConversationWebView} that it has become visible. It can use this signal
* to switch between software and hardware layer.
*/
public void onRenderComplete() {
if (mUseSoftwareLayer) {
// Schedule to switch from software layer to hardware layer in 1s.
postDelayed(mNotifyPageRenderedInHardwareLayer, mWebviewInitialDelay);
}
}
public void onUserVisibilityChanged(boolean visible) {
mVisible = visible;
}
private final int mViewportWidth;
private final float mDensity;
private final Set This assumes that we are able to control the logical HTML viewport with a meta-viewport
* tag.
*/
public float getInitialScale() {
final float scale;
if (getSettings().getLoadWithOverviewMode()) {
// in overview mode (aka auto-fit mode), the base ratio is screen px : viewport px
scale = (float) getWidth() / getViewportWidth();
} else {
// in no-zoom mode, the base ratio is just screen px : mdpi css px (i.e. density)
scale = mDensity;
}
return scale;
}
public int screenPxToWebPx(int screenPx) {
return (int) (screenPx / getInitialScale());
}
public int webPxToScreenPx(int webPx) {
return (int) (webPx * getInitialScale());
}
public float screenPxToWebPxError(int screenPx) {
return screenPx / getInitialScale() - screenPxToWebPx(screenPx);
}
public float webPxToScreenPxError(int webPx) {
return webPx * getInitialScale() - webPxToScreenPx(webPx);
}
}