Statistics
| Branch: | Tag: | Revision:

notecipher / app / src / info / guardianproject / notepadbot / NoteCipher.java @ d572d209

History | View | Annotate | Download (16.4 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
                    startActivity(new Intent(this, Settings.class));
236
                return true;
237
        }
238

    
239
        return super.onMenuItemSelected(featureId, item);
240
    }
241

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

    
250
    }
251

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

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

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

    
271
                return true;
272
        }
273
        return super.onContextItemSelected(item);
274
    }
275

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

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

    
293
                        if (mimeType == null)
294
                            mimeType = "text/plain";
295

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

    
310
                        @Override
311
                        public void onLoaderReset(Loader<Cursor> loader) { }
312
                });
313
    }
314

    
315
    private void viewEntry(final long id) {
316
        if (mCacheWord.isLocked())
317
            return;
318

    
319
        getSupportLoaderManager().restartLoader(VIEW_ID, null, new LoaderManager.LoaderCallbacks<Cursor>() {
320

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

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

    
338
                            NoteUtils.savePublicFile(NoteCipher.this, title, mimeType, blob);
339

    
340
                        }
341
                        note.close();
342
                        }
343

    
344
                        @Override
345
                        public void onLoaderReset(Loader<Cursor> loader) { }
346
                });
347
    }
348

    
349
    private void createNote() {
350
        if (mCacheWord.isLocked())
351
            return;
352

    
353
        Intent i = new Intent(this, NoteEdit.class);
354
        startActivityForResult(i, ACTIVITY_CREATE);
355
    }
356

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

    
367
        mDbHelper = new NotesDbAdapter(mCacheWord, this);
368

    
369
        fillData();
370
    }
371

    
372
    @Override
373
    protected void onStop() {
374
        super.onStop();
375
    }
376

    
377
    @Override
378
    public void onConfigurationChanged(Configuration newConfig) {
379
        super.onConfigurationChanged(newConfig);
380
    }
381

    
382
    @Override
383
    protected void onSaveInstanceState(Bundle outState) {
384
        super.onSaveInstanceState(outState);
385
    }
386

    
387
    @Override
388
    protected void onDestroy() {
389
        super.onDestroy();
390

    
391
        closeDatabase();
392
        NoteUtils.cleanupTmp(this);
393
    }
394

    
395
    private void importDataStream() {
396
        if (mCacheWord.isLocked())
397
            return;
398

    
399
        try {
400
            ContentResolver cr = getContentResolver();
401
            InputStream is = cr.openInputStream(dataStream);
402

    
403
            String mimeType = cr.getType(dataStream);
404

    
405
            byte[] data = NoteUtils.readBytesAndClose(is);
406

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

    
413
                new NotesDbAdapter(mCacheWord, this).createNote(title, body, data, mimeType);
414

    
415
                Toast.makeText(this, getString(R.string.on_import, title), Toast.LENGTH_LONG).show();
416

    
417
                // handleDelete();
418

    
419
                data = null;
420
                dataStream = null;
421
                title = null;
422
                body = null;
423

    
424
                System.gc();
425

    
426
                fillData();
427
            }
428

    
429
        } catch (FileNotFoundException e) {
430
            Log.e(NConstants.TAG, e.getMessage(), e);
431

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

    
435
        } catch (OutOfMemoryError e) {
436
            Toast.makeText(this, R.string.err_size, Toast.LENGTH_LONG).show();
437

    
438
        } finally {
439
            dataStream = null;
440

    
441
        }
442
    }
443

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

    
456
                // User clicked OK so go ahead and delete
457
                ContentResolver cr = getContentResolver();
458

    
459
                if (cr != null)
460
                    cr.delete(dataStream, null, null);
461
                else {
462
                    Toast.makeText(NoteCipher.this, R.string.unable_to_delete_original, Toast.LENGTH_SHORT).show();
463
                }
464

    
465
            }
466
        });
467
        b.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
468
            @Override
469
            public void onClick(DialogInterface dialog, int whichButton) {
470

    
471
            }
472
        });
473
        b.show();
474
    }
475

    
476
    void showLockScreen() {
477
        Intent intent = new Intent(this, LockScreenActivity.class);
478
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
479
        intent.putExtra("originalIntent", getIntent());
480
        startActivity(intent);
481
        finish();
482
    }
483

    
484
    void clearViewsAndLock() {
485
        closeDatabase();
486
        notesListView.setAdapter(null);
487
        System.gc();
488
        showLockScreen();
489
    }
490

    
491
    @Override
492
    public void onCacheWordUninitialized() {
493
        Log.d(NConstants.TAG, "onCacheWordUninitialized");
494
        clearViewsAndLock();
495
    }
496

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

    
503
    @Override
504
    public void onCacheWordOpened() {
505
        Log.d(NConstants.TAG, "onCacheWordOpened");
506
        unlockDatabase();
507

    
508
        if (mDbHelper.isOpen()) {
509
            if (dataStream != null)
510
                importDataStream();
511
            else
512
                fillData();
513
        }
514
    }
515

    
516
    public static class NotesLoader extends CursorLoader {
517
                NotesDbAdapter db;
518
                
519
                public NotesLoader(Context context) {
520
                        super(context);
521
                }
522
                
523
                public NotesLoader(Context context, NotesDbAdapter db) {
524
                        super(context);
525
                        this.db = db;
526
                }
527
                
528
                 @Override
529
                 public Cursor loadInBackground() {
530
                         if (db == null)
531
                                 return null;
532
                         return db.fetchAllNotes();
533
                 }
534
        }
535
}