blob: 2fb12bb29233c471cc12d7d5038db33115488eb4 [file] [log] [blame]
Jeff Sharkey76112212013-08-06 11:26:10 -07001/*
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
Jeff Sharkey76112212013-08-06 11:26:10 -070019import android.app.Fragment;
20import android.app.FragmentManager;
21import android.app.FragmentTransaction;
Jeff Sharkey8b997042013-09-19 15:25:56 -070022import android.app.LoaderManager.LoaderCallbacks;
Jeff Sharkey76112212013-08-06 11:26:10 -070023import android.content.Context;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070024import android.content.Intent;
Jeff Sharkey8b997042013-09-19 15:25:56 -070025import android.content.Loader;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070026import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
Jeff Sharkey76112212013-08-06 11:26:10 -070028import android.os.Bundle;
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -070029import android.text.TextUtils;
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -070030import android.text.format.Formatter;
Jeff Sharkey76112212013-08-06 11:26:10 -070031import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.widget.AdapterView;
35import android.widget.AdapterView.OnItemClickListener;
36import android.widget.ArrayAdapter;
37import android.widget.ImageView;
38import android.widget.ListView;
39import android.widget.TextView;
40
Jeff Sharkey1c903cc2013-09-02 17:19:40 -070041import com.android.documentsui.DocumentsActivity.State;
Jeff Sharkey724deeb2013-08-31 15:02:20 -070042import com.android.documentsui.model.DocumentInfo;
43import com.android.documentsui.model.RootInfo;
Jeff Sharkey42d26792013-09-06 13:22:09 -070044import com.android.internal.util.Objects;
Jeff Sharkey9dd02622013-09-27 16:44:11 -070045import com.google.common.collect.Lists;
Jeff Sharkey76112212013-08-06 11:26:10 -070046
Jeff Sharkey8b997042013-09-19 15:25:56 -070047import java.util.Collection;
Jeff Sharkey9dd02622013-09-27 16:44:11 -070048import java.util.Collections;
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -070049import java.util.Comparator;
Jeff Sharkey1d890e02013-08-15 11:24:03 -070050import java.util.List;
Jeff Sharkey76112212013-08-06 11:26:10 -070051
52/**
53 * Display list of known storage backend roots.
54 */
55public class RootsFragment extends Fragment {
56
57 private ListView mList;
Jeff Sharkey9dd02622013-09-27 16:44:11 -070058 private RootsAdapter mAdapter;
Jeff Sharkey76112212013-08-06 11:26:10 -070059
Jeff Sharkey8b997042013-09-19 15:25:56 -070060 private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
61
Jeff Sharkey1d890e02013-08-15 11:24:03 -070062 private static final String EXTRA_INCLUDE_APPS = "includeApps";
63
64 public static void show(FragmentManager fm, Intent includeApps) {
65 final Bundle args = new Bundle();
66 args.putParcelable(EXTRA_INCLUDE_APPS, includeApps);
67
Jeff Sharkey76112212013-08-06 11:26:10 -070068 final RootsFragment fragment = new RootsFragment();
Jeff Sharkey1d890e02013-08-15 11:24:03 -070069 fragment.setArguments(args);
Jeff Sharkey76112212013-08-06 11:26:10 -070070
71 final FragmentTransaction ft = fm.beginTransaction();
72 ft.replace(R.id.container_roots, fragment);
73 ft.commitAllowingStateLoss();
74 }
75
76 public static RootsFragment get(FragmentManager fm) {
77 return (RootsFragment) fm.findFragmentById(R.id.container_roots);
78 }
79
80 @Override
81 public View onCreateView(
82 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
83 final Context context = inflater.getContext();
84
85 final View view = inflater.inflate(R.layout.fragment_roots, container, false);
86 mList = (ListView) view.findViewById(android.R.id.list);
Jeff Sharkey76112212013-08-06 11:26:10 -070087 mList.setOnItemClickListener(mItemListener);
Jeff Sharkey42d26792013-09-06 13:22:09 -070088 mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Jeff Sharkey76112212013-08-06 11:26:10 -070089
90 return view;
91 }
92
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -070093 @Override
Jeff Sharkey8b997042013-09-19 15:25:56 -070094 public void onActivityCreated(Bundle savedInstanceState) {
95 super.onActivityCreated(savedInstanceState);
96
97 final Context context = getActivity();
98 final RootsCache roots = DocumentsApplication.getRootsCache(context);
99 final State state = ((DocumentsActivity) context).getDisplayState();
100
101 mCallbacks = new LoaderCallbacks<Collection<RootInfo>>() {
102 @Override
103 public Loader<Collection<RootInfo>> onCreateLoader(int id, Bundle args) {
104 return new RootsLoader(context, roots, state);
105 }
106
107 @Override
108 public void onLoadFinished(
109 Loader<Collection<RootInfo>> loader, Collection<RootInfo> result) {
110 if (!isAdded()) return;
111
112 final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
113
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700114 mAdapter = new RootsAdapter(context, result, includeApps);
Jeff Sharkey8b997042013-09-19 15:25:56 -0700115 mList.setAdapter(mAdapter);
116
117 onCurrentRootChanged();
118 }
119
120 @Override
121 public void onLoaderReset(Loader<Collection<RootInfo>> loader) {
122 mAdapter = null;
123 mList.setAdapter(null);
124 }
125 };
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700126 }
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700127
Jeff Sharkey8b997042013-09-19 15:25:56 -0700128 @Override
129 public void onResume() {
130 super.onResume();
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700131
Jeff Sharkey8b997042013-09-19 15:25:56 -0700132 final Context context = getActivity();
Jeff Sharkey1c903cc2013-09-02 17:19:40 -0700133 final State state = ((DocumentsActivity) context).getDisplayState();
134 state.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(context);
135
Jeff Sharkey8b997042013-09-19 15:25:56 -0700136 getLoaderManager().restartLoader(2, null, mCallbacks);
Jeff Sharkey42d26792013-09-06 13:22:09 -0700137 }
138
139 public void onCurrentRootChanged() {
140 if (mAdapter == null) return;
141
142 final RootInfo root = ((DocumentsActivity) getActivity()).getCurrentRoot();
143 for (int i = 0; i < mAdapter.getCount(); i++) {
144 final Object item = mAdapter.getItem(i);
145 if (Objects.equal(item, root)) {
146 mList.setItemChecked(i, true);
147 return;
148 }
149 }
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700150 }
151
Jeff Sharkey76112212013-08-06 11:26:10 -0700152 private OnItemClickListener mItemListener = new OnItemClickListener() {
153 @Override
154 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700155 final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700156 final Item item = mAdapter.getItem(position);
157 if (item instanceof RootItem) {
158 activity.onRootPicked(((RootItem) item).root, true);
159 } else if (item instanceof AppItem) {
160 activity.onAppPicked(((AppItem) item).info);
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700161 } else {
162 throw new IllegalStateException("Unknown root: " + item);
163 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700164 }
165 };
166
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700167 private static abstract class Item {
168 private final int mLayoutId;
169
170 public Item(int layoutId) {
171 mLayoutId = layoutId;
172 }
173
174 public View getView(View convertView, ViewGroup parent) {
175 if (convertView == null) {
176 convertView = LayoutInflater.from(parent.getContext())
177 .inflate(mLayoutId, parent, false);
178 }
179 bindView(convertView);
180 return convertView;
181 }
182
183 public abstract void bindView(View convertView);
184 }
185
186 private static class RootItem extends Item {
187 public final RootInfo root;
188
189 public RootItem(RootInfo root) {
190 super(R.layout.item_root);
191 this.root = root;
Jeff Sharkey76112212013-08-06 11:26:10 -0700192 }
193
194 @Override
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700195 public void bindView(View convertView) {
Jeff Sharkey76112212013-08-06 11:26:10 -0700196 final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
197 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
198 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
199
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700200 final Context context = convertView.getContext();
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700201 icon.setImageDrawable(root.loadIcon(context));
Jeff Sharkey76112212013-08-06 11:26:10 -0700202 title.setText(root.title);
203
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700204 // Show available space if no summary
205 String summaryText = root.summary;
206 if (TextUtils.isEmpty(summaryText) && root.availableBytes >= 0) {
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700207 summaryText = context.getString(R.string.root_available_bytes,
208 Formatter.formatFileSize(context, root.availableBytes));
Jeff Sharkeyc6cbdf12013-08-07 16:22:02 -0700209 }
210
211 summary.setText(summaryText);
Jeff Sharkeya35ac2d2013-09-10 12:04:26 -0700212 summary.setVisibility(TextUtils.isEmpty(summaryText) ? View.GONE : View.VISIBLE);
Jeff Sharkey76112212013-08-06 11:26:10 -0700213 }
214 }
215
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700216 private static class SpacerItem extends Item {
217 public SpacerItem() {
218 super(R.layout.item_root_spacer);
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700219 }
220
221 @Override
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700222 public void bindView(View convertView) {
223 // Nothing to bind
224 }
225 }
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700226
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700227 private static class AppItem extends Item {
228 public final ResolveInfo info;
229
230 public AppItem(ResolveInfo info) {
231 super(R.layout.item_root);
232 this.info = info;
233 }
234
235 @Override
236 public void bindView(View convertView) {
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700237 final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
238 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
239 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
240
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700241 final PackageManager pm = convertView.getContext().getPackageManager();
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700242 icon.setImageDrawable(info.loadIcon(pm));
243 title.setText(info.loadLabel(pm));
244
245 // TODO: match existing summary behavior from disambig dialog
246 summary.setVisibility(View.GONE);
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700247 }
248 }
249
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700250 private static class RootsAdapter extends ArrayAdapter<Item> {
251 public RootsAdapter(Context context, Collection<RootInfo> roots, Intent includeApps) {
252 super(context, 0);
Jeff Sharkey76112212013-08-06 11:26:10 -0700253
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700254 RootItem recents = null;
255 RootItem images = null;
256 RootItem videos = null;
257 RootItem audio = null;
258 RootItem downloads = null;
259
260 final List<RootInfo> clouds = Lists.newArrayList();
261 final List<RootInfo> locals = Lists.newArrayList();
Jeff Sharkey76112212013-08-06 11:26:10 -0700262
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700263 for (RootInfo root : roots) {
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700264 if (root.isRecents()) {
265 recents = new RootItem(root);
266 } else if (root.isExternalStorage()) {
267 locals.add(root);
268 } else if (root.isDownloads()) {
269 downloads = new RootItem(root);
270 } else if (root.isImages()) {
271 images = new RootItem(root);
272 } else if (root.isVideos()) {
273 videos = new RootItem(root);
274 } else if (root.isAudio()) {
275 audio = new RootItem(root);
276 } else {
277 clouds.add(root);
Jeff Sharkey40457802013-09-21 13:57:33 -0700278 }
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700279 }
Jeff Sharkey40457802013-09-21 13:57:33 -0700280
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700281 final RootComparator comp = new RootComparator();
282 Collections.sort(clouds, comp);
283 Collections.sort(locals, comp);
284
285 if (recents != null) add(recents);
286
287 for (RootInfo cloud : clouds) {
288 add(new RootItem(cloud));
289 }
290
291 if (images != null) add(images);
292 if (videos != null) add(videos);
293 if (audio != null) add(audio);
294 if (downloads != null) add(downloads);
295
296 for (RootInfo local : locals) {
297 add(new RootItem(local));
Jeff Sharkey76112212013-08-06 11:26:10 -0700298 }
299
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700300 if (includeApps != null) {
301 final PackageManager pm = context.getPackageManager();
302 final List<ResolveInfo> infos = pm.queryIntentActivities(
303 includeApps, PackageManager.MATCH_DEFAULT_ONLY);
304
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700305 final List<AppItem> apps = Lists.newArrayList();
306
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700307 // Omit ourselves from the list
308 for (ResolveInfo info : infos) {
309 if (!context.getPackageName().equals(info.activityInfo.packageName)) {
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700310 apps.add(new AppItem(info));
311 }
312 }
313
314 if (apps.size() > 0) {
315 add(new SpacerItem());
316 for (Item item : apps) {
317 add(item);
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700318 }
319 }
320 }
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700321 }
Jeff Sharkey1d890e02013-08-15 11:24:03 -0700322
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700323 @Override
324 public View getView(int position, View convertView, ViewGroup parent) {
325 final Item item = getItem(position);
326 return item.getView(convertView, parent);
327 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700328
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700329 @Override
330 public boolean areAllItemsEnabled() {
331 return false;
332 }
333
334 @Override
335 public boolean isEnabled(int position) {
336 return getItemViewType(position) != 1;
337 }
338
339 @Override
340 public int getItemViewType(int position) {
341 final Item item = getItem(position);
342 if (item instanceof RootItem || item instanceof AppItem) {
343 return 0;
344 } else {
345 return 1;
Jeff Sharkey40457802013-09-21 13:57:33 -0700346 }
Jeff Sharkey9dd02622013-09-27 16:44:11 -0700347 }
348
349 @Override
350 public int getViewTypeCount() {
351 return 2;
Jeff Sharkey76112212013-08-06 11:26:10 -0700352 }
353 }
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700354
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700355 public static class RootComparator implements Comparator<RootInfo> {
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700356 @Override
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700357 public int compare(RootInfo lhs, RootInfo rhs) {
358 final int score = DocumentInfo.compareToIgnoreCaseNullable(lhs.title, rhs.title);
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700359 if (score != 0) {
360 return score;
361 } else {
Jeff Sharkey724deeb2013-08-31 15:02:20 -0700362 return DocumentInfo.compareToIgnoreCaseNullable(lhs.summary, rhs.summary);
Jeff Sharkeyb6a7f2c2013-08-27 18:26:48 -0700363 }
364 }
365 }
Jeff Sharkey76112212013-08-06 11:26:10 -0700366}