1/*
2 * Copyright (C) 2017 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 */
16package com.android.documentsui;
17
18import android.annotation.DrawableRes;
19import android.content.Context;
20import android.content.Intent;
21import android.content.pm.ShortcutInfo;
22import android.content.pm.ShortcutManager;
23import android.graphics.drawable.Icon;
24
25import com.android.documentsui.R;
26import com.android.documentsui.base.Providers;
27import com.android.documentsui.base.RootInfo;
28import com.android.documentsui.files.FilesActivity;
29import com.android.documentsui.prefs.ScopedPreferences;
30
31import java.util.ArrayList;
32import java.util.Collection;
33import java.util.HashMap;
34import java.util.List;
35import java.util.Map;
36
37/**
38 * Manages dynamic shortcuts.
39 */
40public final class ShortcutsUpdater {
41
42    private final ScopedPreferences mPrefs;
43    private final Context mContext;
44
45    public ShortcutsUpdater(Context context, ScopedPreferences prefs) {
46        mContext = context;
47        mPrefs = prefs;
48    }
49
50    public void update(Collection<RootInfo> roots) {
51        ShortcutManager mgr = mContext.getSystemService(ShortcutManager.class);
52
53        Map<String, ShortcutInfo> existing = getPinnedShortcuts(mgr);
54        List<ShortcutInfo> devices = getDeviceShortcuts(roots);
55        List<String> deviceIds = new ArrayList<>();
56        for (ShortcutInfo s : devices) {
57            deviceIds.add(s.getId());
58        }
59
60        mgr.setDynamicShortcuts(devices.subList(0, getNumDynSlots(mgr, devices.size())));
61
62        // Mark any shortcut that doesn't correspond to a current root as disabled.
63        List<String> disabled = new ArrayList<>();
64        for (String id : existing.keySet()) {
65            // If it isn't in candidates, it isn't a live target, so we disable it.
66            if (!deviceIds.contains(id)) {
67                disabled.add(id);
68            }
69        }
70
71        mgr.enableShortcuts(deviceIds);
72        mgr.disableShortcuts(disabled);
73    }
74
75    /**
76     * Return at most four awesome devices/roots to include as dynamic shortcuts.
77     */
78    private List<ShortcutInfo> getDeviceShortcuts(Collection<RootInfo> roots) {
79        List<ShortcutInfo> devices = new ArrayList<>();
80        for (RootInfo root : roots) {
81            String id = root.getUri().toString();
82            // TODO: Hook up third party providers. For now, there may be dupes when
83            // user has multiple accounts installed, and the plain title doesn't
84            // disambiguate for the user. So, we don't add them.
85            // if (!Providers.isSystemProvider(root.authority)) {
86            //    // add third party providers at the beginning of the list.
87            //    devices.add(createShortcut(root, R.drawable.ic_folder_shortcut));
88            // } else
89            if (root.isAdvanced() && root.authority.equals(Providers.AUTHORITY_STORAGE)) {
90                // internal storage
91                if (mPrefs.getShowDeviceRoot()) {
92                    devices.add(0, createShortcut(root, R.drawable.ic_advanced_shortcut));
93                }
94            } else if (root.isAdvanced()) {
95                // probably just bugreports provider
96                devices.add(0, createShortcut(root, R.drawable.ic_folder_shortcut));
97            }
98            // TODO: Hook up USB and MTP devices. In order to do this we need
99            // to fire up a broadcast to listen for ACTION_MEDIA_MOUNTED
100            // and ACTION_MEDIA_REMOVED. But doing so now would require a good
101            // bit of refactoring, rendering out of scope for now. <sadface>.
102            // else if (root.isUsb() || root.isMtp()) {
103            //    // probably just bugreports provider
104            //    devices.add(0, createShortcut(root, R.drawable.ic_usb_shortcut));
105            // }
106        }
107
108        return devices;
109    }
110
111    private Map<String, ShortcutInfo> getPinnedShortcuts(ShortcutManager mgr) {
112        Map<String, ShortcutInfo> pinned = new HashMap<>();
113        for (ShortcutInfo s : mgr.getDynamicShortcuts()) {
114            pinned.put(s.getId(), s);
115        }
116        return pinned;
117    }
118
119    private int getNumDynSlots(ShortcutManager mgr, int numDevices) {
120        int slots = mgr.getMaxShortcutCountForActivity() - mgr.getManifestShortcuts().size();
121        return numDevices >= slots ? slots : numDevices;
122    }
123
124    private ShortcutInfo createShortcut(RootInfo root, @DrawableRes int resId) {
125        Intent intent = new Intent(mContext, FilesActivity.class);
126        intent.setAction(Intent.ACTION_VIEW);
127        intent.setData(root.getUri());
128
129        return new ShortcutInfo.Builder(mContext, root.getUri().toString())
130                .setShortLabel(root.title)
131                .setIcon(Icon.createWithResource(mContext, resId))
132                .setIntent(intent)
133                .build();
134    }
135}
136