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;
18 
19 import static com.android.documentsui.Shared.DEBUG;
20 
21 import android.annotation.IntDef;
22 import android.content.Intent;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.util.Log;
26 import android.util.SparseArray;
27 
28 import com.android.documentsui.dirlist.MultiSelectManager.Selection;
29 import com.android.documentsui.model.DocumentInfo;
30 import com.android.documentsui.model.DocumentStack;
31 import com.android.documentsui.model.DurableUtils;
32 import com.android.documentsui.model.RootInfo;
33 import com.android.documentsui.services.FileOperationService;
34 import com.android.documentsui.services.FileOperationService.OpType;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.List;
41 
42 public class State implements android.os.Parcelable {
43 
44     private static final String TAG = "State";
45 
46     @IntDef(flag = true, value = {
47             ACTION_BROWSE,
48             ACTION_PICK_COPY_DESTINATION,
49             ACTION_OPEN,
50             ACTION_CREATE,
51             ACTION_GET_CONTENT,
52             ACTION_OPEN_TREE
53     })
54     @Retention(RetentionPolicy.SOURCE)
55     public @interface ActionType {}
56     // File manager and related private picking activity.
57     public static final int ACTION_BROWSE = 1;
58     public static final int ACTION_PICK_COPY_DESTINATION = 2;
59     // All public picking activities
60     public static final int ACTION_OPEN = 3;
61     public static final int ACTION_CREATE = 4;
62     public static final int ACTION_GET_CONTENT = 5;
63     public static final int ACTION_OPEN_TREE = 6;
64 
65     @IntDef(flag = true, value = {
66             MODE_UNKNOWN,
67             MODE_LIST,
68             MODE_GRID
69     })
70     @Retention(RetentionPolicy.SOURCE)
71     public @interface ViewMode {}
72     public static final int MODE_UNKNOWN = 0;
73     public static final int MODE_LIST = 1;
74     public static final int MODE_GRID = 2;
75 
76     public static final int SORT_ORDER_UNKNOWN = 0;
77     public static final int SORT_ORDER_DISPLAY_NAME = 1;
78     public static final int SORT_ORDER_LAST_MODIFIED = 2;
79     public static final int SORT_ORDER_SIZE = 3;
80 
81     public @ActionType int action;
82     public String[] acceptMimes;
83 
84     /** Derived from local preferences */
85     public @ViewMode int derivedMode = MODE_GRID;
86 
87     /** Explicit user choice */
88     public int userSortOrder = SORT_ORDER_UNKNOWN;
89     /** Derived after loader */
90     public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
91 
92     public boolean allowMultiple;
93     public boolean forceSize;
94     public boolean showSize;
95     public boolean localOnly;
96     public boolean showAdvancedOption;
97     public boolean showAdvanced;
98     public boolean restored;
99     /*
100      * Indicates handler was an external app, like photos.
101      */
102     public boolean external;
103 
104     // Indicates that a copy operation (or move) includes a directory.
105     // Why? Directory creation isn't supported by some roots (like Downloads).
106     // This allows us to restrict available roots to just those with support.
107     public boolean directoryCopy;
108     public boolean openableOnly;
109 
110     /**
111      * This is basically a sub-type for the copy operation. It can be either COPY or MOVE.
112      * The only legal values, if set, are: OPERATION_COPY, OPERATION_MOVE. Other pick
113      * operations don't use this. In those cases OPERATION_UNKNOWN is also legal.
114      */
115     public @OpType int copyOperationSubType = FileOperationService.OPERATION_UNKNOWN;
116 
117     /** Current user navigation stack; empty implies recents. */
118     public DocumentStack stack = new DocumentStack();
119     private boolean mStackTouched;
120     private boolean mInitialRootChanged;
121     private boolean mInitialDocChanged;
122 
123     /** Instance state for every shown directory */
124     public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
125 
126     /** Currently copying file */
127     public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<>();
128 
129     /** Name of the package that started DocsUI */
130     public List<String> excludedAuthorities = new ArrayList<>();
131 
initAcceptMimes(Intent intent)132     public void initAcceptMimes(Intent intent) {
133         if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
134             acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
135         } else {
136             String glob = intent.getType();
137             acceptMimes = new String[] { glob != null ? glob : "*/*" };
138         }
139     }
140 
onRootChanged(RootInfo root)141     public void onRootChanged(RootInfo root) {
142         if (DEBUG) Log.d(TAG, "Root changed to: " + root);
143         if (!mInitialRootChanged && stack.root != null && !root.equals(stack.root)) {
144             mInitialRootChanged = true;
145         }
146         stack.root = root;
147         stack.clear();
148         mStackTouched = true;
149     }
150 
pushDocument(DocumentInfo info)151     public void pushDocument(DocumentInfo info) {
152         if (DEBUG) Log.d(TAG, "Adding doc to stack: " + info);
153         if (!mInitialDocChanged && stack.size() > 0 && !info.equals(stack.peek())) {
154             mInitialDocChanged = true;
155         }
156         stack.push(info);
157         mStackTouched = true;
158     }
159 
popDocument()160     public void popDocument() {
161         if (DEBUG) Log.d(TAG, "Popping doc off stack.");
162         stack.pop();
163         mStackTouched = true;
164     }
165 
setStack(DocumentStack stack)166     public void setStack(DocumentStack stack) {
167         if (DEBUG) Log.d(TAG, "Setting the whole darn stack to: " + stack);
168         this.stack = stack;
169         mStackTouched = true;
170     }
171 
172     // This will return true even when the initial location is set.
173     // To get a read on if the user has changed something, use #hasInitialLocationChanged.
hasLocationChanged()174     public boolean hasLocationChanged() {
175         return mStackTouched;
176     }
177 
hasInitialLocationChanged()178     public boolean hasInitialLocationChanged() {
179         return mInitialRootChanged || mInitialDocChanged;
180     }
181 
182     @Override
describeContents()183     public int describeContents() {
184         return 0;
185     }
186 
187     @Override
writeToParcel(Parcel out, int flags)188     public void writeToParcel(Parcel out, int flags) {
189         out.writeInt(action);
190         out.writeStringArray(acceptMimes);
191         out.writeInt(userSortOrder);
192         out.writeInt(allowMultiple ? 1 : 0);
193         out.writeInt(forceSize ? 1 : 0);
194         out.writeInt(showSize ? 1 : 0);
195         out.writeInt(localOnly ? 1 : 0);
196         out.writeInt(showAdvancedOption ? 1 : 0);
197         out.writeInt(showAdvanced ? 1 : 0);
198         out.writeInt(restored ? 1 : 0);
199         out.writeInt(external ? 1 : 0);
200         DurableUtils.writeToParcel(out, stack);
201         out.writeMap(dirState);
202         out.writeList(selectedDocumentsForCopy);
203         out.writeList(excludedAuthorities);
204         out.writeInt(openableOnly ? 1 : 0);
205         out.writeInt(mStackTouched ? 1 : 0);
206         out.writeInt(mInitialRootChanged ? 1 : 0);
207         out.writeInt(mInitialDocChanged ? 1 : 0);
208     }
209 
210     public static final ClassLoaderCreator<State> CREATOR = new ClassLoaderCreator<State>() {
211         @Override
212         public State createFromParcel(Parcel in) {
213             return createFromParcel(in, null);
214         }
215 
216         @Override
217         public State createFromParcel(Parcel in, ClassLoader loader) {
218             final State state = new State();
219             state.action = in.readInt();
220             state.acceptMimes = in.readStringArray();
221             state.userSortOrder = in.readInt();
222             state.allowMultiple = in.readInt() != 0;
223             state.forceSize = in.readInt() != 0;
224             state.showSize = in.readInt() != 0;
225             state.localOnly = in.readInt() != 0;
226             state.showAdvancedOption = in.readInt() != 0;
227             state.showAdvanced = in.readInt() != 0;
228             state.restored = in.readInt() != 0;
229             state.external = in.readInt() != 0;
230             DurableUtils.readFromParcel(in, state.stack);
231             in.readMap(state.dirState, loader);
232             in.readList(state.selectedDocumentsForCopy, loader);
233             in.readList(state.excludedAuthorities, loader);
234             state.openableOnly = in.readInt() != 0;
235             state.mStackTouched = in.readInt() != 0;
236             state.mInitialRootChanged = in.readInt() != 0;
237             state.mInitialDocChanged = in.readInt() != 0;
238             return state;
239         }
240 
241         @Override
242         public State[] newArray(int size) {
243             return new State[size];
244         }
245     };
246 }
247