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
17package com.android.documentsui;
18
19import static com.android.documentsui.Shared.DEBUG;
20
21import android.annotation.IntDef;
22import android.content.Intent;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.util.Log;
26import android.util.SparseArray;
27
28import com.android.documentsui.dirlist.MultiSelectManager.Selection;
29import com.android.documentsui.model.DocumentInfo;
30import com.android.documentsui.model.DocumentStack;
31import com.android.documentsui.model.DurableUtils;
32import com.android.documentsui.model.RootInfo;
33import com.android.documentsui.services.FileOperationService;
34import com.android.documentsui.services.FileOperationService.OpType;
35
36import java.lang.annotation.Retention;
37import java.lang.annotation.RetentionPolicy;
38import java.util.ArrayList;
39import java.util.HashMap;
40import java.util.List;
41
42public 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
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
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
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
160    public void popDocument() {
161        if (DEBUG) Log.d(TAG, "Popping doc off stack.");
162        stack.pop();
163        mStackTouched = true;
164    }
165
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.
174    public boolean hasLocationChanged() {
175        return mStackTouched;
176    }
177
178    public boolean hasInitialLocationChanged() {
179        return mInitialRootChanged || mInitialDocChanged;
180    }
181
182    @Override
183    public int describeContents() {
184        return 0;
185    }
186
187    @Override
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