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