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