Java tutorial
/* * Copyright (C) 2012 - 2014 Brandon Tate, bossturbo * * 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 it.angrydroids.epub3reader; import org.json.JSONException; import org.json.JSONObject; import com.blahti.drag.DragController; import com.blahti.drag.DragController.DragBehavior; import com.blahti.drag.DragLayer; import com.blahti.drag.DragListener; import com.blahti.drag.DragSource; import com.blahti.drag.MyAbsoluteLayout; import android.annotation.SuppressLint; import android.app.Activity; import android.app.FragmentManager; import android.content.Context; import android.graphics.Rect; import android.os.Build; import android.os.Handler; import android.os.Message; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.WindowManager; import android.webkit.WebSettings; import android.webkit.WebView; import android.widget.ImageView; import android.widget.Toast; @SuppressLint("DefaultLocale") public class TextSelectionSupport extends SplitPanel implements TextSelectionControlListener, OnTouchListener, OnLongClickListener, DragListener { public interface SelectionListener { void startSelection(); void selectionChanged(String text); void endSelection(); } private enum HandleType { START, END, UNKNOWN } private static final String TAG = "SelectionSupport"; private static final float CENTERING_SHORTER_MARGIN_RATIO = 12.0f / 48.0f; private static final int JACK_UP_PADDING = 2; private static final int SCROLLING_THRESHOLD = 10; private Activity mActivity; private WebView mWebView; private SelectionListener mSelectionListener; private DragLayer mSelectionDragLayer; private DragController mDragController; private ImageView mStartSelectionHandle; private ImageView mEndSelectionHandle; private Rect mSelectionBounds = null; private final Rect mSelectionBoundsTemp = new Rect(); private TextSelectionController mSelectionController = null; private int mContentWidth = 0; private HandleType mLastTouchedSelectionHandle = HandleType.UNKNOWN; private boolean mScrolling = false; private float mScrollDiffY = 0; private float mLastTouchY = 0; private float mScrollDiffX = 0; private float mLastTouchX = 0; private float mScale = 1.0f; private Runnable mStartSelectionModeHandler = new Runnable() { public void run() { if (mSelectionBounds != null) { mWebView.addView(mSelectionDragLayer); drawSelectionHandles(); final int contentHeight = (int) Math .ceil(getDensityDependentValue(mWebView.getContentHeight(), mActivity)); final int contentWidth = mWebView.getWidth(); ViewGroup.LayoutParams layerParams = mSelectionDragLayer.getLayoutParams(); layerParams.height = contentHeight; layerParams.width = Math.max(contentWidth, mContentWidth); mSelectionDragLayer.setLayoutParams(layerParams); if (mSelectionListener != null) { mSelectionListener.startSelection(); } } } }; private Runnable endSelectionModeHandler = new Runnable() { public void run() { mWebView.removeView(mSelectionDragLayer); mSelectionBounds = null; mLastTouchedSelectionHandle = HandleType.UNKNOWN; mWebView.loadUrl("javascript: android.selection.clearSelection();"); if (mSelectionListener != null) { mSelectionListener.endSelection(); } } }; private TextSelectionSupport(Activity activity, WebView webview) { mActivity = activity; mWebView = webview; } public static TextSelectionSupport support(Activity activity, WebView webview) { final TextSelectionSupport selectionSupport = new TextSelectionSupport(activity, webview); selectionSupport.setup(); return selectionSupport; } public void onScaleChanged(float oldScale, float newScale) { mScale = newScale; } public void setSelectionListener(SelectionListener listener) { mSelectionListener = listener; } // // Interfaces of TextSelectionControlListener // @Override public void jsError(String error) { Log.e(TAG, "JSError: " + error); } @Override public void jsLog(String message) { Log.d(TAG, "JSLog: " + message); } @Override public void startSelectionMode() { mActivity.runOnUiThread(mStartSelectionModeHandler); } @Override public void endSelectionMode() { mActivity.runOnUiThread(endSelectionModeHandler); } @Override public void setContentWidth(float contentWidth) { mContentWidth = (int) getDensityDependentValue(contentWidth, mActivity); } @Override public void selectionChanged(String range, String text, String handleBounds, boolean isReallyChanged) { final Context ctx = mActivity; try { final JSONObject selectionBoundsObject = new JSONObject(handleBounds); final float scale = getDensityIndependentValue(mScale, ctx); Rect rect = mSelectionBoundsTemp; rect.left = (int) (getDensityDependentValue(selectionBoundsObject.getInt("left"), ctx) * scale); rect.top = (int) (getDensityDependentValue(selectionBoundsObject.getInt("top"), ctx) * scale); rect.right = (int) (getDensityDependentValue(selectionBoundsObject.getInt("right"), ctx) * scale); rect.bottom = (int) (getDensityDependentValue(selectionBoundsObject.getInt("bottom"), ctx) * scale); mSelectionBounds = rect; if (!isInSelectionMode()) { startSelectionMode(); } drawSelectionHandles(); if (mSelectionListener != null && isReallyChanged) { mSelectionListener.selectionChanged(text); } } catch (JSONException e) { e.printStackTrace(); } } // // Interface of OnTouchListener // @Override public boolean onTouch(View v, MotionEvent event) { final Context ctx = mActivity; float xPoint = getDensityIndependentValue(event.getX(), ctx) / getDensityIndependentValue(mScale, ctx); float yPoint = getDensityIndependentValue(event.getY(), ctx) / getDensityIndependentValue(mScale, ctx); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: final String startTouchUrl = String.format("javascript:android.selection.startTouch(%f, %f);", xPoint, yPoint); mLastTouchX = xPoint; mLastTouchY = yPoint; mWebView.loadUrl(startTouchUrl); break; case MotionEvent.ACTION_UP: if (!mScrolling) { endSelectionMode(); // // Fixes 4.4 double selection // See: // http://stackoverflow.com/questions/20391783/how-to-avoid-default-selection-on-long-press-in-android-kitkat-4-4 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return false; } } mScrollDiffX = 0; mScrollDiffY = 0; mScrolling = false; // // Fixes 4.4 double selection // See: // http://stackoverflow.com/questions/20391783/how-to-avoid-default-selection-on-long-press-in-android-kitkat-4-4 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return true; } break; case MotionEvent.ACTION_MOVE: mScrollDiffX += (xPoint - mLastTouchX); mScrollDiffY += (yPoint - mLastTouchY); mLastTouchX = xPoint; mLastTouchY = yPoint; if (Math.abs(mScrollDiffX) > SCROLLING_THRESHOLD || Math.abs(mScrollDiffY) > SCROLLING_THRESHOLD) { mScrolling = true; } break; } return false; } // // Interface of OnLongClickListener // @Override public boolean onLongClick(View v) { if (!isInSelectionMode()) { mWebView.loadUrl("javascript:android.selection.longTouch();"); mScrolling = true; } Message msg = new Message(); msg.setTarget(new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); String url = msg.getData().getString("url"); if (url != null) navigator.setNote(url, index); } }); mWebView.requestFocusNodeHref(msg); return true; } // // Interface of DragListener // @Override public void onDragStart(DragSource source, Object info, DragBehavior dragBehavior) { } @Override public void onDragEnd() { mActivity.runOnUiThread(new Runnable() { @Override public void run() { MyAbsoluteLayout.LayoutParams startHandleParams = (MyAbsoluteLayout.LayoutParams) mStartSelectionHandle .getLayoutParams(); MyAbsoluteLayout.LayoutParams endHandleParams = (MyAbsoluteLayout.LayoutParams) mEndSelectionHandle .getLayoutParams(); final Context ctx = mActivity; final float scale = getDensityIndependentValue(mScale, ctx); float startX = startHandleParams.x - mWebView.getScrollX() + mStartSelectionHandle.getWidth() * (1 - CENTERING_SHORTER_MARGIN_RATIO); float startY = startHandleParams.y - mWebView.getScrollY() - JACK_UP_PADDING; float endX = endHandleParams.x - mWebView.getScrollX() + mEndSelectionHandle.getWidth() * CENTERING_SHORTER_MARGIN_RATIO; float endY = endHandleParams.y - mWebView.getScrollY() - JACK_UP_PADDING; startX = getDensityIndependentValue(startX, ctx) / scale; startY = getDensityIndependentValue(startY, ctx) / scale; endX = getDensityIndependentValue(endX, ctx) / scale; endY = getDensityIndependentValue(endY, ctx) / scale; if (mLastTouchedSelectionHandle == HandleType.START && startX > 0 && startY > 0) { String saveStartString = String.format("javascript: android.selection.setStartPos(%f, %f);", startX, startY); mWebView.loadUrl(saveStartString); } else if (mLastTouchedSelectionHandle == HandleType.END && endX > 0 && endY > 0) { String saveEndString = String.format("javascript: android.selection.setEndPos(%f, %f);", endX, endY); mWebView.loadUrl(saveEndString); } else { mWebView.loadUrl("javascript: android.selection.restoreStartEndPos();"); } } }); } @SuppressLint("SetJavaScriptEnabled") private void setup() { mScale = mActivity.getResources().getDisplayMetrics().density; mWebView.setOnLongClickListener(this); mWebView.setOnTouchListener(this); mSelectionController = new TextSelectionController(this); mWebView.addJavascriptInterface(mSelectionController, TextSelectionController.INTERFACE_NAME); createSelectionLayer(mActivity); } private void createSelectionLayer(Context context) { final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mSelectionDragLayer = (DragLayer) inflater.inflate(R.layout.selection_drag_layer, null); mDragController = new DragController(context); mDragController.setDragListener(this); mDragController.addDropTarget(mSelectionDragLayer); mSelectionDragLayer.setDragController(mDragController); mStartSelectionHandle = (ImageView) mSelectionDragLayer.findViewById(R.id.startHandle); mStartSelectionHandle.setTag(HandleType.START); mEndSelectionHandle = (ImageView) mSelectionDragLayer.findViewById(R.id.endHandle); mEndSelectionHandle.setTag(HandleType.END); final OnTouchListener handleTouchListener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { boolean handledHere = false; if (event.getAction() == MotionEvent.ACTION_DOWN) { handledHere = startDrag(v); mLastTouchedSelectionHandle = (HandleType) v.getTag(); } return handledHere; } }; mStartSelectionHandle.setOnTouchListener(handleTouchListener); mEndSelectionHandle.setOnTouchListener(handleTouchListener); } private void drawSelectionHandles() { mActivity.runOnUiThread(drawSelectionHandlesHandler); } private Runnable drawSelectionHandlesHandler = new Runnable() { public void run() { MyAbsoluteLayout.LayoutParams startParams = (com.blahti.drag.MyAbsoluteLayout.LayoutParams) mStartSelectionHandle .getLayoutParams(); final int startWidth = mStartSelectionHandle.getDrawable().getIntrinsicWidth(); startParams.x = (int) (mSelectionBounds.left - startWidth * (1.0f - CENTERING_SHORTER_MARGIN_RATIO)); startParams.y = (int) (mSelectionBounds.top); final int startMinLeft = -(int) (startWidth * (1 - CENTERING_SHORTER_MARGIN_RATIO)); startParams.x = (startParams.x < startMinLeft) ? startMinLeft : startParams.x; startParams.y = (startParams.y < 0) ? 0 : startParams.y; mStartSelectionHandle.setLayoutParams(startParams); MyAbsoluteLayout.LayoutParams endParams = (com.blahti.drag.MyAbsoluteLayout.LayoutParams) mEndSelectionHandle .getLayoutParams(); final int endWidth = mEndSelectionHandle.getDrawable().getIntrinsicWidth(); endParams.x = (int) (mSelectionBounds.right - endWidth * CENTERING_SHORTER_MARGIN_RATIO); endParams.y = (int) (mSelectionBounds.bottom); final int endMinLeft = -(int) (endWidth * (1 - CENTERING_SHORTER_MARGIN_RATIO)); endParams.x = (endParams.x < endMinLeft) ? endMinLeft : endParams.x; endParams.y = (endParams.y < 0) ? 0 : endParams.y; mEndSelectionHandle.setLayoutParams(endParams); } }; private boolean isInSelectionMode() { return this.mSelectionDragLayer.getParent() != null; } private boolean startDrag(View v) { Object dragInfo = v; mDragController.startDrag(v, mSelectionDragLayer, dragInfo, DragBehavior.MOVE); return true; } private float getDensityDependentValue(float val, Context ctx) { Display display = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); return val * (metrics.densityDpi / 160f); } private float getDensityIndependentValue(float val, Context ctx) { Display display = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); return val / (metrics.densityDpi / 160f); } }