New Notification engine

This commit is contained in:
J-Jamet
2018-03-20 13:43:11 +01:00
parent 846930a4f9
commit b2f985aa03
3 changed files with 229 additions and 109 deletions

View File

@@ -39,8 +39,9 @@ import com.keepassdroid.compat.ActivityCompat;
import com.keepassdroid.database.Database; import com.keepassdroid.database.Database;
import com.keepassdroid.database.PwDatabase; import com.keepassdroid.database.PwDatabase;
import com.keepassdroid.database.PwEntry; import com.keepassdroid.database.PwEntry;
import com.keepassdroid.password.PasswordActivity;
import com.keepassdroid.notifications.NotificationCopyingService; import com.keepassdroid.notifications.NotificationCopyingService;
import com.keepassdroid.notifications.NotificationField;
import com.keepassdroid.password.PasswordActivity;
import com.keepassdroid.settings.PreferencesUtil; import com.keepassdroid.settings.PreferencesUtil;
import com.keepassdroid.timeout.ClipboardHelper; import com.keepassdroid.timeout.ClipboardHelper;
import com.keepassdroid.utils.EmptyUtils; import com.keepassdroid.utils.EmptyUtils;
@@ -50,6 +51,7 @@ import com.keepassdroid.utils.Util;
import com.keepassdroid.view.EntryContentsView; import com.keepassdroid.view.EntryContentsView;
import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.R;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@@ -146,12 +148,26 @@ public class EntryActivity extends LockingHideActivity {
// username already copied, waiting for user's action before copy password. // username already copied, waiting for user's action before copy password.
Intent intent = new Intent(this, NotificationCopyingService.class); Intent intent = new Intent(this, NotificationCopyingService.class);
intent.setAction(NotificationCopyingService.ACTION_NEW_NOTIFICATION); intent.setAction(NotificationCopyingService.ACTION_NEW_NOTIFICATION);
intent.putExtra(NotificationCopyingService.EXTRA_PASSWORD, mEntry.getPassword());
if (mEntry.getUsername().length() > 0) {
intent.putExtra(NotificationCopyingService.EXTRA_USERNAME, mEntry.getUsername());
}
if (mEntry.getTitle() != null) if (mEntry.getTitle() != null)
intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getTitle()); intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getTitle());
// Construct notification fields
ArrayList<NotificationField> notificationFields = new ArrayList<>();
// Add username if exists to notifications
if (mEntry.getUsername().length() > 0)
notificationFields.add(
new NotificationField(
NotificationField.NotificationFieldId.USERNAME,
mEntry.getUsername(),
getResources()));
// Add password to notifications
notificationFields.add(
new NotificationField(
NotificationField.NotificationFieldId.PASSWORD,
mEntry.getPassword(),
getResources()));
// Add notifications
intent.putParcelableArrayListExtra(NotificationCopyingService.EXTRA_FIELDS, notificationFields);
startService(intent); startService(intent);
} }
} }

View File

@@ -30,12 +30,7 @@ public class NotificationCopyingService extends Service {
public static final String ACTION_NEW_NOTIFICATION = "ACTION_NEW_NOTIFICATION"; public static final String ACTION_NEW_NOTIFICATION = "ACTION_NEW_NOTIFICATION";
public static final String EXTRA_ENTRY_TITLE = "EXTRA_ENTRY_TITLE"; public static final String EXTRA_ENTRY_TITLE = "EXTRA_ENTRY_TITLE";
public static final String EXTRA_USERNAME = "EXTRA_USERNAME"; public static final String EXTRA_FIELDS = "EXTRA_FIELDS";
public static final String EXTRA_PASSWORD = "EXTRA_PASSWORD";
public static final String ACTION_COPY_USERNAME = "ACTION_COPY_USERNAME";
public static final String ACTION_COPY_PASSWORD = "ACTION_COPY_PASSWORD";
public static final String ACTION_CLEAN_CLIPBOARD = "ACTION_CLEAN_CLIPBOARD"; public static final String ACTION_CLEAN_CLIPBOARD = "ACTION_CLEAN_CLIPBOARD";
private NotificationManager notificationManager; private NotificationManager notificationManager;
@@ -81,21 +76,8 @@ public class NotificationCopyingService extends Service {
} else if (ACTION_NEW_NOTIFICATION.equals(intent.getAction())) { } else if (ACTION_NEW_NOTIFICATION.equals(intent.getAction())) {
String title = intent.getStringExtra(EXTRA_ENTRY_TITLE); String title = intent.getStringExtra(EXTRA_ENTRY_TITLE);
String password = intent.getStringExtra(EXTRA_PASSWORD);
if (password == null) {
Log.e(TAG, "password is null");
return START_NOT_STICKY;
}
newNotification(title, constructListOfField(intent)); newNotification(title, constructListOfField(intent));
} else if (ACTION_COPY_USERNAME.equals(intent.getAction())) {
String fieldValueToCopy = intent.getStringExtra(EXTRA_USERNAME);
copyField(new Field(EXTRA_USERNAME, fieldValueToCopy), constructListOfField(intent));
} else if (ACTION_COPY_PASSWORD.equals(intent.getAction())) {
String fieldValueToCopy = intent.getStringExtra(EXTRA_PASSWORD);
copyField(new Field(EXTRA_PASSWORD, fieldValueToCopy), constructListOfField(intent));
} else if (ACTION_CLEAN_CLIPBOARD.equals(intent.getAction())) { } else if (ACTION_CLEAN_CLIPBOARD.equals(intent.getAction())) {
stopTask(countingDownTask); stopTask(countingDownTask);
try { try {
@@ -103,66 +85,43 @@ public class NotificationCopyingService extends Service {
} catch (SamsungClipboardException e) { } catch (SamsungClipboardException e) {
Log.e(TAG, "Clipboard can't be cleaned", e); Log.e(TAG, "Clipboard can't be cleaned", e);
} }
} else { } else {
Log.w(TAG, "unknown action"); for (String actionKey : NotificationField.getAllActionKeys()) {
if (actionKey.equals(intent.getAction())) {
NotificationField fieldToCopy = intent.getParcelableExtra(
NotificationField.getExtraKeyLinkToActionKey(actionKey));
ArrayList<NotificationField> nextFields = constructListOfField(intent);
// Remove the current field from the next fields
if (nextFields.contains(fieldToCopy))
nextFields.remove(fieldToCopy);
copyField(fieldToCopy, nextFields);
}
}
} }
return START_NOT_STICKY; return START_NOT_STICKY;
} }
private List<Field> constructListOfField(Intent intent) { private ArrayList<NotificationField> constructListOfField(Intent intent) {
List<Field> fieldList = new ArrayList<>(); ArrayList<NotificationField> fieldList = new ArrayList<>();
if (intent != null && intent.getExtras() != null) { if (intent != null && intent.getExtras() != null) {
if (intent.getExtras().containsKey(EXTRA_USERNAME)) if (intent.getExtras().containsKey(EXTRA_FIELDS))
fieldList.add(new Field( fieldList = intent.getParcelableArrayListExtra(EXTRA_FIELDS);
EXTRA_USERNAME,
intent.getStringExtra(EXTRA_USERNAME)));
if (intent.getExtras().containsKey(EXTRA_PASSWORD))
fieldList.add(new Field(
EXTRA_PASSWORD,
intent.getStringExtra(EXTRA_PASSWORD)));
} }
return fieldList; return fieldList;
} }
private String getLabelFromExtra(String extra) { private PendingIntent getCopyPendingIntent(NotificationField fieldToCopy, ArrayList<NotificationField> fieldsToAdd) {
switch (extra) {
case EXTRA_USERNAME:
return getString(R.string.entry_user_name);
case EXTRA_PASSWORD:
return getString(R.string.entry_password);
default:
return "";
}
}
private String getCopyTextFromExtra(String extra) {
switch (extra) {
case EXTRA_USERNAME:
return getString(R.string.copy_username);
case EXTRA_PASSWORD:
return getString(R.string.copy_password);
default:
return "";
}
}
private PendingIntent getCopyPendingIntent(Field field, List<Field> fieldsToAdd) {
Intent copyIntent = new Intent(this, NotificationCopyingService.class); Intent copyIntent = new Intent(this, NotificationCopyingService.class);
switch (field.extra) { copyIntent.setAction(fieldToCopy.getActionKey());
case EXTRA_USERNAME: copyIntent.putExtra(fieldToCopy.getExtraKey(), fieldToCopy);
copyIntent.setAction(ACTION_COPY_USERNAME); copyIntent.putParcelableArrayListExtra(EXTRA_FIELDS, fieldsToAdd);
break;
case EXTRA_PASSWORD:
copyIntent.setAction(ACTION_COPY_PASSWORD);
}
for (Field fieldToAdd : fieldsToAdd) {
copyIntent.putExtra(fieldToAdd.extra, fieldToAdd.value);
}
return PendingIntent.getService( return PendingIntent.getService(
this, 0, copyIntent, PendingIntent.FLAG_UPDATE_CURRENT); this, 0, copyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
} }
private void newNotification(@Nullable String title, List<Field> fieldsToAdd) { private void newNotification(@Nullable String title, ArrayList<NotificationField> fieldsToAdd) {
stopTask(countingDownTask); stopTask(countingDownTask);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_COPYING) NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_COPYING)
@@ -173,15 +132,15 @@ public class NotificationCopyingService extends Service {
builder.setVisibility(Notification.VISIBILITY_SECRET); builder.setVisibility(Notification.VISIBILITY_SECRET);
if (fieldsToAdd.size() > 0) { if (fieldsToAdd.size() > 0) {
Field field = fieldsToAdd.get(fieldsToAdd.size() - 1); NotificationField field = fieldsToAdd.get(0);
builder.setContentText(field.copyText); builder.setContentText(field.copyText);
builder.setContentIntent(getCopyPendingIntent(field, fieldsToAdd)); builder.setContentIntent(getCopyPendingIntent(field, fieldsToAdd));
// Add extra actions without password // Add extra actions without 1st field
List<Field> fieldsWithoutPassword = new ArrayList<>(fieldsToAdd); List<NotificationField> fieldsWithoutFirstField = new ArrayList<>(fieldsToAdd);
fieldsWithoutPassword.remove(field); fieldsWithoutFirstField.remove(field);
// Add extra actions // Add extra actions
for (Field fieldToAdd : fieldsWithoutPassword) { for (NotificationField fieldToAdd : fieldsWithoutFirstField) {
builder.addAction(R.drawable.ic_key_white_24dp, fieldToAdd.label, builder.addAction(R.drawable.ic_key_white_24dp, fieldToAdd.label,
getCopyPendingIntent(fieldToAdd, fieldsToAdd)); getCopyPendingIntent(fieldToAdd, fieldsToAdd));
} }
@@ -204,7 +163,7 @@ public class NotificationCopyingService extends Service {
cleanNotificationTimer.start(); cleanNotificationTimer.start();
} }
private void copyField(Field fieldToCopy, List<Field> nextFields) { private void copyField(NotificationField fieldToCopy, ArrayList<NotificationField> nextFields) {
stopTask(countingDownTask); stopTask(countingDownTask);
stopTask(cleanNotificationTimer); stopTask(cleanNotificationTimer);
@@ -215,12 +174,9 @@ public class NotificationCopyingService extends Service {
.setSmallIcon(R.drawable.ic_key_white_24dp) .setSmallIcon(R.drawable.ic_key_white_24dp)
.setContentTitle(fieldToCopy.label); .setContentTitle(fieldToCopy.label);
// Remove the current field from the next fields
if (nextFields.contains(fieldToCopy))
nextFields.remove(fieldToCopy);
// New action with next field if click // New action with next field if click
if (nextFields.size() > 0) { if (nextFields.size() > 0) {
Field nextField = nextFields.get(0); NotificationField nextField = nextFields.get(0);
builder.setContentText(nextField.copyText); builder.setContentText(nextField.copyText);
builder.setContentIntent(getCopyPendingIntent(nextField, nextFields)); builder.setContentIntent(getCopyPendingIntent(nextField, nextFields));
// Else tell to swipe for a clean // Else tell to swipe for a clean
@@ -260,7 +216,7 @@ public class NotificationCopyingService extends Service {
}); });
countingDownTask.start(); countingDownTask.start();
} catch (SamsungClipboardException e) { } catch (Exception e) {
Log.e(TAG, "Clipboard can't be populate", e); Log.e(TAG, "Clipboard can't be populate", e);
} }
} }
@@ -270,33 +226,4 @@ public class NotificationCopyingService extends Service {
task.interrupt(); task.interrupt();
} }
/**
* Utility class to manage fields in Notifications
*/
private class Field {
String extra;
String label;
String copyText;
String value;
Field(String extra, String value) {
this.extra = extra;
this.label = getLabelFromExtra(extra);
this.copyText = getCopyTextFromExtra(extra);
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Field field = (Field) o;
return extra.equals(field.extra);
}
@Override
public int hashCode() {
return extra.hashCode();
}
}
} }

View File

@@ -0,0 +1,177 @@
/*
*
* Copyright 2017 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* KeePass DX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.notifications;
import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import com.kunzisoft.keepass.R;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
/**
* Utility class to manage fields in Notifications
*/
public class NotificationField implements Parcelable {
private static final String TAG = NotificationField.class.getName();
private NotificationFieldId id;
String value;
String label;
String copyText;
public NotificationField(NotificationFieldId id, String value, Resources resources) {
this.id = id;
this.value = value;
this.label = getLabel(resources);
this.copyText = getCopyText(resources);
}
protected NotificationField(Parcel in) {
id = NotificationFieldId.values()[in.readInt()];
value = in.readString();
label = in.readString();
copyText = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id.ordinal());
dest.writeString(value);
dest.writeString(label);
dest.writeString(copyText);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<NotificationField> CREATOR = new Creator<NotificationField>() {
@Override
public NotificationField createFromParcel(Parcel in) {
return new NotificationField(in);
}
@Override
public NotificationField[] newArray(int size) {
return new NotificationField[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NotificationField field = (NotificationField) o;
return id.equals(field.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
public enum NotificationFieldId {
USERNAME, PASSWORD, FIELD_A, FIELD_B, FIELD_C;
}
private static final String ACTION_COPY_PREFIX = "ACTION_COPY_";
private static final String EXTRA_KEY_PREFIX = "EXTRA_";
/**
* Return EXTRA_KEY link to ACTION_KEY, or null if ACTION_KEY is unknown
*/
public static @Nullable String getExtraKeyLinkToActionKey(String actionKey) {
try {
if (actionKey.startsWith(ACTION_COPY_PREFIX)) {
String idName = actionKey.substring(ACTION_COPY_PREFIX.length(), actionKey.length());
return getExtraKey(NotificationFieldId.valueOf(idName));
}
} catch (Exception e) {
Log.e(TAG, "Can't get Extra Key from Action Key", e);
}
return null;
}
private static String getActionKey(NotificationFieldId id) {
return ACTION_COPY_PREFIX + id.name();
}
public String getActionKey() {
return getActionKey(id);
}
private static String getExtraKey(NotificationFieldId id) {
return EXTRA_KEY_PREFIX + id.name();
}
public String getExtraKey() {
return getExtraKey(id);
}
public static List<String> getAllActionKeys() {
List<String> actionKeys = new ArrayList<>();
for (NotificationFieldId id : NotificationFieldId.values()) {
actionKeys.add(getActionKey(id));
}
return actionKeys;
}
public static List<String> getAllExtraKeys() {
List<String> extraKeys = new ArrayList<>();
for (NotificationFieldId id : NotificationFieldId.values()) {
extraKeys.add(getExtraKey(id));
}
return extraKeys;
}
private String getLabel(Resources resources) {
switch (id) {
case USERNAME:
return resources.getString(R.string.entry_user_name);
case PASSWORD:
return resources.getString(R.string.entry_password);
// TODO default
default:
return "";
}
}
private String getCopyText(Resources resources) {
switch (id) {
case USERNAME:
return resources.getString(R.string.copy_username);
case PASSWORD:
return resources.getString(R.string.copy_password);
// TODO default
default:
return "";
}
}
}