/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: 2009-01-13 06:35:26 UTC
  • Revision ID: edam@waxworlds.org-20090113063526-l9t1s9git4bav60a
- new contact's phone numebrs and email addresses are added to the caches after those contacts are updated to account for the situation where the same contact is imported again from another file (or the contact exists twice in the same file!?)

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