Bug 1127901 - Zoomed view tool bar implementation. r=mcomella

This commit is contained in:
dominique vincent 2015-05-04 14:11:23 -04:00
Родитель af4515617b
Коммит 25a861dd5c
5 изменённых файлов: 218 добавлений и 55 удалений

Просмотреть файл

@ -17,13 +17,19 @@ import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@ -32,6 +38,7 @@ import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
@ -40,20 +47,30 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
LayerView.ZoomedViewListener, GeckoEventListener {
private static final String LOGTAG = "Gecko" + ZoomedView.class.getSimpleName();
private static final int DEFAULT_ZOOM_FACTOR = 3;
private static final int W_CAPTURED_VIEW_IN_PERCENT = 80;
private static final float[] ZOOM_FACTORS_LIST = {2.0f, 3.0f, 1.5f};
private static final int W_CAPTURED_VIEW_IN_PERCENT = 50;
private static final int H_CAPTURED_VIEW_IN_PERCENT = 50;
private static final int MINIMUM_DELAY_BETWEEN_TWO_RENDER_CALLS_NS = 1000000;
private static final int DELAY_BEFORE_NEXT_RENDER_REQUEST_MS = 2000;
private int zoomFactor;
private float zoomFactor;
private int currentZoomFactorIndex;
private ImageView zoomedImageView;
private LayerView layerView;
private int viewWidth;
private int viewHeight;
private int viewHeight; // Only the zoomed view height, no toolbar, no shadow ...
private int viewContainerWidth;
private int viewContainerHeight; // Zoomed view height with toolbar and other elements like shadow, ...
private int containterSize; // shadow, margin, ...
private Point lastPosition;
private boolean shouldSetVisibleOnUpdate;
private PointF returnValue;
private ImageView closeButton;
private TextView changeZoomFactorButton;
private boolean toolbarOnTop;
private float offsetDueToToolBarPosition;
private int toolbarHeight;
private int cornerRadius;
private boolean stopUpdateView;
@ -64,6 +81,39 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
private long startTimeReRender;
private long lastStartTimeReRender;
private class RoundedBitmapDrawable extends BitmapDrawable {
private Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
final float cornerRadius;
final boolean squareOnTopOfDrawable;
RoundedBitmapDrawable(Resources res, Bitmap bitmap, boolean squareOnTop, int radius) {
super(res, bitmap);
squareOnTopOfDrawable = squareOnTop;
final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
paint.setAntiAlias(true);
paint.setShader(shader);
cornerRadius = radius;
}
@Override
public void draw(Canvas canvas) {
int height = getBounds().height();
int width = getBounds().width();
RectF rect = new RectF(0.0f, 0.0f, width, height);
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
//draw rectangles over the corners we want to be square
if (squareOnTopOfDrawable) {
canvas.drawRect(0, 0, cornerRadius, cornerRadius, paint);
canvas.drawRect(width - cornerRadius, 0, width, cornerRadius, paint);
} else {
canvas.drawRect(0, height - cornerRadius, cornerRadius, height, paint);
canvas.drawRect(width - cornerRadius, height - cornerRadius, width, height, paint);
}
}
}
private class ZoomedViewTouchListener implements View.OnTouchListener {
private float originRawX;
private float originRawY;
@ -87,6 +137,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
if (dragged) {
dragged = false;
} else {
if (isClickInZoomedView(event.getY())) {
GeckoEvent eClickInZoomedView = GeckoEvent.createBroadcastEvent("Gesture:ClickInZoomedView", "");
GeckoAppShell.sendEventToGecko(eClickInZoomedView);
layerView.dispatchTouchEvent(actionDownEvent);
@ -98,6 +149,7 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
layerView.dispatchTouchEvent(e);
e.recycle();
}
}
break;
case MotionEvent.ACTION_DOWN:
@ -113,6 +165,11 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
return true;
}
private boolean isClickInZoomedView(float y) {
return ((toolbarOnTop && y > toolbarHeight) ||
(!toolbarOnTop && y < ZoomedView.this.viewHeight));
}
private boolean moveZoomedView(MotionEvent event) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ZoomedView.this.getLayoutParams();
if ((!dragged) && (Math.abs((int) (event.getRawX() - originRawX)) < PanZoomController.CLICK_THRESHOLD)
@ -143,19 +200,21 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
public ZoomedView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
returnValue = new PointF();
currentZoomFactorIndex = 0;
zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex];
requestRenderRunnable = new Runnable() {
@Override
public void run() {
requestZoomedViewRender();
}
};
EventDispatcher.getInstance().registerGeckoThreadListener(this, "Gesture:nothingDoneOnLongPress",
EventDispatcher.getInstance().registerGeckoThreadListener(this,
"Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange");
}
void destroy() {
ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Gesture:nothingDoneOnLongPress",
EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
"Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange");
}
@ -165,15 +224,29 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
@Override
protected void onFinishInflate() {
super.onFinishInflate();
ImageView closeButton = (ImageView) findViewById(R.id.dialog_close);
closeButton = (ImageView) findViewById(R.id.dialog_close);
closeButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
stopZoomDisplay();
}
});
changeZoomFactorButton = (TextView) findViewById(R.id.change_zoom_factor);
changeZoomFactorButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
changeZoomFactor();
}
});
setTextInZoomFactorButton(ZOOM_FACTORS_LIST[0]);
zoomedImageView = (ImageView) findViewById(R.id.zoomed_image_view);
zoomedImageView.setOnTouchListener(new ZoomedViewTouchListener());
this.setOnTouchListener(new ZoomedViewTouchListener());
toolbarHeight = getResources().getDimensionPixelSize(R.dimen.zoomed_view_toolbar_height);
containterSize = getResources().getDimensionPixelSize(R.dimen.drawable_dropshadow_size);
cornerRadius = getResources().getDimensionPixelSize(R.dimen.button_corner_radius);
moveToolbar(true);
}
/*
@ -181,6 +254,10 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
* LayerView
*/
private PointF getUnzoomedPositionFromPointInZoomedView(float x, float y) {
if (toolbarOnTop && y > toolbarHeight) {
y = y - toolbarHeight;
}
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
PointF offset = metrics.getMarginOffset();
final float parentWidth = metrics.getWidth();
@ -199,14 +276,15 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
*/
(((float) params.leftMargin) - offset.x) *
((parentWidth - offset.x - (viewWidth / zoomFactor)) /
(parentWidth - offset.x - viewWidth)));
(parentWidth - offset.x - viewContainerWidth)));
// Same comments here vertically
returnValue.y = (int) ((y / zoomFactor) +
offset.y +
offset.y -
offsetDueToToolBarPosition +
(((float) params.topMargin) - offset.y) *
((parentHeight - offset.y - (viewHeight / zoomFactor)) /
(parentHeight - offset.y - viewHeight)));
((parentHeight - offset.y + offsetDueToToolBarPosition - (viewHeight / zoomFactor)) /
(parentHeight - offset.y - viewContainerHeight)));
return returnValue;
}
@ -232,14 +310,14 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
* the LayerView to the ZoomedView.
*/
((parentWidth - offset.x - (viewWidth / zoomFactor)) /
(parentWidth - offset.x - viewWidth)))
(parentWidth - offset.x - viewContainerWidth)))
+ offset.x); // The offset of the layerView
// Same comments here vertically
returnValue.y = (int) ((((y - (viewHeight / (2 * zoomFactor)))) /
((parentHeight - offset.y - (viewHeight / zoomFactor)) /
(parentHeight - offset.y - viewHeight)))
returnValue.y = (int) ((((y + offsetDueToToolBarPosition - (viewHeight / (2 * zoomFactor)))) /
((parentHeight - offset.y + offsetDueToToolBarPosition - (viewHeight / zoomFactor)) /
(parentHeight - offset.y - viewContainerHeight)))
+ offset.y);
return returnValue;
@ -259,14 +337,20 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
if (newTopMargin < topMarginMin) {
newLayoutParams.topMargin = topMarginMin;
} else if (newTopMargin + viewHeight > parentHeight) {
newLayoutParams.topMargin = (int) (parentHeight - viewHeight);
} else if (newTopMargin + viewContainerHeight > parentHeight) {
newLayoutParams.topMargin = (int) (parentHeight - viewContainerHeight);
}
if (newLeftMargin < leftMarginMin) {
newLayoutParams.leftMargin = leftMarginMin;
} else if (newLeftMargin + viewWidth > parentWidth) {
newLayoutParams.leftMargin = (int) (parentWidth - viewWidth);
} else if (newLeftMargin + viewContainerWidth > parentWidth) {
newLayoutParams.leftMargin = (int) (parentWidth - viewContainerWidth);
}
if (newLayoutParams.topMargin < topMarginMin + 1) {
moveToolbar(false);
} else if (newLayoutParams.topMargin + viewContainerHeight > parentHeight - 1) {
moveToolbar(true);
}
setLayoutParams(newLayoutParams);
@ -275,6 +359,37 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
requestZoomedViewRender();
}
private void moveToolbar(boolean moveTop) {
if (toolbarOnTop == moveTop) {
return;
}
toolbarOnTop = moveTop;
if (toolbarOnTop) {
offsetDueToToolBarPosition = toolbarHeight;
} else {
offsetDueToToolBarPosition = 0;
}
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) zoomedImageView.getLayoutParams();
RelativeLayout.LayoutParams pChangeZoomFactorButton = (RelativeLayout.LayoutParams) changeZoomFactorButton.getLayoutParams();
RelativeLayout.LayoutParams pCloseButton = (RelativeLayout.LayoutParams) closeButton.getLayoutParams();
if (moveTop) {
p.addRule(RelativeLayout.BELOW, R.id.change_zoom_factor);
pChangeZoomFactorButton.addRule(RelativeLayout.BELOW, 0);
pCloseButton.addRule(RelativeLayout.BELOW, 0);
} else {
p.addRule(RelativeLayout.BELOW, 0);
pChangeZoomFactorButton.addRule(RelativeLayout.BELOW, R.id.zoomed_image_view);
pCloseButton.addRule(RelativeLayout.BELOW, R.id.zoomed_image_view);
}
pChangeZoomFactorButton.addRule(RelativeLayout.ALIGN_LEFT, R.id.zoomed_image_view);
pCloseButton.addRule(RelativeLayout.ALIGN_RIGHT, R.id.zoomed_image_view);
zoomedImageView.setLayoutParams(p);
changeZoomFactorButton.setLayoutParams(pChangeZoomFactorButton);
closeButton.setLayoutParams(pCloseButton);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@ -299,13 +414,12 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
private void setCapturedSize(ImmutableViewportMetrics metrics) {
float parentMinSize = Math.min(metrics.getWidth(), metrics.getHeight());
// For metrics.zoomFactor lower than 1, the zoom factor of the zoomed view is calculated
// to get always the same size for the content in the zoomed view.
// For metrics.zoomFactor greater than 1, the zoom factor is always set to the default
// value DEFAULT_ZOOM_FACTOR, thus the zoomed view is always a zoom of the normal view.
zoomFactor = Math.max(DEFAULT_ZOOM_FACTOR, (int) (DEFAULT_ZOOM_FACTOR / metrics.zoomFactor));
viewWidth = (int) (parentMinSize * W_CAPTURED_VIEW_IN_PERCENT / (zoomFactor * 100.0)) * zoomFactor;
viewHeight = (int) (parentMinSize * H_CAPTURED_VIEW_IN_PERCENT / (zoomFactor * 100.0)) * zoomFactor;
viewWidth = (int) ((parentMinSize * W_CAPTURED_VIEW_IN_PERCENT / (zoomFactor * 100.0)) * zoomFactor);
viewHeight = (int) ((parentMinSize * H_CAPTURED_VIEW_IN_PERCENT / (zoomFactor * 100.0)) * zoomFactor);
viewContainerHeight = viewHeight + toolbarHeight +
2 * containterSize; // Top and bottom shadows
viewContainerWidth = viewWidth +
2 * containterSize; // Right and left shadows
// Display in zoomedview is corrupted when width is an odd number
// More details about this issue here: bug 776906 comment 11
viewWidth &= ~0x1;
@ -343,13 +457,31 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
}
}
private void changeZoomFactor() {
if (currentZoomFactorIndex < ZOOM_FACTORS_LIST.length - 1) {
currentZoomFactorIndex++;
} else {
currentZoomFactorIndex = 0;
}
zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex];
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
refreshZoomedViewSize(metrics);
setTextInZoomFactorButton(zoomFactor);
}
private void setTextInZoomFactorButton(float zoom) {
final String percentageValue = Integer.toString((int) (100*zoom));
changeZoomFactorButton.setText(getResources().getString(R.string.percent, percentageValue));
}
@Override
public void handleMessage(final String event, final JSONObject message) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
try {
if (event.equals("Gesture:nothingDoneOnLongPress") || event.equals("Gesture:clusteredLinksClicked")) {
if (event.equals("Gesture:clusteredLinksClicked")) {
final JSONObject clickPosition = message.getJSONObject("clickPosition");
int left = clickPosition.getInt("x");
int top = clickPosition.getInt("y");
@ -373,6 +505,10 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
private void moveUsingGeckoPosition(int leftFromGecko, int topFromGecko) {
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
final float parentHeight = metrics.getHeight();
// moveToolbar is called before getZoomedViewTopLeftPositionFromTouchPosition in order to
// correctly center vertically the zoomed area
moveToolbar((topFromGecko * metrics.zoomFactor > parentHeight / 2));
PointF convertedPosition = getZoomedViewTopLeftPositionFromTouchPosition((leftFromGecko * metrics.zoomFactor),
(topFromGecko * metrics.zoomFactor));
moveZoomedView(metrics, convertedPosition.x, convertedPosition.y);
@ -406,8 +542,8 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
} catch (Exception iae) {
Log.w(LOGTAG, iae.toString());
}
BitmapDrawable ob3 = new BitmapDrawable(getResources(), sb3);
if (zoomedImageView != null) {
RoundedBitmapDrawable ob3 = new RoundedBitmapDrawable(getResources(), sb3, toolbarOnTop, cornerRadius);
zoomedImageView.setImageDrawable(ob3);
}
}

Просмотреть файл

@ -614,6 +614,14 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY colon ":">
<!-- LOCALIZATION NOTE (percent): The percent sign is appended after a number to
display a percentage value. formatS is the number, #37 is the code to display a percent sign.
This format string is typically used by getString method, in such method the percent sign
is a reserved caracter. In order to display one percent sign in the result of getString,
double percent signs must be inserted in the format string.
This entity is used in the zoomed view to display the zoom factor-->
<!ENTITY percent "&formatS;&#37;&#37;">
<!-- These are only used for accessibility for the done and overflow-menu buttons in the actionbar.
They are never shown to users -->
<!ENTITY actionbar_menu "Menu">

Просмотреть файл

@ -10,24 +10,37 @@
android:id="@+id/zoomed_view_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@android:color/white"
android:visibility="gone" >
android:background="@drawable/dropshadow"
android:padding="@dimen/drawable_dropshadow_size"
android:visibility="gone">
<ImageView
android:id="@+id/zoomed_image_view"
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000000"
android:padding="1dip" />
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@drawable/toolbar_grey_round">
<TextView
android:id="@+id/change_zoom_factor"
android:layout_width="wrap_content"
android:layout_height="@dimen/zoomed_view_toolbar_height"
android:background="@android:color/transparent"
android:padding="12dip"
android:layout_alignLeft="@+id/zoomed_image_view"
android:textSize="16sp"
android:textColor="@color/text_and_tabs_tray_grey"/>
<ImageView
android:id="@+id/dialog_close"
android:background="@drawable/close"
android:layout_height="20dp"
android:layout_width="20dp"
android:layout_gravity ="top|right" />
android:scaleType="center"
android:layout_width="@dimen/zoomed_view_toolbar_height"
android:layout_height="@dimen/zoomed_view_toolbar_height"
android:layout_alignRight="@id/zoomed_image_view"
android:src="@drawable/close_edit_mode_selector"/>
<ImageView
android:id="@id/zoomed_image_view"
android:layout_below="@id/change_zoom_factor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
</org.mozilla.gecko.ZoomedView>

Просмотреть файл

@ -205,6 +205,10 @@
<dimen name="horizontal_drag_area">256dp</dimen>
<!-- ZoomedView dimensions. -->
<dimen name="zoomed_view_toolbar_height">44dp</dimen>
<dimen name="drawable_dropshadow_size">3dp</dimen>
<!-- Find-In-Page dialog dimensions. -->
<dimen name="find_in_page_text_margin_left">5dip</dimen>
<dimen name="find_in_page_text_margin_right">12dip</dimen>

Просмотреть файл

@ -528,5 +528,7 @@
<string name="colon">&colon;</string>
<string name="percent">&percent;</string>
<string name="remote_tabs_last_synced">&remote_tabs_last_synced;</string>
</resources>