diff --git a/mobile/android/base/ZoomedView.java b/mobile/android/base/ZoomedView.java index 29cc4036f54a..f8809e86c0ad 100644 --- a/mobile/android/base/ZoomedView.java +++ b/mobile/android/base/ZoomedView.java @@ -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,16 +137,18 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange if (dragged) { dragged = false; } else { - GeckoEvent eClickInZoomedView = GeckoEvent.createBroadcastEvent("Gesture:ClickInZoomedView", ""); - GeckoAppShell.sendEventToGecko(eClickInZoomedView); - layerView.dispatchTouchEvent(actionDownEvent); - actionDownEvent.recycle(); - PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY()); - MotionEvent e = MotionEvent.obtain(event.getDownTime(), event.getEventTime(), - MotionEvent.ACTION_UP, convertedPosition.x, convertedPosition.y, - event.getMetaState()); - layerView.dispatchTouchEvent(e); - e.recycle(); + if (isClickInZoomedView(event.getY())) { + GeckoEvent eClickInZoomedView = GeckoEvent.createBroadcastEvent("Gesture:ClickInZoomedView", ""); + GeckoAppShell.sendEventToGecko(eClickInZoomedView); + layerView.dispatchTouchEvent(actionDownEvent); + actionDownEvent.recycle(); + PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY()); + MotionEvent e = MotionEvent.obtain(event.getDownTime(), event.getEventTime(), + MotionEvent.ACTION_UP, convertedPosition.x, convertedPosition.y, + event.getMetaState()); + layerView.dispatchTouchEvent(e); + e.recycle(); + } } break; @@ -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); } } diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index 9b4b9dcf5f75..91a8b97ca47a 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -614,6 +614,14 @@ just addresses the organization to follow, e.g. "This site is run by " --> + + + diff --git a/mobile/android/base/resources/layout/zoomed_view.xml b/mobile/android/base/resources/layout/zoomed_view.xml index 2db7a54ecd35..667cb08704ab 100644 --- a/mobile/android/base/resources/layout/zoomed_view.xml +++ b/mobile/android/base/resources/layout/zoomed_view.xml @@ -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"> - - - - + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:background="@drawable/toolbar_grey_round"> + + + + \ No newline at end of file diff --git a/mobile/android/base/resources/values/dimens.xml b/mobile/android/base/resources/values/dimens.xml index 245c7cb11ae4..b53c1bd1fa59 100644 --- a/mobile/android/base/resources/values/dimens.xml +++ b/mobile/android/base/resources/values/dimens.xml @@ -205,6 +205,10 @@ 256dp + + 44dp + 3dp + 5dip 12dip diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index 8636378611e7..24213ea6098b 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -528,5 +528,7 @@ : + &percent; + &remote_tabs_last_synced;