/android/import-contacts

To get this branch, use:
bzr branch http://bzr.ed.am/android/import-contacts

« back to all changes in this revision

Viewing changes to src/am/ed/importcontacts/FileChooser.java

  • Committer: edam
  • Date: 2012-12-21 10:44:01 UTC
  • Revision ID: tim@ed.am-20121221104401-onydwv9k4e4heode
attempt to allow arguments before main command by treating the first thing that
doesn't look like an option as the command
added thank you message to intro

Show diffs side-by-side

added added

removed removed

 
1
/*
 
2
 * FileChooser.java
 
3
 *
 
4
 * Copyright (C) 2010 Tim Marston <tim@ed.am>
 
5
 *
 
6
 * This file is part of the Import Contacts program (hereafter referred
 
7
 * to as "this program"). For more information, see
 
8
 * http://ed.am/dev/android/import-contacts
 
9
 *
 
10
 * This program is free software: you can redistribute it and/or modify
 
11
 * it under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 *
 
15
 * This program is distributed in the hope that it will be useful,
 
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
18
 * GNU General Public License for more details.
 
19
 *
 
20
 * You should have received a copy of the GNU General Public License
 
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
22
 */
 
23
 
 
24
package am.ed.importcontacts;
 
25
 
 
26
import java.io.File;
 
27
import java.io.FileFilter;
 
28
import java.io.IOException;
 
29
import java.util.ArrayList;
 
30
import java.util.Collections;
 
31
import java.util.Comparator;
 
32
 
 
33
import android.app.AlertDialog;
 
34
import android.app.Dialog;
 
35
import android.content.Context;
 
36
import android.content.DialogInterface;
 
37
import android.os.Environment;
 
38
import android.view.LayoutInflater;
 
39
import android.view.View;
 
40
import android.view.View.OnClickListener;
 
41
import android.view.ViewGroup;
 
42
import android.widget.AdapterView;
 
43
import android.widget.AdapterView.OnItemClickListener;
 
44
import android.widget.ArrayAdapter;
 
45
import android.widget.Button;
 
46
import android.widget.ImageView;
 
47
import android.widget.ListView;
 
48
import android.widget.TextView;
 
49
 
 
50
public class FileChooser
 
51
{
 
52
        // pick an existing directory
 
53
        public final static int MODE_DIR = 1;
 
54
 
 
55
        // pick an existing file
 
56
        public final static int MODE_FILE = 2;
 
57
 
 
58
 
 
59
        private Dialog _dialog;
 
60
 
 
61
        // mode
 
62
        private int _mode = MODE_DIR;
 
63
 
 
64
        // ok was pressed
 
65
        boolean _ok = false;
 
66
 
 
67
        // working path
 
68
        private String _path;
 
69
 
 
70
        // selected filename
 
71
        private String _filename;
 
72
 
 
73
        // enforce extension (in file-mode)
 
74
        private String[] _extensions;
 
75
 
 
76
        // path to secretly prefix all paths with
 
77
        private String _path_prefix = "";
 
78
 
 
79
        private Context _context;
 
80
        private ArrayList< RowItem > _items;
 
81
        private DialogInterface.OnDismissListener _on_dismiss_listener;
 
82
 
 
83
        // class that represents a row in the list
 
84
        private class RowItem implements Comparable< RowItem >
 
85
        {
 
86
                private String _name;
 
87
                private boolean _directory;
 
88
 
 
89
                public RowItem( String name, boolean directory )
 
90
                {
 
91
                        _name = name;
 
92
                        _directory = directory;
 
93
                }
 
94
 
 
95
                public String getName()
 
96
                {
 
97
                        return _name;
 
98
                }
 
99
 
 
100
                public boolean isDirectory()
 
101
                {
 
102
                        return _directory;
 
103
                }
 
104
 
 
105
                @Override
 
106
                public int compareTo( RowItem that )
 
107
                {
 
108
                        if( this._directory && !that._directory )
 
109
                                return -1;
 
110
                        else if( !this._directory && that._directory )
 
111
                                return 1;
 
112
                        else
 
113
                                return this._name.compareToIgnoreCase( that._name );
 
114
                }
 
115
        }
 
116
 
 
117
        // class to manage our list of RowItems
 
118
        private class RowItemAdapter extends ArrayAdapter< RowItem >
 
119
        {
 
120
                private ArrayList< RowItem > _items;
 
121
 
 
122
                public RowItemAdapter( Context context, int textview_resource_id,
 
123
                        ArrayList< RowItem > items )
 
124
                {
 
125
                        super( context, textview_resource_id, items );
 
126
                        _items = items;
 
127
                }
 
128
 
 
129
                @Override
 
130
                public View getView( int position, View convert_view, ViewGroup parent )
 
131
                {
 
132
                        View view = convert_view;
 
133
                        if( view == null ) {
 
134
                                LayoutInflater factory = LayoutInflater.from( _context );
 
135
                                view = factory.inflate(  R.layout.filechooser_row, null );
 
136
                        }
 
137
                        RowItem rowitem = _items.get( position );
 
138
                        if( rowitem != null ) {
 
139
                                ( (TextView)view.findViewById( R.id.name ) )
 
140
                                        .setText( rowitem.getName() );
 
141
                                ( (ImageView)view.findViewById( R.id.icon ) ).setVisibility(
 
142
                                        rowitem.isDirectory()? View.VISIBLE : View.GONE );
 
143
                        }
 
144
                        return view;
 
145
                }
 
146
        }
 
147
 
 
148
        @SuppressWarnings( "serial" )
 
149
        class InvalidPathPrefixException extends RuntimeException
 
150
        {
 
151
        }
 
152
 
 
153
 
 
154
 
 
155
        // constructor
 
156
        public FileChooser( Context context )
 
157
        {
 
158
                _context = context;
 
159
        }
 
160
 
 
161
        public void setMode( int mode )
 
162
        {
 
163
                _mode = mode;
 
164
        }
 
165
 
 
166
        public void setPath( String path )
 
167
        {
 
168
                _path = cleanUpPath( path );
 
169
                File file = new File( _path_prefix + path.trim() );
 
170
 
 
171
                // path and filename
 
172
                if( file.isFile() ) {
 
173
                        _path = _path.substring( 0, _path.length() - 1 );
 
174
                        _filename = _path.substring( _path.lastIndexOf( '/' ) + 1 );
 
175
                        _path = _path.substring( 0, _path.length() - _filename.length() );
 
176
                }
 
177
 
 
178
                // else, treat as just a path
 
179
                else
 
180
                        _filename = "";
 
181
        }
 
182
 
 
183
        public void setExtensions( String[] extensions )
 
184
        {
 
185
                _extensions = extensions;
 
186
        }
 
187
 
 
188
        // set dismiss listener
 
189
        public void setDismissListener(
 
190
                DialogInterface.OnDismissListener on_dismiss_listener )
 
191
        {
 
192
                _on_dismiss_listener = on_dismiss_listener;
 
193
        }
 
194
 
 
195
        // set the path prefix
 
196
        public void setPathPrefix( String path_prefix )
 
197
        {
 
198
                // set to cleaned-up path, with trailing '/' removed so that it can be
 
199
                // trivially pre-pended to a cleaned-up path
 
200
                _path_prefix = cleanUpPath( path_prefix );
 
201
                _path_prefix = _path_prefix.substring( 0, _path_prefix.length() - 1 );
 
202
        }
 
203
 
 
204
        public boolean getOk()
 
205
        {
 
206
                return _ok;
 
207
        }
 
208
 
 
209
        public String getPath()
 
210
        {
 
211
                return _path + _filename;
 
212
        }
 
213
 
 
214
        public Dialog onCreateDialog()
 
215
        {
 
216
                // custom layout in an AlertDialog
 
217
                LayoutInflater factory = LayoutInflater.from( _context );
 
218
                final View dialogView = factory.inflate(
 
219
                        R.layout.filechooser, null );
 
220
 
 
221
                // wire up buttons
 
222
                ( (Button)dialogView.findViewById( R.id.ok ) )
 
223
                        .setOnClickListener( _fileChooserButtonListener );
 
224
                ( (ListView)dialogView.findViewById( R.id.list ) )
 
225
                        .setOnItemClickListener( _fileChooserItemClickListener );
 
226
 
 
227
                // return dialog
 
228
                Dialog dialog = new AlertDialog.Builder( _context )
 
229
                        .setTitle( " " )
 
230
                        .setView( dialogView )
 
231
                        .create();
 
232
                dialog.setOnDismissListener( _on_dismiss_listener );
 
233
                return dialog;
 
234
        }
 
235
 
 
236
        private OnClickListener _fileChooserButtonListener = new OnClickListener() {
 
237
                public void onClick( View view )
 
238
                {
 
239
                        switch( view.getId() )
 
240
                        {
 
241
                        case R.id.ok:
 
242
                                // close dialog and free (don't keep a reference)
 
243
                                _ok = true;
 
244
                                _dialog.dismiss();
 
245
                                break;
 
246
                        }
 
247
                }
 
248
        };
 
249
 
 
250
        private OnItemClickListener _fileChooserItemClickListener = new OnItemClickListener() {
 
251
                public void onItemClick( AdapterView< ? > adapter_view, View view, int position, long id )
 
252
                {
 
253
                        RowItem rowitem = _items.get( position );
 
254
 
 
255
                        // handle directory changes
 
256
                        if( rowitem.isDirectory() )
 
257
                        {
 
258
                                String dirname = rowitem.getName();
 
259
                                if( dirname.equals( ".." ) )
 
260
                                        strtipLastFilepartFromPath();
 
261
                                else
 
262
                                        _path += dirname + "/";
 
263
                                _filename = "";
 
264
 
 
265
                                updateList();
 
266
                        }
 
267
 
 
268
                        // handle file selections
 
269
                        else
 
270
                        {
 
271
                                _filename = rowitem.getName();
 
272
                                updateCurrentSelection();
 
273
                        }
 
274
                }
 
275
        };
 
276
 
 
277
        public void onPrepareDialog( Context context, Dialog dialog )
 
278
        {
 
279
                // set up reference to dialog
 
280
                _dialog = dialog;
 
281
                _context = context;
 
282
 
 
283
                // reset "ok"
 
284
                _ok = false;
 
285
 
 
286
                // pick text based on mode
 
287
                int title = 0, current = 0;
 
288
                switch( _mode ) {
 
289
                case MODE_DIR:
 
290
                        title = R.string.filechooser_title_dir;
 
291
                        current = R.string.filechooser_current_dir;
 
292
                        break;
 
293
                case MODE_FILE:
 
294
                        title = R.string.filechooser_title_file;
 
295
                        current = R.string.filechooser_current_file;
 
296
                        break;
 
297
                }
 
298
                dialog.setTitle( title );
 
299
                ( (TextView)dialog.findViewById( R.id.current ) )
 
300
                        .setText( _context.getString(  current ) );
 
301
 
 
302
                // clear filename in directory mode
 
303
                if( _mode == MODE_DIR )
 
304
                        _filename = "";
 
305
 
 
306
                // set root path icon
 
307
                ( (ImageView)_dialog.findViewById( R.id.icon ) )
 
308
                        .setImageResource( pathIcon( cleanUpPath( _path_prefix ) ) );
 
309
 
 
310
                // setup current-path-specific stuff
 
311
                updateList();
 
312
        }
 
313
 
 
314
        public static String cleanUpPath( String path )
 
315
        {
 
316
                path = path.trim();
 
317
 
 
318
                // ensure it starts and ends in a '/'
 
319
                if( !path.startsWith( "/" ) ) path = "/" + path;
 
320
                if( !path.endsWith( "/" ) ) path += "/";
 
321
 
 
322
                return path;
 
323
        }
 
324
 
 
325
        public static int pathIcon( String path )
 
326
        {
 
327
                // get sdcard path
 
328
                String sdcard_path;
 
329
                try {
 
330
                        sdcard_path = Environment.getExternalStorageDirectory()
 
331
                                .getCanonicalPath();
 
332
                        if( sdcard_path.charAt( sdcard_path.length() - 1 ) != '/' )
 
333
                                sdcard_path += "/";
 
334
                }
 
335
                catch( IOException e ) {
 
336
                        sdcard_path = null;
 
337
                }
 
338
 
 
339
                // special paths
 
340
                if( sdcard_path != null && path.equals( sdcard_path ) )
 
341
                        return R.drawable.sdcard;
 
342
 
 
343
                // default
 
344
                return R.drawable.directory;
 
345
        }
 
346
 
 
347
        public String prettyPrint( String full_path, boolean return_full )
 
348
        {
 
349
                String path = full_path;
 
350
 
 
351
                // get sdcard path
 
352
                String sdcard_path;
 
353
                try {
 
354
                        sdcard_path = Environment.getExternalStorageDirectory()
 
355
                                .getCanonicalPath();
 
356
                        if( sdcard_path.charAt( sdcard_path.length() - 1 ) != '/' )
 
357
                                sdcard_path += "/";
 
358
                }
 
359
                catch( IOException e ) {
 
360
                        sdcard_path = null;
 
361
                }
 
362
 
 
363
                // special names
 
364
                if( sdcard_path != null && path.equals( sdcard_path ) )
 
365
                        return " " + _context.getString( R.string.filechooser_path_sdcard );
 
366
 
 
367
                // remove prefix, if present
 
368
                if( path.startsWith( _path_prefix + "/" ) )
 
369
                        path = path.substring( _path_prefix.length() );
 
370
 
 
371
                // unless path is "/", strip trailing "/".
 
372
                if( path.length() > 1 && path.endsWith( "/" ) )
 
373
                        path = path.substring( 0, path.length() - 1 );
 
374
 
 
375
                // if full path not required, strip off preceding directories
 
376
                if( !return_full ) {
 
377
                        int idx = path.lastIndexOf( "/" );
 
378
                        if( idx != -1 ) path = path.substring( idx + 1 );
 
379
                }
 
380
 
 
381
                return path;
 
382
        }
 
383
 
 
384
        protected void strtipLastFilepartFromPath()
 
385
        {
 
386
                int at = _path.lastIndexOf( '/', _path.length() - 2 );
 
387
                if( at != -1 ) _path = _path.substring( 0, at + 1 );
 
388
        }
 
389
 
 
390
        protected void updateList()
 
391
        {
 
392
                // reset item list
 
393
                _items = new ArrayList< RowItem >();
 
394
 
 
395
                // open directory (and ensure _path is a directory)
 
396
                File dir = new File( _path_prefix + _path );
 
397
                while( !dir.isDirectory() ) {
 
398
                        if( _path == "/" )
 
399
                                throw new InvalidPathPrefixException();
 
400
                        strtipLastFilepartFromPath();
 
401
                        dir = new File( _path_prefix + _path );
 
402
                }
 
403
 
 
404
                // add ".."?
 
405
                if( !_path.equals( "/" ) )
 
406
                        _items.add( new RowItem( "..", true ) );
 
407
 
 
408
                // get directories
 
409
                class DirFilter implements FileFilter {
 
410
                        public boolean accept( File file ) {
 
411
                                return file.isDirectory() && file.getName().charAt( 0 ) != '.';
 
412
                        }
 
413
                }
 
414
                File[] files = dir.listFiles( new DirFilter() );
 
415
                if( files != null )
 
416
                        for( int i = 0; i < files.length; i++ )
 
417
                                _items.add( new RowItem( files[ i ].getName(), true ) );
 
418
 
 
419
                // get files
 
420
                if( _mode == MODE_FILE )
 
421
                {
 
422
                        class VCardFilter implements FileFilter {
 
423
                                public boolean accept( File file ) {
 
424
                                        if( file.isDirectory() || file.getName().startsWith( "." ) )
 
425
                                                return false;
 
426
                                        String filename = file.getName().toLowerCase();
 
427
                                        for( int i = 0; i < _extensions.length; i++ )
 
428
                                                if( filename.endsWith( "." + _extensions[ i ] ) )
 
429
                                                        return true;
 
430
                                        return false;
 
431
                                }
 
432
                        }
 
433
                        files = dir.listFiles( new VCardFilter() );
 
434
                        if( files != null )
 
435
                                for( int i = 0; i < files.length; i++ )
 
436
                                        _items.add( new RowItem( files[ i ].getName(), false ) );
 
437
                }
 
438
 
 
439
                // sort
 
440
                class RowItemSorter implements Comparator< RowItem > {
 
441
                        @Override
 
442
                        public int compare( RowItem lhs, RowItem rhs ) {
 
443
                                return lhs.compareTo( rhs );
 
444
                        }
 
445
                }
 
446
                Collections.sort( _items, new RowItemSorter() );
 
447
 
 
448
                // setup directory list
 
449
                ( (ListView)_dialog.findViewById( R.id.list ) ).setAdapter(
 
450
                        new RowItemAdapter( _context, R.layout.filechooser_row,
 
451
                                _items ) );
 
452
 
 
453
                updateCurrentSelection();
 
454
        }
 
455
 
 
456
        private void updateCurrentSelection()
 
457
        {
 
458
                // set current path
 
459
                ( (TextView)_dialog.findViewById( R.id.path ) ).setText(
 
460
                        prettyPrint( _path_prefix + _path + _filename, true ) );
 
461
 
 
462
                // enable/disable ok button
 
463
                if( _mode == MODE_FILE )
 
464
                        _dialog.findViewById( R.id.ok ).setEnabled( _filename != "" );
 
465
                else
 
466
                        _dialog.findViewById( R.id.ok ).setEnabled( true );
 
467
        }
 
468
 
 
469
}