blob: b1862efa80fd1136d9728019447ad1c50cbb90b5 [file] [log] [blame]
Maksymilian Osowski6fd71122010-07-07 16:21:02 +01001/*
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;
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +010020import com.android.dumprendertree2.FsUtils;
Maksymilian Osowski5f0ccd72010-07-23 17:15:26 +010021import com.android.dumprendertree2.TestsListActivity;
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010022import com.android.dumprendertree2.R;
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +010023import com.android.dumprendertree2.forwarder.ForwarderManager;
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010024
25import android.app.Activity;
Maksymilian Osowskicc483d22010-07-09 12:33:43 +010026import android.app.AlertDialog;
27import android.app.Dialog;
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010028import android.app.ListActivity;
Maksymilian Osowskicc483d22010-07-09 12:33:43 +010029import android.app.ProgressDialog;
30import android.content.DialogInterface;
Maksymilian Osowski3c8ccb32010-07-12 15:59:26 +010031import android.content.Intent;
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010032import android.content.res.Configuration;
33import android.os.Bundle;
34import android.os.Environment;
Maksymilian Osowskicc483d22010-07-09 12:33:43 +010035import android.os.Handler;
36import android.os.Message;
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010037import android.view.LayoutInflater;
Maksymilian Osowskiff314d72010-09-01 16:46:48 +010038import android.view.Menu;
39import android.view.MenuInflater;
40import android.view.MenuItem;
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010041import 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;
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010051import 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 private static final String ROOT_DIR_PATH =
60 Environment.getExternalStorageDirectory() +
61 File.separator + "android" +
62 File.separator + "LayoutTests";
63
64 /** TODO: This is just a guess - think of a better way to achieve it */
Maksymilian Osowskicc483d22010-07-09 12:33:43 +010065 private static final int MEAN_TITLE_CHAR_SIZE = 13;
66
67 private static final int PROGRESS_DIALOG_DELAY_MS = 200;
68
69 /** Code for the dialog, used in showDialog and onCreateDialog */
70 private static final int DIALOG_RUN_ABORT_DIR = 0;
71
72 /** Messages codes */
73 private static final int MSG_LOADED_ITEMS = 0;
74 private static final int MSG_SHOW_PROGRESS_DIALOG = 1;
75
76 /** Initialized lazily before first sProgressDialog.show() */
77 private static ProgressDialog sProgressDialog;
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010078
79 private ListView mListView;
80
81 /** This is a relative path! */
82 private String mCurrentDirPath;
83
84 /**
85 * TODO: This should not be a constant, but rather be configurable from somewhere.
86 */
87 private String mRootDirPath = ROOT_DIR_PATH;
88
Maksymilian Osowski3c8ccb32010-07-12 15:59:26 +010089 private FileFilter mFileFilter;
90
Maksymilian Osowski6fd71122010-07-07 16:21:02 +010091 /**
Maksymilian Osowskicc483d22010-07-09 12:33:43 +010092 * A thread responsible for loading the contents of the directory from sd card
93 * and sending them via Message to main thread that then loads them into
94 * ListView
95 */
96 private class LoadListItemsThread extends Thread {
97 private Handler mHandler;
98 private String mRelativePath;
99
100 public LoadListItemsThread(String relativePath, Handler handler) {
101 mRelativePath = relativePath;
102 mHandler = handler;
103 }
104
105 @Override
106 public void run() {
107 Message msg = mHandler.obtainMessage(MSG_LOADED_ITEMS);
108 msg.obj = getDirList(mRelativePath);
109 mHandler.sendMessage(msg);
110 }
111 }
112
113 /**
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100114 * Very simple object to use inside ListView as an item.
115 */
116 private static class ListItem implements Comparable<ListItem> {
117 private String mRelativePath;
118 private String mName;
119 private boolean mIsDirectory;
120
121 public ListItem(String relativePath, boolean isDirectory) {
122 mRelativePath = relativePath;
123 mName = new File(relativePath).getName();
124 mIsDirectory = isDirectory;
125 }
126
127 public boolean isDirectory() {
128 return mIsDirectory;
129 }
130
131 public String getRelativePath() {
132 return mRelativePath;
133 }
134
135 public String getName() {
136 return mName;
137 }
138
139 @Override
140 public int compareTo(ListItem another) {
141 return mRelativePath.compareTo(another.getRelativePath());
142 }
143
144 @Override
145 public boolean equals(Object o) {
146 if (!(o instanceof ListItem)) {
147 return false;
148 }
149
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +0100150 return mRelativePath.equals(((ListItem)o).getRelativePath());
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100151 }
152
153 @Override
154 public int hashCode() {
155 return mRelativePath.hashCode();
156 }
157
158 }
159
160 /**
161 * A custom adapter that sets the proper icon and label in the list view.
162 */
163 private static class DirListAdapter extends ArrayAdapter<ListItem> {
164 private Activity mContext;
165 private ListItem[] mItems;
166
167 public DirListAdapter(Activity context, ListItem[] items) {
168 super(context, R.layout.dirlist_row, items);
169
170 mContext = context;
171 mItems = items;
172 }
173
174 @Override
175 public View getView(int position, View convertView, ViewGroup parent) {
176 LayoutInflater inflater = mContext.getLayoutInflater();
177 View row = inflater.inflate(R.layout.dirlist_row, null);
178
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +0100179 TextView label = (TextView)row.findViewById(R.id.label);
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100180 label.setText(mItems[position].getName());
181
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +0100182 ImageView icon = (ImageView)row.findViewById(R.id.icon);
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100183 if (mItems[position].isDirectory()) {
184 icon.setImageResource(R.drawable.folder);
185 } else {
186 icon.setImageResource(R.drawable.runtest);
187 }
188
189 return row;
190 }
191 }
192
193 @Override
194 protected void onCreate(Bundle savedInstanceState) {
195 super.onCreate(savedInstanceState);
196
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +0100197 ForwarderManager.getForwarderManager().start();
198
Maksymilian Osowski3c8ccb32010-07-12 15:59:26 +0100199 mFileFilter = new FileFilter(ROOT_DIR_PATH);
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100200 mListView = getListView();
201
202 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100203 @Override
204 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +0100205 ListItem item = (ListItem)parent.getItemAtPosition(position);
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100206
207 if (item.isDirectory()) {
208 showDir(item.getRelativePath());
209 } else {
Maksymilian Osowski3c8ccb32010-07-12 15:59:26 +0100210 /** Run the test */
Maksymilian Osowskiff314d72010-09-01 16:46:48 +0100211 runAllTestsUnder(item.getRelativePath());
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100212 }
213 }
214 });
215
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100216 mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
217 @Override
218 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +0100219 ListItem item = (ListItem)parent.getItemAtPosition(position);
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100220
221 if (item.isDirectory()) {
222 Bundle arguments = new Bundle(1);
223 arguments.putString("name", item.getName());
224 arguments.putString("relativePath", item.getRelativePath());
225 showDialog(DIALOG_RUN_ABORT_DIR, arguments);
226 } else {
227 /** TODO: Maybe show some info about a test? */
228 }
229
230 return true;
231 }
232 });
233
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100234 /** All the paths are relative to test root dir where possible */
235 showDir("");
236 }
237
Maksymilian Osowskiff314d72010-09-01 16:46:48 +0100238 private void runAllTestsUnder(String relativePath) {
239 Intent intent = new Intent();
240 intent.setClass(DirListActivity.this, TestsListActivity.class);
241 intent.setAction(Intent.ACTION_RUN);
242 intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath);
243 startActivity(intent);
244 }
245
246 @Override
247 public boolean onCreateOptionsMenu(Menu menu) {
248 MenuInflater inflater = getMenuInflater();
249 inflater.inflate(R.menu.gui_menu, menu);
250 return true;
251 }
252
253 @Override
254 public boolean onOptionsItemSelected(MenuItem item) {
255 switch (item.getItemId()) {
256 case R.id.run_all:
257 runAllTestsUnder(mCurrentDirPath);
258 return true;
259 default:
260 return super.onOptionsItemSelected(item);
261 }
262 }
263
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100264 @Override
265 /**
266 * Moves to the parent directory if one exists. Does not allow to move above
267 * the test 'root' directory.
268 */
269 public void onBackPressed() {
270 File currentDirParent = new File(mCurrentDirPath).getParentFile();
271 if (currentDirParent != null) {
272 showDir(currentDirParent.getPath());
273 } else {
274 showDir("");
275 }
276 }
277
278 /**
279 * Prevents the activity from recreating on change of orientation. The title needs to
280 * be recalculated.
281 */
282 @Override
283 public void onConfigurationChanged(Configuration newConfig) {
284 super.onConfigurationChanged(newConfig);
285
286 setTitle(shortenTitle(mCurrentDirPath));
287 }
288
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100289 @Override
Maksymilian Osowski3c8ccb32010-07-12 15:59:26 +0100290 protected Dialog onCreateDialog(int id, final Bundle args) {
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100291 Dialog dialog = null;
292 AlertDialog.Builder builder = new AlertDialog.Builder(this);
293
294 switch (id) {
295 case DIALOG_RUN_ABORT_DIR:
296 builder.setTitle(getText(R.string.dialog_run_abort_dir_title_prefix) + " " +
297 args.getString("name"));
298 builder.setMessage(R.string.dialog_run_abort_dir_msg);
299 builder.setCancelable(true);
300
301 builder.setPositiveButton(R.string.dialog_run_abort_dir_ok_button,
302 new DialogInterface.OnClickListener() {
303 @Override
304 public void onClick(DialogInterface dialog, int which) {
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100305 removeDialog(DIALOG_RUN_ABORT_DIR);
Maksymilian Osowskiff314d72010-09-01 16:46:48 +0100306 runAllTestsUnder(args.getString("relativePath"));
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100307 }
308 });
309
310 builder.setNegativeButton(R.string.dialog_run_abort_dir_abort_button,
311 new DialogInterface.OnClickListener() {
312 @Override
313 public void onClick(DialogInterface dialog, int which) {
314 removeDialog(DIALOG_RUN_ABORT_DIR);
315 }
316 });
317
318 dialog = builder.create();
319 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
320 @Override
321 public void onCancel(DialogInterface dialog) {
322 removeDialog(DIALOG_RUN_ABORT_DIR);
323 }
324 });
325 break;
326 }
327
328 return dialog;
329 }
330
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100331 /**
332 * Loads the contents of dir into the list view.
333 *
334 * @param dirPath
335 * directory to load into list view
336 */
337 private void showDir(String dirPath) {
338 mCurrentDirPath = dirPath;
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100339
340 /** Show progress dialog with a delay */
341 final Handler delayedDialogHandler = new Handler() {
342 @Override
343 public void handleMessage(Message msg) {
344 if (msg.what == MSG_SHOW_PROGRESS_DIALOG) {
345 if (sProgressDialog == null) {
346 sProgressDialog = new ProgressDialog(DirListActivity.this);
347 sProgressDialog.setCancelable(false);
348 sProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
349 sProgressDialog.setTitle(R.string.dialog_progress_title);
350 sProgressDialog.setMessage(getText(R.string.dialog_progress_msg));
351 }
352 sProgressDialog.show();
353 }
354 }
355 };
356 Message msgShowDialog = delayedDialogHandler.obtainMessage(MSG_SHOW_PROGRESS_DIALOG);
357 delayedDialogHandler.sendMessageDelayed(msgShowDialog, PROGRESS_DIALOG_DELAY_MS);
358
359 /** Delegate loading contents from SD card to a new thread */
360 new LoadListItemsThread(mCurrentDirPath, new Handler() {
361 @Override
362 public void handleMessage(Message msg) {
363 if (msg.what == MSG_LOADED_ITEMS) {
364 setListAdapter(new DirListAdapter(DirListActivity.this,
365 (ListItem[])msg.obj));
366 delayedDialogHandler.removeMessages(MSG_SHOW_PROGRESS_DIALOG);
367 setTitle(shortenTitle(mCurrentDirPath));
368 if (sProgressDialog != null) {
369 sProgressDialog.dismiss();
370 }
371 }
372 }
373 }).start();
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100374 }
375
376 /**
377 * TODO: find a neat way to determine number of characters that fit in the title
378 * bar.
379 * */
380 private String shortenTitle(String title) {
381 if (title.equals("")) {
382 return "Tests' root dir:";
383 }
384 int charCount = mListView.getWidth() / MEAN_TITLE_CHAR_SIZE;
385
386 if (title.length() > charCount) {
387 return "..." + title.substring(title.length() - charCount);
388 } else {
389 return title;
390 }
391 }
392
393 /**
394 * Return the array with contents of the given directory.
395 * First it contains the subfolders, then the files. Both sorted
396 * alphabetically.
Maksymilian Osowskicc483d22010-07-09 12:33:43 +0100397 *
398 * The dirPath is relative.
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100399 */
400 private ListItem[] getDirList(String dirPath) {
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100401 List<ListItem> subDirs = new ArrayList<ListItem>();
402 List<ListItem> subFiles = new ArrayList<ListItem>();
403
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +0100404 for (String dirRelativePath : FsUtils.getLayoutTestsDirContents(dirPath, false,
405 true)) {
406 if (FileFilter.isTestDir(new File(dirRelativePath).getName())) {
407 subDirs.add(new ListItem(dirRelativePath, true));
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100408 }
409 }
410
Maksymilian Osowskic6a341d2010-08-23 16:57:27 +0100411 for (String testRelativePath : FsUtils.getLayoutTestsDirContents(dirPath, false,
412 false)) {
413 if (FileFilter.isTestFile(new File(testRelativePath).getName())) {
414 subFiles.add(new ListItem(testRelativePath, false));
415 }
416 }
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100417
418 /** Concatenate the two lists */
419 subDirs.addAll(subFiles);
420
421 return subDirs.toArray(new ListItem[subDirs.size()]);
422 }
Maksymilian Osowski6fd71122010-07-07 16:21:02 +0100423}