Loading src/com/android/documentsui/RefreshTask.java +38 −24 Original line number Original line Diff line number Diff line Loading @@ -28,10 +28,10 @@ import android.net.Uri; import android.os.CancellationSignal; import android.os.CancellationSignal; import android.util.Log; import android.util.Log; import com.android.documentsui.TimeoutTask; import com.android.documentsui.base.ApplicationScope; import com.android.documentsui.base.CheckedTask; import com.android.documentsui.base.CheckedTask; import com.android.documentsui.base.Shared; import com.android.documentsui.base.State; import com.android.documentsui.base.State; import com.android.documentsui.dirlist.Model; import java.util.function.Consumer; import java.util.function.Consumer; Loading @@ -44,60 +44,74 @@ public class RefreshTask extends TimeoutTask<Void, Boolean> { private final static String TAG = "RefreshTask"; private final static String TAG = "RefreshTask"; private final Context mContext; private final @ApplicationScope Context mContext; private final State mState; private final State mState; private final Uri mUri; private final Consumer<Boolean> mCallback; private final Consumer<Boolean> mCallback; private final CancellationSignal mSignal; private final CancellationSignal mSignal; public RefreshTask(State state, Uri uri, long timeout, @ApplicationScope Context context, Check check, public RefreshTask(State state, Activity activity, Consumer<Boolean> callback) { 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); super(check); mContext = context.getApplicationContext(); mUri = uri; mContext = context; mState = state; mState = state; mCallback = callback; mCallback = callback; mSignal = new CancellationSignal(); mSignal = new CancellationSignal(); setTimeout(timeout); } } @Override @Override public @Nullable Boolean run(Void... params) { public @Nullable Boolean run(Void... params) { final Uri uri = mState.stack.peek().derivedUri; if (mUri == null) { Log.w(TAG, "Attempted to refresh on a null uri. Aborting."); return false; } if (mUri != mState.stack.peek().derivedUri) { Log.w(TAG, "Attempted to refresh on a non-top-level uri. Aborting."); return false; } // API O introduces ContentResolver#refresh, and if available and the ContentProvider // supports it, the ContentProvider will automatically send a content updated notification // and we will update accordingly. Else, we just tell the callback that Refresh is not // supported. if (!Shared.ENABLE_OMC_API_FEATURES) { Log.w(TAG, "Attempted to call Refresh on an older Android platform. Aborting."); return false; } final ContentResolver resolver = mContext.getContentResolver(); final ContentResolver resolver = mContext.getContentResolver(); final String authority = uri.getAuthority(); final String authority = mUri.getAuthority(); boolean refreshed = false; boolean refreshSupported = false; ContentProviderClient client = null; ContentProviderClient client = null; try { try { client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); refreshed = client.refresh(uri, null, mSignal); refreshSupported = client.refresh(mUri, null, mSignal); } catch (Exception e) { } catch (Exception e) { Log.w(TAG, "Failed to refresh", e); Log.w(TAG, "Failed to refresh", e); } finally { } finally { ContentProviderClient.releaseQuietly(client); ContentProviderClient.releaseQuietly(client); } } return refreshed; return refreshSupported; } } @Override @Override protected void onTimeout() { protected void onTimeout() { mSignal.cancel(); mSignal.cancel(); Log.w(TAG, "Provider taking too long to respond. Cancelling."); } } @Override @Override public void finish(Boolean refreshed) { public void finish(Boolean refreshSupported) { if (DEBUG) { if (DEBUG) { if (refreshed) { if (refreshSupported) { Log.v(TAG, "Provider has new content and has refreshed"); Log.v(TAG, "Provider supports refresh and has refreshed"); } else { } else { Log.v(TAG, "Provider has no new content and did not refresh"); Log.v(TAG, "Provider does not support refresh and did not refresh"); } } } } mCallback.accept(refreshed); mCallback.accept(refreshSupported); } } } } src/com/android/documentsui/TimeoutTask.java +5 −3 Original line number Original line Diff line number Diff line Loading @@ -58,7 +58,9 @@ public abstract class TimeoutTask<Input, Output> extends CheckedTask<Input, Outp }, mTimeout); }, mTimeout); } } // Override this do more proper clean up in case of timeout, such as using /* // CancellationSignal#cancel. * Override this do more proper clean up in case of timeout, such as using * CancellationSignal#cancel. */ protected void onTimeout() {} protected void onTimeout() {} } } src/com/android/documentsui/base/ApplicationScope.java 0 → 100644 +31 −0 Original line number Original line 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.base; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Retention(SOURCE) @Target({ FIELD, PARAMETER }) public @interface ApplicationScope { } src/com/android/documentsui/dirlist/DirectoryFragment.java +15 −19 Original line number Original line Diff line number Diff line Loading @@ -1145,18 +1145,18 @@ public class DirectoryFragment extends Fragment cache.removeUri(mModel.getItemUri(ids[i])); cache.removeUri(mModel.getItemUri(ids[i])); } } if (Shared.ENABLE_OMC_API_FEATURES) { final Uri uri = mState.stack.peek().derivedUri; RefreshTask refreshTask = new RefreshTask(mState, this, RefreshTask task = new RefreshTask(mState, uri, REFRESH_SPINNER_TIMEOUT, (Boolean refreshed) -> { getContext().getApplicationContext(), this::isDetached, new Handler(Looper.getMainLooper()) (Boolean refreshSupported) -> { .post(() -> mRefreshLayout.setRefreshing(false)); if (refreshSupported) { }); mRefreshLayout.setRefreshing(false); refreshTask.setTimeout(REFRESH_SPINNER_TIMEOUT); refreshTask.executeOnExecutor(mActivity.getExecutorForCurrentDirectory()); } else { } else { // If Refresh API isn't available, we will explicitly reload the loader // If Refresh API isn't available, we will explicitly reload the loader getLoaderManager().restartLoader(LOADER_ID, null, mLoaderCallbacks); getLoaderManager().restartLoader(LOADER_ID, null, mLoaderCallbacks); } } }); task.executeOnExecutor(mActivity.getExecutorForCurrentDirectory()); } } private final class ModelUpdateListener implements EventListener<Model.Update> { private final class ModelUpdateListener implements EventListener<Model.Update> { Loading Loading @@ -1299,8 +1299,6 @@ public class DirectoryFragment extends Fragment mAdapter.notifyDataSetChanged(); mAdapter.notifyDataSetChanged(); mModel.update(result); mModel.update(result); mRefreshLayout.setEnabled( result.cursor.getExtras().getBoolean(EXTRA_REFRESH_SUPPORTED, false)); updateLayout(mState.derivedMode); updateLayout(mState.derivedMode); Loading Loading @@ -1331,7 +1329,7 @@ public class DirectoryFragment extends Fragment mLocalState.mLastSortDimensionId = curSortedDimension.getId(); mLocalState.mLastSortDimensionId = curSortedDimension.getId(); mLocalState.mLastSortDirection = curSortedDimension.getSortDirection(); mLocalState.mLastSortDirection = curSortedDimension.getSortDirection(); if (!Shared.ENABLE_OMC_API_FEATURES && mRefreshLayout.isRefreshing()) { if (mRefreshLayout.isRefreshing()) { new Handler().postDelayed( new Handler().postDelayed( () -> mRefreshLayout.setRefreshing(false), () -> mRefreshLayout.setRefreshing(false), REFRESH_SPINNER_TIMEOUT); REFRESH_SPINNER_TIMEOUT); Loading @@ -1344,9 +1342,7 @@ public class DirectoryFragment extends Fragment + DocumentInfo.debugString(mLocalState.mDocument)); + DocumentInfo.debugString(mLocalState.mDocument)); mModel.onLoaderReset(); mModel.onLoaderReset(); if (!Shared.ENABLE_OMC_API_FEATURES) { mRefreshLayout.setRefreshing(false); mRefreshLayout.setRefreshing(false); } } } } } } } Loading
src/com/android/documentsui/RefreshTask.java +38 −24 Original line number Original line Diff line number Diff line Loading @@ -28,10 +28,10 @@ import android.net.Uri; import android.os.CancellationSignal; import android.os.CancellationSignal; import android.util.Log; import android.util.Log; import com.android.documentsui.TimeoutTask; import com.android.documentsui.base.ApplicationScope; import com.android.documentsui.base.CheckedTask; import com.android.documentsui.base.CheckedTask; import com.android.documentsui.base.Shared; import com.android.documentsui.base.State; import com.android.documentsui.base.State; import com.android.documentsui.dirlist.Model; import java.util.function.Consumer; import java.util.function.Consumer; Loading @@ -44,60 +44,74 @@ public class RefreshTask extends TimeoutTask<Void, Boolean> { private final static String TAG = "RefreshTask"; private final static String TAG = "RefreshTask"; private final Context mContext; private final @ApplicationScope Context mContext; private final State mState; private final State mState; private final Uri mUri; private final Consumer<Boolean> mCallback; private final Consumer<Boolean> mCallback; private final CancellationSignal mSignal; private final CancellationSignal mSignal; public RefreshTask(State state, Uri uri, long timeout, @ApplicationScope Context context, Check check, public RefreshTask(State state, Activity activity, Consumer<Boolean> callback) { 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); super(check); mContext = context.getApplicationContext(); mUri = uri; mContext = context; mState = state; mState = state; mCallback = callback; mCallback = callback; mSignal = new CancellationSignal(); mSignal = new CancellationSignal(); setTimeout(timeout); } } @Override @Override public @Nullable Boolean run(Void... params) { public @Nullable Boolean run(Void... params) { final Uri uri = mState.stack.peek().derivedUri; if (mUri == null) { Log.w(TAG, "Attempted to refresh on a null uri. Aborting."); return false; } if (mUri != mState.stack.peek().derivedUri) { Log.w(TAG, "Attempted to refresh on a non-top-level uri. Aborting."); return false; } // API O introduces ContentResolver#refresh, and if available and the ContentProvider // supports it, the ContentProvider will automatically send a content updated notification // and we will update accordingly. Else, we just tell the callback that Refresh is not // supported. if (!Shared.ENABLE_OMC_API_FEATURES) { Log.w(TAG, "Attempted to call Refresh on an older Android platform. Aborting."); return false; } final ContentResolver resolver = mContext.getContentResolver(); final ContentResolver resolver = mContext.getContentResolver(); final String authority = uri.getAuthority(); final String authority = mUri.getAuthority(); boolean refreshed = false; boolean refreshSupported = false; ContentProviderClient client = null; ContentProviderClient client = null; try { try { client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); refreshed = client.refresh(uri, null, mSignal); refreshSupported = client.refresh(mUri, null, mSignal); } catch (Exception e) { } catch (Exception e) { Log.w(TAG, "Failed to refresh", e); Log.w(TAG, "Failed to refresh", e); } finally { } finally { ContentProviderClient.releaseQuietly(client); ContentProviderClient.releaseQuietly(client); } } return refreshed; return refreshSupported; } } @Override @Override protected void onTimeout() { protected void onTimeout() { mSignal.cancel(); mSignal.cancel(); Log.w(TAG, "Provider taking too long to respond. Cancelling."); } } @Override @Override public void finish(Boolean refreshed) { public void finish(Boolean refreshSupported) { if (DEBUG) { if (DEBUG) { if (refreshed) { if (refreshSupported) { Log.v(TAG, "Provider has new content and has refreshed"); Log.v(TAG, "Provider supports refresh and has refreshed"); } else { } else { Log.v(TAG, "Provider has no new content and did not refresh"); Log.v(TAG, "Provider does not support refresh and did not refresh"); } } } } mCallback.accept(refreshed); mCallback.accept(refreshSupported); } } } }
src/com/android/documentsui/TimeoutTask.java +5 −3 Original line number Original line Diff line number Diff line Loading @@ -58,7 +58,9 @@ public abstract class TimeoutTask<Input, Output> extends CheckedTask<Input, Outp }, mTimeout); }, mTimeout); } } // Override this do more proper clean up in case of timeout, such as using /* // CancellationSignal#cancel. * Override this do more proper clean up in case of timeout, such as using * CancellationSignal#cancel. */ protected void onTimeout() {} protected void onTimeout() {} } }
src/com/android/documentsui/base/ApplicationScope.java 0 → 100644 +31 −0 Original line number Original line 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.base; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Retention(SOURCE) @Target({ FIELD, PARAMETER }) public @interface ApplicationScope { }
src/com/android/documentsui/dirlist/DirectoryFragment.java +15 −19 Original line number Original line Diff line number Diff line Loading @@ -1145,18 +1145,18 @@ public class DirectoryFragment extends Fragment cache.removeUri(mModel.getItemUri(ids[i])); cache.removeUri(mModel.getItemUri(ids[i])); } } if (Shared.ENABLE_OMC_API_FEATURES) { final Uri uri = mState.stack.peek().derivedUri; RefreshTask refreshTask = new RefreshTask(mState, this, RefreshTask task = new RefreshTask(mState, uri, REFRESH_SPINNER_TIMEOUT, (Boolean refreshed) -> { getContext().getApplicationContext(), this::isDetached, new Handler(Looper.getMainLooper()) (Boolean refreshSupported) -> { .post(() -> mRefreshLayout.setRefreshing(false)); if (refreshSupported) { }); mRefreshLayout.setRefreshing(false); refreshTask.setTimeout(REFRESH_SPINNER_TIMEOUT); refreshTask.executeOnExecutor(mActivity.getExecutorForCurrentDirectory()); } else { } else { // If Refresh API isn't available, we will explicitly reload the loader // If Refresh API isn't available, we will explicitly reload the loader getLoaderManager().restartLoader(LOADER_ID, null, mLoaderCallbacks); getLoaderManager().restartLoader(LOADER_ID, null, mLoaderCallbacks); } } }); task.executeOnExecutor(mActivity.getExecutorForCurrentDirectory()); } } private final class ModelUpdateListener implements EventListener<Model.Update> { private final class ModelUpdateListener implements EventListener<Model.Update> { Loading Loading @@ -1299,8 +1299,6 @@ public class DirectoryFragment extends Fragment mAdapter.notifyDataSetChanged(); mAdapter.notifyDataSetChanged(); mModel.update(result); mModel.update(result); mRefreshLayout.setEnabled( result.cursor.getExtras().getBoolean(EXTRA_REFRESH_SUPPORTED, false)); updateLayout(mState.derivedMode); updateLayout(mState.derivedMode); Loading Loading @@ -1331,7 +1329,7 @@ public class DirectoryFragment extends Fragment mLocalState.mLastSortDimensionId = curSortedDimension.getId(); mLocalState.mLastSortDimensionId = curSortedDimension.getId(); mLocalState.mLastSortDirection = curSortedDimension.getSortDirection(); mLocalState.mLastSortDirection = curSortedDimension.getSortDirection(); if (!Shared.ENABLE_OMC_API_FEATURES && mRefreshLayout.isRefreshing()) { if (mRefreshLayout.isRefreshing()) { new Handler().postDelayed( new Handler().postDelayed( () -> mRefreshLayout.setRefreshing(false), () -> mRefreshLayout.setRefreshing(false), REFRESH_SPINNER_TIMEOUT); REFRESH_SPINNER_TIMEOUT); Loading @@ -1344,9 +1342,7 @@ public class DirectoryFragment extends Fragment + DocumentInfo.debugString(mLocalState.mDocument)); + DocumentInfo.debugString(mLocalState.mDocument)); mModel.onLoaderReset(); mModel.onLoaderReset(); if (!Shared.ENABLE_OMC_API_FEATURES) { mRefreshLayout.setRefreshing(false); mRefreshLayout.setRefreshing(false); } } } } } } }