Loading src/com/android/documentsui/RefreshTask.java 0 → 100644 +103 −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 static com.android.documentsui.base.Shared.DEBUG; import android.annotation.Nullable; import android.app.Activity; import android.app.Fragment; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.os.CancellationSignal; import android.util.Log; import com.android.documentsui.TimeoutTask; import com.android.documentsui.base.CheckedTask; import com.android.documentsui.base.State; import com.android.documentsui.dirlist.Model; import java.util.function.Consumer; /** * A {@link CheckedTask} that calls * {@link ContentResolver#refresh(Uri, android.os.Bundle, android.os.CancellationSignal)} on the * current directory, and then calls the supplied callback with the refresh return value. */ public class RefreshTask extends TimeoutTask<Void, Boolean> { private final static String TAG = "RefreshTask"; private final Context mContext; private final State mState; private final Consumer<Boolean> mCallback; private final CancellationSignal mSignal; public RefreshTask(State state, Activity activity, Consumer<Boolean> callback) { this(state, activity, activity::isDestroyed, callback); } public RefreshTask(State state, Fragment fragment, Consumer<Boolean> callback) { this(state, fragment.getContext(), fragment::isDetached, callback); } public RefreshTask(State state, Context context, Check check, Consumer<Boolean> callback) { super(check); mContext = context.getApplicationContext(); mState = state; mCallback = callback; mSignal = new CancellationSignal(); } @Override public @Nullable Boolean run(Void... params) { final Uri uri = mState.stack.peek().derivedUri; final ContentResolver resolver = mContext.getContentResolver(); final String authority = uri.getAuthority(); boolean refreshed = false; ContentProviderClient client = null; try { client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); refreshed = client.refresh(uri, null, mSignal); } catch (Exception e) { Log.w(TAG, "Failed to refresh", e); } finally { ContentProviderClient.releaseQuietly(client); } return refreshed; } @Override protected void onTimeout() { mSignal.cancel(); } @Override public void finish(Boolean refreshed) { if (DEBUG) { if (refreshed) { Log.v(TAG, "Provider has new content and has refreshed"); } else { Log.v(TAG, "Provider has no new content and did not refresh"); } } mCallback.accept(refreshed); } } src/com/android/documentsui/TimeoutTask.java +4 −0 Original line number Diff line number Diff line Loading @@ -51,10 +51,14 @@ public abstract class TimeoutTask<Input, Output> extends CheckedTask<Input, Outp Handler handler = new Handler(); handler.postDelayed(() -> { if (getStatus() == AsyncTask.Status.RUNNING) { onTimeout(); cancel(true); this.finish(null); } }, mTimeout); } // Override this do more proper clean up in case of timeout, such as using // CancellationSignal#cancel. protected void onTimeout() { } } src/com/android/documentsui/dirlist/DirectoryFragment.java +23 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.documentsui.dirlist; import static android.content.ContentResolver.EXTRA_REFRESH_SUPPORTED; import static com.android.documentsui.base.DocumentInfo.getCursorInt; import static com.android.documentsui.base.DocumentInfo.getCursorString; import static com.android.documentsui.base.Shared.DEBUG; Loading @@ -42,6 +43,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; Loading Loading @@ -82,6 +84,7 @@ import com.android.documentsui.MessageBar; import com.android.documentsui.Metrics; import com.android.documentsui.R; import com.android.documentsui.RecentsLoader; import com.android.documentsui.RefreshTask; import com.android.documentsui.ThumbnailCache; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.EventHandler; Loading Loading @@ -145,7 +148,7 @@ public class DirectoryFragment extends Fragment private static final int LOADER_ID = 42; private static final int CACHE_EVICT_LIMIT = 100; private static final int REFRESH_SPINNER_DISMISS_DELAY = 500; private static final int REFRESH_SPINNER_TIMEOUT = 500; private BaseActivity mActivity; Loading Loading @@ -1142,9 +1145,19 @@ public class DirectoryFragment extends Fragment cache.removeUri(mModel.getItemUri(ids[i])); } // Trigger loading if (Shared.ENABLE_OMC_API_FEATURES) { RefreshTask refreshTask = new RefreshTask(mState, this, (Boolean refreshed) -> { new Handler(Looper.getMainLooper()) .post(() -> mRefreshLayout.setRefreshing(false)); }); refreshTask.setTimeout(REFRESH_SPINNER_TIMEOUT); refreshTask.executeOnExecutor(mActivity.getExecutorForCurrentDirectory()); } else { // If Refresh API isn't available, we will explicitly reload the loader getLoaderManager().restartLoader(LOADER_ID, null, mLoaderCallbacks); } } private final class ModelUpdateListener implements EventListener<Model.Update> { Loading Loading @@ -1286,6 +1299,8 @@ public class DirectoryFragment extends Fragment mAdapter.notifyDataSetChanged(); mModel.update(result); mRefreshLayout.setEnabled( result.cursor.getExtras().getBoolean(EXTRA_REFRESH_SUPPORTED, false)); updateLayout(mState.derivedMode); Loading Loading @@ -1316,10 +1331,10 @@ public class DirectoryFragment extends Fragment mLocalState.mLastSortDimensionId = curSortedDimension.getId(); mLocalState.mLastSortDirection = curSortedDimension.getSortDirection(); if (mRefreshLayout.isRefreshing()) { if (!Shared.ENABLE_OMC_API_FEATURES && mRefreshLayout.isRefreshing()) { new Handler().postDelayed( () -> mRefreshLayout.setRefreshing(false), REFRESH_SPINNER_DISMISS_DELAY); REFRESH_SPINNER_TIMEOUT); } } Loading @@ -1329,7 +1344,9 @@ public class DirectoryFragment extends Fragment + DocumentInfo.debugString(mLocalState.mDocument)); mModel.onLoaderReset(); if (!Shared.ENABLE_OMC_API_FEATURES) { mRefreshLayout.setRefreshing(false); } } } } Loading
src/com/android/documentsui/RefreshTask.java 0 → 100644 +103 −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 static com.android.documentsui.base.Shared.DEBUG; import android.annotation.Nullable; import android.app.Activity; import android.app.Fragment; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.os.CancellationSignal; import android.util.Log; import com.android.documentsui.TimeoutTask; import com.android.documentsui.base.CheckedTask; import com.android.documentsui.base.State; import com.android.documentsui.dirlist.Model; import java.util.function.Consumer; /** * A {@link CheckedTask} that calls * {@link ContentResolver#refresh(Uri, android.os.Bundle, android.os.CancellationSignal)} on the * current directory, and then calls the supplied callback with the refresh return value. */ public class RefreshTask extends TimeoutTask<Void, Boolean> { private final static String TAG = "RefreshTask"; private final Context mContext; private final State mState; private final Consumer<Boolean> mCallback; private final CancellationSignal mSignal; public RefreshTask(State state, Activity activity, Consumer<Boolean> callback) { this(state, activity, activity::isDestroyed, callback); } public RefreshTask(State state, Fragment fragment, Consumer<Boolean> callback) { this(state, fragment.getContext(), fragment::isDetached, callback); } public RefreshTask(State state, Context context, Check check, Consumer<Boolean> callback) { super(check); mContext = context.getApplicationContext(); mState = state; mCallback = callback; mSignal = new CancellationSignal(); } @Override public @Nullable Boolean run(Void... params) { final Uri uri = mState.stack.peek().derivedUri; final ContentResolver resolver = mContext.getContentResolver(); final String authority = uri.getAuthority(); boolean refreshed = false; ContentProviderClient client = null; try { client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); refreshed = client.refresh(uri, null, mSignal); } catch (Exception e) { Log.w(TAG, "Failed to refresh", e); } finally { ContentProviderClient.releaseQuietly(client); } return refreshed; } @Override protected void onTimeout() { mSignal.cancel(); } @Override public void finish(Boolean refreshed) { if (DEBUG) { if (refreshed) { Log.v(TAG, "Provider has new content and has refreshed"); } else { Log.v(TAG, "Provider has no new content and did not refresh"); } } mCallback.accept(refreshed); } }
src/com/android/documentsui/TimeoutTask.java +4 −0 Original line number Diff line number Diff line Loading @@ -51,10 +51,14 @@ public abstract class TimeoutTask<Input, Output> extends CheckedTask<Input, Outp Handler handler = new Handler(); handler.postDelayed(() -> { if (getStatus() == AsyncTask.Status.RUNNING) { onTimeout(); cancel(true); this.finish(null); } }, mTimeout); } // Override this do more proper clean up in case of timeout, such as using // CancellationSignal#cancel. protected void onTimeout() { } }
src/com/android/documentsui/dirlist/DirectoryFragment.java +23 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.documentsui.dirlist; import static android.content.ContentResolver.EXTRA_REFRESH_SUPPORTED; import static com.android.documentsui.base.DocumentInfo.getCursorInt; import static com.android.documentsui.base.DocumentInfo.getCursorString; import static com.android.documentsui.base.Shared.DEBUG; Loading @@ -42,6 +43,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; Loading Loading @@ -82,6 +84,7 @@ import com.android.documentsui.MessageBar; import com.android.documentsui.Metrics; import com.android.documentsui.R; import com.android.documentsui.RecentsLoader; import com.android.documentsui.RefreshTask; import com.android.documentsui.ThumbnailCache; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.EventHandler; Loading Loading @@ -145,7 +148,7 @@ public class DirectoryFragment extends Fragment private static final int LOADER_ID = 42; private static final int CACHE_EVICT_LIMIT = 100; private static final int REFRESH_SPINNER_DISMISS_DELAY = 500; private static final int REFRESH_SPINNER_TIMEOUT = 500; private BaseActivity mActivity; Loading Loading @@ -1142,9 +1145,19 @@ public class DirectoryFragment extends Fragment cache.removeUri(mModel.getItemUri(ids[i])); } // Trigger loading if (Shared.ENABLE_OMC_API_FEATURES) { RefreshTask refreshTask = new RefreshTask(mState, this, (Boolean refreshed) -> { new Handler(Looper.getMainLooper()) .post(() -> mRefreshLayout.setRefreshing(false)); }); refreshTask.setTimeout(REFRESH_SPINNER_TIMEOUT); refreshTask.executeOnExecutor(mActivity.getExecutorForCurrentDirectory()); } else { // If Refresh API isn't available, we will explicitly reload the loader getLoaderManager().restartLoader(LOADER_ID, null, mLoaderCallbacks); } } private final class ModelUpdateListener implements EventListener<Model.Update> { Loading Loading @@ -1286,6 +1299,8 @@ public class DirectoryFragment extends Fragment mAdapter.notifyDataSetChanged(); mModel.update(result); mRefreshLayout.setEnabled( result.cursor.getExtras().getBoolean(EXTRA_REFRESH_SUPPORTED, false)); updateLayout(mState.derivedMode); Loading Loading @@ -1316,10 +1331,10 @@ public class DirectoryFragment extends Fragment mLocalState.mLastSortDimensionId = curSortedDimension.getId(); mLocalState.mLastSortDirection = curSortedDimension.getSortDirection(); if (mRefreshLayout.isRefreshing()) { if (!Shared.ENABLE_OMC_API_FEATURES && mRefreshLayout.isRefreshing()) { new Handler().postDelayed( () -> mRefreshLayout.setRefreshing(false), REFRESH_SPINNER_DISMISS_DELAY); REFRESH_SPINNER_TIMEOUT); } } Loading @@ -1329,7 +1344,9 @@ public class DirectoryFragment extends Fragment + DocumentInfo.debugString(mLocalState.mDocument)); mModel.onLoaderReset(); if (!Shared.ENABLE_OMC_API_FEATURES) { mRefreshLayout.setRefreshing(false); } } } }