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.dirlist; 18 19 import static com.android.documentsui.base.DocumentInfo.getCursorInt; 20 import static com.android.documentsui.base.DocumentInfo.getCursorString; 21 22 import android.database.Cursor; 23 import android.provider.DocumentsContract.Document; 24 import android.util.Log; 25 26 import com.android.documentsui.MenuManager; 27 import com.android.documentsui.archives.ArchivesProvider; 28 import com.android.documentsui.base.MimeTypes; 29 import com.android.documentsui.roots.RootCursorWrapper; 30 import com.android.documentsui.selection.SelectionHelper.SelectionObserver; 31 32 import java.util.function.Function; 33 34 /** 35 * A class that aggregates document metadata describing the selection. It can answer questions 36 * like: Can the selection be deleted? and Does the selection contain a folder? 37 * 38 * <p>By collecting information in real-time as the selection changes the need to 39 * traverse the entire selection in order to answer questions is eliminated. 40 */ 41 public class SelectionMetadata extends SelectionObserver 42 implements MenuManager.SelectionDetails { 43 44 private static final String TAG = "SelectionMetadata"; 45 private final static int FLAG_CAN_DELETE = 46 Document.FLAG_SUPPORTS_REMOVE | Document.FLAG_SUPPORTS_DELETE; 47 48 private final Function<String, Cursor> mDocFinder; 49 50 private int mDirectoryCount = 0; 51 private int mFileCount = 0; 52 53 // Partial files are files that haven't been fully downloaded. 54 private int mPartialCount = 0; 55 private int mWritableDirectoryCount = 0; 56 private int mNoDeleteCount = 0; 57 private int mNoRenameCount = 0; 58 private int mInArchiveCount = 0; 59 private boolean mSupportsSettings = false; 60 61 public SelectionMetadata(Function<String, Cursor> docFinder) { 62 mDocFinder = docFinder; 63 } 64 65 @Override 66 public void onItemStateChanged(String modelId, boolean selected) { 67 final Cursor cursor = mDocFinder.apply(modelId); 68 if (cursor == null) { 69 Log.w(TAG, "Model returned null cursor for document: " + modelId 70 + ". Ignoring state changed event."); 71 return; 72 } 73 74 final int delta = selected ? 1 : -1; 75 76 final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); 77 if (MimeTypes.isDirectoryType(mimeType)) { 78 mDirectoryCount += delta; 79 } else { 80 mFileCount += delta; 81 } 82 83 final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); 84 if ((docFlags & Document.FLAG_PARTIAL) != 0) { 85 mPartialCount += delta; 86 } 87 if ((docFlags & Document.FLAG_DIR_SUPPORTS_CREATE) != 0) { 88 mWritableDirectoryCount += delta; 89 } 90 if ((docFlags & FLAG_CAN_DELETE) == 0) { 91 mNoDeleteCount += delta; 92 } 93 if ((docFlags & Document.FLAG_SUPPORTS_RENAME) == 0) { 94 mNoRenameCount += delta; 95 } 96 if ((docFlags & Document.FLAG_PARTIAL) != 0) { 97 mPartialCount += delta; 98 } 99 mSupportsSettings = (docFlags & Document.FLAG_SUPPORTS_SETTINGS) != 0 && 100 (mFileCount + mDirectoryCount) == 1; 101 102 103 final String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY); 104 if (ArchivesProvider.AUTHORITY.equals(authority)) { 105 mInArchiveCount += delta; 106 } 107 } 108 109 @Override 110 public void onSelectionReset() { 111 mFileCount = 0; 112 mDirectoryCount = 0; 113 mPartialCount = 0; 114 mWritableDirectoryCount = 0; 115 mNoDeleteCount = 0; 116 mNoRenameCount = 0; 117 } 118 119 @Override 120 public boolean containsDirectories() { 121 return mDirectoryCount > 0; 122 } 123 124 @Override 125 public boolean containsFiles() { 126 return mFileCount > 0; 127 } 128 129 @Override 130 public int size() { 131 return mDirectoryCount + mFileCount; 132 } 133 134 @Override 135 public boolean containsPartialFiles() { 136 return mPartialCount > 0; 137 } 138 139 @Override 140 public boolean containsFilesInArchive() { 141 return mInArchiveCount > 0; 142 } 143 144 @Override 145 public boolean canDelete() { 146 return size() > 0 && mNoDeleteCount == 0; 147 } 148 149 @Override 150 public boolean canExtract() { 151 return size() > 0 && mInArchiveCount == size(); 152 } 153 154 @Override 155 public boolean canRename() { 156 return mNoRenameCount == 0 && size() == 1; 157 } 158 159 @Override 160 public boolean canViewInOwner() { 161 return mSupportsSettings; 162 } 163 164 @Override 165 public boolean canPasteInto() { 166 return mDirectoryCount == 1 && mWritableDirectoryCount == 1 && size() == 1; 167 } 168 169 @Override 170 public boolean canOpenWith() { 171 return size() == 1 && mDirectoryCount == 0 && mInArchiveCount == 0 && mPartialCount == 0; 172 } 173 } 174