RootsFragment.java revision 348ad6866b91afa4d59d45df533ef88094c74d13
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 android.app.Fragment;
20import android.app.FragmentManager;
21import android.app.FragmentTransaction;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.os.Bundle;
27import android.provider.DocumentsContract.Root;
28import android.text.format.Formatter;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.widget.AdapterView;
33import android.widget.AdapterView.OnItemClickListener;
34import android.widget.ArrayAdapter;
35import android.widget.ImageView;
36import android.widget.ListView;
37import android.widget.TextView;
38
39import com.android.documentsui.DocumentsActivity.State;
40import com.android.documentsui.SectionedListAdapter.SectionAdapter;
41import com.android.documentsui.model.DocumentInfo;
42import com.android.documentsui.model.RootInfo;
43
44import java.util.Comparator;
45import java.util.List;
46
47/**
48 * Display list of known storage backend roots.
49 */
50public class RootsFragment extends Fragment {
51
52    private ListView mList;
53    private SectionedRootsAdapter mAdapter;
54
55    private static final String EXTRA_INCLUDE_APPS = "includeApps";
56
57    public static void show(FragmentManager fm, Intent includeApps) {
58        final Bundle args = new Bundle();
59        args.putParcelable(EXTRA_INCLUDE_APPS, includeApps);
60
61        final RootsFragment fragment = new RootsFragment();
62        fragment.setArguments(args);
63
64        final FragmentTransaction ft = fm.beginTransaction();
65        ft.replace(R.id.container_roots, fragment);
66        ft.commitAllowingStateLoss();
67    }
68
69    public static RootsFragment get(FragmentManager fm) {
70        return (RootsFragment) fm.findFragmentById(R.id.container_roots);
71    }
72
73    @Override
74    public View onCreateView(
75            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
76        final Context context = inflater.getContext();
77
78        final View view = inflater.inflate(R.layout.fragment_roots, container, false);
79        mList = (ListView) view.findViewById(android.R.id.list);
80        mList.setOnItemClickListener(mItemListener);
81
82        return view;
83    }
84
85    @Override
86    public void onStart() {
87        super.onStart();
88        updateRootsAdapter();
89    }
90
91    private void updateRootsAdapter() {
92        final Context context = getActivity();
93
94        final State state = ((DocumentsActivity) context).getDisplayState();
95        state.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(context);
96
97        final RootsCache roots = DocumentsApplication.getRootsCache(context);
98        final List<RootInfo> matchingRoots = roots.getMatchingRoots(state);
99        final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
100
101        mAdapter = new SectionedRootsAdapter(context, matchingRoots, includeApps);
102        mList.setAdapter(mAdapter);
103    }
104
105    private OnItemClickListener mItemListener = new OnItemClickListener() {
106        @Override
107        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
108            final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
109            final Object item = mAdapter.getItem(position);
110            if (item instanceof RootInfo) {
111                activity.onRootPicked((RootInfo) item, true);
112            } else if (item instanceof ResolveInfo) {
113                activity.onAppPicked((ResolveInfo) item);
114            } else {
115                throw new IllegalStateException("Unknown root: " + item);
116            }
117        }
118    };
119
120    private static class RootsAdapter extends ArrayAdapter<RootInfo> implements SectionAdapter {
121        private int mHeaderId;
122
123        public RootsAdapter(Context context, int headerId) {
124            super(context, 0);
125            mHeaderId = headerId;
126        }
127
128        @Override
129        public View getView(int position, View convertView, ViewGroup parent) {
130            final Context context = parent.getContext();
131            if (convertView == null) {
132                convertView = LayoutInflater.from(context)
133                        .inflate(R.layout.item_root, parent, false);
134            }
135
136            final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
137            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
138            final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
139
140            final RootInfo root = getItem(position);
141            icon.setImageDrawable(root.loadIcon(context));
142            title.setText(root.title);
143
144            // Device summary is always available space
145            final String summaryText;
146            if (root.rootType == Root.ROOT_TYPE_DEVICE && root.availableBytes >= 0) {
147                summaryText = context.getString(R.string.root_available_bytes,
148                        Formatter.formatFileSize(context, root.availableBytes));
149            } else {
150                summaryText = root.summary;
151            }
152
153            summary.setText(summaryText);
154            summary.setVisibility(summaryText != null ? View.VISIBLE : View.GONE);
155
156            return convertView;
157        }
158
159        @Override
160        public View getHeaderView(View convertView, ViewGroup parent) {
161            if (convertView == null) {
162                convertView = LayoutInflater.from(parent.getContext())
163                        .inflate(R.layout.item_root_header, parent, false);
164            }
165
166            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
167            title.setText(mHeaderId);
168
169            return convertView;
170        }
171    }
172
173    private static class AppsAdapter extends ArrayAdapter<ResolveInfo> implements SectionAdapter {
174        public AppsAdapter(Context context) {
175            super(context, 0);
176        }
177
178        @Override
179        public View getView(int position, View convertView, ViewGroup parent) {
180            final Context context = parent.getContext();
181            final PackageManager pm = context.getPackageManager();
182            if (convertView == null) {
183                convertView = LayoutInflater.from(context)
184                        .inflate(R.layout.item_root, parent, false);
185            }
186
187            final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
188            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
189            final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
190
191            final ResolveInfo info = getItem(position);
192            icon.setImageDrawable(info.loadIcon(pm));
193            title.setText(info.loadLabel(pm));
194
195            // TODO: match existing summary behavior from disambig dialog
196            summary.setVisibility(View.GONE);
197
198            return convertView;
199        }
200
201        @Override
202        public View getHeaderView(View convertView, ViewGroup parent) {
203            if (convertView == null) {
204                convertView = LayoutInflater.from(parent.getContext())
205                        .inflate(R.layout.item_root_header, parent, false);
206            }
207
208            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
209            title.setText(R.string.root_type_apps);
210
211            return convertView;
212        }
213    }
214
215    private static class SectionedRootsAdapter extends SectionedListAdapter {
216        private final RootsAdapter mServices;
217        private final RootsAdapter mShortcuts;
218        private final RootsAdapter mDevices;
219        private final AppsAdapter mApps;
220
221        public SectionedRootsAdapter(Context context, List<RootInfo> roots, Intent includeApps) {
222            mServices = new RootsAdapter(context, R.string.root_type_service);
223            mShortcuts = new RootsAdapter(context, R.string.root_type_shortcut);
224            mDevices = new RootsAdapter(context, R.string.root_type_device);
225            mApps = new AppsAdapter(context);
226
227            for (RootInfo root : roots) {
228                switch (root.rootType) {
229                    case Root.ROOT_TYPE_SERVICE:
230                        mServices.add(root);
231                        break;
232                    case Root.ROOT_TYPE_SHORTCUT:
233                        mShortcuts.add(root);
234                        break;
235                    case Root.ROOT_TYPE_DEVICE:
236                        mDevices.add(root);
237                        break;
238                }
239            }
240
241            if (includeApps != null) {
242                final PackageManager pm = context.getPackageManager();
243                final List<ResolveInfo> infos = pm.queryIntentActivities(
244                        includeApps, PackageManager.MATCH_DEFAULT_ONLY);
245
246                // Omit ourselves from the list
247                for (ResolveInfo info : infos) {
248                    if (!context.getPackageName().equals(info.activityInfo.packageName)) {
249                        mApps.add(info);
250                    }
251                }
252            }
253
254            final RootComparator comp = new RootComparator();
255            mServices.sort(comp);
256            mShortcuts.sort(comp);
257            mDevices.sort(comp);
258
259            if (mServices.getCount() > 0) {
260                addSection(mServices);
261            }
262            if (mShortcuts.getCount() > 0) {
263                addSection(mShortcuts);
264            }
265            if (mDevices.getCount() > 0) {
266                addSection(mDevices);
267            }
268            if (mApps.getCount() > 0) {
269                addSection(mApps);
270            }
271        }
272    }
273
274    public static class RootComparator implements Comparator<RootInfo> {
275        @Override
276        public int compare(RootInfo lhs, RootInfo rhs) {
277            if (lhs.authority == null) {
278                return -1;
279            } else if (rhs.authority == null) {
280                return 1;
281            }
282
283            final int score = DocumentInfo.compareToIgnoreCaseNullable(lhs.title, rhs.title);
284            if (score != 0) {
285                return score;
286            } else {
287                return DocumentInfo.compareToIgnoreCaseNullable(lhs.summary, rhs.summary);
288            }
289        }
290    }
291}
292