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 |
} |