</activity>
<activity class=".PassGen" android:name="PassGen" android:label="@string/app_name" />
<activity class=".CategoryEdit" android:name="CategoryEdit" android:label="@string/app_name" />
+ <activity class=".PassView" android:name="PassView" android:label="@string/app_name" />
<activity class=".PassList" android:name="PassList" android:label="@string/app_name" />
<activity class=".PassEdit" android:name="PassEdit" android:label="@string/app_name" />
<activity class=".CategoryList" android:name="CategoryList" android:label="@string/app_name" />
android:layout_weight="1"
android:scrollbars="vertical" />
- <Button android:id="@+id/save"
- android:text="@string/save"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
</LinearLayout>\r
</ScrollView>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<!--
+* $Id$\r
+* \r
+* Copyright (C) 2009 OpenIntents.org\r
+*\r
+* Licensed under the Apache License, Version 2.0 (the "License");\r
+* you may not use this file except in compliance with the License.\r
+* You may obtain a copy of the License at\r
+*\r
+* http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*\r
+-->\r
+\r
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:layout_width="fill_parent"\r
+ android:layout_height="wrap_content"\r
+ android:scrollbars="vertical">\r
+
+<LinearLayout
+ android:orientation="vertical"\r
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TableLayout android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <TableRow>
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"\r
+ android:layout_marginRight="2sp"\r
+ android:layout_marginBottom="1sp"\r
+ android:text="@string/description" />
+ <EditText android:id="@+id/description"\r
+ android:editable="false"\r
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </TableRow>
+ <TableRow>
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="2sp"\r
+ android:text="@string/website" />
+ <EditText android:id="@+id/website"\r
+ android:editable="false"\r
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"\r
+ android:layout_weight="1"/>\r
+ <Button android:id="@+id/go" \r
+ android:text="@string/go"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ />
+ </TableRow>\r
+ <TableRow>\r
+ <TextView android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content" \r
+ android:layout_marginRight="2sp"\r
+ android:text="@string/username" />\r
+ <EditText android:id="@+id/username" \r
+ android:editable="false"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content" \r
+ android:layout_weight="1"/>\r
+ </TableRow>\r
+
+ <TableRow>\r
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="2sp"\r
+ android:text="@string/password" />
+ <EditText android:id="@+id/password"\r
+ android:editable="false"\r
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </TableRow>
+ </TableLayout>
+
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notes" />
+ <EditText android:id="@+id/note" android:layout_width="fill_parent"
+ android:editable="false"\r
+ android:gravity="top" \r
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical" />
+\r
+ <TextView android:id="@+id/last_edited"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content" \r
+ android:text="@string/last_edited" />\r
+
+</LinearLayout>\r
+</ScrollView>
\ No newline at end of file
<string name="empty_safe">No Passwords present for this category.</string>\r
<string name="empty_category">No categories present. Please add one via Menu->Add</string>\r
<string name="password_lock">Lock</string>\r
+ <string name="password_view">View</string>\r
<string name="password_edit">Edit</string>\r
<string name="password_add">Add</string>
<string name="password_delete">Delete</string>
<string name="import_complete">Import complete</string>\r
<string name="import_delete_csv">Since it is not secure to keep passwords in clear text CSV files, would you like to delete</string>\r
<string name="edit_entry">Edit Entry</string>\r
+ <string name="view_entry">View Entry</string>\r
<string name="help">Help</string>\r
<string name="close">Close</string>\r
<string name="go">Go</string>\r
<string name="cancel">Cancel</string>\r
<string name="discard_changes">Discard changes</string>\r
<string name="entry_saved">Entry saved</string>\r
+ <string name="last_edited">Last edited</string>\r
</resources>
return;
}
masterKey = CryptoHelper.generateMasterKey();
- Log.i(TAG, "Saving Password: " + masterKey);
+ if (debug) Log.i(TAG, "Saving Password: " + masterKey);
try {
String encryptedMasterKey = ch.encrypt(masterKey);
dbHelper.storeMasterKey(encryptedMasterKey);
if (CategoryId==0)
{
c = db.query(TABLE_PASSWORDS, new String[] {
- "id", "password", "description", "username", "website", "note", "category", "unique_name"},
+ "id", "password", "description", "username", "website",
+ "note", "category", "unique_name", "lastdatetimeedit"},
null, null, null, null, null);
} else {
c = db.query(TABLE_PASSWORDS, new String[] {
- "id", "password", "description", "username", "website", "note", "category", "unique_name"},
+ "id", "password", "description", "username", "website",
+ "note", "category", "unique_name", "lastdatetimeedit"},
"category="+CategoryId, null, null, null, null);
}
int numRows = c.getCount();
row.category = c.getLong(6);
row.uniqueName = c.getString(7);
+ row.lastEdited = c.getString(8);
ret.add(row);
c.moveToNext();
Cursor c =
db.query(true, TABLE_PASSWORDS, new String[] {
"id", "password", "description", "username", "website",
- "note", "category, unique_name"}, "id=" + Id, null, null, null, null, null);
+ "note", "category, unique_name", "lastdatetimeedit"},
+ "id=" + Id, null, null, null, null, null);
if (c.getCount() > 0) {
c.moveToFirst();
row.id = c.getLong(0);
row.category = c.getLong(6);
row.uniqueName = c.getString(7);
+ row.lastEdited = c.getString(8);
} else {
row.id = -1;
row.description = row.password = null;
Cursor c =
db.query(true, TABLE_PASSWORDS, new String[] {
"id", "password", "description", "username", "website",
- "note", "category", "unique_name"}, "unique_name='" + uniqueName + "'",
+ "note", "category", "unique_name", "lastdatetimeedit"},
+ "unique_name='" + uniqueName + "'",
null, null, null, null, null);
if (c.getCount() > 0) {
c.moveToFirst();
row.category = c.getLong(6);
row.uniqueName = c.getString(7);
+ row.lastEdited = c.getString(8);
}
c.close();
} catch (SQLException e)
*/\r
public class FrontDoor extends Activity {\r
\r
- private static final boolean debug = !false;\r
+ private static final boolean debug = false;\r
private static String TAG = "FrontDoor";\r
\r
private static final int REQUEST_CODE_ASK_PASSWORD = 1;\r
*/
public class PassEdit extends Activity {
+ private static boolean debug = true;
+ private static String TAG = "PassEdit";
+
public static final int REQUEST_GEN_PASS = 10;
public static final int SAVE_PASSWORD_INDEX = Menu.FIRST;
private CryptoHelper ch;
private boolean pass_gen_ret = false;
private boolean discardEntry = false;
-
- private static boolean debug = false;
- private static String TAG = "PassEdit";
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
noteText = (EditText) findViewById(R.id.note);
websiteText = (EditText) findViewById(R.id.website);
- Button confirmButton = (Button) findViewById(R.id.save);
Button goButton = (Button) findViewById(R.id.go);
RowId = icicle != null ? icicle.getLong(PassList.KEY_ID) : null;
}
});
- confirmButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View arg0) {
- // Don't allow the user to enter a blank description, we need
- // something useful to show in the list
- if (descriptionText.getText().toString().trim().length() == 0) {
- Toast.makeText(PassEdit.this, R.string.notify_blank_desc,
- Toast.LENGTH_SHORT).show();
- return;
- }
- savePassword();
- }
- });
}
@Override
public String plainUsername;\r
public String plainWebsite;\r
public String plainNote;\r
+ public String lastEdited;\r
\r
public static boolean checkPackageAccess (ArrayList<String> packageAccess, String packageName) {\r
return (packageAccess.contains(packageName));\r
*/
public class PassList extends ListActivity {
- private static final boolean debug= false;
+ private static final boolean debug= true;
private static final String TAG = "PassList";
// Menu Item order
- public static final int EDIT_PASSWORD_INDEX = Menu.FIRST;
- public static final int ADD_PASSWORD_INDEX = Menu.FIRST + 1;
- public static final int DEL_PASSWORD_INDEX = Menu.FIRST + 2;
- public static final int MOVE_PASSWORD_INDEX = Menu.FIRST + 3;
+ public static final int VIEW_PASSWORD_INDEX = Menu.FIRST;
+ public static final int EDIT_PASSWORD_INDEX = Menu.FIRST + 1;
+ public static final int ADD_PASSWORD_INDEX = Menu.FIRST + 2;
+ public static final int DEL_PASSWORD_INDEX = Menu.FIRST + 3;
+ public static final int MOVE_PASSWORD_INDEX = Menu.FIRST + 4;
- public static final int REQUEST_EDIT_PASSWORD = 1;
- public static final int REQUEST_ADD_PASSWORD = 2;
- public static final int REQUEST_MOVE_PASSWORD = 3;
+ public static final int REQUEST_VIEW_PASSWORD = 1;
+ public static final int REQUEST_EDIT_PASSWORD = 2;
+ public static final int REQUEST_ADD_PASSWORD = 3;
+ public static final int REQUEST_MOVE_PASSWORD = 4;
public static final String KEY_ID = "id"; // Intent keys
public static final String KEY_CATEGORY_ID = "categoryId"; // Intent keys
info = (AdapterView.AdapterContextMenuInfo) menuInfo;
menu.setHeaderTitle(rows.get(info.position).plainDescription);
+ menu.add(0, VIEW_PASSWORD_INDEX, 0, R.string.password_view)
+ .setIcon(android.R.drawable.ic_menu_view)
+ .setAlphabeticShortcut('v');
menu.add(0, EDIT_PASSWORD_INDEX, 0, R.string.password_edit)
.setIcon(android.R.drawable.ic_menu_edit)
.setAlphabeticShortcut('e');
case ADD_PASSWORD_INDEX:
addPassword();
break;
+ case VIEW_PASSWORD_INDEX:
+ Intent vi = new Intent(this, PassView.class);
+ vi.putExtra(KEY_ID, rows.get(position).id);
+ vi.putExtra(KEY_CATEGORY_ID, CategoryId);
+ startActivityForResult(vi,REQUEST_VIEW_PASSWORD);
+ break;
case EDIT_PASSWORD_INDEX:
Intent i = new Intent(this, PassEdit.class);
i.putExtra(KEY_ID, rows.get(position).id);
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
- Intent i = new Intent(this, PassEdit.class);
+ Intent i = new Intent(this, PassView.class);
i.putExtra(KEY_ID, rows.get(position).id);
i.putExtra(KEY_CATEGORY_ID, CategoryId);
- startActivityForResult(i,REQUEST_EDIT_PASSWORD);
+ startActivityForResult(i,REQUEST_VIEW_PASSWORD);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent i) {
super.onActivityResult(requestCode, resultCode, i);
+ if (debug) Log.d(TAG,"onActivityResult: requestCode="+requestCode+", resultCode="+resultCode+
+ ", entryEdited="+PassView.entryEdited);
if (dbHelper == null) {
dbHelper = new DBHelper(this);
}
- if (resultCode==RESULT_OK) {
+ if (((requestCode==REQUEST_VIEW_PASSWORD)&&(PassView.entryEdited)) ||
+ (resultCode==RESULT_OK)) {
fillData();
}
}
/**
- * Retreive the decrypted category name based on the provided id.
+ * Retrieve the decrypted category name based on the provided id.
*
* @param Id category id
* @return decrypted category name
--- /dev/null
+/* $Id$
+ *
+ * Copyright (C) 2009 OpenIntents.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openintents.safe;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.ClipboardManager;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * PassView Activity
+ *
+ * @author Randy McEoin
+ */
+public class PassView extends Activity {
+
+ private static boolean debug = true;
+ private static String TAG = "PassView";
+
+ public static final int EDIT_PASSWORD_INDEX = Menu.FIRST;
+ public static final int DEL_PASSWORD_INDEX = Menu.FIRST + 1;
+
+ public static final int REQUEST_EDIT_PASS = 1;
+
+ private EditText descriptionText;
+ private EditText passwordText;
+ private EditText usernameText;
+ private EditText websiteText;
+ private EditText noteText;
+ private TextView lastEditedText;
+ private Long RowId;
+ private Long CategoryId;
+ private DBHelper dbHelper = null;
+ private CryptoHelper ch;
+ public static boolean entryEdited=false;
+
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ if (debug) Log.d(TAG,"onCreate()");
+ String title = getResources().getString(R.string.app_name) + " - "
+ + getResources().getString(R.string.view_entry);
+ setTitle(title);
+
+ ch = new CryptoHelper();
+ ch.setPassword(PassList.getMasterKey());
+
+ if (dbHelper == null) {
+ dbHelper = new DBHelper(this);
+ }
+
+ setContentView(R.layout.pass_view);
+
+ descriptionText = (EditText) findViewById(R.id.description);
+ websiteText = (EditText) findViewById(R.id.website);
+ usernameText = (EditText) findViewById(R.id.username);
+ passwordText = (EditText) findViewById(R.id.password);
+ noteText = (EditText) findViewById(R.id.note);
+ lastEditedText = (TextView) findViewById(R.id.last_edited);
+
+ entryEdited=false;
+
+ Button goButton = (Button) findViewById(R.id.go);
+
+ RowId = icicle != null ? icicle.getLong(PassList.KEY_ID) : null;
+ if (RowId == null) {
+ Bundle extras = getIntent().getExtras();
+ RowId = extras != null ? extras.getLong(PassList.KEY_ID) : null;
+ }
+ CategoryId = icicle != null ? icicle.getLong(PassList.KEY_CATEGORY_ID) : null;
+ if (CategoryId == null) {
+ Bundle extras = getIntent().getExtras();
+ CategoryId = extras != null ? extras.getLong(PassList.KEY_CATEGORY_ID) : null;
+ }
+
+ goButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View arg0) {
+
+ Toast.makeText(PassView.this, R.string.copy_to_clipboard,
+ Toast.LENGTH_SHORT).show();
+
+ ClipboardManager cb = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ cb.setText(passwordText.getText().toString());
+
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ Uri u = Uri.parse(websiteText.getText().toString());
+ i.setData(u);
+ try {
+ startActivity(i);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(PassView.this, R.string.invalid_website,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (RowId != null) {
+ outState.putLong(PassList.KEY_ID, RowId);
+ } else {
+ outState.putLong(PassList.KEY_ID, -1);
+ }
+ outState.putLong(PassList.KEY_CATEGORY_ID, CategoryId);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ dbHelper.close();
+ dbHelper = null;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (debug) Log.d(TAG,"onResume()");
+
+ if (dbHelper == null) {
+ dbHelper = new DBHelper(this);
+ }
+ if (CategoryList.isSignedIn() == false) {
+ finish();
+ }
+ populateFields();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ menu.add(0, EDIT_PASSWORD_INDEX, 0, R.string.password_edit)
+ .setIcon(android.R.drawable.ic_menu_edit).setShortcut('1', 'e');
+ menu.add(0, DEL_PASSWORD_INDEX, 0, R.string.password_delete)
+ .setIcon(android.R.drawable.ic_menu_delete).setShortcut('2', 'd');
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ /**
+ * Prompt the user with a dialog asking them if they really want
+ * to delete the password.
+ */
+ public void deletePassword(){
+ Dialog about = new AlertDialog.Builder(this)
+ .setIcon(R.drawable.passicon)
+ .setTitle(R.string.dialog_delete_password_title)
+ .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ deletePassword2();
+ }
+ })
+ .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ // do nothing
+ }
+ })
+ .setMessage(R.string.dialog_delete_password_msg)
+ .create();
+ about.show();
+ }
+
+ /**
+ * Follow up for the Delete Password dialog. If we have a RowId then
+ * delete the password, otherwise just finish this Activity.
+ */
+ public void deletePassword2(){
+ if ((RowId != null) && (RowId > 0)) {
+ delPassword(RowId);
+ } else {
+ // user specified to delete a new entry
+ // so simply exit out
+ finish();
+ }
+ }
+ /**
+ * Delete the password entry from the database given the row id within the
+ * database.
+ *
+ * @param Id
+ */
+ private void delPassword(long Id) {
+ dbHelper.deletePassword(Id);
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ /**
+ * Handler for when a MenuItem is selected from the Activity.
+ */
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case EDIT_PASSWORD_INDEX:
+ Intent i = new Intent(getApplicationContext(), PassEdit.class);
+ i.putExtra(PassList.KEY_ID, RowId);
+ i.putExtra(PassList.KEY_CATEGORY_ID, CategoryId);
+ startActivityForResult(i, REQUEST_EDIT_PASS);
+ break;
+ case DEL_PASSWORD_INDEX:
+ deletePassword();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ *
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent i) {
+ super.onActivityResult(requestCode, resultCode, i);
+
+ if (debug) Log.d(TAG,"onActivityResult()");
+ if (resultCode == RESULT_OK) {
+ populateFields();
+ entryEdited=true;
+ }
+ }
+
+ /**
+ *
+ */
+ private void populateFields() {
+ if (debug) Log.d(TAG,"populateFields()");
+ if (RowId != null) {
+ if (dbHelper == null) {
+ dbHelper = new DBHelper(this);
+ }
+ PassEntry row = dbHelper.fetchPassword(RowId);
+ if (row.id > -1) {
+ String cryptDesc = row.description;
+ String cryptWebsite = row.website;
+ String cryptUsername = row.username;
+ String cryptPass = row.password;
+ String cryptNote = row.note;
+ try {
+ descriptionText.setText(ch.decrypt(cryptDesc));
+ websiteText.setText(ch.decrypt(cryptWebsite));
+ usernameText.setText(ch.decrypt(cryptUsername));
+ passwordText.setText(ch.decrypt(cryptPass));
+ noteText.setText(ch.decrypt(cryptNote));
+ lastEditedText.setText(getString(R.string.last_edited)+" "+row.lastEdited);
+ } catch (CryptoHelperException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+ }
+}