Files
KeePassDX/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.java
2018-04-05 20:11:08 +02:00

175 lines
6.7 KiB
Java

/*
* 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 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) {
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 onActivityResultSetResult(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);
}
}
}
/**
* 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) {
onActivityResultSetResult(activity, requestCode, resultCode, data);
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;
}
}