Skip to content

Commit 26c2928

Browse files
committed
Progress point -- Trying to convince myself that the logic to balance this out as planned is deterministic (it seems not)
1 parent 76e8d44 commit 26c2928

5 files changed

Lines changed: 133 additions & 28 deletions

File tree

AndroidFilePickerLightLibrary/src/main/java/com/maxieds/androidfilepickerlightlibrary/CustomThemeBuilder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ This program (the AndroidFilePickerLight library) is free software written by
2121

2222
public class CustomThemeBuilder {
2323

24+
/*
25+
* TODO: Note that all of this should be more eaily approached by simply making all
26+
* of these theme-specific stylized attributes accessible in the default themes
27+
* in /res/values/style.xml. Then the user can simply implement all of those
28+
* attributes in their own custom theme, pass a resolved instance of that theme
29+
* into the File Chooser Builder along with an Activity reference from which
30+
* the resources can be resolved, and voila, no need for kludgy settings like
31+
* diagrammed below :)
32+
*/
33+
2434
private static final String LOGTAG = CustomThemeBuilder.class.getSimpleName();
2535

2636
public static CustomThemeBuilder getDefaultsInstance() {

AndroidFilePickerLightLibrary/src/main/java/com/maxieds/androidfilepickerlightlibrary/DisplayAdapters.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ public void onBindViewHolder(BaseViewHolder bvHolder, int posIndex) {
9494
View viewItemContainer = bvHolder.getMainViewLayoutContainer();
9595
DisplayFragments.FileListItemFragment.resetLayout(viewItemContainer, fileItem, posIndex);
9696
}
97-
Log.i(LOGTAG, String.format(Locale.getDefault(), "onBindViewHolder @ %d -- (ADAPTER -> %s) [DATA ITEMS SIZE = %d]", posIndex,
98-
fileListData.get(posIndex), fileItemsData.size()));
97+
//Log.i(LOGTAG, String.format(Locale.getDefault(), "onBindViewHolder @ %d -- (ADAPTER -> %s) [DATA ITEMS SIZE = %d]", posIndex,
98+
// fileListData.get(posIndex), fileItemsData.size()));
9999
}
100100

101101
@Override

AndroidFilePickerLightLibrary/src/main/java/com/maxieds/androidfilepickerlightlibrary/FileChooserRecyclerView.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ public boolean fling(int velocityX, int velocityY) {
9696
return false;*/
9797
}
9898

99+
@Override
100+
public void smoothScrollToPosition(int indexPos) {
101+
getLayoutManager().smoothScrollToPosition(this, new RecyclerView.State(), indexPos);
102+
}
103+
99104
public interface RecyclerViewSlidingContextWindow {
100105

101106
void setWeightBufferSize(int size);
@@ -118,10 +123,6 @@ public static class LayoutManager extends LinearLayoutManager {
118123
private static LayoutManager localStaticInst = null;
119124
public static LayoutManager getInstance() { return localStaticInst; }
120125

121-
private int nextPositionOffsetDiff;
122-
public int getNextPositionOffset() { return nextPositionOffsetDiff; }
123-
public void setNextPositionOffset(int newOffset) { nextPositionOffsetDiff = newOffset; }
124-
125126
public LayoutManager(Context layoutCtx) {
126127
super(layoutCtx);
127128
setOrientation(LinearLayoutManager.VERTICAL);
@@ -130,19 +131,27 @@ public LayoutManager(Context layoutCtx) {
130131
setStackFromEnd(true); // ???
131132
setSmoothScrollbarEnabled(true);
132133
localStaticInst = this;
133-
nextPositionOffsetDiff = 1;
134+
intendedNextScrollPos = 0;
135+
linearSmoothScroller = null;
136+
scrollInvokingRV = null;
134137
}
135138

136139
@Override
137140
public boolean isAutoMeasureEnabled() {
138141
return true;
139142
}
140143

141-
/*
142-
public static final float SCROLLER_MILLISECONDS_PER_INCH = 62.0f; // larger values slow it down
144+
// intent is to speed it up for the prefetch thread smooth scrolling:
145+
public static final float SCROLLER_MILLISECONDS_PER_INCH = 16.0f; // larger values slow it down, 25.0 ~ default behavior
146+
147+
private int intendedNextScrollPos;
148+
private LinearSmoothScroller linearSmoothScroller;
149+
private RecyclerView scrollInvokingRV;
150+
143151
@Override
144152
public void smoothScrollToPosition(RecyclerView recyclerView, State state, int position) {
145-
final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
153+
scrollInvokingRV = recyclerView;
154+
linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
146155
@Override
147156
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
148157
return SCROLLER_MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
@@ -151,7 +160,19 @@ protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
151160
linearSmoothScroller.setTargetPosition(position);
152161
startSmoothScroll(linearSmoothScroller);
153162
}
154-
*/
163+
164+
public boolean completeLastSmoothScroll() {
165+
if(linearSmoothScroller == null || intendedNextScrollPos < 0 ||
166+
intendedNextScrollPos >= getItemCount() || scrollInvokingRV == null) {
167+
return false;
168+
}
169+
else if(!linearSmoothScroller.isRunning()) {
170+
return false;
171+
}
172+
scrollInvokingRV.stopScroll();
173+
scrollInvokingRV.scrollToPosition(intendedNextScrollPos);
174+
return true;
175+
}
155176

156177
}
157178

AndroidFilePickerLightLibrary/src/main/java/com/maxieds/androidfilepickerlightlibrary/PrefetchFilesUpdater.java

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ This program (the AndroidFilePickerLight library) is free software written by
6363
* and post notifications to the adapter. The scroller (and its listener) know what to do by default when it can just
6464
* smooth scroll through a linear layout with items already available. What results is a nothing too fancy interface to
6565
* avoid the messy UI intensive work in the scroll handler.
66+
*
6667
*/
6768
public class PrefetchFilesUpdater extends Thread implements FileChooserRecyclerView.RecyclerViewSlidingContextWindow {
6869

@@ -118,7 +119,10 @@ public UpdateDataStruct(UpdateDataType dataType, List<String> fileNames, List<Di
118119

119120
public PrefetchFilesUpdater() {
120121

121-
// Set a sane default with some scroll buffer space: My testing Android phone comfortably fits 12-15 layout items.
122+
// Set a sane default with some scroll buffer space:
123+
// My testing Android phone comfortably fits 12-15 layout items.
124+
// Let's buffer in 25 by default to not have to restruct the scroller
125+
// and default fling velocities too much:
122126
BalancedBufferSize = 25;
123127
isInit = false;
124128
topBufferSize = bottomBufferSize = 0;
@@ -157,10 +161,7 @@ public PrefetchFilesAsyncTask() {
157161
itemsAppendedCount = itemsPrunedCount = 0;
158162
}
159163

160-
protected Long doInBackground(Void... unusedArgsList) {
161-
162-
// Figure out which edge (if either) needs to get balanced first:
163-
//boolean balanceBottomFirst = (balanceBottomCount >= balanceTopCount) || (balanceTopCount <= 0);
164+
private void loadInDataAtBottomEdge() {
164165
if(balanceBottomCount > 0) { // Need to append to the bottom, trim the extra buffered layout up top:
165166

166167
int itemsCountToAppend = balanceBottomCount;
@@ -194,7 +195,10 @@ protected Long doInBackground(Void... unusedArgsList) {
194195
itemsAppendedCount = itemsCountToAppend;
195196

196197
}
197-
else if(balanceTopCount > 0) { // Prepend at top, trim from bottom:
198+
}
199+
200+
private void loadInDataAtTopEdge() {
201+
if(balanceTopCount > 0) { // Prepend at top, trim from bottom:
198202

199203
int itemsCountToAppend = balanceTopCount;
200204
int bottomItemsToPrune = 0;
@@ -227,12 +231,56 @@ else if(balanceTopCount > 0) { // Prepend at top, trim from bottom:
227231
itemsAppendedCount = itemsCountToAppend;
228232

229233
}
234+
}
235+
236+
protected Long doInBackground(Void... unusedArgsList) {
237+
238+
// Figure out which edge (if either) needs to get balanced first:
239+
// Basically the procedure is:
240+
// At the start, favor loading in new buffer items from the bottom.
241+
// Once things get going, we want to maintain at least semi-comparable
242+
// buffering on each side. So handle the strange corner situations where:
243+
// IF: BOT wants balance > 0, but if
244+
// TopBufferSize < BAL_BUFFER_SIZE and FirstVisible - TopBufferSize > BottomToBalance,
245+
// THEN: Offer up the loading time to pad the TOP buffer instead.
246+
// OW: If TOP and BOT both want balance > 0, defer to the edge that requires a larger
247+
// balance.
248+
// OW: If only one wants balance > 0, give that one the loading time.
249+
// OW: Break ties by loading at the BOT edge first and/or by loading where there is a smaller
250+
// buffer size currently instated.
251+
if(balanceTopCount > 0 && balanceBottomCount > 0) {
252+
if(balanceTopCount > balanceBottomCount) {
253+
loadInDataAtTopEdge();
254+
}
255+
else {
256+
loadInDataAtBottomEdge();
257+
}
258+
}
259+
else if(balanceTopCount > 0) {
260+
loadInDataAtTopEdge();
261+
}
262+
else if(topBufferSize < BalancedBufferSize && firstVisibleIndex >= topBufferSize &&
263+
firstVisibleIndex - topBufferSize > balanceBottomCount &&
264+
firstVisibleIndex - topBufferSize - balanceBottomCount < displayCtx.lastFileDataStartIndex) {
265+
balanceTopCount = firstVisibleIndex - topBufferSize - balanceBottomCount;
266+
loadInDataAtTopEdge();
267+
}
268+
else if(balanceBottomCount > 0) {
269+
loadInDataAtBottomEdge();
270+
}
271+
230272
return Long.valueOf(0);
231273

232274
}
233275

234276
protected void onPreExecute() {
235277

278+
// Otherwise, weird behaviors arise (make sure the last balance got things
279+
// where they were supposed to be headed):
280+
FileChooserRecyclerView mainRV = displayCtx.getMainRecyclerView();
281+
FileChooserRecyclerView.LayoutManager rvLayoutManager = (FileChooserRecyclerView.LayoutManager) mainRV.getLayoutManager();
282+
rvLayoutManager.completeLastSmoothScroll();
283+
236284
balanceBottomCount = getActiveCountToBalanceBottom();
237285
balanceTopCount = getActiveCountToBalanceTop();
238286
firstVisibleIndex = getLayoutFirstVisibleItemIndex();
@@ -314,11 +362,23 @@ else if(updateDataType.equals(UpdateDataStruct.UpdateDataType.PREPEND_DATA_AT_TO
314362
mainRV.smoothScrollToPosition(prevFirstVisibleIndex);
315363

316364
}
365+
else {
366+
return;
367+
}
368+
// We have smooth scrolled. Let it have some brief time to behave itself, otherwise
369+
// kick it along down it's merry way:
370+
final FileChooserRecyclerView.LayoutManager rvLayoutManager = (FileChooserRecyclerView.LayoutManager) mainRV.getLayoutManager();
371+
mainRV.postDelayed(new Runnable() {
372+
@Override
373+
public void run() {
374+
rvLayoutManager.completeLastSmoothScroll();
375+
}
376+
}, 200);
317377
}
318378

319379
}
320380

321-
private static final long THREAD_INIT_PAUSE_TIMEOUT = 50; // Milliseconds
381+
private static final long THREAD_INIT_PAUSE_TIMEOUT = 125; // Milliseconds
322382
private static final long THREAD_PAUSE_TIMEOUT = 500; //250; // Milliseconds
323383

324384
@Override
@@ -398,14 +458,28 @@ public int getActiveCountToBalanceTop() {
398458
if(getActiveLayoutItemsCount() == 0 || getLayoutVisibleDisplaySize() >= getActiveFolderContentsSize()) {
399459
return 0;
400460
}
461+
// Handle competing cases that arise when inflating from the bottom first:
462+
int balanceChoiceCount = 0;
463+
int[] prospectiveBalanceSizes = new int[] { 0, 0 };
464+
if(topBufferSize < Math.min(Math.max(0, getActiveLayoutItemsCount() - getLayoutVisibleDisplaySize() - bottomBufferSize), BalancedBufferSize)) {
465+
// Handles the initialization case (loading the buffer the first time):
466+
prospectiveBalanceSizes[balanceChoiceCount++] =
467+
Math.min(Math.max(0, getActiveLayoutItemsCount() - getLayoutVisibleDisplaySize() - bottomBufferSize), BalancedBufferSize) - topBufferSize;
468+
}
401469
int firstVisibleItemIndex = getLayoutFirstVisibleItemIndex();
402470
int remainingFrontSpace = Math.min(getTopBufferPosition(), BalancedBufferSize);
403-
if(remainingFrontSpace > 0 && firstVisibleItemIndex >= getTopBufferPosition() && firstVisibleItemIndex - getTopBufferPosition() <= remainingFrontSpace) {
404-
return remainingFrontSpace - firstVisibleItemIndex + getTopBufferPosition();
471+
if(firstVisibleItemIndex >= getTopBufferPosition() && firstVisibleItemIndex - getTopBufferPosition() < remainingFrontSpace) {
472+
prospectiveBalanceSizes[balanceChoiceCount++] = remainingFrontSpace - 1 - firstVisibleItemIndex + getTopBufferPosition();
405473
}
406-
else {
474+
if(balanceChoiceCount == 0) {
407475
return 0;
408476
}
477+
else if(balanceChoiceCount == 1) {
478+
return prospectiveBalanceSizes[0];
479+
}
480+
else {
481+
return Math.max(prospectiveBalanceSizes[0], prospectiveBalanceSizes[1]);
482+
}
409483
}
410484

411485
public int getTopBufferPosition() {
@@ -418,15 +492,15 @@ public int getActiveCountToBalanceBottom() {
418492
// If layout not initialized, or fits into single window, do not grow the padding buffer:
419493
return 0;
420494
}
421-
if(bottomBufferSize < Math.min(Math.max(0, getActiveFolderContentsSize() - getLayoutVisibleDisplaySize()), BalancedBufferSize)) {
495+
else if(bottomBufferSize < Math.min(Math.max(0, getActiveFolderContentsSize() - getLayoutVisibleDisplaySize() - topBufferSize), BalancedBufferSize)) {
422496
// Handles the initialization case (loading the buffer the first time):
423-
return Math.min(Math.max(0, getActiveFolderContentsSize() - getLayoutVisibleDisplaySize()), BalancedBufferSize) - bottomBufferSize;
497+
return Math.min(Math.max(0, getActiveFolderContentsSize() - getLayoutVisibleDisplaySize() - topBufferSize), BalancedBufferSize) - bottomBufferSize;
424498
}
425499
int lastVisibleItemIndex = getLayoutLastVisibleItemIndex(); // avoiding a potential race condition when scrolling
426500
int itemsInFolderRemaining = Math.max(0, getActiveFolderContentsSize() - getBottomBufferPosition() - 1);
427501
if(lastVisibleItemIndex <= getBottomBufferPosition() &&
428-
getBottomBufferPosition() - lastVisibleItemIndex <= Math.min(itemsInFolderRemaining, BalancedBufferSize)) {
429-
return Math.min(itemsInFolderRemaining, BalancedBufferSize) - getBottomBufferPosition() + lastVisibleItemIndex;
502+
getBottomBufferPosition() - lastVisibleItemIndex < Math.min(itemsInFolderRemaining, BalancedBufferSize)) {
503+
return Math.min(itemsInFolderRemaining, BalancedBufferSize) - 1 - getBottomBufferPosition() + lastVisibleItemIndex;
430504
}
431505
else {
432506
return 0;
@@ -449,13 +523,13 @@ public int getLayoutVisibleDisplaySize() {
449523
public int getLayoutFirstVisibleItemIndex() {
450524
FileChooserRecyclerView mainRV = DisplayFragments.getInstance().getMainRecyclerView();
451525
FileChooserRecyclerView.LayoutManager rvLayoutManager = (FileChooserRecyclerView.LayoutManager) mainRV.getLayoutManager();
452-
return rvLayoutManager.findFirstVisibleItemPosition();
526+
return rvLayoutManager.findFirstCompletelyVisibleItemPosition();
453527
}
454528

455529
public int getLayoutLastVisibleItemIndex() {
456530
FileChooserRecyclerView mainRV = DisplayFragments.getInstance().getMainRecyclerView();
457531
FileChooserRecyclerView.LayoutManager rvLayoutManager = (FileChooserRecyclerView.LayoutManager) mainRV.getLayoutManager();
458-
return rvLayoutManager.findLastVisibleItemPosition();
532+
return rvLayoutManager.findLastCompletelyVisibleItemPosition();
459533
}
460534

461535
public int getActiveLayoutItemsCount() {

app/src/main/java/com/maxieds/androidfilepickerlight/AndroidFilePickerLightExampleActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public void showFileChooserResultsDialog(List<String> fileItemsList, String onEr
7171
errorRationaleDisplayText.setTextColor(getColor(R.color.colorOnErrorDisplayText));
7272
errorRationaleDisplayText.setText(errorDisplayText.toString());
7373
adBuilder.setView(errorRationaleDisplayText);
74-
adBuilder.setNegativeButton("[X] That sucks, boo.", null);
74+
adBuilder.setNegativeButton("[X] That bytes, boo.", null);
7575
adBuilder.create().show();
7676
return;
7777
}

0 commit comments

Comments
 (0)