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