/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
 
 
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 =
251
 
                        new OnItemClickListener() {
252
 
                public void onItemClick( AdapterView< ? > adapter_view, View view,
253
 
                        int position, long id )
254
 
                {
255
 
                        RowItem rowitem = _items.get( position );
256
 
 
257
 
                        // handle directory changes
258
 
                        if( rowitem.isDirectory() )
259
 
                        {
260
 
                                String dirname = rowitem.getName();
261
 
                                if( dirname.equals( ".." ) )
262
 
                                        strtipLastFilepartFromPath();
263
 
                                else
264
 
                                        _path += dirname + "/";
265
 
                                _filename = "";
266
 
 
267
 
                                updateList();
268
 
                        }
269
 
 
270
 
                        // handle file selections
271
 
                        else
272
 
                        {
273
 
                                _filename = rowitem.getName();
274
 
                                updateCurrentSelection();
275
 
                        }
276
 
                }
277
 
        };
278
 
 
279
 
        public void onPrepareDialog( Context context, Dialog dialog )
280
 
        {
281
 
                // set up reference to dialog
282
 
                _dialog = dialog;
283
 
                _context = context;
284
 
 
285
 
                // reset "ok"
286
 
                _ok = false;
287
 
 
288
 
                // pick text based on mode
289
 
                int title = 0, current = 0;
290
 
                switch( _mode ) {
291
 
                case MODE_DIR:
292
 
                        title = R.string.filechooser_title_dir;
293
 
                        current = R.string.filechooser_current_dir;
294
 
                        break;
295
 
                case MODE_FILE:
296
 
                        title = R.string.filechooser_title_file;
297
 
                        current = R.string.filechooser_current_file;
298
 
                        break;
299
 
                }
300
 
                dialog.setTitle( title );
301
 
                ( (TextView)dialog.findViewById( R.id.current ) )
302
 
                        .setText( _context.getString(  current ) );
303
 
 
304
 
                // clear filename in directory mode
305
 
                if( _mode == MODE_DIR )
306
 
                        _filename = "";
307
 
 
308
 
                // set root path icon
309
 
                ( (ImageView)_dialog.findViewById( R.id.icon ) )
310
 
                        .setImageResource( pathIcon( cleanUpPath( _path_prefix ) ) );
311
 
 
312
 
                // setup current-path-specific stuff
313
 
                updateList();
314
 
        }
315
 
 
316
 
        public static String cleanUpPath( String path )
317
 
        {
318
 
                path = path.trim();
319
 
 
320
 
                // ensure it starts and ends in a '/'
321
 
                if( !path.startsWith( "/" ) ) path = "/" + path;
322
 
                if( !path.endsWith( "/" ) ) path += "/";
323
 
 
324
 
                return path;
325
 
        }
326
 
 
327
 
        public static int pathIcon( String path )
328
 
        {
329
 
                // get sdcard path
330
 
                String sdcard_path;
331
 
                try {
332
 
                        sdcard_path = Environment.getExternalStorageDirectory()
333
 
                                .getCanonicalPath();
334
 
                        if( sdcard_path.charAt( sdcard_path.length() - 1 ) != '/' )
335
 
                                sdcard_path += "/";
336
 
                }
337
 
                catch( IOException e ) {
338
 
                        sdcard_path = null;
339
 
                }
340
 
 
341
 
                // special paths
342
 
                if( sdcard_path != null && path.equals( sdcard_path ) )
343
 
                        return R.drawable.sdcard;
344
 
 
345
 
                // default
346
 
                return R.drawable.directory;
347
 
        }
348
 
 
349
 
        public String prettyPrint( String full_path, boolean return_full )
350
 
        {
351
 
                String path = full_path;
352
 
 
353
 
                // get sdcard path
354
 
                String sdcard_path;
355
 
                try {
356
 
                        sdcard_path = Environment.getExternalStorageDirectory()
357
 
                                .getCanonicalPath();
358
 
                        if( sdcard_path.charAt( sdcard_path.length() - 1 ) != '/' )
359
 
                                sdcard_path += "/";
360
 
                }
361
 
                catch( IOException e ) {
362
 
                        sdcard_path = null;
363
 
                }
364
 
 
365
 
                // special names
366
 
                if( sdcard_path != null && path.equals( sdcard_path ) )
367
 
                        return " " + _context.getString( R.string.filechooser_path_sdcard );
368
 
 
369
 
                // remove prefix, if present
370
 
                if( path.startsWith( _path_prefix + "/" ) )
371
 
                        path = path.substring( _path_prefix.length() );
372
 
 
373
 
                // unless path is "/", strip trailing "/"
374
 
                if( path.length() > 1 && path.endsWith( "/" ) )
375
 
                        path = path.substring( 0, path.length() - 1 );
376
 
 
377
 
                // if full path not required, strip off preceding directories
378
 
                if( !return_full ) {
379
 
                        int idx = path.lastIndexOf( "/" );
380
 
                        if( idx != -1 ) path = path.substring( idx + 1 );
381
 
                }
382
 
 
383
 
                return path;
384
 
        }
385
 
 
386
 
        protected void strtipLastFilepartFromPath()
387
 
        {
388
 
                int at = _path.lastIndexOf( '/', _path.length() - 2 );
389
 
                if( at != -1 ) _path = _path.substring( 0, at + 1 );
390
 
        }
391
 
 
392
 
        protected void updateList()
393
 
        {
394
 
                // reset item list
395
 
                _items = new ArrayList< RowItem >();
396
 
 
397
 
                // open directory (and ensure _path is a directory)
398
 
                File dir = new File( _path_prefix + _path );
399
 
                while( !dir.isDirectory() ) {
400
 
                        if( _path == "/" )
401
 
                                throw new InvalidPathPrefixException();
402
 
                        strtipLastFilepartFromPath();
403
 
                        dir = new File( _path_prefix + _path );
404
 
                }
405
 
 
406
 
                // add ".."?
407
 
                if( !_path.equals( "/" ) )
408
 
                        _items.add( new RowItem( "..", true ) );
409
 
 
410
 
                // get directories
411
 
                class DirFilter implements FileFilter {
412
 
                        public boolean accept( File file ) {
413
 
                                return file.isDirectory() && file.getName().charAt( 0 ) != '.';
414
 
                        }
415
 
                }
416
 
                File[] files = dir.listFiles( new DirFilter() );
417
 
                if( files != null )
418
 
                        for( int i = 0; i < files.length; i++ )
419
 
                                _items.add( new RowItem( files[ i ].getName(), true ) );
420
 
 
421
 
                // get files
422
 
                if( _mode == MODE_FILE )
423
 
                {
424
 
                        class VCardFilter implements FileFilter {
425
 
                                public boolean accept( File file ) {
426
 
                                        if( file.isDirectory() || file.getName().startsWith( "." ) )
427
 
                                                return false;
428
 
                                        String filename = file.getName().toLowerCase();
429
 
                                        for( int i = 0; i < _extensions.length; i++ )
430
 
                                                if( filename.endsWith( "." + _extensions[ i ] ) )
431
 
                                                        return true;
432
 
                                        return false;
433
 
                                }
434
 
                        }
435
 
                        files = dir.listFiles( new VCardFilter() );
436
 
                        if( files != null )
437
 
                                for( int i = 0; i < files.length; i++ )
438
 
                                        _items.add( new RowItem( files[ i ].getName(), false ) );
439
 
                }
440
 
 
441
 
                // sort
442
 
                class RowItemSorter implements Comparator< RowItem > {
443
 
                        @Override
444
 
                        public int compare( RowItem lhs, RowItem rhs ) {
445
 
                                return lhs.compareTo( rhs );
446
 
                        }
447
 
                }
448
 
                Collections.sort( _items, new RowItemSorter() );
449
 
 
450
 
                // setup directory list
451
 
                ( (ListView)_dialog.findViewById( R.id.list ) ).setAdapter(
452
 
                        new RowItemAdapter( _context, R.layout.filechooser_row,
453
 
                                _items ) );
454
 
 
455
 
                updateCurrentSelection();
456
 
        }
457
 
 
458
 
        private void updateCurrentSelection()
459
 
        {
460
 
                // set current path
461
 
                ( (TextView)_dialog.findViewById( R.id.path ) ).setText(
462
 
                        prettyPrint( _path_prefix + _path + _filename, true ) );
463
 
 
464
 
                // enable/disable ok button
465
 
                if( _mode == MODE_FILE )
466
 
                        _dialog.findViewById( R.id.ok ).setEnabled( _filename != "" );
467
 
                else
468
 
                        _dialog.findViewById( R.id.ok ).setEnabled( true );
469
 
        }
470
 
 
471
 
}