mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Refactor Autofill and Keyboard selection
This commit is contained in:
@@ -126,10 +126,10 @@
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity android:name="com.kunzisoft.keepass.settings.SettingsActivity" />
|
||||
<activity android:name="com.kunzisoft.keepass.autofill.AutoFillAuthActivity"
|
||||
<activity android:name="com.kunzisoft.keepass.autofill.AutoFillLauncherActivity"
|
||||
android:configChanges="orientation|keyboardHidden" />
|
||||
<activity android:name="com.kunzisoft.keepass.settings.SettingsAutofillActivity" />
|
||||
<activity android:name="com.kunzisoft.keepass.selection.KeyboardEntryRetrieverActivity"
|
||||
<activity android:name="com.kunzisoft.keepass.magikeyboard.KeyboardLauncherActivity"
|
||||
android:label="@string/keyboard_name">
|
||||
</activity>
|
||||
<activity android:name="com.kunzisoft.keepass.settings.MagikIMESettings"
|
||||
|
||||
@@ -86,7 +86,7 @@ public class EntryActivity extends LockingHideActivity {
|
||||
private int iconColor;
|
||||
|
||||
public static void launch(Activity activity, PwEntry pw, boolean readOnly) {
|
||||
if (TimeoutHelper.INSTANCE.checkTime(activity)) {
|
||||
if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) {
|
||||
Intent intent = new Intent(activity, EntryActivity.class);
|
||||
intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||
ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly);
|
||||
|
||||
@@ -117,7 +117,7 @@ public class EntryEditActivity extends LockingHideActivity
|
||||
* @param pwEntry Entry to update
|
||||
*/
|
||||
public static void launch(Activity activity, PwEntry pwEntry) {
|
||||
if (TimeoutHelper.INSTANCE.checkTime(activity)) {
|
||||
if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) {
|
||||
Intent intent = new Intent(activity, EntryEditActivity.class);
|
||||
intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pwEntry.getUUID()));
|
||||
activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE);
|
||||
@@ -131,7 +131,7 @@ public class EntryEditActivity extends LockingHideActivity
|
||||
* @param pwGroup Group who will contains new entry
|
||||
*/
|
||||
public static void launch(Activity activity, PwGroup pwGroup) {
|
||||
if (TimeoutHelper.INSTANCE.checkTime(activity)) {
|
||||
if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) {
|
||||
Intent intent = new Intent(activity, EntryEditActivity.class);
|
||||
intent.putExtra(KEY_PARENT, pwGroup.getId());
|
||||
activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE);
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.app.Fragment;
|
||||
@@ -81,6 +82,9 @@ import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment;
|
||||
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment;
|
||||
import com.kunzisoft.keepass.dialogs.ReadOnlyDialog;
|
||||
import com.kunzisoft.keepass.dialogs.SortDialogFragment;
|
||||
import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService;
|
||||
import com.kunzisoft.keepass.magikeyboard.KeyboardHelper;
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikIME;
|
||||
import com.kunzisoft.keepass.password.AssignPasswordHelper;
|
||||
import com.kunzisoft.keepass.selection.EntrySelectionHelper;
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil;
|
||||
@@ -132,16 +136,13 @@ public class GroupActivity extends LockingActivity
|
||||
private PwNode nodeToCopy;
|
||||
private PwNode nodeToMove;
|
||||
|
||||
private boolean entrySelectionMode;
|
||||
private AutofillHelper autofillHelper;
|
||||
|
||||
private SearchEntryCursorAdapter searchSuggestionAdapter;
|
||||
|
||||
private int iconColor;
|
||||
|
||||
private static void buildAndLaunchIntent(Activity activity, PwGroup group, boolean readOnly,
|
||||
IntentBuildLauncher intentBuildLauncher) {
|
||||
if (TimeoutHelper.INSTANCE.checkTime(activity)) {
|
||||
if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) {
|
||||
Intent intent = new Intent(activity, GroupActivity.class);
|
||||
if (group != null) {
|
||||
intent.putExtra(GROUP_ID_KEY, group.getId());
|
||||
@@ -157,17 +158,16 @@ public class GroupActivity extends LockingActivity
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
// After a database creation
|
||||
public static void launch(Activity act) {
|
||||
launch(act, READ_ONLY_DEFAULT);
|
||||
public static void launch(Activity activity) {
|
||||
launch(activity, READ_ONLY_DEFAULT);
|
||||
}
|
||||
|
||||
public static void launch(Activity act, boolean readOnly) {
|
||||
TimeoutHelper.INSTANCE.recordTime(act);
|
||||
launch(act, null, readOnly);
|
||||
public static void launch(Activity activity, boolean readOnly) {
|
||||
launch(activity, null, readOnly);
|
||||
}
|
||||
|
||||
public static void launch(Activity activity, PwGroup group, boolean readOnly) {
|
||||
TimeoutHelper.INSTANCE.recordTime(activity);
|
||||
buildAndLaunchIntent(activity, group, readOnly,
|
||||
(intent) -> activity.startActivityForResult(intent, 0));
|
||||
}
|
||||
@@ -179,16 +179,15 @@ public class GroupActivity extends LockingActivity
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
public static void launchForKeyboardResult(Activity activity, boolean readOnly) {
|
||||
TimeoutHelper.INSTANCE.recordTime(activity);
|
||||
launchForKeyboardResult(activity, null, readOnly);
|
||||
public static void launchForKeyboardSelection(Activity activity, boolean readOnly) {
|
||||
launchForKeyboardSelection(activity, null, readOnly);
|
||||
}
|
||||
|
||||
public static void launchForKeyboardResult(Activity activity, PwGroup group, boolean readOnly) {
|
||||
public static void launchForKeyboardSelection(Activity activity, PwGroup group, boolean readOnly) {
|
||||
// TODO implement pre search to directly open the direct group
|
||||
TimeoutHelper.INSTANCE.recordTime(activity);
|
||||
buildAndLaunchIntent(activity, group, readOnly, (intent) -> {
|
||||
EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent);
|
||||
activity.startActivityForResult(intent, EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE);
|
||||
KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -199,26 +198,17 @@ public class GroupActivity extends LockingActivity
|
||||
*/
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void launchForAutofillResult(Activity activity, AssistStructure assistStructure, boolean readOnly) {
|
||||
if ( assistStructure != null ) {
|
||||
TimeoutHelper.INSTANCE.recordTime(activity);
|
||||
public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure, boolean readOnly) {
|
||||
launchForAutofillResult(activity, null, assistStructure, readOnly);
|
||||
} else {
|
||||
launch(activity, readOnly);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void launchForAutofillResult(Activity activity, PwGroup group, AssistStructure assistStructure, boolean readOnly) {
|
||||
public static void launchForAutofillResult(Activity activity, PwGroup group, @NonNull AssistStructure assistStructure, boolean readOnly) {
|
||||
// TODO implement pre search to directly open the direct group
|
||||
if ( assistStructure != null ) {
|
||||
TimeoutHelper.INSTANCE.recordTime(activity);
|
||||
buildAndLaunchIntent(activity, group, readOnly, (intent) -> {
|
||||
AutofillHelper.addAssistStructureExtraInIntent(intent, assistStructure);
|
||||
activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE);
|
||||
AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, intent, assistStructure);
|
||||
});
|
||||
} else {
|
||||
launch(activity, group, readOnly);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -316,13 +306,6 @@ public class GroupActivity extends LockingActivity
|
||||
addNodeButtonView.setAddEntryClickListener(v ->
|
||||
EntryEditActivity.launch(GroupActivity.this, mCurrentGroup));
|
||||
|
||||
// To init autofill
|
||||
entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
autofillHelper = new AutofillHelper();
|
||||
autofillHelper.retrieveAssistStructure(getIntent());
|
||||
}
|
||||
|
||||
// Search suggestion
|
||||
searchSuggestionAdapter = new SearchEntryCursorAdapter(this, database);
|
||||
|
||||
@@ -507,47 +490,46 @@ public class GroupActivity extends LockingActivity
|
||||
|
||||
@Override
|
||||
public void onNodeClick(PwNode node) {
|
||||
|
||||
// Add event when we have Autofill
|
||||
AssistStructure assistStructure = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
assistStructure = autofillHelper.getAssistStructure();
|
||||
if (assistStructure != null) {
|
||||
switch (node.getType()) {
|
||||
case GROUP:
|
||||
try {
|
||||
openChildGroup((PwGroup) node);
|
||||
} catch (ClassCastException e) {
|
||||
Log.e(TAG, "Node can't be cast in Group");
|
||||
}
|
||||
break;
|
||||
case ENTRY:
|
||||
try {
|
||||
PwEntry entry = ((PwEntry) node);
|
||||
EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(),
|
||||
() -> {
|
||||
EntryActivity.launch(GroupActivity.this, entry, getReadOnly());
|
||||
return null;
|
||||
},
|
||||
() -> {
|
||||
MagikIME.setEntryKey(entry.getEntry());
|
||||
// Show the notification if allowed in Preferences
|
||||
if (PreferencesUtil.enableKeyboardNotificationEntry(GroupActivity.this)) {
|
||||
Intent notificationIntent = new Intent(GroupActivity.this, KeyboardEntryNotificationService.class);
|
||||
startService(notificationIntent);
|
||||
}
|
||||
moveTaskToBack(true);
|
||||
return null;
|
||||
},
|
||||
assistStructure -> {
|
||||
// Build response with the entry selected
|
||||
autofillHelper.buildResponseWhenEntrySelected(this, (PwEntry) node);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AutofillHelper.INSTANCE.buildResponseWhenEntrySelected(GroupActivity.this, entry);
|
||||
}
|
||||
finish();
|
||||
return null;
|
||||
});
|
||||
} catch (ClassCastException e) {
|
||||
Log.e(TAG, "Node can't be cast in Entry");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( assistStructure == null ){
|
||||
if (entrySelectionMode) {
|
||||
switch (node.getType()) {
|
||||
case GROUP:
|
||||
openChildGroup((PwGroup) node);
|
||||
break;
|
||||
case ENTRY:
|
||||
EntrySelectionHelper.buildResponseWhenEntrySelected(this, (PwEntry) node);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (node.getType()) {
|
||||
case GROUP:
|
||||
openChildGroup((PwGroup) node);
|
||||
break;
|
||||
case ENTRY:
|
||||
EntryActivity.launch(this, (PwEntry) node, getReadOnly());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOpenMenuClick(PwNode node) {
|
||||
@@ -920,7 +902,6 @@ public class GroupActivity extends LockingActivity
|
||||
|
||||
@Override
|
||||
public void startActivity(Intent intent) {
|
||||
boolean customSearchQueryExecuted = false;
|
||||
|
||||
// Get the intent, verify the action and get the query
|
||||
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||
@@ -931,22 +912,26 @@ public class GroupActivity extends LockingActivity
|
||||
searchIntent.setAction(Intent.ACTION_SEARCH);
|
||||
searchIntent.putExtra(SearchManager.QUERY, query);
|
||||
|
||||
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
&& autofillHelper.getAssistStructure() != null ) {
|
||||
AutofillHelper.addAssistStructureExtraInIntent(searchIntent, autofillHelper.getAssistStructure());
|
||||
startActivityForResult(searchIntent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE);
|
||||
customSearchQueryExecuted = true;
|
||||
EntrySelectionHelper.INSTANCE.doEntrySelectionAction(intent,
|
||||
() -> {
|
||||
GroupActivity.super.startActivity(intent);
|
||||
return null;
|
||||
},
|
||||
() -> {
|
||||
KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(
|
||||
GroupActivity.this,
|
||||
searchIntent);
|
||||
return null;
|
||||
},
|
||||
assistStructure -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AutofillHelper.INSTANCE.startActivityForAutofillResult(
|
||||
GroupActivity.this,
|
||||
searchIntent,
|
||||
assistStructure);
|
||||
}
|
||||
// To get the keyboard response, verify if the current intent contains the EntrySelection key
|
||||
else if (EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent())){
|
||||
EntrySelectionHelper.addEntrySelectionModeExtraInIntent(searchIntent);
|
||||
startActivityForResult(searchIntent, EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE);
|
||||
customSearchQueryExecuted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!customSearchQueryExecuted) {
|
||||
super.startActivity(intent);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1179,9 +1164,8 @@ public class GroupActivity extends LockingActivity
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ abstract class LockingActivity : StylishActivity() {
|
||||
// After the first creation
|
||||
// or If simply swipe with another application
|
||||
// If the time is out -> close the Activity
|
||||
TimeoutHelper.checkTime(this)
|
||||
TimeoutHelper.checkTimeAndLockIfTimeout(this)
|
||||
// If onCreate already record time
|
||||
if (!exitLock)
|
||||
TimeoutHelper.recordTime(this)
|
||||
@@ -121,7 +121,7 @@ abstract class LockingActivity : StylishActivity() {
|
||||
|
||||
if (timeoutEnable) {
|
||||
// If the time is out during our navigation in activity -> close the Activity
|
||||
TimeoutHelper.checkTime(this)
|
||||
TimeoutHelper.checkTimeAndLockIfTimeout(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 Jeremy Jamet / Kunzisoft.
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
@@ -34,12 +34,12 @@ import com.kunzisoft.keepass.fileselect.FileSelectActivity
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
class AutoFillAuthActivity : AppCompatActivity() {
|
||||
class AutoFillLauncherActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
TimeoutHelper.checkTime(this) {}
|
||||
// Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE)
|
||||
val assistStructure = AutofillHelper().retrieveAssistStructure(intent)
|
||||
TimeoutHelper.checkTime(this)
|
||||
val assistStructure = AutofillHelper.retrieveAssistStructure(intent)
|
||||
if (assistStructure != null) {
|
||||
if (App.getDB().loaded)
|
||||
GroupActivity.launchForAutofillResult(this, assistStructure, true)
|
||||
@@ -54,14 +54,14 @@ class AutoFillAuthActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun getAuthIntentSenderForResponse(context: Context): IntentSender {
|
||||
val intent = Intent(context, AutoFillAuthActivity::class.java)
|
||||
val intent = Intent(context, AutoFillLauncherActivity::class.java)
|
||||
return PendingIntent.getActivity(context, 0,
|
||||
intent, PendingIntent.FLAG_CANCEL_CURRENT).intentSender
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, 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 3 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.kunzisoft.keepass.autofill;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.assist.AssistStructure;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.service.autofill.Dataset;
|
||||
import android.service.autofill.FillResponse;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.util.Log;
|
||||
import android.view.autofill.AutofillId;
|
||||
import android.view.autofill.AutofillManager;
|
||||
import android.view.autofill.AutofillValue;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.kunzisoft.keepass.database.PwEntry;
|
||||
import com.kunzisoft.keepass.selection.EntrySelectionHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public class AutofillHelper {
|
||||
|
||||
public static final int AUTOFILL_RESPONSE_REQUEST_CODE = 8165;
|
||||
|
||||
private AssistStructure assistStructure = null;
|
||||
|
||||
public AssistStructure retrieveAssistStructure(Intent intent) {
|
||||
if (intent != null && intent.getExtras() != null) {
|
||||
assistStructure = intent.getParcelableExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE);
|
||||
}
|
||||
return assistStructure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call retrieveAssistStructure before
|
||||
*/
|
||||
public AssistStructure getAssistStructure() {
|
||||
return assistStructure;
|
||||
}
|
||||
|
||||
public static void addAssistStructureExtraInIntent(Intent intent, AssistStructure assistStructure) {
|
||||
if (assistStructure != null) {
|
||||
EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent);
|
||||
intent.putExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE, assistStructure);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define if android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE is an extra bundle key present in the Intent
|
||||
*/
|
||||
public static boolean isIntentContainsExtraAssistStructureKey(Intent intent) {
|
||||
return (intent != null
|
||||
&& intent.getExtras() != null
|
||||
&& intent.getExtras().containsKey(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE));
|
||||
}
|
||||
|
||||
private @Nullable Dataset buildDataset(Context context, PwEntry entry,
|
||||
StructureParser.Result struct) {
|
||||
String title = makeEntryTitle(entry);
|
||||
RemoteViews views = newRemoteViews(context.getPackageName(), title);
|
||||
Dataset.Builder builder = new Dataset.Builder(views);
|
||||
builder.setId(entry.getUUID().toString());
|
||||
|
||||
if (entry.getPassword() != null) {
|
||||
AutofillValue value = AutofillValue.forText(entry.getPassword());
|
||||
struct.password.forEach(id -> builder.setValue(id, value));
|
||||
}
|
||||
if (entry.getUsername() != null) {
|
||||
AutofillValue value = AutofillValue.forText(entry.getUsername());
|
||||
List<AutofillId> ids = new ArrayList<>(struct.username);
|
||||
if (entry.getUsername().contains("@") || struct.username.isEmpty())
|
||||
ids.addAll(struct.email);
|
||||
ids.forEach(id -> builder.setValue(id, value));
|
||||
}
|
||||
try {
|
||||
return builder.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// if not value be set
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static private String makeEntryTitle(PwEntry entry) {
|
||||
if (!entry.getTitle().isEmpty() && !entry.getUsername().isEmpty())
|
||||
return String.format("%s (%s)", entry.getTitle(), entry.getUsername());
|
||||
if (!entry.getTitle().isEmpty())
|
||||
return entry.getTitle();
|
||||
if (!entry.getUsername().isEmpty())
|
||||
return entry.getUsername();
|
||||
if (!entry.getNotes().isEmpty())
|
||||
return entry.getNotes().trim();
|
||||
return ""; // TODO No title
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to hit when right key is selected
|
||||
*/
|
||||
public void buildResponseWhenEntrySelected(Activity activity, PwEntry entry) {
|
||||
Intent mReplyIntent;
|
||||
Intent intent = activity.getIntent();
|
||||
if (isIntentContainsExtraAssistStructureKey(intent)) {
|
||||
AssistStructure structure = intent.getParcelableExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE);
|
||||
StructureParser.Result result = new StructureParser(structure).parse();
|
||||
|
||||
// New Response
|
||||
FillResponse.Builder responseBuilder = new FillResponse.Builder();
|
||||
Dataset dataset = buildDataset(activity, entry, result);
|
||||
responseBuilder.addDataset(dataset);
|
||||
mReplyIntent = new Intent();
|
||||
Log.d(activity.getClass().getName(), "Successed Autofill auth.");
|
||||
mReplyIntent.putExtra(
|
||||
AutofillManager.EXTRA_AUTHENTICATION_RESULT,
|
||||
responseBuilder.build());
|
||||
activity.setResult(Activity.RESULT_OK, mReplyIntent);
|
||||
} else {
|
||||
Log.w(activity.getClass().getName(), "Failed Autofill auth.");
|
||||
activity.setResult(Activity.RESULT_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to loop and close each activity with return data
|
||||
*/
|
||||
public static void onActivityResultSetResultAndFinish(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == AUTOFILL_RESPONSE_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
activity.setResult(resultCode, data);
|
||||
}
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
activity.setResult(Activity.RESULT_CANCELED);
|
||||
}
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private static RemoteViews newRemoteViews(String packageName, String remoteViewsText) {
|
||||
RemoteViews presentation =
|
||||
new RemoteViews(packageName, R.layout.autofill_service_list_item);
|
||||
presentation.setTextViewText(R.id.text, remoteViewsText);
|
||||
return presentation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2019 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 3 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.kunzisoft.keepass.autofill
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.assist.AssistStructure
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.service.autofill.Dataset
|
||||
import android.service.autofill.FillResponse
|
||||
import android.support.annotation.RequiresApi
|
||||
import android.util.Log
|
||||
import android.view.autofill.AutofillManager
|
||||
import android.view.autofill.AutofillValue
|
||||
import android.widget.RemoteViews
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.PwEntry
|
||||
import com.kunzisoft.keepass.selection.EntrySelectionHelper
|
||||
import java.util.*
|
||||
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
object AutofillHelper {
|
||||
|
||||
private const val AUTOFILL_RESPONSE_REQUEST_CODE = 8165
|
||||
|
||||
private const val ASSIST_STRUCTURE = android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE
|
||||
|
||||
fun retrieveAssistStructure(intent: Intent?): AssistStructure? {
|
||||
intent?.let {
|
||||
return it.getParcelableExtra(ASSIST_STRUCTURE)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun makeEntryTitle(entry: PwEntry<*>): String {
|
||||
if (!entry.title.isEmpty() && !entry.username.isEmpty())
|
||||
return String.format("%s (%s)", entry.title, entry.username)
|
||||
if (!entry.title.isEmpty())
|
||||
return entry.title
|
||||
if (!entry.username.isEmpty())
|
||||
return entry.username
|
||||
return if (!entry.notes.isEmpty()) entry.notes.trim { it <= ' ' } else ""
|
||||
// TODO No title
|
||||
}
|
||||
|
||||
private fun buildDataset(context: Context, entry: PwEntry<*>,
|
||||
struct: StructureParser.Result): Dataset? {
|
||||
val title = makeEntryTitle(entry)
|
||||
val views = newRemoteViews(context.packageName, title)
|
||||
val builder = Dataset.Builder(views)
|
||||
builder.setId(entry.uuid.toString())
|
||||
|
||||
if (entry.password != null) {
|
||||
val value = AutofillValue.forText(entry.password)
|
||||
struct.password.forEach { id -> builder.setValue(id, value) }
|
||||
}
|
||||
if (entry.username != null) {
|
||||
val value = AutofillValue.forText(entry.username)
|
||||
val ids = ArrayList(struct.username)
|
||||
if (entry.username.contains("@") || struct.username.isEmpty())
|
||||
ids.addAll(struct.email)
|
||||
ids.forEach { id -> builder.setValue(id, value) }
|
||||
}
|
||||
return try {
|
||||
builder.build()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// if not value be set
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to hit when right key is selected
|
||||
*/
|
||||
fun buildResponseWhenEntrySelected(activity: Activity, entry: PwEntry<*>) {
|
||||
val mReplyIntent: Intent
|
||||
activity.intent?.let { intent ->
|
||||
if (intent.extras.containsKey(ASSIST_STRUCTURE)) {
|
||||
val structure = intent.getParcelableExtra<AssistStructure>(ASSIST_STRUCTURE)
|
||||
val result = StructureParser(structure).parse()
|
||||
|
||||
// New Response
|
||||
val responseBuilder = FillResponse.Builder()
|
||||
val dataset = buildDataset(activity, entry, result)
|
||||
responseBuilder.addDataset(dataset)
|
||||
mReplyIntent = Intent()
|
||||
Log.d(activity.javaClass.name, "Successed Autofill auth.")
|
||||
mReplyIntent.putExtra(
|
||||
AutofillManager.EXTRA_AUTHENTICATION_RESULT,
|
||||
responseBuilder.build())
|
||||
activity.setResult(Activity.RESULT_OK, mReplyIntent)
|
||||
} else {
|
||||
Log.w(activity.javaClass.name, "Failed Autofill auth.")
|
||||
activity.setResult(Activity.RESULT_CANCELED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to start an activity with an Autofill for result
|
||||
*/
|
||||
fun startActivityForAutofillResult(activity: Activity, intent: Intent, assistStructure: AssistStructure) {
|
||||
EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent)
|
||||
intent.putExtra(ASSIST_STRUCTURE, assistStructure)
|
||||
activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to loop and close each activity with return data
|
||||
*/
|
||||
fun onActivityResultSetResultAndFinish(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == AUTOFILL_RESPONSE_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
activity.setResult(resultCode, data)
|
||||
}
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
activity.setResult(Activity.RESULT_CANCELED)
|
||||
}
|
||||
activity.finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun newRemoteViews(packageName: String, remoteViewsText: String): RemoteViews {
|
||||
val presentation = RemoteViews(packageName, R.layout.autofill_service_list_item)
|
||||
presentation.setTextViewText(R.id.text, remoteViewsText)
|
||||
return presentation
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class KeeAutofillService extends AutofillService {
|
||||
if (!Arrays.asList(autofillIds).isEmpty()) {
|
||||
// If the entire Autofill Response is authenticated, AuthActivity is used
|
||||
// to generate Response.
|
||||
IntentSender sender = AutoFillAuthActivity.Companion.getAuthIntentSenderForResponse(this);
|
||||
IntentSender sender = AutoFillLauncherActivity.Companion.getAuthIntentSenderForResponse(this);
|
||||
RemoteViews presentation = new RemoteViews(getPackageName(), R.layout.autofill_service_unlock);
|
||||
responseBuilder.setAuthentication(autofillIds, sender, presentation);
|
||||
callback.onSuccess(responseBuilder.build());
|
||||
|
||||
@@ -23,6 +23,8 @@ import android.os.Parcel;
|
||||
|
||||
import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator;
|
||||
import com.kunzisoft.keepass.database.security.ProtectedString;
|
||||
import com.kunzisoft.keepass.model.Entry;
|
||||
import com.kunzisoft.keepass.model.Field;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -199,6 +201,21 @@ public abstract class PwEntry<Parent extends PwGroup> extends PwNode<Parent> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Entry getEntry() {
|
||||
Entry entryModel = new Entry();
|
||||
entryModel.setTitle(getTitle());
|
||||
entryModel.setUsername(getUsername());
|
||||
entryModel.setPassword(getPassword());
|
||||
entryModel.setUrl(getUrl());
|
||||
if (containsCustomFields()) {
|
||||
getFields()
|
||||
.doActionToAllCustomProtectedField(
|
||||
(key, value) -> entryModel.addCustomField(
|
||||
new Field(key, value.toString())));
|
||||
}
|
||||
return entryModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@@ -53,9 +53,9 @@ import com.kunzisoft.keepass.app.App;
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper;
|
||||
import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable;
|
||||
import com.kunzisoft.keepass.database.action.FileOnFinishRunnable;
|
||||
import com.kunzisoft.keepass.database.exception.ContentFileNotFoundException;
|
||||
import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment;
|
||||
import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment;
|
||||
import com.kunzisoft.keepass.magikeyboard.KeyboardHelper;
|
||||
import com.kunzisoft.keepass.password.AssignPasswordHelper;
|
||||
import com.kunzisoft.keepass.password.PasswordActivity;
|
||||
import com.kunzisoft.keepass.selection.EntrySelectionHelper;
|
||||
@@ -104,9 +104,6 @@ public class FileSelectActivity extends StylishActivity implements
|
||||
// TODO Consultation Mode
|
||||
private boolean consultationMode = false;
|
||||
|
||||
private boolean entrySelectionMode;
|
||||
private AutofillHelper autofillHelper;
|
||||
|
||||
private View fileSelectExpandableButton;
|
||||
private ExpandableLayout fileSelectExpandable;
|
||||
private EditText openFileNameView;
|
||||
@@ -118,27 +115,33 @@ public class FileSelectActivity extends StylishActivity implements
|
||||
|
||||
private String defaultPath;
|
||||
|
||||
public static void launch(Activity activity) {
|
||||
Intent intent = new Intent(activity, FileSelectActivity.class);
|
||||
// only to avoid visible flickering when redirecting
|
||||
activity.startActivityForResult(intent, RESULT_CANCELED);
|
||||
/*
|
||||
* -------------------------
|
||||
* No Standard Launch, pass by PasswordActivity
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* -------------------------
|
||||
* Keyboard Launch
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
public static void launchForKeyboardSelection(Activity activity) {
|
||||
KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, new Intent(activity, FileSelectActivity.class));
|
||||
}
|
||||
|
||||
public static void launchForKeyboardResult(Activity activity) {
|
||||
Intent intent = new Intent(activity, FileSelectActivity.class);
|
||||
EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent);
|
||||
activity.startActivityForResult(intent, EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE);
|
||||
}
|
||||
/*
|
||||
* -------------------------
|
||||
* Autofill Launch
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void launchForAutofillResult(Activity activity, AssistStructure assistStructure) {
|
||||
if ( assistStructure != null ) {
|
||||
Intent intent = new Intent(activity, FileSelectActivity.class);
|
||||
AutofillHelper.addAssistStructureExtraInIntent(intent, assistStructure);
|
||||
activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE);
|
||||
} else {
|
||||
launch(activity);
|
||||
}
|
||||
public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure) {
|
||||
AutofillHelper.INSTANCE.startActivityForAutofillResult(activity,
|
||||
new Intent(activity, FileSelectActivity.class),
|
||||
assistStructure);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,13 +180,6 @@ public class FileSelectActivity extends StylishActivity implements
|
||||
RecyclerView mListFiles = findViewById(R.id.file_list);
|
||||
mListFiles.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent());
|
||||
// To retrieve info for AutoFill
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
autofillHelper = new AutofillHelper();
|
||||
autofillHelper.retrieveAssistStructure(getIntent());
|
||||
}
|
||||
|
||||
// Open button
|
||||
openButtonView = findViewById(R.id.open_database);
|
||||
openButtonView.setOnClickListener(v -> {
|
||||
@@ -243,40 +239,52 @@ public class FileSelectActivity extends StylishActivity implements
|
||||
checkAndPerformedEducation();
|
||||
}
|
||||
|
||||
private void launchPasswordActivityWithPath(String path) {
|
||||
try {
|
||||
AssistStructure assistStructure = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
assistStructure = autofillHelper.getAssistStructure();
|
||||
if (assistStructure != null) {
|
||||
PasswordActivity.launchForAutofillResult(FileSelectActivity.this,
|
||||
path,
|
||||
assistStructure);
|
||||
}
|
||||
}
|
||||
if (assistStructure == null) {
|
||||
if (entrySelectionMode) {
|
||||
PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, path);
|
||||
} else {
|
||||
PasswordActivity.launch(FileSelectActivity.this, path);
|
||||
}
|
||||
}
|
||||
// Delete flickering for kitkat <=
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
overridePendingTransition(0, 0);
|
||||
} catch (ContentFileNotFoundException e) {
|
||||
private void fileNoFoundAction(FileNotFoundException e) {
|
||||
String error = getString(R.string.file_not_found_content);
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
error, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, error, e);
|
||||
} catch (FileNotFoundException e) {
|
||||
String error = getString(R.string.file_not_found);
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
error, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, error, e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Can't launch PasswordActivity", e);
|
||||
}
|
||||
|
||||
private void launchPasswordActivity(String fileName, String keyFile) {
|
||||
EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(),
|
||||
() -> {
|
||||
try {
|
||||
PasswordActivity.launch(FileSelectActivity.this,
|
||||
fileName, keyFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
fileNoFoundAction(e);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
() -> {
|
||||
try {
|
||||
PasswordActivity.launchForKeyboardResult(FileSelectActivity.this,
|
||||
fileName, keyFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
fileNoFoundAction(e);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
assistStructure -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
try {
|
||||
PasswordActivity.launchForAutofillResult(FileSelectActivity.this,
|
||||
fileName, keyFile,
|
||||
assistStructure);
|
||||
} catch (FileNotFoundException e) {
|
||||
fileNoFoundAction(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void launchPasswordActivityWithPath(String path) {
|
||||
launchPasswordActivity(path, "");
|
||||
// Delete flickering for kitkat <=
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
private void updateExternalStorageWarning() {
|
||||
@@ -603,32 +611,7 @@ public class FileSelectActivity extends StylishActivity implements
|
||||
@Override
|
||||
public void onFileItemOpenListener(int itemPosition) {
|
||||
new OpenFileHistoryAsyncTask((fileName, keyFile) -> {
|
||||
// TODO ENCAPSULATE
|
||||
try {
|
||||
AssistStructure assistStructure = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
assistStructure = autofillHelper.getAssistStructure();
|
||||
if (assistStructure != null) {
|
||||
PasswordActivity.launchForAutofillResult(FileSelectActivity.this,
|
||||
fileName, keyFile, assistStructure);
|
||||
}
|
||||
}
|
||||
if (assistStructure == null) {
|
||||
if (entrySelectionMode) {
|
||||
PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, fileName, keyFile);
|
||||
} else {
|
||||
PasswordActivity.launch(FileSelectActivity.this, fileName, keyFile);
|
||||
}
|
||||
}
|
||||
} catch (ContentFileNotFoundException e) {
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.file_not_found_content, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
} catch (FileNotFoundException e) {
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.file_not_found, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
launchPasswordActivity(fileName, keyFile);
|
||||
updateFileListVisibility();
|
||||
}, fileHistory).execute(itemPosition);
|
||||
}
|
||||
@@ -656,10 +639,8 @@ public class FileSelectActivity extends StylishActivity implements
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
// Get the entry result in entry selection mode
|
||||
EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
keyFileHelper.onActivityResultCallback(requestCode, resultCode, data,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.kunzisoft.keepass.magikeyboard
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import com.kunzisoft.keepass.selection.EntrySelectionHelper
|
||||
|
||||
object KeyboardHelper {
|
||||
|
||||
fun startActivityForKeyboardSelection(activity: Activity, intent: Intent) {
|
||||
EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent)
|
||||
// only to avoid visible flickering when redirecting
|
||||
activity.startActivityForResult(intent, 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.kunzisoft.keepass.magikeyboard
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import com.kunzisoft.keepass.activities.GroupActivity
|
||||
import com.kunzisoft.keepass.app.App
|
||||
import com.kunzisoft.keepass.fileselect.FileSelectActivity
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
|
||||
class KeyboardLauncherActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
val TAG = KeyboardLauncherActivity::class.java.name!!
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
TimeoutHelper.checkTime(this)
|
||||
if (App.getDB().loaded)
|
||||
GroupActivity.launchForKeyboardSelection(this, true)
|
||||
else {
|
||||
// Pass extra to get entry
|
||||
FileSelectActivity.launchForKeyboardSelection(this)
|
||||
}
|
||||
finish()
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,6 @@ import com.kunzisoft.keepass.magikeyboard.adapter.FieldsAdapter;
|
||||
import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver;
|
||||
import com.kunzisoft.keepass.magikeyboard.view.MagikeyboardView;
|
||||
import com.kunzisoft.keepass.model.Entry;
|
||||
import com.kunzisoft.keepass.selection.KeyboardEntryRetrieverActivity;
|
||||
|
||||
import static com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver.LOCK_ACTION;
|
||||
|
||||
@@ -206,8 +205,7 @@ public class MagikIME extends InputMethodService
|
||||
break;
|
||||
case KEY_ENTRY:
|
||||
deleteEntryKey(this);
|
||||
Intent intent = new Intent(this, KeyboardEntryRetrieverActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
Intent intent = new Intent(this, KeyboardLauncherActivity.class);
|
||||
startActivity(intent);
|
||||
break;
|
||||
case KEY_LOCK:
|
||||
|
||||
@@ -70,6 +70,7 @@ import com.kunzisoft.keepass.fileselect.KeyFileHelper;
|
||||
import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector;
|
||||
import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog;
|
||||
import com.kunzisoft.keepass.fingerprint.FingerPrintHelper;
|
||||
import com.kunzisoft.keepass.magikeyboard.KeyboardHelper;
|
||||
import com.kunzisoft.keepass.selection.EntrySelectionHelper;
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil;
|
||||
import com.kunzisoft.keepass.stylish.StylishActivity;
|
||||
@@ -133,9 +134,6 @@ public class PasswordActivity extends StylishActivity
|
||||
|
||||
private KeyFileHelper keyFileHelper;
|
||||
|
||||
protected boolean entrySelectionMode;
|
||||
private AutofillHelper autofillHelper;
|
||||
|
||||
private static void buildAndLaunchIntent(Activity activity, String fileName, String keyFile,
|
||||
IntentBuildLauncher intentBuildLauncher) {
|
||||
Intent intent = new Intent(activity, PasswordActivity.class);
|
||||
@@ -167,12 +165,6 @@ public class PasswordActivity extends StylishActivity
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
public static void launch(
|
||||
Activity act,
|
||||
String fileName) throws FileNotFoundException {
|
||||
launch(act, fileName, "");
|
||||
}
|
||||
|
||||
public static void launch(
|
||||
Activity activity,
|
||||
String fileName,
|
||||
@@ -187,12 +179,6 @@ public class PasswordActivity extends StylishActivity
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
public static void launchForKeyboardResult(
|
||||
Activity act,
|
||||
String fileName) throws FileNotFoundException {
|
||||
launchForKeyboardResult(act, fileName, "");
|
||||
}
|
||||
|
||||
public static void launchForKeyboardResult(
|
||||
Activity activity,
|
||||
String fileName,
|
||||
@@ -200,9 +186,7 @@ public class PasswordActivity extends StylishActivity
|
||||
verifyFileNameUriFromLaunch(fileName);
|
||||
|
||||
buildAndLaunchIntent(activity, fileName, keyFile, (intent) -> {
|
||||
EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent);
|
||||
// only to avoid visible flickering when redirecting
|
||||
activity.startActivityForResult(intent, EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE);
|
||||
KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,14 +196,6 @@ public class PasswordActivity extends StylishActivity
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void launchForAutofillResult(
|
||||
Activity act,
|
||||
String fileName,
|
||||
AssistStructure assistStructure) throws FileNotFoundException {
|
||||
launchForAutofillResult(act, fileName, "", assistStructure);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void launchForAutofillResult(
|
||||
Activity activity,
|
||||
@@ -230,8 +206,10 @@ public class PasswordActivity extends StylishActivity
|
||||
|
||||
if ( assistStructure != null ) {
|
||||
buildAndLaunchIntent(activity, fileName, keyFile, (intent) -> {
|
||||
AutofillHelper.addAssistStructureExtraInIntent(intent, assistStructure);
|
||||
activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE);
|
||||
AutofillHelper.INSTANCE.startActivityForAutofillResult(
|
||||
activity,
|
||||
intent,
|
||||
assistStructure);
|
||||
});
|
||||
} else {
|
||||
launch(activity, fileName, keyFile);
|
||||
@@ -310,13 +288,6 @@ public class PasswordActivity extends StylishActivity
|
||||
fingerPrintAnimatedVector = new FingerPrintAnimatedVector(this,
|
||||
fingerprintImageView);
|
||||
}
|
||||
|
||||
entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
autofillHelper = new AutofillHelper();
|
||||
autofillHelper.retrieveAssistStructure(getIntent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -974,20 +945,21 @@ public class PasswordActivity extends StylishActivity
|
||||
}
|
||||
|
||||
private void launchGroupActivity() {
|
||||
AssistStructure assistStructure = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
assistStructure = autofillHelper.getAssistStructure();
|
||||
if (assistStructure != null) {
|
||||
EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(),
|
||||
() -> {
|
||||
GroupActivity.launch(PasswordActivity.this, readOnly);
|
||||
return null;
|
||||
},
|
||||
() -> {
|
||||
GroupActivity.launchForKeyboardSelection(PasswordActivity.this, readOnly);
|
||||
return null;
|
||||
},
|
||||
assistStructure -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
GroupActivity.launchForAutofillResult(PasswordActivity.this, assistStructure, readOnly);
|
||||
}
|
||||
}
|
||||
if (assistStructure == null) {
|
||||
if (entrySelectionMode) {
|
||||
GroupActivity.launchForKeyboardResult(PasswordActivity.this, readOnly);
|
||||
} else {
|
||||
GroupActivity.launch(PasswordActivity.this, readOnly);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1060,9 +1032,8 @@ public class PasswordActivity extends StylishActivity
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
// To get entry in result
|
||||
EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
boolean keyFileResult = false;
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.kunzisoft.keepass.selection;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import com.kunzisoft.keepass.database.PwEntry;
|
||||
import com.kunzisoft.keepass.model.Entry;
|
||||
import com.kunzisoft.keepass.model.Field;
|
||||
|
||||
public class EntrySelectionHelper {
|
||||
|
||||
public static final int ENTRY_SELECTION_RESPONSE_REQUEST_CODE = 5164;
|
||||
|
||||
public static final String EXTRA_ENTRY_SELECTION_MODE = "com.kunzisoft.keepass.extra.ENTRY_SELECTION_MODE";
|
||||
|
||||
public static void addEntrySelectionModeExtraInIntent(Intent intent) {
|
||||
intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, true);
|
||||
}
|
||||
|
||||
public static boolean isIntentInEntrySelectionMode(Intent intent) {
|
||||
return intent.getBooleanExtra(EXTRA_ENTRY_SELECTION_MODE, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to hit when right key is selected
|
||||
*/
|
||||
public static void buildResponseWhenEntrySelected(Activity activity, PwEntry entry) {
|
||||
Intent mReplyIntent;
|
||||
Intent intent = activity.getIntent();
|
||||
boolean entrySelectionMode = isIntentInEntrySelectionMode(intent);
|
||||
if (entrySelectionMode) {
|
||||
mReplyIntent = new Intent();
|
||||
|
||||
Entry entryModel = new Entry();
|
||||
entryModel.setTitle(entry.getTitle());
|
||||
entryModel.setUsername(entry.getUsername());
|
||||
entryModel.setPassword(entry.getPassword());
|
||||
entryModel.setUrl(entry.getUrl());
|
||||
if (entry.containsCustomFields()) {
|
||||
entry.getFields()
|
||||
.doActionToAllCustomProtectedField(
|
||||
(key, value) -> entryModel.addCustomField(
|
||||
new Field(key, value.toString())));
|
||||
}
|
||||
Log.d(activity.getClass().getName(), "Build entry selection for reply: " + entryModel.getTitle());
|
||||
|
||||
mReplyIntent.putExtra(
|
||||
EXTRA_ENTRY_SELECTION_MODE,
|
||||
entryModel);
|
||||
activity.setResult(Activity.RESULT_OK, mReplyIntent);
|
||||
} else {
|
||||
activity.setResult(Activity.RESULT_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to loop and close each activity with return data
|
||||
*/
|
||||
public static void onActivityResultSetResultAndFinish(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == ENTRY_SELECTION_RESPONSE_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
activity.setResult(resultCode, data);
|
||||
}
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
activity.setResult(Activity.RESULT_CANCELED);
|
||||
}
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.kunzisoft.keepass.selection
|
||||
|
||||
import android.app.assist.AssistStructure
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||
|
||||
object EntrySelectionHelper {
|
||||
|
||||
private const val EXTRA_ENTRY_SELECTION_MODE = "com.kunzisoft.keepass.extra.ENTRY_SELECTION_MODE"
|
||||
|
||||
fun addEntrySelectionModeExtraInIntent(intent: Intent) {
|
||||
intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, true)
|
||||
}
|
||||
|
||||
fun doEntrySelectionAction(intent: Intent,
|
||||
standardAction: () -> Unit,
|
||||
keyboardAction: () -> Unit,
|
||||
autofillAction: (assistStructure: AssistStructure) -> Unit) {
|
||||
var assistStructure: AssistStructure? = null
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
assistStructure = AutofillHelper.retrieveAssistStructure(intent)
|
||||
assistStructure?.let {
|
||||
autofillAction.invoke(assistStructure)
|
||||
}
|
||||
}
|
||||
if (assistStructure == null) {
|
||||
if (intent.getBooleanExtra(EXTRA_ENTRY_SELECTION_MODE, false)) {
|
||||
intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, false)
|
||||
keyboardAction.invoke()
|
||||
} else {
|
||||
standardAction.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package com.kunzisoft.keepass.selection
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.util.Log
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.GroupActivity
|
||||
import com.kunzisoft.keepass.app.App
|
||||
import com.kunzisoft.keepass.fileselect.FileSelectActivity
|
||||
import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikIME
|
||||
import com.kunzisoft.keepass.model.Entry
|
||||
import com.kunzisoft.keepass.selection.EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE
|
||||
import com.kunzisoft.keepass.selection.EntrySelectionHelper.EXTRA_ENTRY_SELECTION_MODE
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
|
||||
class KeyboardEntryRetrieverActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
|
||||
val TAG = KeyboardEntryRetrieverActivity::class.java.name!!
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
TimeoutHelper.checkTime(this) {}
|
||||
if (App.getDB().loaded)
|
||||
GroupActivity.launchForKeyboardResult(this, true)
|
||||
else {
|
||||
// Pass extra to get entry
|
||||
FileSelectActivity.launchForKeyboardResult(this)
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
Log.d(TAG, "Retrieve the entry selected, requestCode: $requestCode, resultCode: $resultCode")
|
||||
if (requestCode == ENTRY_SELECTION_RESPONSE_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
val entry = data?.getParcelableExtra<Entry>(EXTRA_ENTRY_SELECTION_MODE)
|
||||
Log.d(TAG, "Set the entry ${entry?.title} to keyboard")
|
||||
MagikIME.setEntryKey(entry)
|
||||
|
||||
// Show the notification if allowed in Preferences
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
if (sharedPreferences.getBoolean(getString(R.string.keyboard_notification_entry_key),
|
||||
resources.getBoolean(R.bool.keyboard_notification_entry_default))) {
|
||||
val notificationIntent = Intent(this, KeyboardEntryNotificationService::class.java)
|
||||
startService(notificationIntent)
|
||||
}
|
||||
}
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
Log.w(TAG, "Entry not retrieved")
|
||||
}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -197,6 +197,12 @@ public class PreferencesUtil {
|
||||
context.getResources().getBoolean(R.bool.enable_read_only_default));
|
||||
}
|
||||
|
||||
public static boolean enableKeyboardNotificationEntry(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return prefs.getBoolean(context.getString(R.string.keyboard_notification_entry_key),
|
||||
context.getResources().getBoolean(R.bool.keyboard_notification_entry_default));
|
||||
}
|
||||
|
||||
/**
|
||||
* All preference keys associated with education
|
||||
*/
|
||||
|
||||
@@ -48,7 +48,7 @@ open class SettingsActivity : LockingActivity(), MainPreferenceFragment.Callback
|
||||
intent.putExtra(TIMEOUT_ENABLE_KEY, timeoutEnable)
|
||||
if (!timeoutEnable) {
|
||||
activity.startActivity(intent)
|
||||
} else if (TimeoutHelper.checkTime(activity)) {
|
||||
} else if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,9 +78,10 @@ object TimeoutHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the time previously record with recordTime and do the shutdown action if timeout
|
||||
* Check the time previously record with recordTime and do the [timeoutAction] if timeout
|
||||
* return 'false' if timeout, 'true' if in time
|
||||
*/
|
||||
fun checkTime(context: Context, shutdown: (() -> Unit)): Boolean {
|
||||
fun checkTime(context: Context, timeoutAction: (() -> Unit)? = null): Boolean {
|
||||
// Cancel the lock PendingIntent
|
||||
if (App.getDB().loaded) {
|
||||
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
@@ -118,7 +119,7 @@ object TimeoutHelper {
|
||||
if (diff >= appTimeout) {
|
||||
// We have timed out
|
||||
App.getDB().loaded = false
|
||||
shutdown.invoke()
|
||||
timeoutAction?.invoke()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -127,14 +128,14 @@ object TimeoutHelper {
|
||||
/**
|
||||
* Check the time previously record with recordTime and lock the activity if timeout
|
||||
*/
|
||||
fun checkTime(activity: Activity): Boolean {
|
||||
fun checkTimeAndLockIfTimeout(activity: Activity): Boolean {
|
||||
return checkTime(activity) {
|
||||
activity.lock()
|
||||
}
|
||||
}
|
||||
|
||||
fun lockOrResetTimeout(activity: Activity, action: (() -> Unit)? = null) {
|
||||
if (checkTime(activity)) {
|
||||
if (checkTimeAndLockIfTimeout(activity)) {
|
||||
recordTime(activity)
|
||||
action?.invoke()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user