DirListActivity.java revision 34c68912be678ad50a70c1bfa54a91444e993df5
1/* 2 * Copyright (C) 2010 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.dumprendertree2.ui; 18 19import com.android.dumprendertree2.FileFilter; 20import com.android.dumprendertree2.FsUtils; 21import com.android.dumprendertree2.TestsListActivity; 22import com.android.dumprendertree2.R; 23import com.android.dumprendertree2.forwarder.ForwarderManager; 24 25import android.app.Activity; 26import android.app.AlertDialog; 27import android.app.Dialog; 28import android.app.ListActivity; 29import android.app.ProgressDialog; 30import android.content.DialogInterface; 31import android.content.Intent; 32import android.content.res.Configuration; 33import android.os.Bundle; 34import android.os.Environment; 35import android.os.Handler; 36import android.os.Message; 37import android.view.LayoutInflater; 38import android.view.Menu; 39import android.view.MenuInflater; 40import android.view.MenuItem; 41import android.view.View; 42import android.view.ViewGroup; 43import android.widget.AdapterView; 44import android.widget.ArrayAdapter; 45import android.widget.ImageView; 46import android.widget.ListView; 47import android.widget.TextView; 48 49import java.io.File; 50import java.util.ArrayList; 51import java.util.List; 52 53/** 54 * An Activity that allows navigating through tests folders and choosing folders or tests to run. 55 */ 56public class DirListActivity extends ListActivity { 57 58 private static final String LOG_TAG = "DirListActivity"; 59 60 /** TODO: This is just a guess - think of a better way to achieve it */ 61 private static final int MEAN_TITLE_CHAR_SIZE = 13; 62 63 private static final int PROGRESS_DIALOG_DELAY_MS = 200; 64 65 /** Code for the dialog, used in showDialog and onCreateDialog */ 66 private static final int DIALOG_RUN_ABORT_DIR = 0; 67 68 /** Messages codes */ 69 private static final int MSG_LOADED_ITEMS = 0; 70 private static final int MSG_SHOW_PROGRESS_DIALOG = 1; 71 72 /** Initialized lazily before first sProgressDialog.show() */ 73 private static ProgressDialog sProgressDialog; 74 75 private ListView mListView; 76 77 /** This is a relative path! */ 78 private String mCurrentDirPath; 79 80 private FileFilter mFileFilter; 81 82 /** 83 * A thread responsible for loading the contents of the directory from sd card 84 * and sending them via Message to main thread that then loads them into 85 * ListView 86 */ 87 private class LoadListItemsThread extends Thread { 88 private Handler mHandler; 89 private String mRelativePath; 90 91 public LoadListItemsThread(String relativePath, Handler handler) { 92 mRelativePath = relativePath; 93 mHandler = handler; 94 } 95 96 @Override 97 public void run() { 98 Message msg = mHandler.obtainMessage(MSG_LOADED_ITEMS); 99 msg.obj = getDirList(mRelativePath); 100 mHandler.sendMessage(msg); 101 } 102 } 103 104 /** 105 * Very simple object to use inside ListView as an item. 106 */ 107 private static class ListItem implements Comparable<ListItem> { 108 private String mRelativePath; 109 private String mName; 110 private boolean mIsDirectory; 111 112 public ListItem(String relativePath, boolean isDirectory) { 113 mRelativePath = relativePath; 114 mName = new File(relativePath).getName(); 115 mIsDirectory = isDirectory; 116 } 117 118 public boolean isDirectory() { 119 return mIsDirectory; 120 } 121 122 public String getRelativePath() { 123 return mRelativePath; 124 } 125 126 public String getName() { 127 return mName; 128 } 129 130 @Override 131 public int compareTo(ListItem another) { 132 return mRelativePath.compareTo(another.getRelativePath()); 133 } 134 135 @Override 136 public boolean equals(Object o) { 137 if (!(o instanceof ListItem)) { 138 return false; 139 } 140 141 return mRelativePath.equals(((ListItem)o).getRelativePath()); 142 } 143 144 @Override 145 public int hashCode() { 146 return mRelativePath.hashCode(); 147 } 148 149 } 150 151 /** 152 * A custom adapter that sets the proper icon and label in the list view. 153 */ 154 private static class DirListAdapter extends ArrayAdapter<ListItem> { 155 private Activity mContext; 156 private ListItem[] mItems; 157 158 public DirListAdapter(Activity context, ListItem[] items) { 159 super(context, R.layout.dirlist_row, items); 160 161 mContext = context; 162 mItems = items; 163 } 164 165 @Override 166 public View getView(int position, View convertView, ViewGroup parent) { 167 LayoutInflater inflater = mContext.getLayoutInflater(); 168 View row = inflater.inflate(R.layout.dirlist_row, null); 169 170 TextView label = (TextView)row.findViewById(R.id.label); 171 label.setText(mItems[position].getName()); 172 173 ImageView icon = (ImageView)row.findViewById(R.id.icon); 174 if (mItems[position].isDirectory()) { 175 icon.setImageResource(R.drawable.folder); 176 } else { 177 icon.setImageResource(R.drawable.runtest); 178 } 179 180 return row; 181 } 182 } 183 184 @Override 185 protected void onCreate(Bundle savedInstanceState) { 186 super.onCreate(savedInstanceState); 187 188 ForwarderManager.getForwarderManager().start(); 189 190 mFileFilter = new FileFilter(); 191 mListView = getListView(); 192 193 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 194 @Override 195 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 196 ListItem item = (ListItem)parent.getItemAtPosition(position); 197 198 if (item.isDirectory()) { 199 showDir(item.getRelativePath()); 200 } else { 201 /** Run the test */ 202 runAllTestsUnder(item.getRelativePath()); 203 } 204 } 205 }); 206 207 mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { 208 @Override 209 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { 210 ListItem item = (ListItem)parent.getItemAtPosition(position); 211 212 if (item.isDirectory()) { 213 Bundle arguments = new Bundle(1); 214 arguments.putString("name", item.getName()); 215 arguments.putString("relativePath", item.getRelativePath()); 216 showDialog(DIALOG_RUN_ABORT_DIR, arguments); 217 } else { 218 /** TODO: Maybe show some info about a test? */ 219 } 220 221 return true; 222 } 223 }); 224 225 /** All the paths are relative to test root dir where possible */ 226 showDir(""); 227 } 228 229 private void runAllTestsUnder(String relativePath) { 230 Intent intent = new Intent(); 231 intent.setClass(DirListActivity.this, TestsListActivity.class); 232 intent.setAction(Intent.ACTION_RUN); 233 intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath); 234 startActivity(intent); 235 } 236 237 @Override 238 public boolean onCreateOptionsMenu(Menu menu) { 239 MenuInflater inflater = getMenuInflater(); 240 inflater.inflate(R.menu.gui_menu, menu); 241 return true; 242 } 243 244 @Override 245 public boolean onOptionsItemSelected(MenuItem item) { 246 switch (item.getItemId()) { 247 case R.id.run_all: 248 runAllTestsUnder(mCurrentDirPath); 249 return true; 250 default: 251 return super.onOptionsItemSelected(item); 252 } 253 } 254 255 @Override 256 /** 257 * Moves to the parent directory if one exists. Does not allow to move above 258 * the test 'root' directory. 259 */ 260 public void onBackPressed() { 261 File currentDirParent = new File(mCurrentDirPath).getParentFile(); 262 if (currentDirParent != null) { 263 showDir(currentDirParent.getPath()); 264 } else { 265 showDir(""); 266 } 267 } 268 269 /** 270 * Prevents the activity from recreating on change of orientation. The title needs to 271 * be recalculated. 272 */ 273 @Override 274 public void onConfigurationChanged(Configuration newConfig) { 275 super.onConfigurationChanged(newConfig); 276 277 setTitle(shortenTitle(mCurrentDirPath)); 278 } 279 280 @Override 281 protected Dialog onCreateDialog(int id, final Bundle args) { 282 Dialog dialog = null; 283 AlertDialog.Builder builder = new AlertDialog.Builder(this); 284 285 switch (id) { 286 case DIALOG_RUN_ABORT_DIR: 287 builder.setTitle(getText(R.string.dialog_run_abort_dir_title_prefix) + " " + 288 args.getString("name")); 289 builder.setMessage(R.string.dialog_run_abort_dir_msg); 290 builder.setCancelable(true); 291 292 builder.setPositiveButton(R.string.dialog_run_abort_dir_ok_button, 293 new DialogInterface.OnClickListener() { 294 @Override 295 public void onClick(DialogInterface dialog, int which) { 296 removeDialog(DIALOG_RUN_ABORT_DIR); 297 runAllTestsUnder(args.getString("relativePath")); 298 } 299 }); 300 301 builder.setNegativeButton(R.string.dialog_run_abort_dir_abort_button, 302 new DialogInterface.OnClickListener() { 303 @Override 304 public void onClick(DialogInterface dialog, int which) { 305 removeDialog(DIALOG_RUN_ABORT_DIR); 306 } 307 }); 308 309 dialog = builder.create(); 310 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { 311 @Override 312 public void onCancel(DialogInterface dialog) { 313 removeDialog(DIALOG_RUN_ABORT_DIR); 314 } 315 }); 316 break; 317 } 318 319 return dialog; 320 } 321 322 /** 323 * Loads the contents of dir into the list view. 324 * 325 * @param dirPath 326 * directory to load into list view 327 */ 328 private void showDir(String dirPath) { 329 mCurrentDirPath = dirPath; 330 331 /** Show progress dialog with a delay */ 332 final Handler delayedDialogHandler = new Handler() { 333 @Override 334 public void handleMessage(Message msg) { 335 if (msg.what == MSG_SHOW_PROGRESS_DIALOG) { 336 if (sProgressDialog == null) { 337 sProgressDialog = new ProgressDialog(DirListActivity.this); 338 sProgressDialog.setCancelable(false); 339 sProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); 340 sProgressDialog.setTitle(R.string.dialog_progress_title); 341 sProgressDialog.setMessage(getText(R.string.dialog_progress_msg)); 342 } 343 sProgressDialog.show(); 344 } 345 } 346 }; 347 Message msgShowDialog = delayedDialogHandler.obtainMessage(MSG_SHOW_PROGRESS_DIALOG); 348 delayedDialogHandler.sendMessageDelayed(msgShowDialog, PROGRESS_DIALOG_DELAY_MS); 349 350 /** Delegate loading contents from SD card to a new thread */ 351 new LoadListItemsThread(mCurrentDirPath, new Handler() { 352 @Override 353 public void handleMessage(Message msg) { 354 if (msg.what == MSG_LOADED_ITEMS) { 355 setListAdapter(new DirListAdapter(DirListActivity.this, 356 (ListItem[])msg.obj)); 357 delayedDialogHandler.removeMessages(MSG_SHOW_PROGRESS_DIALOG); 358 setTitle(shortenTitle(mCurrentDirPath)); 359 if (sProgressDialog != null) { 360 sProgressDialog.dismiss(); 361 } 362 } 363 } 364 }).start(); 365 } 366 367 /** 368 * TODO: find a neat way to determine number of characters that fit in the title 369 * bar. 370 * */ 371 private String shortenTitle(String title) { 372 if (title.equals("")) { 373 return "Tests' root dir:"; 374 } 375 int charCount = mListView.getWidth() / MEAN_TITLE_CHAR_SIZE; 376 377 if (title.length() > charCount) { 378 return "..." + title.substring(title.length() - charCount); 379 } else { 380 return title; 381 } 382 } 383 384 /** 385 * Return the array with contents of the given directory. 386 * First it contains the subfolders, then the files. Both sorted 387 * alphabetically. 388 * 389 * The dirPath is relative. 390 */ 391 private ListItem[] getDirList(String dirPath) { 392 List<ListItem> subDirs = new ArrayList<ListItem>(); 393 List<ListItem> subFiles = new ArrayList<ListItem>(); 394 395 for (String dirRelativePath : FsUtils.getLayoutTestsDirContents(dirPath, false, 396 true)) { 397 if (FileFilter.isTestDir(new File(dirRelativePath).getName())) { 398 subDirs.add(new ListItem(dirRelativePath, true)); 399 } 400 } 401 402 for (String testRelativePath : FsUtils.getLayoutTestsDirContents(dirPath, false, 403 false)) { 404 if (FileFilter.isTestFile(new File(testRelativePath).getName())) { 405 subFiles.add(new ListItem(testRelativePath, false)); 406 } 407 } 408 409 /** Concatenate the two lists */ 410 subDirs.addAll(subFiles); 411 412 return subDirs.toArray(new ListItem[subDirs.size()]); 413 } 414} 415