1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.documentsui; 18 19 import static com.android.documentsui.base.SharedMinimal.DEBUG; 20 21 import android.content.ContentProviderClient; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.net.Uri; 25 import android.os.CancellationSignal; 26 import android.os.FileUtils; 27 import android.util.Log; 28 29 import androidx.annotation.Nullable; 30 31 import com.android.documentsui.base.ApplicationScope; 32 import com.android.documentsui.base.BooleanConsumer; 33 import com.android.documentsui.base.CheckedTask; 34 import com.android.documentsui.base.DocumentInfo; 35 import com.android.documentsui.base.Features; 36 import com.android.documentsui.base.State; 37 38 /** 39 * A {@link CheckedTask} that calls 40 * {@link ContentResolver#refresh(Uri, android.os.Bundle, android.os.CancellationSignal)} on the 41 * current directory, and then calls the supplied callback with the refresh return value. 42 */ 43 public class RefreshTask extends TimeoutTask<Void, Boolean> { 44 45 private final static String TAG = "RefreshTask"; 46 47 private final @ApplicationScope Context mContext; 48 private final Features mFeatures; 49 private final State mState; 50 private final DocumentInfo mDoc; 51 private final BooleanConsumer mCallback; 52 private final CancellationSignal mSignal; 53 54 RefreshTask(Features features, State state, DocumentInfo doc, long timeout, @ApplicationScope Context context, Check check, BooleanConsumer callback)55 public RefreshTask(Features features, State state, DocumentInfo doc, long timeout, 56 @ApplicationScope Context context, Check check, BooleanConsumer callback) { 57 super(check, timeout); 58 mFeatures = features; 59 mState = state; 60 mDoc = doc; 61 mContext = context; 62 mCallback = callback; 63 mSignal = new CancellationSignal(); 64 } 65 66 @Override run(Void... params)67 public @Nullable Boolean run(Void... params) { 68 if (mDoc == null) { 69 Log.w(TAG, "Ignoring attempt to refresh due to null DocumentInfo."); 70 return false; 71 } 72 73 if (mState.stack.isEmpty()) { 74 Log.w(TAG, "Ignoring attempt to refresh due to empty stack."); 75 return false; 76 } 77 78 if (mDoc.derivedUri == null) { 79 Log.w(TAG, "Ignoring attempt to refresh due to null derived uri in DocumentInfo."); 80 return false; 81 } 82 83 if (!mDoc.derivedUri.equals(mState.stack.peek().derivedUri)) { 84 Log.w(TAG, "Ignoring attempt to refresh on a non-top-level uri."); 85 return false; 86 } 87 88 // API O introduces ContentResolver#refresh, and if available and the ContentProvider 89 // supports it, the ContentProvider will automatically send a content updated notification 90 // and we will update accordingly. Else, we just tell the callback that Refresh is not 91 // supported. 92 if (!mFeatures.isContentRefreshEnabled()) { 93 Log.w(TAG, "Ignoring attempt to call Refresh on an older Android platform."); 94 return false; 95 } 96 97 final ContentResolver resolver = mContext.getContentResolver(); 98 final String authority = mDoc.authority; 99 boolean refreshSupported = false; 100 ContentProviderClient client = null; 101 try { 102 client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); 103 refreshSupported = client.refresh(mDoc.derivedUri, null, mSignal); 104 } catch (Exception e) { 105 Log.w(TAG, "Failed to refresh", e); 106 } finally { 107 FileUtils.closeQuietly(client); 108 } 109 return refreshSupported; 110 } 111 112 @Override onTimeout()113 protected void onTimeout() { 114 mSignal.cancel(); 115 Log.w(TAG, "Provider taking too long to respond. Cancelling."); 116 } 117 118 @Override finish(Boolean refreshSupported)119 public void finish(Boolean refreshSupported) { 120 if (DEBUG) { 121 // In case of timeout, refreshSupported is null. 122 if (Boolean.TRUE.equals(refreshSupported)) { 123 Log.v(TAG, "Provider supports refresh and has refreshed"); 124 } else { 125 Log.v(TAG, "Provider does not support refresh and did not refresh"); 126 } 127 } 128 mCallback.accept(refreshSupported != null ? refreshSupported : Boolean.FALSE); 129 } 130 } 131