1 /* 2 * Copyright (C) 2013 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.base; 18 19 import android.content.Intent; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 import android.util.SparseArray; 23 24 import androidx.annotation.IntDef; 25 26 import com.android.documentsui.ConfigStore; 27 import com.android.documentsui.services.FileOperationService; 28 import com.android.documentsui.services.FileOperationService.OpType; 29 import com.android.documentsui.sorting.SortModel; 30 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.RetentionPolicy; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Map; 38 39 public class State implements android.os.Parcelable { 40 41 private static final String TAG = "State"; 42 43 @IntDef(flag = true, value = { 44 ACTION_BROWSE, 45 ACTION_PICK_COPY_DESTINATION, 46 ACTION_OPEN, 47 ACTION_CREATE, 48 ACTION_GET_CONTENT, 49 ACTION_OPEN_TREE 50 }) 51 @Retention(RetentionPolicy.SOURCE) 52 public @interface ActionType { 53 } 54 55 // File manager and related private picking activity. 56 public static final int ACTION_BROWSE = 1; 57 public static final int ACTION_PICK_COPY_DESTINATION = 2; 58 // All public picking activities 59 public static final int ACTION_OPEN = 3; 60 public static final int ACTION_CREATE = 4; 61 public static final int ACTION_GET_CONTENT = 5; 62 public static final int ACTION_OPEN_TREE = 6; 63 64 @IntDef(flag = true, value = { 65 MODE_UNKNOWN, 66 MODE_LIST, 67 MODE_GRID 68 }) 69 @Retention(RetentionPolicy.SOURCE) 70 public @interface ViewMode { 71 } 72 73 public static final int MODE_UNKNOWN = 0; 74 public static final int MODE_LIST = 1; 75 public static final int MODE_GRID = 2; 76 77 public @ActionType int action; 78 public String[] acceptMimes; 79 80 /** Derived from local preferences */ 81 public @ViewMode int derivedMode = MODE_GRID; 82 83 public boolean debugMode = false; 84 85 /** Current sort state */ 86 public SortModel sortModel; 87 88 public boolean allowMultiple; 89 public boolean localOnly; 90 91 public boolean openableOnly; 92 public boolean restrictScopeStorage; 93 public boolean showHiddenFiles; 94 public ConfigStore configStore; 95 96 /** 97 * Represents whether the state supports cross-profile file picking. 98 */ 99 public boolean supportsCrossProfile = false; 100 101 /** 102 * Represents whether the intent is a cross-profile intent 103 */ 104 public boolean canShareAcrossProfile = false; 105 106 /** 107 * Returns true if we are allowed to interact with the user. 108 */ canInteractWith(UserId userId)109 public boolean canInteractWith(UserId userId) { 110 if (configStore.isPrivateSpaceInDocsUIEnabled()) { 111 if (canForwardToProfileIdMap.isEmpty() && UserId.CURRENT_USER.equals(userId)) { 112 return true; 113 } 114 return canForwardToProfileIdMap.getOrDefault(userId, false); 115 } 116 return canShareAcrossProfile || UserId.CURRENT_USER.equals(userId); 117 } 118 119 /** 120 * Represents whether the intent can be forwarded to the {@link UserId} in the map 121 */ 122 public Map<UserId, Boolean> canForwardToProfileIdMap = new HashMap<>(); 123 124 125 /** 126 * This is basically a sub-type for the copy operation. It can be either COPY, 127 * COMPRESS, EXTRACT or MOVE. 128 * The only legal values, if set, are: OPERATION_COPY, OPERATION_COMPRESS, 129 * OPERATION_EXTRACT and OPERATION_MOVE. Other pick 130 * operations don't use this. In those cases OPERATION_UNKNOWN is also legal. 131 */ 132 public @OpType int copyOperationSubType = FileOperationService.OPERATION_UNKNOWN; 133 134 /** Current user navigation stack; empty implies recents. */ 135 public final DocumentStack stack = new DocumentStack(); 136 137 /** Instance configs for every shown directory */ 138 public HashMap<String, SparseArray<Parcelable>> dirConfigs = new HashMap<>(); 139 140 /** Name of the package that started DocsUI */ 141 public List<String> excludedAuthorities = new ArrayList<>(); 142 initAcceptMimes(Intent intent, String defaultAcceptMimeType)143 public void initAcceptMimes(Intent intent, String defaultAcceptMimeType) { 144 if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) { 145 acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES); 146 } else { 147 acceptMimes = new String[]{defaultAcceptMimeType}; 148 } 149 } 150 151 /** 152 * Check current action should have preview function or not. 153 * 154 * @return True, if the action should have preview. 155 */ shouldShowPreview()156 public boolean shouldShowPreview() { 157 return action == ACTION_GET_CONTENT 158 || action == ACTION_OPEN_TREE 159 || action == ACTION_OPEN; 160 } 161 162 /** 163 * Check the action is file picking and acceptMimes are all images type or not. 164 * 165 * @return True, if acceptMimes are all image type and action is file picking. 166 */ isPhotoPicking()167 public boolean isPhotoPicking() { 168 if (action != ACTION_GET_CONTENT && action != ACTION_OPEN || acceptMimes == null) { 169 return false; 170 } 171 172 for (String mimeType : acceptMimes) { 173 if (!MimeTypes.mimeMatches(MimeTypes.IMAGE_MIME, mimeType)) { 174 return false; 175 } 176 } 177 return true; 178 } 179 180 /** 181 * Returns true if DocsUI supports cross-profile for this {@link State}. 182 */ supportsCrossProfile()183 public boolean supportsCrossProfile() { 184 return supportsCrossProfile; 185 } 186 187 @Override describeContents()188 public int describeContents() { 189 return 0; 190 } 191 192 @Override writeToParcel(Parcel out, int flags)193 public void writeToParcel(Parcel out, int flags) { 194 out.writeInt(action); 195 out.writeStringArray(acceptMimes); 196 out.writeInt(allowMultiple ? 1 : 0); 197 out.writeInt(localOnly ? 1 : 0); 198 DurableUtils.writeToParcel(out, stack); 199 out.writeMap(dirConfigs); 200 out.writeList(excludedAuthorities); 201 out.writeInt(openableOnly ? 1 : 0); 202 out.writeInt(restrictScopeStorage ? 1 : 0); 203 out.writeParcelable(sortModel, 0); 204 } 205 206 @Override toString()207 public String toString() { 208 return "State{" 209 + "action=" + action 210 + ", acceptMimes=" + Arrays.toString(acceptMimes) 211 + ", allowMultiple=" + allowMultiple 212 + ", localOnly=" + localOnly 213 + ", stack=" + stack 214 + ", dirConfigs=" + dirConfigs 215 + ", excludedAuthorities=" + excludedAuthorities 216 + ", openableOnly=" + openableOnly 217 + ", restrictScopeStorage=" + restrictScopeStorage 218 + ", sortModel=" + sortModel 219 + "}"; 220 } 221 222 public static final ClassLoaderCreator<State> CREATOR = new ClassLoaderCreator<State>() { 223 @Override 224 public State createFromParcel(Parcel in) { 225 return createFromParcel(in, null); 226 } 227 228 @Override 229 public State createFromParcel(Parcel in, ClassLoader loader) { 230 final State state = new State(); 231 state.action = in.readInt(); 232 state.acceptMimes = in.createStringArray(); 233 state.allowMultiple = in.readInt() != 0; 234 state.localOnly = in.readInt() != 0; 235 DurableUtils.readFromParcel(in, state.stack); 236 in.readMap(state.dirConfigs, loader); 237 in.readList(state.excludedAuthorities, loader); 238 state.openableOnly = in.readInt() != 0; 239 state.restrictScopeStorage = in.readInt() != 0; 240 state.sortModel = in.readParcelable(loader); 241 return state; 242 } 243 244 @Override 245 public State[] newArray(int size) { 246 return new State[size]; 247 } 248 }; 249 } 250