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
17package com.android.documentsui.roots;
18
19import static com.android.documentsui.base.Shared.DEBUG;
20import static com.android.documentsui.base.Shared.VERBOSE;
21
22import android.util.Log;
23
24import com.android.documentsui.base.MimeTypes;
25import com.android.documentsui.base.RootInfo;
26import com.android.documentsui.base.State;
27
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collection;
31import java.util.List;
32
33/**
34 * Provides testable access to key {@link ProvidersCache} methods.
35 */
36public interface ProvidersAccess {
37
38    String BROADCAST_ACTION = "com.android.documentsui.action.ROOT_CHANGED";
39
40    /**
41     * Return the requested {@link RootInfo}, but only loading the roots for the
42     * requested authority. This is useful when we want to load fast without
43     * waiting for all the other roots to come back.
44     */
45    RootInfo getRootOneshot(String authority, String rootId);
46
47    Collection<RootInfo> getMatchingRootsBlocking(State state);
48
49    Collection<RootInfo> getRootsBlocking();
50
51    RootInfo getDefaultRootBlocking(State state);
52
53    RootInfo getRecentsRoot();
54
55    String getApplicationName(String authority);
56
57    String getPackageName(String authority);
58
59    /**
60     * Returns a list of roots for the specified authority. If not found, then
61     * an empty list is returned.
62     */
63    Collection<RootInfo> getRootsForAuthorityBlocking(String authority);
64
65    public static List<RootInfo> getMatchingRoots(Collection<RootInfo> roots, State state) {
66
67        final String tag = "ProvidersAccess";
68
69        final List<RootInfo> matching = new ArrayList<>();
70        for (RootInfo root : roots) {
71
72            if (VERBOSE) Log.v(tag, "Evaluationg root: " + root);
73
74            if (state.action == State.ACTION_CREATE && !root.supportsCreate()) {
75                if (VERBOSE) Log.v(tag, "Excluding read-only root because: ACTION_CREATE.");
76                continue;
77            }
78
79            if (state.action == State.ACTION_PICK_COPY_DESTINATION
80                    && !root.supportsCreate()) {
81                if (VERBOSE) Log.v(
82                        tag, "Excluding read-only root because: ACTION_PICK_COPY_DESTINATION.");
83                continue;
84            }
85
86            if (state.action == State.ACTION_OPEN_TREE && !root.supportsChildren()) {
87                if (VERBOSE) Log.v(
88                        tag, "Excluding root !supportsChildren because: ACTION_OPEN_TREE.");
89                continue;
90            }
91
92            if (!state.showAdvanced && root.isAdvanced()) {
93                if (VERBOSE) Log.v(tag, "Excluding root because: unwanted advanced device.");
94                continue;
95            }
96
97            if (state.localOnly && !root.isLocalOnly()) {
98                if (VERBOSE) Log.v(tag, "Excluding root because: unwanted non-local device.");
99                continue;
100            }
101
102            if (state.directoryCopy && root.isDownloads()) {
103                if (VERBOSE) Log.v(
104                        tag, "Excluding downloads root because: unsupported directory copy.");
105                continue;
106            }
107
108            if (state.action == State.ACTION_OPEN && root.isEmpty()) {
109                if (VERBOSE) Log.v(tag, "Excluding empty root because: ACTION_OPEN.");
110                continue;
111            }
112
113            if (state.action == State.ACTION_GET_CONTENT && root.isEmpty()) {
114                if (VERBOSE) Log.v(tag, "Excluding empty root because: ACTION_GET_CONTENT.");
115                continue;
116            }
117
118            final boolean overlap =
119                    MimeTypes.mimeMatches(root.derivedMimeTypes, state.acceptMimes) ||
120                    MimeTypes.mimeMatches(state.acceptMimes, root.derivedMimeTypes);
121            if (!overlap) {
122                if (VERBOSE) Log.v(
123                        tag, "Excluding root because: unsupported content types > "
124                        + Arrays.toString(state.acceptMimes));
125                continue;
126            }
127
128            if (state.excludedAuthorities.contains(root.authority)) {
129                if (VERBOSE) Log.v(tag, "Excluding root because: owned by calling package.");
130                continue;
131            }
132
133            matching.add(root);
134        }
135
136        if (DEBUG) Log.d(tag, "Matched roots: " + matching);
137        return matching;
138    }
139}
140