Statistics
| Branch: | Tag: | Revision:

notecipher / app / src / info / guardianproject / notepadbot / NoteCipher.java @ 61b66389

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
    public static final String TAG = "notecipher";
72

    
73
    private NotesDbAdapter mDbHelper;
74

    
75
    private Uri dataStream;
76

    
77
    private final static int MAX_SIZE = 1000000;
78

    
79
    private CacheWordActivityHandler mCacheWord;
80

    
81
    private ListView notesListView;
82
    private SimpleCursorAdapter notesCursorAdapter;
83
    
84
    /** Called when the activity is first created. */
85
    @Override
86
    public void onCreate(Bundle savedInstanceState) {
87
        super.onCreate(savedInstanceState);
88

    
89
        if (getIntent() != null) {
90
            if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {
91
                dataStream = (Uri) getIntent().getExtras().get(Intent.EXTRA_STREAM);
92
            } else {
93
                dataStream = getIntent().getData();
94
            }
95
        }
96

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

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

    
125
    @Override
126
    protected void onPause() {
127
        super.onPause();
128
        mCacheWord.onPause();
129
    }
130

    
131
    @Override
132
    protected void onResume() {
133
        super.onResume();
134
        mCacheWord.onResume();
135
    }
136

    
137
    @Override
138
    public void onAttachedToWindow() {
139
        super.onAttachedToWindow();
140

    
141
        findViewById(R.id.listlayout).setOnTouchListener(new OnTouchListener() {
142
            @Override
143
            public boolean onTouch(View v, MotionEvent event) {
144

    
145
                if (mDbHelper != null && mDbHelper.isOpen())
146
                    createNote();
147

    
148
                return false;
149
            }
150
        });
151
    }
152

    
153
    private void closeDatabase() {
154
        if (mDbHelper != null) {
155
            mDbHelper.close();
156
            mDbHelper = null;
157
        }
158
    }
159

    
160
    private void unlockDatabase() {
161
        if (mCacheWord.isLocked())
162
            return;
163
        mDbHelper = new NotesDbAdapter(mCacheWord, this);
164
        try {
165

    
166
            mDbHelper.open();
167

    
168
            if (dataStream != null)
169
                importDataStream();
170
            else
171
                fillData();
172

    
173
        } catch (Exception e) {
174
            e.printStackTrace();
175
            Toast.makeText(this, getString(R.string.err_pass), Toast.LENGTH_LONG).show();
176
        }
177
    }
178

    
179
    private void fillData() {
180
        if (mCacheWord.isLocked())
181
            return;
182
        
183
        getSupportLoaderManager().restartLoader(VIEW_ID, null, new LoaderManager.LoaderCallbacks<Cursor>() {
184

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

    
203
                        @Override
204
                        public void onLoaderReset(Loader<Cursor> loader) { }
205
                });
206
    }
207

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

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

    
243
        return super.onMenuItemSelected(featureId, item);
244
    }
245

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

    
254
    }
255

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

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

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

    
275
                return true;
276
        }
277
        return super.onContextItemSelected(item);
278
    }
279

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
373
        fillData();
374
    }
375

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

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

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

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

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

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

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

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

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

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

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

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

    
421
                // handleDelete();
422

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

    
428
                System.gc();
429

    
430
                fillData();
431
            }
432

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

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

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

    
442
        } finally {
443
            dataStream = null;
444

    
445
        }
446
    }
447

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

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

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

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

    
475
            }
476
        });
477
        b.show();
478
    }
479

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

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

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

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

    
507
    @Override
508
    public void onCacheWordOpened() {
509
        Log.d(TAG, "onCacheWordOpened");
510
        unlockDatabase();
511

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

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