Loading src/com/android/documentsui/DirectoryLoader.java +26 −2 Original line number Diff line number Diff line Loading @@ -20,9 +20,11 @@ import android.content.AsyncTaskLoader; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.CancellationSignal; import android.os.Handler; import android.os.OperationCanceledException; import android.os.RemoteException; import android.provider.DocumentsContract.Document; Loading @@ -42,8 +44,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR }; private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); private final LockingContentObserver mObserver; private final RootInfo mRoot; private final Uri mUri; private final SortModel mModel; Loading @@ -59,6 +60,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { DocumentInfo doc, Uri uri, SortModel model, DirectoryReloadLock lock, boolean inSearchMode) { super(context, ProviderExecutor.forAuthority(root.authority)); Loading @@ -67,6 +69,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { mModel = model; mDoc = doc; mSearchMode = inSearchMode; mObserver = new LockingContentObserver(lock, this::onContentChanged); } @Override Loading Loading @@ -181,4 +184,25 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { getContext().getContentResolver().unregisterContentObserver(mObserver); } private static final class LockingContentObserver extends ContentObserver { private final DirectoryReloadLock mLock; private final Runnable mContentChangedCallback; public LockingContentObserver(DirectoryReloadLock lock, Runnable contentChangedCallback) { super(new Handler()); mLock = lock; mContentChangedCallback = contentChangedCallback; } @Override public boolean deliverSelfNotifications() { return true; } @Override public void onChange(boolean selfChange) { mLock.tryUpdate(mContentChangedCallback); } } } src/com/android/documentsui/DirectoryReloadLock.java 0 → 100644 +67 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * 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 com.android.documentsui; import android.annotation.MainThread; import android.annotation.Nullable; import com.android.documentsui.base.Shared; import com.android.documentsui.selection.BandController; /** * A lock used by {@link DirectoryLoader} and {@link BandController} to ensure refresh is blocked * while Band Selection is active. */ public final class DirectoryReloadLock { private int mPauseCount = 0; private @Nullable Runnable mCallback; /** * Increment the block count by 1 */ @MainThread public void block() { Shared.checkMainLoop(); mPauseCount++; } /** * Decrement the block count by 1; If no other object is trying to block and there exists some * callback, that callback will be run */ @MainThread public void unblock() { Shared.checkMainLoop(); mPauseCount--; if (mPauseCount == 0 && mCallback != null) { mCallback.run(); mCallback = null; } } /** * Attempts to run the given Runnable if not-blocked, or else the Runnable is set to be ran next * (replacing any previous set Runnables). */ public void tryUpdate(Runnable update) { if (mPauseCount == 0) { update.run(); } else { mCallback = update; } } } No newline at end of file src/com/android/documentsui/dirlist/DirectoryFragment.java +5 −1 Original line number Diff line number Diff line Loading @@ -67,6 +67,7 @@ import com.android.documentsui.BaseActivity; import com.android.documentsui.BaseActivity.RetainedState; import com.android.documentsui.DirectoryLoader; import com.android.documentsui.DirectoryResult; import com.android.documentsui.DirectoryReloadLock; import com.android.documentsui.DocumentsApplication; import com.android.documentsui.FocusManager; import com.android.documentsui.ItemDragListener; Loading Loading @@ -186,6 +187,7 @@ public class DirectoryFragment extends Fragment private View mProgressBar; private DirectoryState mLocalState; private DirectoryReloadLock mReloadLock = new DirectoryReloadLock(); // Note, we use !null to indicate that selection was restored (from rotation). // So don't fiddle with this field unless you've got the bigger picture in mind. Loading Loading @@ -308,13 +310,14 @@ public class DirectoryFragment extends Fragment mSelectionMetadata = new SelectionMetadata(mModel::getItem); mSelectionMgr.addItemCallback(mSelectionMetadata); GestureSelector gestureSel = GestureSelector.create(mSelectionMgr, mRecView); GestureSelector gestureSel = GestureSelector.create(mSelectionMgr, mRecView, mReloadLock); if (mState.allowMultiple) { mBandController = new BandController( mRecView, mAdapter, mSelectionMgr, mReloadLock, (int pos) -> { // The band selection model only operates on documents and directories. // Exclude other types of adapter items like whitespace and dividers. Loading Loading @@ -1232,6 +1235,7 @@ public class DirectoryFragment extends Fragment mLocalState.mDocument, contentsUri, mState.sortModel, mReloadLock, mLocalState.mSearchMode); case TYPE_RECENT_OPEN: Loading src/com/android/documentsui/selection/BandController.java +8 −1 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.view.View; import com.android.documentsui.DirectoryReloadLock; import com.android.documentsui.R; import com.android.documentsui.base.Events.InputEvent; import com.android.documentsui.dirlist.DocumentsAdapter; Loading Loading @@ -61,6 +62,7 @@ public class BandController extends OnScrollListener { private final SelectionEnvironment mEnvironment; private final DocumentsAdapter mAdapter; private final SelectionManager mSelectionManager; private final DirectoryReloadLock mLock; private final Runnable mViewScroller; private final GridModel.OnSelectionChangedListener mGridListener; Loading @@ -75,16 +77,19 @@ public class BandController extends OnScrollListener { final RecyclerView view, DocumentsAdapter adapter, SelectionManager selectionManager, DirectoryReloadLock lock, IntPredicate gridItemTester) { this(new RuntimeSelectionEnvironment(view), adapter, selectionManager, gridItemTester); this(new RuntimeSelectionEnvironment(view), adapter, selectionManager, lock, gridItemTester); } private BandController( SelectionEnvironment env, DocumentsAdapter adapter, SelectionManager selectionManager, DirectoryReloadLock lock, IntPredicate gridItemTester) { mLock = lock; selectionManager.bindContoller(this); mEnvironment = env; Loading Loading @@ -265,6 +270,7 @@ public class BandController extends OnScrollListener { private void startBandSelect(Point origin) { if (DEBUG) Log.d(TAG, "Starting band select @ " + origin); mLock.block(); mOrigin = origin; mModelBuilder.run(); // Creates a new selection model. mModel.startSelection(mOrigin); Loading Loading @@ -314,6 +320,7 @@ public class BandController extends OnScrollListener { mModel = null; mOrigin = null; mLock.unblock(); } private void onSelectionChanged(Set<String> updatedSelection) { Loading src/com/android/documentsui/selection/GestureSelector.java +12 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.widget.RecyclerView; import android.view.View; import com.android.documentsui.DirectoryReloadLock; import com.android.documentsui.base.Events.InputEvent; import com.android.documentsui.ui.ViewAutoScroller; import com.android.documentsui.ui.ViewAutoScroller.ScrollActionDelegate; Loading @@ -40,6 +41,7 @@ public final class GestureSelector { private final Runnable mDragScroller; private final IntSupplier mHeight; private final ViewFinder mViewFinder; private final DirectoryReloadLock mLock; private int mLastStartedItemPos = -1; private boolean mStarted = false; private Point mLastInterceptedPoint; Loading @@ -48,10 +50,12 @@ public final class GestureSelector { SelectionManager selectionMgr, IntSupplier heightSupplier, ViewFinder viewFinder, ScrollActionDelegate actionDelegate) { ScrollActionDelegate actionDelegate, DirectoryReloadLock lock) { mSelectionMgr = selectionMgr; mHeight = heightSupplier; mViewFinder = viewFinder; mLock = lock; ScrollDistanceDelegate distanceDelegate = new ScrollDistanceDelegate() { @Override Loading @@ -75,7 +79,8 @@ public final class GestureSelector { public static GestureSelector create( SelectionManager selectionMgr, RecyclerView scrollView) { RecyclerView scrollView, DirectoryReloadLock lock) { ScrollActionDelegate actionDelegate = new ScrollActionDelegate() { @Override public void scrollBy(int dy) { Loading @@ -97,7 +102,8 @@ public final class GestureSelector { selectionMgr, scrollView::getHeight, scrollView::findChildViewUnder, actionDelegate); actionDelegate, lock); return helper; } Loading Loading @@ -162,6 +168,8 @@ public final class GestureSelector { mLastInterceptedPoint = e.getOrigin(); if (mStarted) { mSelectionMgr.startRangeSelection(mLastStartedItemPos); // Gesture Selection about to start mLock.block(); return true; } return false; Loading @@ -173,6 +181,7 @@ public final class GestureSelector { mLastStartedItemPos = -1; mStarted = false; mSelectionMgr.getSelection().applyProvisionalSelection(); mLock.unblock(); return false; } Loading Loading
src/com/android/documentsui/DirectoryLoader.java +26 −2 Original line number Diff line number Diff line Loading @@ -20,9 +20,11 @@ import android.content.AsyncTaskLoader; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.CancellationSignal; import android.os.Handler; import android.os.OperationCanceledException; import android.os.RemoteException; import android.provider.DocumentsContract.Document; Loading @@ -42,8 +44,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR }; private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); private final LockingContentObserver mObserver; private final RootInfo mRoot; private final Uri mUri; private final SortModel mModel; Loading @@ -59,6 +60,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { DocumentInfo doc, Uri uri, SortModel model, DirectoryReloadLock lock, boolean inSearchMode) { super(context, ProviderExecutor.forAuthority(root.authority)); Loading @@ -67,6 +69,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { mModel = model; mDoc = doc; mSearchMode = inSearchMode; mObserver = new LockingContentObserver(lock, this::onContentChanged); } @Override Loading Loading @@ -181,4 +184,25 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { getContext().getContentResolver().unregisterContentObserver(mObserver); } private static final class LockingContentObserver extends ContentObserver { private final DirectoryReloadLock mLock; private final Runnable mContentChangedCallback; public LockingContentObserver(DirectoryReloadLock lock, Runnable contentChangedCallback) { super(new Handler()); mLock = lock; mContentChangedCallback = contentChangedCallback; } @Override public boolean deliverSelfNotifications() { return true; } @Override public void onChange(boolean selfChange) { mLock.tryUpdate(mContentChangedCallback); } } }
src/com/android/documentsui/DirectoryReloadLock.java 0 → 100644 +67 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * 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 com.android.documentsui; import android.annotation.MainThread; import android.annotation.Nullable; import com.android.documentsui.base.Shared; import com.android.documentsui.selection.BandController; /** * A lock used by {@link DirectoryLoader} and {@link BandController} to ensure refresh is blocked * while Band Selection is active. */ public final class DirectoryReloadLock { private int mPauseCount = 0; private @Nullable Runnable mCallback; /** * Increment the block count by 1 */ @MainThread public void block() { Shared.checkMainLoop(); mPauseCount++; } /** * Decrement the block count by 1; If no other object is trying to block and there exists some * callback, that callback will be run */ @MainThread public void unblock() { Shared.checkMainLoop(); mPauseCount--; if (mPauseCount == 0 && mCallback != null) { mCallback.run(); mCallback = null; } } /** * Attempts to run the given Runnable if not-blocked, or else the Runnable is set to be ran next * (replacing any previous set Runnables). */ public void tryUpdate(Runnable update) { if (mPauseCount == 0) { update.run(); } else { mCallback = update; } } } No newline at end of file
src/com/android/documentsui/dirlist/DirectoryFragment.java +5 −1 Original line number Diff line number Diff line Loading @@ -67,6 +67,7 @@ import com.android.documentsui.BaseActivity; import com.android.documentsui.BaseActivity.RetainedState; import com.android.documentsui.DirectoryLoader; import com.android.documentsui.DirectoryResult; import com.android.documentsui.DirectoryReloadLock; import com.android.documentsui.DocumentsApplication; import com.android.documentsui.FocusManager; import com.android.documentsui.ItemDragListener; Loading Loading @@ -186,6 +187,7 @@ public class DirectoryFragment extends Fragment private View mProgressBar; private DirectoryState mLocalState; private DirectoryReloadLock mReloadLock = new DirectoryReloadLock(); // Note, we use !null to indicate that selection was restored (from rotation). // So don't fiddle with this field unless you've got the bigger picture in mind. Loading Loading @@ -308,13 +310,14 @@ public class DirectoryFragment extends Fragment mSelectionMetadata = new SelectionMetadata(mModel::getItem); mSelectionMgr.addItemCallback(mSelectionMetadata); GestureSelector gestureSel = GestureSelector.create(mSelectionMgr, mRecView); GestureSelector gestureSel = GestureSelector.create(mSelectionMgr, mRecView, mReloadLock); if (mState.allowMultiple) { mBandController = new BandController( mRecView, mAdapter, mSelectionMgr, mReloadLock, (int pos) -> { // The band selection model only operates on documents and directories. // Exclude other types of adapter items like whitespace and dividers. Loading Loading @@ -1232,6 +1235,7 @@ public class DirectoryFragment extends Fragment mLocalState.mDocument, contentsUri, mState.sortModel, mReloadLock, mLocalState.mSearchMode); case TYPE_RECENT_OPEN: Loading
src/com/android/documentsui/selection/BandController.java +8 −1 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.view.View; import com.android.documentsui.DirectoryReloadLock; import com.android.documentsui.R; import com.android.documentsui.base.Events.InputEvent; import com.android.documentsui.dirlist.DocumentsAdapter; Loading Loading @@ -61,6 +62,7 @@ public class BandController extends OnScrollListener { private final SelectionEnvironment mEnvironment; private final DocumentsAdapter mAdapter; private final SelectionManager mSelectionManager; private final DirectoryReloadLock mLock; private final Runnable mViewScroller; private final GridModel.OnSelectionChangedListener mGridListener; Loading @@ -75,16 +77,19 @@ public class BandController extends OnScrollListener { final RecyclerView view, DocumentsAdapter adapter, SelectionManager selectionManager, DirectoryReloadLock lock, IntPredicate gridItemTester) { this(new RuntimeSelectionEnvironment(view), adapter, selectionManager, gridItemTester); this(new RuntimeSelectionEnvironment(view), adapter, selectionManager, lock, gridItemTester); } private BandController( SelectionEnvironment env, DocumentsAdapter adapter, SelectionManager selectionManager, DirectoryReloadLock lock, IntPredicate gridItemTester) { mLock = lock; selectionManager.bindContoller(this); mEnvironment = env; Loading Loading @@ -265,6 +270,7 @@ public class BandController extends OnScrollListener { private void startBandSelect(Point origin) { if (DEBUG) Log.d(TAG, "Starting band select @ " + origin); mLock.block(); mOrigin = origin; mModelBuilder.run(); // Creates a new selection model. mModel.startSelection(mOrigin); Loading Loading @@ -314,6 +320,7 @@ public class BandController extends OnScrollListener { mModel = null; mOrigin = null; mLock.unblock(); } private void onSelectionChanged(Set<String> updatedSelection) { Loading
src/com/android/documentsui/selection/GestureSelector.java +12 −3 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.widget.RecyclerView; import android.view.View; import com.android.documentsui.DirectoryReloadLock; import com.android.documentsui.base.Events.InputEvent; import com.android.documentsui.ui.ViewAutoScroller; import com.android.documentsui.ui.ViewAutoScroller.ScrollActionDelegate; Loading @@ -40,6 +41,7 @@ public final class GestureSelector { private final Runnable mDragScroller; private final IntSupplier mHeight; private final ViewFinder mViewFinder; private final DirectoryReloadLock mLock; private int mLastStartedItemPos = -1; private boolean mStarted = false; private Point mLastInterceptedPoint; Loading @@ -48,10 +50,12 @@ public final class GestureSelector { SelectionManager selectionMgr, IntSupplier heightSupplier, ViewFinder viewFinder, ScrollActionDelegate actionDelegate) { ScrollActionDelegate actionDelegate, DirectoryReloadLock lock) { mSelectionMgr = selectionMgr; mHeight = heightSupplier; mViewFinder = viewFinder; mLock = lock; ScrollDistanceDelegate distanceDelegate = new ScrollDistanceDelegate() { @Override Loading @@ -75,7 +79,8 @@ public final class GestureSelector { public static GestureSelector create( SelectionManager selectionMgr, RecyclerView scrollView) { RecyclerView scrollView, DirectoryReloadLock lock) { ScrollActionDelegate actionDelegate = new ScrollActionDelegate() { @Override public void scrollBy(int dy) { Loading @@ -97,7 +102,8 @@ public final class GestureSelector { selectionMgr, scrollView::getHeight, scrollView::findChildViewUnder, actionDelegate); actionDelegate, lock); return helper; } Loading Loading @@ -162,6 +168,8 @@ public final class GestureSelector { mLastInterceptedPoint = e.getOrigin(); if (mStarted) { mSelectionMgr.startRangeSelection(mLastStartedItemPos); // Gesture Selection about to start mLock.block(); return true; } return false; Loading @@ -173,6 +181,7 @@ public final class GestureSelector { mLastStartedItemPos = -1; mStarted = false; mSelectionMgr.getSelection().applyProvisionalSelection(); mLock.unblock(); return false; } Loading