|
38 | 38 | import android.print.PrintManager; |
39 | 39 | import android.text.Editable; |
40 | 40 | import android.text.InputType; |
| 41 | +import android.text.Layout; |
41 | 42 | import android.text.Spanned; |
42 | 43 | import android.text.TextWatcher; |
43 | 44 | import android.text.style.BackgroundColorSpan; |
44 | 45 | import android.view.KeyEvent; |
| 46 | +import android.view.MotionEvent; |
45 | 47 | import android.view.Menu; |
46 | 48 | import android.view.MenuItem; |
47 | 49 | import android.view.WindowInsets; |
@@ -214,6 +216,7 @@ public void onCreate(Bundle savedInstanceState) { |
214 | 216 | } |
215 | 217 | mText = this.findViewById(R.id.editText1); |
216 | 218 | mText.setBackgroundResource(android.R.color.transparent); |
| 219 | + mText.setOnTouchListener(new TwoFingerPanTouchListener()); |
217 | 220 | if (!settingsService.isAutoWrapping()) { |
218 | 221 | disableEditorAutowrapping(); |
219 | 222 | } |
@@ -1960,6 +1963,105 @@ protected void showToast(String toast_str) { |
1960 | 1963 | toast.show(); |
1961 | 1964 | } |
1962 | 1965 |
|
| 1966 | + // TwoFingerPanTouchListener |
| 1967 | + private class TwoFingerPanTouchListener implements View.OnTouchListener { |
| 1968 | + |
| 1969 | + private boolean panningActive = false; |
| 1970 | + private float lastFocalX; |
| 1971 | + private float lastFocalY; |
| 1972 | + |
| 1973 | + @Override |
| 1974 | + public boolean onTouch(View view, MotionEvent event) { |
| 1975 | + switch (event.getActionMasked()) { |
| 1976 | + case MotionEvent.ACTION_DOWN: |
| 1977 | + panningActive = false; |
| 1978 | + return false; |
| 1979 | + case MotionEvent.ACTION_POINTER_DOWN: |
| 1980 | + if (event.getPointerCount() == 2) { |
| 1981 | + panningActive = true; |
| 1982 | + view.getParent().requestDisallowInterceptTouchEvent(true); |
| 1983 | + lastFocalX = focalX(event); |
| 1984 | + lastFocalY = focalY(event); |
| 1985 | + } |
| 1986 | + return panningActive; |
| 1987 | + case MotionEvent.ACTION_MOVE: |
| 1988 | + if (!panningActive) { |
| 1989 | + return false; |
| 1990 | + } |
| 1991 | + float focalX = focalX(event); |
| 1992 | + float focalY = focalY(event); |
| 1993 | + panContent( |
| 1994 | + Math.round(focalX - lastFocalX), |
| 1995 | + Math.round(focalY - lastFocalY) |
| 1996 | + ); |
| 1997 | + lastFocalX = focalX; |
| 1998 | + lastFocalY = focalY; |
| 1999 | + return true; |
| 2000 | + case MotionEvent.ACTION_POINTER_UP: |
| 2001 | + if (panningActive) { |
| 2002 | + view.getParent().requestDisallowInterceptTouchEvent(false); |
| 2003 | + } |
| 2004 | + return panningActive; |
| 2005 | + case MotionEvent.ACTION_UP: |
| 2006 | + case MotionEvent.ACTION_CANCEL: |
| 2007 | + boolean wasPanning = panningActive; |
| 2008 | + panningActive = false; |
| 2009 | + view.getParent().requestDisallowInterceptTouchEvent(false); |
| 2010 | + return wasPanning; |
| 2011 | + default: |
| 2012 | + return panningActive; |
| 2013 | + } |
| 2014 | + } |
| 2015 | + |
| 2016 | + private float focalX(MotionEvent event) { |
| 2017 | + float sum = 0; |
| 2018 | + for (int i = 0; i < event.getPointerCount(); i++) { |
| 2019 | + sum += event.getX(i); |
| 2020 | + } |
| 2021 | + return sum / event.getPointerCount(); |
| 2022 | + } |
| 2023 | + |
| 2024 | + private float focalY(MotionEvent event) { |
| 2025 | + float sum = 0; |
| 2026 | + for (int i = 0; i < event.getPointerCount(); i++) { |
| 2027 | + sum += event.getY(i); |
| 2028 | + } |
| 2029 | + return sum / event.getPointerCount(); |
| 2030 | + } |
| 2031 | + } |
| 2032 | + |
| 2033 | + private void panContent(int deltaX, int deltaY) { |
| 2034 | + Layout layout = mText.getLayout(); |
| 2035 | + int maxX = layout == null ? 0 : |
| 2036 | + layout.getWidth() + mText.getTotalPaddingLeft() + mText.getTotalPaddingRight() |
| 2037 | + - mText.getWidth(); |
| 2038 | + int newX = clampScroll(mText.getScrollX() - deltaX, maxX); |
| 2039 | + |
| 2040 | + if (simpleScrolling()) { |
| 2041 | + int maxY = layout == null ? 0 : |
| 2042 | + layout.getHeight() + mText.getTotalPaddingTop() + mText.getTotalPaddingBottom() |
| 2043 | + - mText.getHeight(); |
| 2044 | + int newY = clampScroll(mText.getScrollY() - deltaY, maxY); |
| 2045 | + mText.scrollTo(newX, newY); |
| 2046 | + return; |
| 2047 | + } |
| 2048 | + |
| 2049 | + if (scrollView != null) { |
| 2050 | + scrollView.scrollBy(0, -deltaY); |
| 2051 | + } |
| 2052 | + mText.scrollTo(newX, mText.getScrollY()); |
| 2053 | + } |
| 2054 | + |
| 2055 | + private int clampScroll(int value, int max) { |
| 2056 | + if (value < 0) { |
| 2057 | + return 0; |
| 2058 | + } |
| 2059 | + if (max > 0 && value > max) { |
| 2060 | + return max; |
| 2061 | + } |
| 2062 | + return value; |
| 2063 | + } |
| 2064 | + |
1963 | 2065 | // QueryTextListener |
1964 | 2066 | private class QueryTextListener |
1965 | 2067 | implements SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener { |
|
0 commit comments