diff options
author | Ximeng Zu <uznomis@yahoo.com> | 2017-06-18 16:37:13 -0500 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2017-07-23 23:00:48 +0200 |
commit | 02f3c4848ab52358bfee56c570a19a3a071574f2 (patch) | |
tree | 7614b82abc304cea2b4d59de52147a5b53d37219 /android | |
parent | fadf31b32566bc5e7b729de3e112addb93585be8 (diff) |
Calc UI on Android Viewer
Adding Calc UI. Two blank views are added as
row and column headers. CommonCanvasElement
is used to draw header cells on the views. [WIP]
Change-Id: I37eaa82805045ab650fd127e54c8421c61a4ea27
Reviewed-on: https://gerrit.libreoffice.org/38936
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'android')
12 files changed, 369 insertions, 19 deletions
diff --git a/android/source/res/layout/activity_main.xml b/android/source/res/layout/activity_main.xml index bfa575b57f36..fb5ba1ef7d32 100644 --- a/android/source/res/layout/activity_main.xml +++ b/android/source/res/layout/activity_main.xml @@ -36,22 +36,53 @@ </android.support.design.widget.AppBarLayout> <RelativeLayout - android:id="@+id/gecko_layout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> + android:layout_height="match_parent"> - <org.mozilla.gecko.gfx.LayerView - android:id="@+id/layer_view" + <View + android:id="@+id/calc_header_top_left" + android:layout_width="@dimen/calc_header_width" + android:layout_height="@dimen/calc_header_height" + android:visibility="gone"/> + + <org.libreoffice.overlay.CalcHeadersView + android:id="@+id/calc_header_column" + android:layout_width="match_parent" + android:layout_height="@dimen/calc_header_height" + android:layout_toRightOf="@+id/calc_header_top_left" + android:layout_toEndOf="@+id/calc_header_top_left" + android:visibility="gone"/> + + <org.libreoffice.overlay.CalcHeadersView + android:id="@+id/calc_header_row" + android:layout_width="@dimen/calc_header_width" + android:layout_height="match_parent" + android:layout_below="@+id/calc_header_top_left" + android:visibility="gone"/> + + <RelativeLayout + android:id="@+id/gecko_layout" android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_toRightOf="@+id/calc_header_row" + android:layout_toEndOf="@+id/calc_header_row" + android:layout_below="@+id/calc_header_column"> + + <org.mozilla.gecko.gfx.LayerView + android:id="@+id/layer_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> - <org.libreoffice.overlay.DocumentOverlayView - android:id="@+id/text_cursor_view" - android:layout_width="fill_parent" - android:layout_height="fill_parent"/> + <org.libreoffice.overlay.DocumentOverlayView + android:id="@+id/text_cursor_view" + android:layout_width="fill_parent" + android:layout_height="fill_parent"/> + + </RelativeLayout> </RelativeLayout> + </LinearLayout> <include layout="@layout/toolbar_bottom"/> diff --git a/android/source/res/values/dimens.xml b/android/source/res/values/dimens.xml index 3de476c0cc42..8a153790ac69 100644 --- a/android/source/res/values/dimens.xml +++ b/android/source/res/values/dimens.xml @@ -9,4 +9,6 @@ <dimen name="list_item_margin">8dp</dimen> <dimen name="file_icon_width">32dp</dimen> <dimen name="toolbar_height">256dp</dimen> + <dimen name="calc_header_width">48dp</dimen> + <dimen name="calc_header_height">24dp</dimen> </resources> diff --git a/android/source/src/java/org/libreoffice/LOEvent.java b/android/source/src/java/org/libreoffice/LOEvent.java index 14980dca0ec4..7846f7331bbd 100644 --- a/android/source/src/java/org/libreoffice/LOEvent.java +++ b/android/source/src/java/org/libreoffice/LOEvent.java @@ -38,6 +38,7 @@ public class LOEvent implements Comparable<LOEvent> { public static final int SAVE_AS = 17; public static final int UPDATE_PART_PAGE_RECT = 18; public static final int UPDATE_ZOOM_CONSTRAINTS = 19; + public static final int UPDATE_CALC_HEADERS = 20; public final int mType; public int mPriority = 0; diff --git a/android/source/src/java/org/libreoffice/LOKitThread.java b/android/source/src/java/org/libreoffice/LOKitThread.java index dac8cc43109c..9ab54c20a1f8 100644 --- a/android/source/src/java/org/libreoffice/LOKitThread.java +++ b/android/source/src/java/org/libreoffice/LOKitThread.java @@ -156,6 +156,9 @@ class LOKitThread extends Thread { mLayerClient.clearAndResetlayers(); redraw(); updatePartPageRectangles(); + if (mTileProvider.isSpreadsheet()) { + updateCalcHeaders(); + } } /** @@ -170,11 +173,15 @@ class LOKitThread extends Thread { private void updateZoomConstraints() { mLayerClient = mContext.getLayerClient(); - - // Set min zoom to the page width so that you cannot zoom below page width - // applies to all types of document; in the future spreadsheets may be singled out - float minZoom = mLayerClient.getViewportMetrics().getWidth()/mTileProvider.getPageWidth(); - mLayerClient.setZoomConstraints(new ZoomConstraints(true, 0.0f, minZoom, 0.0f)); + if (mTileProvider.isSpreadsheet()) { + // Calc has a fixed zoom at 1x and doesn't allow zooming for now + mLayerClient.setZoomConstraints(new ZoomConstraints(false, 1f, 0f, 0f)); + } else { + // Set min zoom to the page width so that you cannot zoom below page width + // applies to all types of document; in the future spreadsheets may be singled out + float minZoom = mLayerClient.getViewportMetrics().getWidth()/mTileProvider.getPageWidth(); + mLayerClient.setZoomConstraints(new ZoomConstraints(true, 1f, minZoom, 0f)); + } } @@ -343,9 +350,19 @@ class LOKitThread extends Thread { case LOEvent.UPDATE_ZOOM_CONSTRAINTS: updateZoomConstraints(); break; + case LOEvent.UPDATE_CALC_HEADERS: + updateCalcHeaders(); + break; + } } + private void updateCalcHeaders() { + LOKitTileProvider tileProvider = (LOKitTileProvider)mTileProvider; + String values = tileProvider.getCalcHeaders(); + mContext.getCalcHeadersController().setHeaders(values); + } + /** * Request a change of the handle position. */ diff --git a/android/source/src/java/org/libreoffice/LOKitTileProvider.java b/android/source/src/java/org/libreoffice/LOKitTileProvider.java index 6fb8a9b80671..3278265dfa3f 100644 --- a/android/source/src/java/org/libreoffice/LOKitTileProvider.java +++ b/android/source/src/java/org/libreoffice/LOKitTileProvider.java @@ -28,6 +28,7 @@ import java.nio.ByteBuffer; */ class LOKitTileProvider implements TileProvider { private static final String LOGTAG = LOKitTileProvider.class.getSimpleName(); + private static final float DPI_1X_ZOOM = 96f; // for use in Calc at fixed zoom 1x private static int TILE_SIZE = 256; private final float mTileWidth; private final float mTileHeight; @@ -53,9 +54,6 @@ class LOKitTileProvider implements TileProvider { LOKitTileProvider(LibreOfficeMainActivity context, Document.MessageCallback messageCallback, String input) { mContext = context; mMessageCallback = messageCallback; - mDPI = LOKitShell.getDpi(mContext); - mTileWidth = pixelToTwip(TILE_SIZE, mDPI); - mTileHeight = pixelToTwip(TILE_SIZE, mDPI); LibreOfficeKit.putenv("SAL_LOG=+WARN+INFO"); LibreOfficeKit.init(mContext); @@ -80,6 +78,15 @@ class LOKitTileProvider implements TileProvider { Log.i(LOGTAG, "====> mDocument = " + mDocument); + if(isSpreadsheet()) { + mDPI = DPI_1X_ZOOM; // Calc has a fixed zoom at 1x + } else { + mDPI = LOKitShell.getDpi(mContext); + } + + mTileWidth = pixelToTwip(TILE_SIZE, mDPI); + mTileHeight = pixelToTwip(TILE_SIZE, mDPI); + if (mDocument != null) mDocument.initializeForRendering(); @@ -124,6 +131,11 @@ class LOKitTileProvider implements TileProvider { mContext.getToolbarController().disableMenuItem(R.id.action_parts, true); } + // Enable headers for Calc documents + if (mDocument.getDocumentType() == Document.DOCTYPE_SPREADSHEET) { + mContext.initializeCalcHeaders(); + } + mDocument.setPart(0); setupDocumentFonts(); @@ -230,6 +242,18 @@ class LOKitTileProvider implements TileProvider { } /** + * Fetch Calc header information. + */ + public String getCalcHeaders() { + long nX = 0; + long nY = 0; + long nWidth = mDocument.getDocumentWidth(); + long nHeight = mDocument.getDocumentHeight(); + return mDocument.getCommandValues(".uno:ViewRowColumnHeaders?x=" + nX + "&y=" + nY + + "&width=" + nWidth + "&height=" + nHeight); + } + + /** * @see TileProvider#onSwipeLeft() */ @Override diff --git a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java index 8aeb1f5ff816..95c90de9b69a 100755 --- a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java +++ b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java @@ -33,6 +33,7 @@ import android.widget.ListView; import android.widget.TabHost; import android.widget.Toast; +import org.libreoffice.overlay.CalcHeadersController; import org.libreoffice.overlay.DocumentOverlay; import org.libreoffice.storage.DocumentProviderFactory; import org.libreoffice.storage.IFile; @@ -91,6 +92,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin private ToolbarController mToolbarController; private FontController mFontController; private SearchController mSearchController; + private CalcHeadersController mCalcHeadersController; public GeckoLayerClient getLayerClient() { return mLayerClient; @@ -645,6 +647,10 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin return mDocumentOverlay; } + public CalcHeadersController getCalcHeadersController() { + return mCalcHeadersController; + } + public ToolbarController getToolbarController() { return mToolbarController; } @@ -685,6 +691,18 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin } } + public void initializeCalcHeaders() { + mCalcHeadersController = new CalcHeadersController(this, mLayerClient.getView()); + LOKitShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + findViewById(R.id.calc_header_top_left).setVisibility(View.VISIBLE); + findViewById(R.id.calc_header_row).setVisibility(View.VISIBLE); + findViewById(R.id.calc_header_column).setVisibility(View.VISIBLE); + } + }); + } + private class DocumentPartClickListener implements android.widget.AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { diff --git a/android/source/src/java/org/libreoffice/canvas/CalcHeaderCell.java b/android/source/src/java/org/libreoffice/canvas/CalcHeaderCell.java new file mode 100644 index 000000000000..dc46f047c10c --- /dev/null +++ b/android/source/src/java/org/libreoffice/canvas/CalcHeaderCell.java @@ -0,0 +1,48 @@ +package org.libreoffice.canvas; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.text.TextPaint; + +public class CalcHeaderCell extends CommonCanvasElement { + private TextPaint mTextPaint = new TextPaint(); + private Paint mBgPaint = new Paint(); + private RectF mBounds; + private String mText; + + public CalcHeaderCell(float left, float top, float width, float height, String text) { + mBounds = new RectF(left, top, left + width, top + height); + mBgPaint.setStyle(Style.STROKE); + mBgPaint.setColor(Color.GRAY); + mBgPaint.setAlpha(100); // hard coded for now + mTextPaint.setColor(Color.GRAY); + mTextPaint.setTextSize(24f); // hard coded for now + mText = text; + } + + /** + * Implement hit test here + * + * @param x - x coordinate of the + * @param y - y coordinate of the + */ + @Override + public boolean onHitTest(float x, float y) { + return false; + } + + /** + * Called inside draw if the element is visible. Override this method to + * draw the element on the canvas. + * + * @param canvas - the canvas + */ + @Override + public void onDraw(Canvas canvas) { + canvas.drawRect(mBounds, mBgPaint); + canvas.drawText(mText, mBounds.left, mBounds.bottom, mTextPaint); + } +}
\ No newline at end of file diff --git a/android/source/src/java/org/libreoffice/overlay/CalcHeadersController.java b/android/source/src/java/org/libreoffice/overlay/CalcHeadersController.java new file mode 100644 index 000000000000..80cb0e5791d8 --- /dev/null +++ b/android/source/src/java/org/libreoffice/overlay/CalcHeadersController.java @@ -0,0 +1,98 @@ +package org.libreoffice.overlay; + +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.libreoffice.LOEvent; +import org.libreoffice.LOKitShell; +import org.libreoffice.LibreOfficeMainActivity; +import org.libreoffice.R; +import org.mozilla.gecko.gfx.LayerView; + +import java.util.ArrayList; + +public class CalcHeadersController { + private static final String LOGTAG = CalcHeadersController.class.getSimpleName(); + private static final float DPI_1X_ZOOM = 96f; // Calc uses a fixed zoom a 1x which is 96 dpi + + private final CalcHeadersView mCalcRowHeadersView; + private final CalcHeadersView mCalcColumnHeadersView; + + private LibreOfficeMainActivity mContext; + + public CalcHeadersController(LibreOfficeMainActivity context, LayerView layerView) { + mContext = context; + mContext.getDocumentOverlay().setCalcHeadersController(this); + mCalcRowHeadersView = (CalcHeadersView) context.findViewById(R.id.calc_header_row); + mCalcColumnHeadersView = (CalcHeadersView) context.findViewById(R.id.calc_header_column); + if (mCalcColumnHeadersView == null || mCalcRowHeadersView == null) { + Log.e(LOGTAG, "Failed to initialize Calc headers - View is null"); + } else { + mCalcRowHeadersView.initialize(layerView, true); + mCalcColumnHeadersView.initialize(layerView, false); + } + LOKitShell.sendEvent(new LOEvent(LOEvent.UPDATE_CALC_HEADERS)); + } + + public void setHeaders(String headers) { + HeaderInfo parsedHeaders = parseHeaderInfo(headers); + if (parsedHeaders != null) { + mCalcRowHeadersView.setHeaders(parsedHeaders.rowLabels, parsedHeaders.rowDimens); + mCalcColumnHeadersView.setHeaders(parsedHeaders.columnLabels, parsedHeaders.columnDimens); + showHeaders(); + } else { + Log.e(LOGTAG, "Parse header info JSON failed."); + } + } + + public void showHeaders() { + LOKitShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + mCalcColumnHeadersView.invalidate(); + mCalcRowHeadersView.invalidate(); + } + }); + } + + private HeaderInfo parseHeaderInfo(String headers) { + HeaderInfo headerInfo = new HeaderInfo(); + try { + JSONObject collectiveResult = new JSONObject(headers); + JSONArray rowResult = collectiveResult.getJSONArray("rows"); + for (int i = 0; i < rowResult.length(); i++) { + headerInfo.rowLabels.add(rowResult.getJSONObject(i).getString("text")); + headerInfo.rowDimens.add(twipToPixel(rowResult.getJSONObject(i).getLong("size"), DPI_1X_ZOOM)); + } + JSONArray columnResult = collectiveResult.getJSONArray("columns"); + for (int i = 0; i < columnResult.length(); i++) { + headerInfo.columnLabels.add(columnResult.getJSONObject(i).getString("text")); + headerInfo.columnDimens.add(twipToPixel(columnResult.getJSONObject(i).getLong("size"), DPI_1X_ZOOM)); + } + return headerInfo; + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + private float twipToPixel(float input, float dpi) { + return input / 1440.0f * dpi; + } + + private class HeaderInfo { + ArrayList<String> rowLabels; + ArrayList<Float> rowDimens; + ArrayList<String> columnLabels; + ArrayList<Float> columnDimens; + + private HeaderInfo() { + rowLabels = new ArrayList<String>(); + rowDimens = new ArrayList<Float>(); + columnDimens = new ArrayList<Float>(); + columnLabels = new ArrayList<String>(); + } + } +} diff --git a/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java b/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java new file mode 100644 index 000000000000..d15941470bd7 --- /dev/null +++ b/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java @@ -0,0 +1,95 @@ +package org.libreoffice.overlay; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.PointF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import org.libreoffice.canvas.CalcHeaderCell; +import org.mozilla.gecko.gfx.ImmutableViewportMetrics; +import org.mozilla.gecko.gfx.LayerView; + +import java.util.ArrayList; + +public class CalcHeadersView extends View implements View.OnTouchListener { + + private boolean mInitialized; + private LayerView mLayerView; + private boolean mIsRow; // true if this is for row headers, false for column + private ArrayList<String> mLabels; + private ArrayList<Float> mDimens; + + public CalcHeadersView(Context context) { + super(context); + } + + public CalcHeadersView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CalcHeadersView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void initialize(LayerView layerView, boolean isRow) { + if (!mInitialized) { + setOnTouchListener(this); + mLayerView = layerView; + mIsRow = isRow; + + mInitialized = true; + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mInitialized && mDimens != null && mLabels != null) { + updateHeaders(canvas); + } + } + + private void updateHeaders(Canvas canvas) { + ImmutableViewportMetrics metrics = mLayerView.getViewportMetrics(); + float zoom = metrics.getZoomFactor(); + PointF origin = metrics.getOrigin(); + boolean inRangeOfVisibleHeaders = false; // a helper variable for skipping unnecessary onDraw()'s + float top,bottom,left,right; + for (int i = 1; i < mLabels.size(); i++) { + if (mIsRow) { + top = -origin.y + zoom*mDimens.get(i-1); + bottom = -origin.y + zoom*mDimens.get(i); + if (top <= getHeight() && bottom >= 0) { + inRangeOfVisibleHeaders = true; + new CalcHeaderCell(0f, top, getWidth(), bottom - top, mLabels.get(i)).onDraw(canvas); + } else { + if (inRangeOfVisibleHeaders) { + break; + } + } + } else { + left = -origin.x + zoom*mDimens.get(i-1); + right = -origin.x + zoom*mDimens.get(i); + if (left <= getWidth() && right >= 0) { + new CalcHeaderCell(left, 0f, right - left, getHeight(), mLabels.get(i)).onDraw(canvas); + } else { + if (inRangeOfVisibleHeaders) { + break; + } + } + } + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } + + public void setHeaders(ArrayList<String> labels, ArrayList<Float> dimens) { + mLabels = labels; + mDimens = dimens; + } +} diff --git a/android/source/src/java/org/libreoffice/overlay/DocumentOverlay.java b/android/source/src/java/org/libreoffice/overlay/DocumentOverlay.java index d9d2b372b714..4809921012bc 100644 --- a/android/source/src/java/org/libreoffice/overlay/DocumentOverlay.java +++ b/android/source/src/java/org/libreoffice/overlay/DocumentOverlay.java @@ -8,11 +8,11 @@ */ package org.libreoffice.overlay; -import org.libreoffice.LibreOfficeMainActivity; import android.graphics.RectF; import android.util.Log; import org.libreoffice.LOKitShell; +import org.libreoffice.LibreOfficeMainActivity; import org.libreoffice.R; import org.libreoffice.canvas.SelectionHandle; import org.mozilla.gecko.gfx.Layer; @@ -237,6 +237,11 @@ public class DocumentOverlay { public RectF getCurrentCursorPosition() { return mDocumentOverlayView.getCurrentCursorPosition(); } + + public void setCalcHeadersController(CalcHeadersController calcHeadersController) { + mDocumentOverlayView.setCalcHeadersController(calcHeadersController); + } + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/android/source/src/java/org/libreoffice/overlay/DocumentOverlayView.java b/android/source/src/java/org/libreoffice/overlay/DocumentOverlayView.java index 6afd71309d1d..7d5ab1972d39 100644 --- a/android/source/src/java/org/libreoffice/overlay/DocumentOverlayView.java +++ b/android/source/src/java/org/libreoffice/overlay/DocumentOverlayView.java @@ -67,6 +67,7 @@ public class DocumentOverlayView extends View implements View.OnTouchListener { private PageNumberRect mPageNumberRect; private boolean mPageNumberAvailable = false; private int previousIndex = 0; // previous page number, used to compare with the current + private CalcHeadersController mCalcHeadersController; public DocumentOverlayView(Context context) { super(context); @@ -217,6 +218,9 @@ public class DocumentOverlayView extends View implements View.OnTouchListener { mGraphicSelection.draw(canvas); + if (mCalcHeadersController != null) { + mCalcHeadersController.showHeaders(); + } } /** @@ -450,6 +454,10 @@ public class DocumentOverlayView extends View implements View.OnTouchListener { public RectF getCurrentCursorPosition() { return mCursor.mPosition; } + + public void setCalcHeadersController(CalcHeadersController calcHeadersController) { + mCalcHeadersController = calcHeadersController; + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java index b834fa0798d5..d3568216c159 100644 --- a/android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java +++ b/android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java @@ -984,6 +984,9 @@ class JavaPanZoomController @Override public boolean onDoubleTap(MotionEvent motionEvent) { + if (!mTarget.getZoomConstraints().getAllowDoubleTapZoom()) { + return true; + } // Double tap zooms in or out depending on the current zoom factor PointF pointOfTap = getMotionInDocumentCoordinates(motionEvent); ImmutableViewportMetrics metrics = getMetrics(); |