diff --git a/app/src/androidTest/java/com/zeapo/pwdstore/PasswordEntryTest.java b/app/src/androidTest/java/com/zeapo/pwdstore/PasswordEntryTest.java
new file mode 100644
index 000000000..66636b1b2
--- /dev/null
+++ b/app/src/androidTest/java/com/zeapo/pwdstore/PasswordEntryTest.java
@@ -0,0 +1,44 @@
+package com.zeapo.pwdstore;
+
+import junit.framework.TestCase;
+
+public class PasswordEntryTest extends TestCase {
+
+ public void testGetPassword() throws Exception {
+ assertEquals("fooooo", new PasswordEntry("fooooo\nbla\n").getPassword());
+ assertEquals("fooooo", new PasswordEntry("fooooo\nbla").getPassword());
+ assertEquals("fooooo", new PasswordEntry("fooooo\n").getPassword());
+ assertEquals("fooooo", new PasswordEntry("fooooo").getPassword());
+ assertEquals("", new PasswordEntry("\nblubb\n").getPassword());
+ assertEquals("", new PasswordEntry("\nblubb").getPassword());
+ assertEquals("", new PasswordEntry("\n").getPassword());
+ assertEquals("", new PasswordEntry("").getPassword());
+ }
+
+ public void testGetExtraContent() throws Exception {
+ assertEquals("bla\n", new PasswordEntry("fooooo\nbla\n").getExtraContent());
+ assertEquals("bla", new PasswordEntry("fooooo\nbla").getExtraContent());
+ assertEquals("", new PasswordEntry("fooooo\n").getExtraContent());
+ assertEquals("", new PasswordEntry("fooooo").getExtraContent());
+ assertEquals("blubb\n", new PasswordEntry("\nblubb\n").getExtraContent());
+ assertEquals("blubb", new PasswordEntry("\nblubb").getExtraContent());
+ assertEquals("", new PasswordEntry("\n").getExtraContent());
+ assertEquals("", new PasswordEntry("").getExtraContent());
+ }
+
+ public void testGetUsername() throws Exception {
+ assertEquals("username", new PasswordEntry("secret\nextra\nlogin: username\ncontent\n").getUsername());
+ assertEquals("username", new PasswordEntry("\nextra\nusername: username\ncontent\n").getUsername());
+ assertEquals("username", new PasswordEntry("\nUSERNaMe: username\ncontent\n").getUsername());
+ assertEquals("username", new PasswordEntry("\nLOGiN:username").getUsername());
+ assertNull(new PasswordEntry("secret\nextra\ncontent\n").getUsername());
+ }
+
+ public void testHasUsername() throws Exception {
+ assertTrue(new PasswordEntry("secret\nextra\nlogin: username\ncontent\n").hasUsername());
+ assertFalse(new PasswordEntry("secret\nextra\ncontent\n").hasUsername());
+ assertFalse(new PasswordEntry("secret\nlogin failed\n").hasUsername());
+ assertFalse(new PasswordEntry("\n").hasUsername());
+ assertFalse(new PasswordEntry("").hasUsername());
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java
new file mode 100644
index 000000000..d4d3fe810
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java
@@ -0,0 +1,59 @@
+package com.zeapo.pwdstore;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * A single entry in password store.
+ */
+public class PasswordEntry {
+
+ private static final String[] USERNAME_FIELDS = new String[]{"login", "username"};
+
+ private final String extraContent;
+ private final String password;
+ private final String username;
+
+ public PasswordEntry(final ByteArrayOutputStream os) throws UnsupportedEncodingException {
+ this(os.toString("UTF-8"));
+ }
+
+ public PasswordEntry(final String decryptedContent) {
+ final String[] passContent = decryptedContent.split("\n", 2);
+ password = passContent[0];
+ extraContent = passContent.length > 1 ? passContent[1] : "";
+ username = findUsername();
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getExtraContent() {
+ return extraContent;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public boolean hasExtraContent() {
+ return extraContent.length() != 0;
+ }
+
+ public boolean hasUsername() {
+ return username != null;
+ }
+
+ private String findUsername() {
+ final String[] extraLines = extraContent.split("\n");
+ for (String line : extraLines) {
+ for (String field : USERNAME_FIELDS) {
+ if (line.toLowerCase().startsWith(field + ":")) {
+ return line.split(": *", 2)[1];
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java
index 447f39f33..004a0bbda 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java
@@ -23,6 +23,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.widget.Toast;
+import com.zeapo.pwdstore.PasswordEntry;
import com.zeapo.pwdstore.R;
import com.zeapo.pwdstore.utils.PasswordRepository;
@@ -492,7 +493,7 @@ public class AutofillService extends AccessibilityService {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: {
try {
- String[] passContent = os.toString("UTF-8").split("\n");
+ final PasswordEntry entry = new PasswordEntry(os);
// if the user focused on something else, take focus back
// but this will open another dialog...hack to ignore this
@@ -501,11 +502,11 @@ public class AutofillService extends AccessibilityService {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Bundle args = new Bundle();
args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
- passContent[0]);
+ entry.getPassword());
info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
} else {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("autofill_pm", passContent[0]);
+ ClipData clip = ClipData.newPlainText("autofill_pm", entry.getPassword());
clipboard.setPrimaryClip(clip);
info.performAction(AccessibilityNodeInfo.ACTION_PASTE);
diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpHandler.java b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpHandler.java
index 232c7b944..e938fdd46 100644
--- a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpHandler.java
+++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpHandler.java
@@ -34,6 +34,7 @@ import android.widget.Toast;
import com.google.common.primitives.Longs;
import com.zeapo.pwdstore.BuildConfig;
+import com.zeapo.pwdstore.PasswordEntry;
import com.zeapo.pwdstore.R;
import com.zeapo.pwdstore.SelectFolderFragment;
import com.zeapo.pwdstore.UserPreference;
@@ -163,7 +164,7 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
finish();
return true;
case R.id.copy_password:
- copyToClipBoard();
+ copyPasswordToClipBoard();
break;
case R.id.share_password_as_plaintext:
shareAsPlaintext();
@@ -245,7 +246,7 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_plaintext_password_to)));//Always show a picker to give the user a chance to cancel
}
- public void copyToClipBoard() {
+ public void copyPasswordToClipBoard() {
if (findViewById(R.id.crypto_password_show) == null)
return;
@@ -261,7 +262,13 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
} catch (NumberFormatException e) {
// ignore and keep default
}
- showToast(this.getResources().getString(R.string.clipboard_toast_text, clearAfter));
+ showToast(this.getResources().getString(R.string.clipboard_password_toast_text, clearAfter));
+ }
+
+ public void copyUsernameToClipBoard(final String username) {
+ ClipData clip = ClipData.newPlainText("pgp_handler_result_pm", username);
+ clipboard.setPrimaryClip(clip);
+ showToast(this.getResources().getString(R.string.clipboard_username_toast_text));
}
public void handleClick(View view) {
@@ -490,34 +497,46 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
findViewById(R.id.crypto_container).setVisibility(View.VISIBLE);
Typeface monoTypeface = Typeface.createFromAsset(getAssets(), "fonts/sourcecodepro.ttf");
- final String[] passContent = os.toString("UTF-8").split("\n", 2);
- final String decodedPassword = passContent[0];
- final String extraContent = passContent.length > 1 ? passContent[1] : "";
- textViewPassword
- .setTypeface(monoTypeface);
- textViewPassword
- .setText(decodedPassword);
+ final PasswordEntry entry = new PasswordEntry(os);
+ textViewPassword.setTypeface(monoTypeface);
+ textViewPassword.setText(entry.getPassword());
Button toggleVisibilityButton = (Button) findViewById(R.id.crypto_password_toggle_show);
toggleVisibilityButton.setVisibility(showPassword?View.GONE:View.VISIBLE);
textViewPassword.setTransformationMethod(showPassword?null:new HoldToShowPasswordTransformation(toggleVisibilityButton, new Runnable() {
@Override
public void run() {
- textViewPassword
- .setText(decodedPassword);
+ textViewPassword.setText(entry.getPassword());
}
}));
- if (extraContent.length() != 0) {
+ if (entry.hasExtraContent()) {
findViewById(R.id.crypto_extra_show_layout).setVisibility(showExtraContent ? View.VISIBLE : View.GONE);
- ((TextView) findViewById(R.id.crypto_extra_show))
- .setTypeface(monoTypeface);
- ((TextView) findViewById(R.id.crypto_extra_show))
- .setText(extraContent);
+ final TextView extraView = (TextView) findViewById(R.id.crypto_extra_show);
+ extraView.setTypeface(monoTypeface);
+ extraView.setText(entry.getExtraContent());
+ if (entry.hasUsername()) {
+ findViewById(R.id.crypto_username_show).setVisibility(View.VISIBLE);
+ findViewById(R.id.crypto_username_show_label).setVisibility(View.VISIBLE);
+ findViewById(R.id.crypto_copy_username).setVisibility(View.VISIBLE);
+ findViewById(R.id.crypto_copy_username).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ copyUsernameToClipBoard(entry.getUsername());
+ }
+ });
+ final TextView usernameView = (TextView) findViewById(R.id.crypto_username_show);
+ usernameView.setTypeface(monoTypeface);
+ usernameView.setText(entry.getUsername());
+ } else {
+ findViewById(R.id.crypto_username_show).setVisibility(View.GONE);
+ findViewById(R.id.crypto_username_show_label).setVisibility(View.GONE);
+ findViewById(R.id.crypto_copy_username).setVisibility(View.GONE);
+ }
}
if (settings.getBoolean("copy_on_decrypt", true)) {
- copyToClipBoard();
+ copyPasswordToClipBoard();
}
} else {
Log.d("PGPHANDLER", "Error message after decrypt : " + os.toString());
@@ -569,12 +588,12 @@ public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConne
findViewById(R.id.crypto_container).setVisibility(View.VISIBLE);
Typeface monoTypeface = Typeface.createFromAsset(getAssets(), "fonts/sourcecodepro.ttf");
- String[] passContent = os.toString("UTF-8").split("\n");
+ final PasswordEntry entry = new PasswordEntry(os);
+ decodedPassword = entry.getPassword();
textViewPassword
.setTypeface(monoTypeface);
textViewPassword
- .setText(passContent[0]);
- decodedPassword = passContent[0];
+ .setText(decodedPassword);
String extraContent = os.toString("UTF-8").replaceFirst(".*\n", "");
if (extraContent.length() != 0) {
diff --git a/app/src/main/res/drawable-hdpi/ic_content_copy.png b/app/src/main/res/drawable-hdpi/ic_content_copy.png
new file mode 100644
index 000000000..92cdb3efb
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_content_copy.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_content_copy.png b/app/src/main/res/drawable-mdpi/ic_content_copy.png
new file mode 100644
index 000000000..ce7782b14
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_content_copy.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_content_copy.png b/app/src/main/res/drawable-xhdpi/ic_content_copy.png
new file mode 100644
index 000000000..7d208997b
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_content_copy.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_content_copy.png b/app/src/main/res/drawable-xxhdpi/ic_content_copy.png
new file mode 100644
index 000000000..6d24f4341
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_content_copy.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_content_copy.png b/app/src/main/res/drawable-xxxhdpi/ic_content_copy.png
new file mode 100644
index 000000000..0949977cf
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_content_copy.png differ
diff --git a/app/src/main/res/layout/activity_pgp_handler.xml b/app/src/main/res/layout/activity_pgp_handler.xml
index 3c79b37d0..71dcff3e4 100644
--- a/app/src/main/res/layout/activity_pgp_handler.xml
+++ b/app/src/main/res/layout/activity_pgp_handler.xml
@@ -8,7 +8,7 @@
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.zeapo.pwdstore.crypto.PgpHandler"
android:orientation="vertical"
- android:background="#eee">
+ android:background="@color/background">
+ android:background="@color/background">
-
-
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@string/copy_username"
+ android:background="@color/background"
+ android:src="@drawable/ic_content_copy"/>
+
+
+
+ android:textIsSelectable="true"
+ android:typeface="monospace" />
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/encrypt_layout.xml b/app/src/main/res/layout/encrypt_layout.xml
index 98a6b04db..30ccfba48 100644
--- a/app/src/main/res/layout/encrypt_layout.xml
+++ b/app/src/main/res/layout/encrypt_layout.xml
@@ -5,7 +5,7 @@
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.zeapo.pwdstore.crypto.PgpHandler"
- android:background="#eee"
+ android:background="@color/background"
android:padding="@dimen/activity_horizontal_margin">
+ android:background="@color/background">
-
-
- -
Nebyl vybrán poskytovatel OpenPGP!
Čekání na OpenKeychain…
- Heslo zkopírováno do schránky, máte %d sekund na jeho zkopírování.
+ Heslo zkopírováno do schránky, máte %d sekund na jeho zkopírování.
Až to bude možné, tak si zadejte jméno účtu v nastavení
Zadejte si v nastavení svůj OpenKeychain účet (email)
Jméno účtu je prázdné!
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 6b1161d17..cfb2d2d26 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -30,7 +30,8 @@
Kein OpenPGP-Provider ausgewählt!
Warte auf OpenKeychain…
- Passwort ist in der Zwischen ablage, du hast %d Sekunden, um es einzufügen.
+ Passwort ist in der Zwischenablage, du hast %d Sekunden, um es einzufügen.
+ Benutzername ist in der Zwischenablage
Bitte setze deinen Accountnamen in den Einstellungen.
Bitte setze deinen OpenKeychain Account (E-Mail) in den Einstellungen
Accountname ist leer!
@@ -88,6 +89,11 @@
Suche
Passwort:
Weiterer Inhalt:
+ Benutzername:
+ Passwort bearbeiten
+ Passwort kopieren
+ Benutzername kopieren
+ Als Klartext teilen
Git
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 6fd7ab11a..d496f79a8 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -31,7 +31,7 @@
Aucun prestataire OpenPGP sélectionné!
Attente de OpenKeychain…
- Mot de passe copié dans le presse papier, vous avez %d secondes pour coller celui-ci.
+ Mot de passe copié dans le presse papier, vous avez %d secondes pour coller celui-ci.
Renseignez le nom de votre compte dans les paramètres dès que vous pouvez
Renseignez votre compte OpenKeychain (email) dans les préférences
Nom du compte absent!
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 764dc004c..5c15f3ebd 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -31,7 +31,7 @@
OpenPGP プロバイダが選択されていません!
OpenKeychain の待機中…
- パスワードをクリップボードにコピーしました %d 秒以内に張り付けしてください。
+ パスワードをクリップボードにコピーしました %d 秒以内に張り付けしてください。
できるだけ設定に毎回アカウント名を設定してください
プリファレンスに OpenKeychain アカウント (メールアドレス) を設定してください
アカウント名が空です!
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 4c8510e60..e2711cc31 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -31,7 +31,7 @@
Не выбран поставщик OpenPGP!
Ожидание OpenKeychain…
- Пароль скопирован в буфер обмена, у вас есть %d секунд чтобы вставить его.
+ Пароль скопирован в буфер обмена, у вас есть %d секунд чтобы вставить его.
Пожалуйста, задайте имя пользователя в настройках
Пожалуйста, задайте почту для OpenKeychain в настройках
Пустое имя пользователя!
diff --git a/app/src/main/res/values-zh-rCH/strings.xml b/app/src/main/res/values-zh-rCH/strings.xml
index d5cad54f4..997914a41 100644
--- a/app/src/main/res/values-zh-rCH/strings.xml
+++ b/app/src/main/res/values-zh-rCH/strings.xml
@@ -15,7 +15,7 @@
在app中自动输入密码. 此功能只在 Andorid 4.3 及以上版本中可用. 在 Andorid 5.0 及以上版本中不依赖剪贴板
输入
“类别:\"
- 密码已复制到剪贴板, 你有 %d 秒的时间将其粘贴到其他地方.
+ 密码已复制到剪贴板, 你有 %d 秒的时间将其粘贴到其他地方.
从服务器Clone
欢迎使用 Password Store 你可以选择创建新的版本库或将你已有的 git 版本库克隆到你的设备上
Clone
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 1e6db97ef..088ab0be6 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -15,7 +15,7 @@
在app中自動填入密碼. 此功能只能在 Andorid 4.3 及以上版本中使用. 在 Andorid 5.0 及以上版本中不需要剪貼簿
輸入
“分類:\"
- 密碼已複製到剪貼簿, 你有 %d 秒的時間將其貼上到其他地方.
+ 密碼已複製到剪貼簿, 你有 %d 秒的時間將其貼上到其他地方.
從伺服器 Clone
歡迎使用 Password Store 你可以選擇建立新的 Repo 或將你已有的 git repo clone 到你的裝置上
Clone
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 96ebf1bba..7e1e38140 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,7 @@
#ff7043
+ #eee
#fde0dc
#f9bdbb
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 444bbf08a..51b230021 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -32,7 +32,8 @@
No OpenPGP Provider selected!
Waiting for OpenKeychain…
- Password copied to clipboard, you have %d seconds to paste it somewhere.
+ Password copied to clipboard, you have %d seconds to paste it somewhere.
+ Username copied to clipboard
Please set your account name in settings whenever you can
Please set your OpenKeychain account (email) in the preferences
Account name empty!
@@ -97,6 +98,11 @@
Search
Password:
Extra content:
+ Username:
+ Edit password
+ Copy password
+ Copy username
+ Share as plaintext
Git