Statistics
| Branch: | Tag: | Revision:

notecipher / app / src / info / guardianproject / notepadbot / NoteCipher.java @ 72b0db4c

History | View | Annotate | Download (16.5 KB)

1
/*
2
 * Copyright (C) 2008 Google Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License")savedInstanceState;
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

    
17
package info.guardianproject.notepadbot;
18

    
19
import android.app.AlertDialog;
20
import android.content.ContentResolver;
21
import android.content.Context;
22
import android.content.DialogInterface;
23
import android.content.Intent;
24
import android.content.res.Configuration;
25
import android.database.Cursor;
26
import android.net.Uri;
27
import android.os.Bundle;
28
import android.support.v4.app.LoaderManager;
29
import android.support.v4.content.CursorLoader;
30
import android.support.v4.content.Loader;
31
import android.support.v4.widget.SimpleCursorAdapter;
32
import android.util.Log;
33
import android.view.ContextMenu;
34
import android.view.ContextMenu.ContextMenuInfo;
35
import android.view.MotionEvent;
36
import android.view.View;
37
import android.view.View.OnTouchListener;
38
import android.widget.AdapterView;
39
import android.widget.AdapterView.AdapterContextMenuInfo;
40
import android.widget.AdapterView.OnItemClickListener;
41
import android.widget.ListView;
42
import android.widget.TextView;
43
import android.widget.Toast;
44

    
45
import com.actionbarsherlock.app.SherlockFragmentActivity;
46
import com.actionbarsherlock.view.Menu;
47
import com.actionbarsherlock.view.MenuItem;
48

    
49
import info.guardianproject.cacheword.CacheWordActivityHandler;
50
import info.guardianproject.cacheword.ICacheWordSubscriber;
51

    
52
import net.sqlcipher.database.SQLiteDatabase;
53

    
54
import java.io.FileNotFoundException;
55
import java.io.IOException;
56
import java.io.InputStream;
57

    
58
public class NoteCipher extends SherlockFragmentActivity implements ICacheWordSubscriber {
59
        
60
    private static final int ACTIVITY_CREATE = 0;
61
    private static final int ACTIVITY_EDIT = 1;
62

    
63
    private static final int INSERT_ID = Menu.FIRST;
64
    private static final int DELETE_ID = Menu.FIRST + 1;
65
    private static final int REKEY_ID = Menu.FIRST + 2;
66
    private static final int SHARE_ID = Menu.FIRST + 3;
67
    private static final int VIEW_ID = Menu.FIRST + 4;
68
    private static final int LOCK_ID = Menu.FIRST + 5;
69
    private static final int SETTINGS_ID = Menu.FIRST + 6;
70

    
71
    private NotesDbAdapter mDbHelper;
72

    
73
    private Uri dataStream;
74

    
75
    private CacheWordActivityHandler mCacheWord;
76

    
77
    private ListView notesListView;
78
    private SimpleCursorAdapter notesCursorAdapter;
79
    
80
    /** Called when the activity is first created. */
81
    @Override
82
    public void onCreate(Bundle savedInstanceState) {
83
        super.onCreate(savedInstanceState);
84

    
85
        if (getIntent() != null) {
86
            if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {
87
                dataStream = (Uri) getIntent().getExtras().get(Intent.EXTRA_STREAM);
88
            } else {
89
                dataStream = getIntent().getData();
90
            }
91
        }
92

    
93
        SQLiteDatabase.loadLibs(this);
94
        setContentView(R.layout.notes_list);
95
        notesListView = (ListView) findViewById(R.id.notesListView);
96
        notesListView.setOnItemClickListener(new OnItemClickListener() {
97
                        @Override
98
                        public void onItemClick(AdapterView<?> ad, View v, int position,
99
                                        long id) {
100
                                Intent i = new Intent(getApplication(), NoteEdit.class);
101
                    i.putExtra(NotesDbAdapter.KEY_ROWID, id);
102
                    startActivityForResult(i, ACTIVITY_EDIT);
103
                        }
104
        });
105
        registerForContextMenu(notesListView);
106
        mCacheWord = new CacheWordActivityHandler(this, ((App)getApplication()).getCWSettings());
107
        
108
        // Create an array to specify the fields we want to display in the list (only TITLE)
109
        String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
110

    
111
        // and an array of the fields we want to bind those fields to (in this
112
        // case just text1)
113
        int[] to = new int[] { R.id.row_text };
114
        
115
        // Now create an empty simple cursor adapter that later will display the notes
116
        notesCursorAdapter = new SimpleCursorAdapter(this, R.layout.notes_row, null, from, to
117
                                        , SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
118
        notesListView.setAdapter(notesCursorAdapter);
119
    }
120

    
121
    @Override
122
    protected void onPause() {
123
        super.onPause();
124
        mCacheWord.onPause();
125
    }
126

    
127
    @Override
128
    protected void onResume() {
129
        super.onResume();
130
        mCacheWord.onResume();
131
    }
132

    
133
    @Override
134
    public void onAttachedToWindow() {
135
        super.onAttachedToWindow();
136

    
137
        findViewById(R.id.listlayout).setOnTouchListener(new OnTouchListener() {
138
            @Override
139
            public boolean onTouch(View v, MotionEvent event) {
140

    
141
                if (mDbHelper != null && mDbHelper.isOpen())
142
                    createNote();
143

    
144
                return false;
145
            }
146
        });
147
    }
148

    
149
    private void closeDatabase() {
150
        if (mDbHelper != null) {
151
            mDbHelper.close();
152
            mDbHelper = null;
153
        }
154
    }
155

    
156
    private void unlockDatabase() {
157
        if (mCacheWord.isLocked())
158
            return;
159
        mDbHelper = new NotesDbAdapter(mCacheWord, this);
160
        try {
161

    
162
            mDbHelper.open();
163

    
164
            if (dataStream != null)
165
                importDataStream();
166
            else
167
                fillData();
168

    
169
        } catch (Exception e) {
170
            e.printStackTrace();
171
            Toast.makeText(this, getString(R.string.err_pass), Toast.LENGTH_LONG).show();
172
        }
173
    }
174

    
175
    private void fillData() {
176
        if (mCacheWord.isLocked())
177
            return;
178
        
179
        getSupportLoaderManager().restartLoader(VIEW_ID, null, new LoaderManager.LoaderCallbacks<Cursor>() {
180

    
181
                        @Override
182
                        public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
183
                                return new NotesLoader(NoteCipher.this, mDbHelper);
184
                        }
185
 
186
                        @Override
187
                        public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
188
                                notesCursorAdapter.swapCursor(cursor);
189
                                
190
                        TextView emptyTV = (TextView) findViewById(R.id.emptytext);
191
                        if (notesCursorAdapter.isEmpty()) {
192
                            Toast.makeText(NoteCipher.this, R.string.on_start, Toast.LENGTH_LONG).show();
193
                            emptyTV.setText(R.string.no_notes);
194
                        } else {
195
                            emptyTV.setText("");
196
                        }
197
                        }
198

    
199
                        @Override
200
                        public void onLoaderReset(Loader<Cursor> loader) { }
201
                });
202
    }
203

    
204
    @Override
205
    public boolean onCreateOptionsMenu(Menu menu) {
206
        super.onCreateOptionsMenu(menu);
207
        menu.add(0, INSERT_ID, 0, R.string.menu_insert)
208
                .setIcon(R.drawable.new_content)
209
                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
210
        //menu.add(0, REKEY_ID, 0, R.string.menu_rekey)
211
        //        .setIcon(R.drawable.key_icon)
212
        //        .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
213
        menu.add(0, LOCK_ID, 0, R.string.menu_lock)
214
                .setIcon(R.drawable.lock)
215
                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
216
        menu.add(0, SETTINGS_ID, 0, R.string.settings)
217
                .setIcon(R.drawable.settings)
218
                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
219
        return true;
220
    }
221

    
222
    @Override
223
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
224
        switch (item.getItemId()) {
225
            case INSERT_ID:
226
                createNote();
227
                return true;
228
            case REKEY_ID:
229
                return true;
230
            case LOCK_ID:
231
                if (!mCacheWord.isLocked())
232
                    mCacheWord.manuallyLock();
233
                return true;
234
            case SETTINGS_ID:
235
                    if ( !mCacheWord.isLocked()) {
236
                            startActivity(new Intent(this, Settings.class));
237
                    }
238
                return true;
239
        }
240

    
241
        return super.onMenuItemSelected(featureId, item);
242
    }
243

    
244
    @Override
245
    public void onCreateContextMenu(ContextMenu menu, View v,
246
            ContextMenuInfo menuInfo) {
247
        super.onCreateContextMenu(menu, v, menuInfo);
248
        // menu.add(0, VIEW_ID, 0, R.string.menu_view);
249
        menu.add(0, SHARE_ID, 0, R.string.menu_share);
250
        menu.add(0, DELETE_ID, 0, R.string.menu_delete);
251

    
252
    }
253

    
254
    @Override
255
    public boolean onContextItemSelected(android.view.MenuItem item) {
256
        AdapterContextMenuInfo info;
257

    
258
        switch (item.getItemId()) {
259
            case DELETE_ID:
260
                info = (AdapterContextMenuInfo) item.getMenuInfo();
261
                mDbHelper.deleteNote(info.id);
262
                fillData();
263
                return true;
264
            case SHARE_ID:
265
                info = (AdapterContextMenuInfo) item.getMenuInfo();
266
                shareEntry(info.id);
267

    
268
                return true;
269
            case VIEW_ID:
270
                info = (AdapterContextMenuInfo) item.getMenuInfo();
271
                viewEntry(info.id);
272

    
273
                return true;
274
        }
275
        return super.onContextItemSelected(item);
276
    }
277

    
278
    private void shareEntry(final long id) {
279
        if (mCacheWord.isLocked()) {
280
            return;
281
        }
282
        
283
        getSupportLoaderManager().restartLoader(SHARE_ID, null, new LoaderManager.LoaderCallbacks<Cursor>() {
284

    
285
                        @Override
286
                        public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
287
                                return new NoteEdit.NoteContentLoader(NoteCipher.this, mDbHelper, id);
288
                        }
289
 
290
                        @Override
291
                        public void onLoadFinished(Loader<Cursor> loader, Cursor note) {
292
                                byte[] blob = note.getBlob(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_DATA));
293
                        String title = note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE));
294
                        String mimeType = note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TYPE));
295

    
296
                        if (mimeType == null)
297
                            mimeType = "text/plain";
298

    
299
                        if (blob != null) {
300
                            try {
301
                                NoteUtils.shareData(NoteCipher.this, title, mimeType, blob);
302
                            } catch (IOException e) {
303
                                Toast.makeText(NoteCipher.this, getString(R.string.err_export, e.getMessage()), Toast.LENGTH_LONG)
304
                                        .show();
305
                            }
306
                        } else {
307
                            String body = note.getString( note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY) );
308
                            NoteUtils.shareText(NoteCipher.this, body);
309
                        }
310
                        note.close();
311
                        }
312

    
313
                        @Override
314
                        public void onLoaderReset(Loader<Cursor> loader) { }
315
                });
316
    }
317

    
318
    private void viewEntry(final long id) {
319
        if (mCacheWord.isLocked())
320
            return;
321

    
322
        getSupportLoaderManager().restartLoader(VIEW_ID, null, new LoaderManager.LoaderCallbacks<Cursor>() {
323

    
324
                        @Override
325
                        public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
326
                                return new NoteEdit.NoteContentLoader(NoteCipher.this, mDbHelper, id);
327
                        }
328
 
329
                        @Override
330
                        public void onLoadFinished(Loader<Cursor> loader, Cursor note) {
331
                                byte[] blob = note.getBlob(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_DATA));
332
                        String mimeType = note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TYPE));
333

    
334
                        if (mimeType == null)
335
                            mimeType = "text/plain";
336
                        
337
                        if (blob != null) {
338
                            String title = note.getString(
339
                                    note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE));
340

    
341
                            NoteUtils.savePublicFile(NoteCipher.this, title, mimeType, blob);
342

    
343
                        }
344
                        note.close();
345
                        }
346

    
347
                        @Override
348
                        public void onLoaderReset(Loader<Cursor> loader) { }
349
                });
350
    }
351

    
352
    private void createNote() {
353
        if (mCacheWord.isLocked())
354
            return;
355

    
356
        Intent i = new Intent(this, NoteEdit.class);
357
        startActivityForResult(i, ACTIVITY_CREATE);
358
    }
359

    
360
    /*
361
     * Called after the return from creating a new note (non-Javadoc)
362
     * @see android.app.Activity#onActivityResult(int, int,
363
     * android.content.Intent)
364
     */
365
    @Override
366
    protected void onActivityResult(int requestCode, int resultCode,
367
            Intent intent) {
368
        super.onActivityResult(requestCode, resultCode, intent);
369

    
370
        mDbHelper = new NotesDbAdapter(mCacheWord, this);
371

    
372
        fillData();
373
    }
374

    
375
    @Override
376
    protected void onStop() {
377
        super.onStop();
378
    }
379

    
380
    @Override
381
    public void onConfigurationChanged(Configuration newConfig) {
382
        super.onConfigurationChanged(newConfig);
383
    }
384

    
385
    @Override
386
    protected void onSaveInstanceState(Bundle outState) {
387
        super.onSaveInstanceState(outState);
388
    }
389

    
390
    @Override
391
    protected void onDestroy() {
392
        super.onDestroy();
393

    
394
        closeDatabase();
395
        NoteUtils.cleanupTmp(this);
396
    }
397

    
398
    private void importDataStream() {
399
        if (mCacheWord.isLocked())
400
            return;
401

    
402
        try {
403
            ContentResolver cr = getContentResolver();
404
            InputStream is = cr.openInputStream(dataStream);
405

    
406
            String mimeType = cr.getType(dataStream);
407

    
408
            byte[] data = NoteUtils.readBytesAndClose(is);
409

    
410
            if (data.length > NConstants.MAX_STREAM_SIZE) {
411
                Toast.makeText(this, R.string.err_size, Toast.LENGTH_LONG).show();
412
            } else {
413
                String title = dataStream.getLastPathSegment();
414
                String body = dataStream.getPath();
415

    
416
                new NotesDbAdapter(mCacheWord, this).createNote(title, body, data, mimeType);
417

    
418
                Toast.makeText(this, getString(R.string.on_import, title), Toast.LENGTH_LONG).show();
419

    
420
                // handleDelete();
421

    
422
                data = null;
423
                dataStream = null;
424
                title = null;
425
                body = null;
426

    
427
                System.gc();
428

    
429
                fillData();
430
            }
431

    
432
        } catch (FileNotFoundException e) {
433
            Log.e(NConstants.TAG, e.getMessage(), e);
434

    
435
        } catch (IOException e) {
436
            Log.e(NConstants.TAG, e.getMessage(), e);
437

    
438
        } catch (OutOfMemoryError e) {
439
            Toast.makeText(this, R.string.err_size, Toast.LENGTH_LONG).show();
440

    
441
        } finally {
442
            dataStream = null;
443

    
444
        }
445
    }
446

    
447
    /*
448
     * Call this to delete the original image, will ask the user
449
     */
450
    private void handleDelete() {
451
            if (mCacheWord.isLocked()) {
452
            return;
453
        }
454
        final AlertDialog.Builder b = new AlertDialog.Builder(this);
455
        b.setIcon(android.R.drawable.ic_dialog_alert);
456
        b.setTitle(R.string.app_name);
457
        b.setMessage(R.string.confirm_delete);
458
        b.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
459
            @Override
460
            public void onClick(DialogInterface dialog, int whichButton) {
461

    
462
                // User clicked OK so go ahead and delete
463
                ContentResolver cr = getContentResolver();
464

    
465
                if (cr != null)
466
                    cr.delete(dataStream, null, null);
467
                else {
468
                    Toast.makeText(NoteCipher.this, R.string.unable_to_delete_original, Toast.LENGTH_SHORT).show();
469
                }
470

    
471
            }
472
        });
473
        b.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
474
            @Override
475
            public void onClick(DialogInterface dialog, int whichButton) {
476

    
477
            }
478
        });
479
        b.show();
480
    }
481

    
482
    void showLockScreen() {
483
        Intent intent = new Intent(this, LockScreenActivity.class);
484
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
485
        intent.putExtra("originalIntent", getIntent());
486
        startActivity(intent);
487
        finish();
488
    }
489

    
490
    void clearViewsAndLock() {
491
        closeDatabase();
492
        notesListView.setAdapter(null);
493
        System.gc();
494
        showLockScreen();
495
    }
496

    
497
    @Override
498
    public void onCacheWordUninitialized() {
499
        Log.d(NConstants.TAG, "onCacheWordUninitialized");
500
        clearViewsAndLock();
501
    }
502

    
503
    @Override
504
    public void onCacheWordLocked() {
505
        Log.d(NConstants.TAG, "onCacheWordLocked");
506
        clearViewsAndLock();
507
    }
508

    
509
    @Override
510
    public void onCacheWordOpened() {
511
        Log.d(NConstants.TAG, "onCacheWordOpened");
512
        unlockDatabase();
513

    
514
        if (mDbHelper.isOpen()) {
515
            if (dataStream != null)
516
                importDataStream();
517
            else
518
                fillData();
519
        }
520
    }
521

    
522
    public static class NotesLoader extends CursorLoader {
523
                NotesDbAdapter db;
524
                
525
                public NotesLoader(Context context) {
526
                        super(context);
527
                }
528
                
529
                public NotesLoader(Context context, NotesDbAdapter db) {
530
                        super(context);
531
                        this.db = db;
532
                }
533
                
534
                 @Override
535
                 public Cursor loadInBackground() {
536
                         if (db == null)
537
                                 return null;
538
                         return db.fetchAllNotes();
539
                 }
540
        }
541
}