Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf01dcab authored by Ben Lin's avatar Ben Lin Committed by Android (Google) Code Review
Browse files

Merge "Integration ContentResolver#refresh into RefreshLayout." into nyc-andromeda-dev

parents 5bd7d1cb bbb7d035
Loading
Loading
Loading
Loading
+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);
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -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() { }
}
+23 −6
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;

@@ -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> {

@@ -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);

@@ -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);
            }
        }

@@ -1329,7 +1344,9 @@ public class DirectoryFragment extends Fragment
                    + DocumentInfo.debugString(mLocalState.mDocument));
            mModel.onLoaderReset();

            if (!Shared.ENABLE_OMC_API_FEATURES) {
                mRefreshLayout.setRefreshing(false);
            }
        }
    }
}