Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions android/src/org/coolreader/crengine/BaseActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -1323,6 +1323,8 @@ public void applyAppSetting(String key, String value) {
setScreenBacklightDuration(Utils.parseInt(value, 0));
} else if (key.equals(PROP_NIGHT_MODE)) {
setNightMode(flg);
} else if (key.equals(PROP_APP_PAGEWISE_SCROLL)) {
DeviceInfo.PAGEWISE_SCROLLING = flg;
} else if (key.equals(PROP_APP_SCREEN_UPDATE_MODE)) {
setScreenUpdateMode(EinkScreen.EinkUpdateMode.byCode(Utils.parseInt(value, 0)), getContentView());
} else if (key.equals(PROP_APP_SCREEN_UPDATE_INTERVAL)) {
Expand Down Expand Up @@ -1957,6 +1959,7 @@ public Properties loadSettings(BaseActivity activity, File file) {
props.applyDefault(ReaderView.PROP_APP_SHOW_COVERPAGES, "1");
props.applyDefault(ReaderView.PROP_APP_COVERPAGE_SIZE, "1");
props.applyDefault(ReaderView.PROP_APP_SCREEN_ORIENTATION, "0"); // "0"
props.applyDefault(ReaderView.PROP_APP_PAGEWISE_SCROLL, "0");
props.applyDefault(ReaderView.PROP_CONTROLS_ENABLE_VOLUME_KEYS, "1");
props.applyDefault(ReaderView.PROP_APP_TAP_ZONE_HILIGHT, "0");
props.applyDefault(ReaderView.PROP_APP_BOOK_SORT_ORDER, FileInfo.DEF_SORT_ORDER.name());
Expand Down
171 changes: 143 additions & 28 deletions android/src/org/coolreader/crengine/BaseListView.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,93 @@

import android.content.Context;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;

public class BaseListView extends ListView {
public BaseListView(Context context, boolean fastScrollEnabled) {
super(context);

//The Values were originally tested on a 440dpi screen, hence the conversion factor in the constructor
private float SWIPE_THRESHOLD_PX = 100f;

private float NO_MOVE_THRESHOLD_PX = 5f;

private float touchStartY = 0f;


public BaseListView(Context context, boolean fastScrollEnabled) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
setFastScrollEnabled(fastScrollEnabled);
}
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
//The Values were originally tested on a 440dpi screen, hence the conversion factor
final float dpiDependentFactor = displayMetrics.ydpi / 440;
SWIPE_THRESHOLD_PX = Math.round(dpiDependentFactor * SWIPE_THRESHOLD_PX);
NO_MOVE_THRESHOLD_PX = Math.round(dpiDependentFactor * NO_MOVE_THRESHOLD_PX);
}

/** This method is designed to allow the ListView to scroll page-wise.
* FileBrowser.MyGestureListener or any other listener should not be impaired by this.
* */

@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean returnValue = false;

if (DeviceInfo.PAGEWISE_SCROLLING) {
switch (ev.getAction()) {
//Start registering the events.
//Allow ACTION_DOWN to be passed normally to the ListView
//to keep the click/longclick functionality
case MotionEvent.ACTION_DOWN:
touchStartY = ev.getY();
returnValue = super.onTouchEvent(ev);
break;

case MotionEvent.ACTION_MOVE:
/**
* Interrupt normal scrolling, when NO_MOVE_THRESHOLD_PX is reached.
* Once the movement starts, claim the following events.
* NO_MOVE_THRESHOLD_PX is there to allow for some little movement when
* performing a click.
*/

//TODO: NO_MOVE_THRESHOLD_PX may need some testing and fine tuning
returnValue = true;
if (Math.abs(touchStartY - ev.getY()) >= NO_MOVE_THRESHOLD_PX) {
ev.setAction(MotionEvent.ACTION_CANCEL);
}

super.onTouchEvent(ev);
break;

case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
/**
* Scroll page-wise when SWIPE_THRESHOLD_PX is reached.
*/
//TODO maybe tweak SWIPE_THRESHOLD_PX too, current values are rather experimental
super.onTouchEvent(ev);

if (Math.abs(touchStartY - ev.getY()) >= SWIPE_THRESHOLD_PX) { //delta is positive = swiped up (next page)
if (touchStartY - ev.getY() > 0) {
scrollPage(1);
} else {
scrollPage(-1);
}
returnValue = true;
}
break;
}
} else {
returnValue = super.onTouchEvent(ev);
}
return returnValue;
}


@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Expand All @@ -46,36 +122,75 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
else if (keyCode == ReaderView.SONY_DPAD_LEFT_SCANCODE || keyCode == ReaderView.SONY_DPAD_UP_SCANCODE || keyCode==KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_LEFT )
dir = -1;
//} else {
else if (keyCode == KeyEvent.KEYCODE_8 || keyCode == ReaderView.NOOK_KEY_NEXT_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == ReaderView.NOOK_KEY_SHIFT_DOWN)
else if (keyCode == KeyEvent.KEYCODE_8 || keyCode == ReaderView.NOOK_KEY_NEXT_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == ReaderView.NOOK_KEY_SHIFT_DOWN || keyCode == KeyEvent.KEYCODE_PAGE_DOWN)
dir = 1;
else if (keyCode == KeyEvent.KEYCODE_2 || keyCode == ReaderView.NOOK_KEY_PREV_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == ReaderView.NOOK_KEY_SHIFT_UP)
else if (keyCode == KeyEvent.KEYCODE_2 || keyCode == ReaderView.NOOK_KEY_PREV_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == ReaderView.NOOK_KEY_SHIFT_UP || keyCode == KeyEvent.KEYCODE_PAGE_UP)
dir = -1;
//}
if (dir != 0) {
int firstPos = getFirstVisiblePosition();
int lastPos = getLastVisiblePosition();
int count = getCount();

int delta = 1;
if( dir < 0 ) {
View v = getChildAt(0);
if( v != null ) {
int fh = v.getHeight();
Rect r = new Rect(0,0,v.getWidth(),fh);
getChildVisibleRect(v, r, null);
delta = (r.height() < fh) ? 1 : 0;
}
}

int nextPos = ( dir > 0 ) ? Math.min(lastPos + 1, count - 1) : Math.max(0, firstPos - (lastPos - firstPos) + delta);

// Log.w("CoolReader", "first =" + firstPos + " last = " + lastPos + " next = " + nextPos + " count = " + count);

setSelection(nextPos);
clearFocus();

if (dir != 0) {
scrollPage(dir);
return true;
}
return super.onKeyDown(keyCode, event);
}

/** Scroll the ListView page-wise.
* Logic is:
* 1. Scrolling down - the last (bottom) not fully visible view should be the first
* fully displayed view on the next page.
* 2. Scrolling up - the first (top) not fully visible view should be the last
* fully displayed view on the previous page
* */
private void scrollPage(int dir) {
int firstPos = getFirstVisiblePosition();
int lastPos = getLastVisiblePosition();
int count = getCount();

int delta = 0;
int newTopItem = 0;
if( dir < 0 ) {
//Check if the currently displayed top childview is fully visible or incomplete
View v = getChildAt(0);
if( v != null ) {
int fh = v.getHeight();
Rect r = new Rect(0,0,v.getWidth(),fh);
getChildVisibleRect(v, r, null);
delta = (r.height() < fh) ? 1 : 0;
}

//Scrolling up, account for when the items can have different heights
// (e.g. by using a larger font, breaking into more lines, etc.)
newTopItem = Math.max(0, firstPos - 1 + delta);
Rect visibleListViewRect = new Rect();
this.getGlobalVisibleRect(visibleListViewRect);
int visibleHeight = visibleListViewRect.height();
int usableHeight = 0;
for (int i = newTopItem; i >= 0; i--) {
//getchildAt() returns null if the item is not visible.
//Ask adapter directly to get the children parameters
View view = getAdapter().getView(i, null, this);
/**
* Because normally ListViews are measured lazy, view.getMeasuredHeight() can return 0
* when the view hasn't gone through a layout pass yet.
* Inflate and measure each item view, forcing a layout pass.
* MeasureSpec.UNSPECIFIED for height lets the view report its natural height,
* and MeasureSpec.EXACTLY for width constrains it to the real ListView width
* so it wraps correctly.
*/
view.measure( View.MeasureSpec.makeMeasureSpec(this.getWidth(), MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
usableHeight += view.getMeasuredHeight() + getDividerHeight();
if (usableHeight > visibleHeight) {
//use last view that fits fully into the next page as top view
break;
}
newTopItem = i;
}
}

int nextPos = ( dir > 0 ) ? Math.min(lastPos, count - 1) : Math.max(0, newTopItem) ;

setSelection(nextPos);
clearFocus();
}
}
2 changes: 2 additions & 0 deletions android/src/org/coolreader/crengine/DeviceInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public class DeviceInfo {
public final static Integer DEF_FONT_SIZE;
public final static boolean ONE_COLUMN_IN_LANDSCAPE;

public static boolean PAGEWISE_SCROLLING = false;

// minimal screen backlight level percent for different devices
private static final String[] MIN_SCREEN_BRIGHTNESS_DB = {
"LGE;LG-P500", "6", // LG Optimus One
Expand Down
4 changes: 4 additions & 0 deletions android/src/org/coolreader/crengine/OptionsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -2905,6 +2905,10 @@ private void setupReaderOptions()
mBounceProtectionOption = new ListOption(this, getString(R.string.options_controls_bonce_protection), PROP_APP_BOUNCE_TAP_INTERVAL).add(mBounceProtectionValues, mBounceProtectionTitles).setDefaultValue(String.valueOf(150));
mOptionsControls.add(mBounceProtectionOption);
doubleTapOnChange.run();
Runnable changehandlerPagewiseScroll = () -> {
DeviceInfo.PAGEWISE_SCROLLING = mProperties.getBool(PROP_APP_PAGEWISE_SCROLL, false);
};
mOptionsControls.add(new BoolOption(this, "Page-wise scroll", PROP_APP_PAGEWISE_SCROLL).setDefaultValue("0").setOnChangeHandler(changehandlerPagewiseScroll));
if ( !DeviceInfo.EINK_SCREEN )
mOptionsControls.add(new BoolOption(this, getString(R.string.options_controls_enable_volume_keys), PROP_CONTROLS_ENABLE_VOLUME_KEYS).setDefaultValue("1"));
mOptionsControls.add(new BoolOption(this, getString(R.string.options_app_tapzone_hilite), PROP_APP_TAP_ZONE_HILIGHT).setDefaultValue("0").setIconIdByAttr(R.attr.cr3_option_touch_drawable, R.drawable.cr3_option_touch));
Expand Down
4 changes: 4 additions & 0 deletions android/src/org/coolreader/crengine/ReaderView.java
Original file line number Diff line number Diff line change
Expand Up @@ -2874,6 +2874,7 @@ public boolean showManual() {
}

private boolean hiliteTapZoneOnTap = false;
private boolean enablePagewiseScroll = false;
private boolean enableVolumeKeys = true;
static private final int DEF_PAGE_FLIP_MS = 300;

Expand Down Expand Up @@ -2907,6 +2908,8 @@ public void applyAppSetting(String key, String value) {
pageFlipAnimationSpeedMs = pageFlipAnimationMode != PAGE_ANIMATION_NONE ? DEF_PAGE_FLIP_MS : 0;
} else if (PROP_CONTROLS_ENABLE_VOLUME_KEYS.equals(key)) {
enableVolumeKeys = flg;
} else if (PROP_APP_PAGEWISE_SCROLL.equals(key)) {
enablePagewiseScroll = flg;
} else if (PROP_APP_SELECTION_ACTION.equals(key)) {
mSelectionAction = Utils.parseInt(value, SELECTION_ACTION_TOOLBAR);
} else if (PROP_APP_MULTI_SELECTION_ACTION.equals(key)) {
Expand Down Expand Up @@ -2948,6 +2951,7 @@ public void setAppSettings(Properties newSettings, Properties oldSettings) {
|| PROP_APP_SCREEN_BACKLIGHT_LOCK.equals(key)
|| PROP_APP_TAP_ZONE_HILIGHT.equals(key)
|| PROP_APP_DICTIONARY.equals(key)
|| PROP_APP_PAGEWISE_SCROLL.equals(key)
|| PROP_APP_DOUBLE_TAP_SELECTION.equals(key)
|| PROP_APP_BOUNCE_TAP_INTERVAL.equals(key)
|| PROP_APP_FLICK_BACKLIGHT_CONTROL.equals(key)
Expand Down
1 change: 1 addition & 0 deletions android/src/org/coolreader/crengine/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public interface Settings {
public static final String PROP_APP_SCREEN_BACKLIGHT_DAY ="app.screen.backlight.day";
public static final String PROP_APP_SCREEN_BACKLIGHT_NIGHT ="app.screen.backlight.night";
public static final String PROP_APP_DOUBLE_TAP_SELECTION ="app.controls.doubletap.selection";
public static final String PROP_APP_PAGEWISE_SCROLL = "app.controls.pagewise.scroll";
public static final String PROP_APP_BOUNCE_TAP_INTERVAL ="app.controls.bounce.interval";
public static final String PROP_APP_TAP_ZONE_ACTIONS_TAP ="app.tapzone.action.tap";
public static final String PROP_APP_KEY_ACTIONS_PRESS ="app.key.action.press";
Expand Down