Add autofill service and preference

This commit is contained in:
Jeremy
2017-11-23 19:07:13 +01:00
parent 64a4f43945
commit d20eef0922
52 changed files with 2311 additions and 135 deletions

2
.idea/misc.xml generated
View File

@@ -24,7 +24,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion = 25
compileSdkVersion = 26
buildToolsVersion = "26.0.2"
defaultConfig {
applicationId "com.kunzisoft.keepass"
minSdkVersion 14
targetSdkVersion 25
targetSdkVersion 26
versionCode = 1
versionName = "2.5.0.0beta1"
@@ -52,18 +52,24 @@ android {
buildConfigField "boolean", "GOOGLE_PLAY_VERSION", "true"
}
}
compileOptions {
targetCompatibility 1.8
sourceCompatibility 1.8
}
}
def supportVersion = "25.4.0"
def supportVersion = "26.1.0"
def spongycastleVersion = "1.58.0.0"
dependencies {
androidTestCompile files('libs/junit4.jar')
compile "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:design:$supportVersion"
compile "com.android.support:preference-v7:$supportVersion"
compile "com.android.support:preference-v14:$supportVersion"
compile "com.madgag.spongycastle:core:$spongycastleVersion"
compile "com.madgag.spongycastle:prov:$spongycastleVersion"
compile "joda-time:joda-time:2.9.9"
androidTestImplementation files('libs/junit4.jar')
implementation "com.android.support:appcompat-v7:$supportVersion"
implementation "com.android.support:design:$supportVersion"
implementation "com.android.support:preference-v7:$supportVersion"
implementation "com.android.support:preference-v14:$supportVersion"
implementation "com.madgag.spongycastle:core:$spongycastleVersion"
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
implementation "joda-time:joda-time:2.9.9"
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.1'
implementation group: 'com.google.guava', name: 'guava', version: '22.0-android'
}

View File

@@ -66,7 +66,7 @@ public class SearchTest extends AndroidTestCase {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(ctx.getString(R.string.omitbackup_key), setting);
editor.putBoolean(ctx.getString(R.string.settings_omitbackup_key), setting);
editor.commit();
}

View File

@@ -102,8 +102,26 @@
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable" />
</activity>
<activity android:name="com.keepassdroid.settings.SettingsActivity" />
<activity android:name="com.keepassdroid.AutoFillAuthActivity"
android:configChanges="orientation|keyboardHidden" />
<activity
android:name="com.keepassdroid.settings.SettingsAutofillActivity"
android:exported="true">
</activity>
<service android:name="com.keepassdroid.services.TimeoutService" />
<service
android:name="com.keepassdroid.autofill.KeeAutofillService"
android:label="@string/autofill_service_name"
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
<meta-data
android:name="android.autofill"
android:resource="@xml/dataset_service" />
<intent-filter>
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
</service>
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
</application>
</manifest>

View File

@@ -0,0 +1,34 @@
/*
* 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;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
public class AutoFillAuthActivity extends PasswordActivity {
public static IntentSender getAuthIntentSenderForResponse(Context context) {
final Intent intent = new Intent(context, PasswordActivity.class);
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT)
.getIntentSender();
}
}

View File

@@ -124,7 +124,7 @@ public class EntryActivity extends LockCloseHideActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
mShowPassword = ! prefs.getBoolean(getString(R.string.maskpass_key), getResources().getBoolean(R.bool.maskpass_default));
mShowPassword = ! prefs.getBoolean(getString(R.string.settings_maskpass_key), getResources().getBoolean(R.bool.settings_maskpass_default));
super.onCreate(savedInstanceState);
setEntryView();
@@ -428,7 +428,7 @@ public class EntryActivity extends LockCloseHideActivity {
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String sClipClear = prefs.getString(getString(R.string.clipboard_timeout_key), getString(R.string.clipboard_timeout_default));
String sClipClear = prefs.getString(getString(R.string.settings_clipboard_timeout_key), getString(R.string.clipboard_timeout_default));
long clipClearTime = Long.parseLong(sClipClear);

View File

@@ -247,7 +247,7 @@ public abstract class GroupActivity extends GroupBaseActivity
if (App.getDB().readOnly) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean(getString(R.string.show_read_only_warning), true)) {
if (prefs.getBoolean(getString(R.string.settings_show_read_only_warning), true)) {
Dialog dialog = new ReadOnlyDialog(this);
dialog.show();
}

View File

@@ -191,7 +191,7 @@ public abstract class GroupBaseActivity extends LockCloseListActivity {
// Will be null if onPrepareOptionsMenu is called before onCreate
if (prefs != null) {
sortByName = prefs.getBoolean(getString(R.string.sort_key), getResources().getBoolean(R.bool.sort_default));
sortByName = prefs.getBoolean(getString(R.string.settings_sort_key), getResources().getBoolean(R.bool.settings_sort_default));
}
int resId;
@@ -245,8 +245,8 @@ public abstract class GroupBaseActivity extends LockCloseListActivity {
private void toggleSort() {
// Toggle setting
String sortKey = getString(R.string.sort_key);
boolean sortByName = prefs.getBoolean(sortKey, getResources().getBoolean(R.bool.sort_default));
String sortKey = getString(R.string.settings_sort_key);
boolean sortByName = prefs.getBoolean(sortKey, getResources().getBoolean(R.bool.settings_sort_default));
Editor editor = prefs.edit();
editor.putBoolean(sortKey, ! sortByName);
EditorCompat.apply(editor);

View File

@@ -194,7 +194,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefsNoBackup = getSharedPreferences("nobackup", Context.MODE_PRIVATE);
mRememberKeyfile = prefs.getBoolean(getString(R.string.keyfile_key), getResources().getBoolean(R.bool.keyfile_default));
mRememberKeyfile = prefs.getBoolean(getString(R.string.settings_keyfile_key), getResources().getBoolean(R.bool.settings_keyfile_default));
setContentView(R.layout.password);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

View File

@@ -79,7 +79,7 @@ public class PwGroupListAdapter extends BaseAdapter {
}
}
boolean sortLists = prefs.getBoolean(mAct.getString(R.string.sort_key), mAct.getResources().getBoolean(R.bool.sort_default));
boolean sortLists = prefs.getBoolean(mAct.getString(R.string.settings_sort_key), mAct.getResources().getBoolean(R.bool.settings_sort_default));
if ( sortLists ) {
groupsForViewing = new ArrayList<PwGroup>(mGroup.childGroups);

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill;
import android.app.assist.AssistStructure.ViewNode;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.view.autofill.AutofillId;
/**
* A stripped down version of a {@link ViewNode} that contains only autofill-relevant metadata. It
* also contains a {@code mSaveType} flag that is calculated based on the {@link ViewNode}]'s
* autofill hints.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public class AutofillFieldMetadata {
private int mSaveType = 0;
private String[] mAutofillHints;
private AutofillId mAutofillId;
private int mAutofillType;
private CharSequence[] mAutofillOptions;
private boolean mFocused;
public AutofillFieldMetadata(ViewNode view) {
mAutofillId = view.getAutofillId();
mAutofillType = view.getAutofillType();
mAutofillOptions = view.getAutofillOptions();
mFocused = view.isFocused();
String[] hints = AutofillHints.filterForSupportedHints(view.getAutofillHints());
if (hints != null) {
AutofillHints.convertToStoredHintNames(hints);
setHints(hints);
}
}
public String[] getHints() {
return mAutofillHints;
}
public void setHints(String[] hints) {
mAutofillHints = hints;
mSaveType = AutofillHints.getSaveTypeForHints(hints);
}
public int getSaveType() {
return mSaveType;
}
public AutofillId getId() {
return mAutofillId;
}
public int getAutofillType() {
return mAutofillType;
}
/**
* When the {@link ViewNode} is a list that the user needs to choose a string from (i.e. a
* spinner), this is called to return the index of a specific item in the list.
*/
public int getAutofillOptionIndex(String value) {
for (int i = 0; i < mAutofillOptions.length; i++) {
if (mAutofillOptions[i].toString().equals(value)) {
return i;
}
}
return -1;
}
public boolean isFocused() {
return mFocused;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill;
import android.view.autofill.AutofillId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* Data structure that stores a collection of {@code AutofillFieldMetadata}s. Contains all of the
* client's {@code View} hierarchy autofill-relevant metadata.
*/
public final class AutofillFieldMetadataCollection {
private final List<AutofillId> mAutofillIds = new ArrayList<>();
private final HashMap<String, List<AutofillFieldMetadata>> mAutofillHintsToFieldsMap = new HashMap<>();
private final List<String> mAllAutofillHints = new ArrayList<>();
private final List<String> mFocusedAutofillHints = new ArrayList<>();
private int mSize = 0;
private int mSaveType = 0;
public void add(AutofillFieldMetadata autofillFieldMetadata) {
mSaveType |= autofillFieldMetadata.getSaveType();
mSize++;
mAutofillIds.add(autofillFieldMetadata.getId());
List<String> hintsList = Arrays.asList(autofillFieldMetadata.getHints());
mAllAutofillHints.addAll(hintsList);
if (autofillFieldMetadata.isFocused()) {
mFocusedAutofillHints.addAll(hintsList);
}
for (String hint : autofillFieldMetadata.getHints()) {
if (!mAutofillHintsToFieldsMap.containsKey(hint)) {
mAutofillHintsToFieldsMap.put(hint, new ArrayList<>());
}
mAutofillHintsToFieldsMap.get(hint).add(autofillFieldMetadata);
}
}
public int getSaveType() {
return mSaveType;
}
public AutofillId[] getAutofillIds() {
return mAutofillIds.toArray(new AutofillId[mSize]);
}
public List<AutofillFieldMetadata> getFieldsForHint(String hint) {
return mAutofillHintsToFieldsMap.get(hint);
}
public List<String> getFocusedHints() {
return mFocusedAutofillHints;
}
public List<String> getAllHints() {
return mAllAutofillHints;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill;
import com.keepassdroid.model.FilledAutofillField;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Holds the properties associated with an autofill hint in this Autofill Service.
*/
public final class AutofillHintProperties {
private String mAutofillHint;
private FakeFieldGenerator mFakeFieldGenerator;
private Set<Integer> mValidTypes;
private int mSaveType;
private int mPartition;
// TODO Change to real field generator
public AutofillHintProperties(String autofillHint, int saveType, int partitionNumber,
FakeFieldGenerator fakeFieldGenerator, Integer... validTypes) {
mAutofillHint = autofillHint;
mSaveType = saveType;
mPartition = partitionNumber;
mFakeFieldGenerator = fakeFieldGenerator;
mValidTypes = new HashSet<>(Arrays.asList(validTypes));
}
/**
* Generates dummy autofill field data that is relevant to the autofill hint.
*/
public FilledAutofillField generateFakeField(int seed) {
return mFakeFieldGenerator.generate(seed);
}
/**
* Returns autofill hint associated with these properties. If you save a field that uses a W3C
* hint, there is a chance this will return a different but analogous hint, when applicable.
* For example, W3C has hint 'email' and {@link android.view.View} has hint 'emailAddress', so
* the W3C hint should map to the hint defined in {@link android.view.View} ('emailAddress').
*/
public String getAutofillHint() {
return mAutofillHint;
}
/**
* Returns how this hint maps to a {@link android.service.autofill.SaveInfo} type.
*/
public int getSaveType() {
return mSaveType;
}
/**
* Returns which data partition this autofill hint should be a part of. See partitions defined
* in {@link AutofillHints}.
*/
public int getPartition() {
return mPartition;
}
/**
* Sometimes, data for a hint should only be stored as a certain AutofillValue type. For
* example, it is recommended that data representing a Credit Card Expiration date, annotated
* with the hint {@link android.view.View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}, should
* only be stored as {@link android.view.View.AUTOFILL_TYPE_DATE}.
*/
public boolean isValidType(int type) {
return mValidTypes.contains(type);
}
}

View File

@@ -0,0 +1,775 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill;
import android.os.Build;
import android.service.autofill.SaveInfo;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.View;
import com.google.common.collect.ImmutableMap;
import com.keepassdroid.model.FilledAutofillField;
import com.keepassdroid.model.FilledAutofillFieldCollection;
import java.util.Calendar;
@RequiresApi(api = Build.VERSION_CODES.O)
public final class AutofillHints {
public static final int PARTITION_OTHER = 0;
public static final int PARTITION_ADDRESS = 1;
public static final int PARTITION_EMAIL = 2;
public static final int PARTITION_CREDIT_CARD = 3;
public static final int[] PARTITIONS = {
PARTITION_OTHER, PARTITION_ADDRESS, PARTITION_EMAIL, PARTITION_CREDIT_CARD
};
/* TODO: finish building fake data for all hints. */
private static final ImmutableMap<String, AutofillHintProperties> sValidHints =
new ImmutableMap.Builder<String, AutofillHintProperties>()
.put(View.AUTOFILL_HINT_EMAIL_ADDRESS, new AutofillHintProperties(
View.AUTOFILL_HINT_EMAIL_ADDRESS, SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS,
PARTITION_EMAIL,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_EMAIL_ADDRESS);
filledAutofillField.setTextValue("email" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(View.AUTOFILL_HINT_NAME, new AutofillHintProperties(
View.AUTOFILL_HINT_NAME, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_NAME);
filledAutofillField.setTextValue("name" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(View.AUTOFILL_HINT_USERNAME, new AutofillHintProperties(
View.AUTOFILL_HINT_USERNAME, SaveInfo.SAVE_DATA_TYPE_USERNAME,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_USERNAME);
filledAutofillField.setTextValue("login" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(View.AUTOFILL_HINT_PASSWORD, new AutofillHintProperties(
View.AUTOFILL_HINT_PASSWORD, SaveInfo.SAVE_DATA_TYPE_PASSWORD,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_PASSWORD);
filledAutofillField.setTextValue("login" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(View.AUTOFILL_HINT_PHONE, new AutofillHintProperties(
View.AUTOFILL_HINT_PHONE, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_PHONE);
filledAutofillField.setTextValue("" + seed + "2345678910");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(View.AUTOFILL_HINT_POSTAL_ADDRESS, new AutofillHintProperties(
View.AUTOFILL_HINT_POSTAL_ADDRESS, SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_POSTAL_ADDRESS);
filledAutofillField.setTextValue(
"" + seed + " Fake Ln, Fake, FA, FAA 10001");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(View.AUTOFILL_HINT_POSTAL_CODE, new AutofillHintProperties(
View.AUTOFILL_HINT_POSTAL_CODE, SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_POSTAL_CODE);
filledAutofillField.setTextValue("1000" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(View.AUTOFILL_HINT_CREDIT_CARD_NUMBER, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_NUMBER,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD,
PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
filledAutofillField.setTextValue("" + seed + "234567");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD,
PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
filledAutofillField.setTextValue("" + seed + seed + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE,
new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + seed);
filledAutofillField.setDateValue(calendar.getTimeInMillis());
return filledAutofillField;
}, View.AUTOFILL_TYPE_DATE))
.put(View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,
new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
CharSequence[] months = monthRange();
int month = seed % months.length;
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MONTH, month);
FilledAutofillField filledAutofillField =
new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH);
filledAutofillField.setListValue(months, month);
filledAutofillField.setTextValue(Integer.toString(month));
filledAutofillField.setDateValue(calendar.getTimeInMillis());
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST,
View.AUTOFILL_TYPE_DATE))
.put(View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR);
Calendar calendar = Calendar.getInstance();
int expYear = calendar.get(Calendar.YEAR) + seed;
calendar.set(Calendar.YEAR, expYear);
filledAutofillField.setDateValue(calendar.getTimeInMillis());
filledAutofillField.setTextValue(Integer.toString(expYear));
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST,
View.AUTOFILL_TYPE_DATE))
.put(View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
CharSequence[] days = dayRange();
int day = seed % days.length;
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DATE, day);
filledAutofillField.setListValue(days, day);
filledAutofillField.setTextValue(Integer.toString(day));
filledAutofillField.setDateValue(calendar.getTimeInMillis());
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST,
View.AUTOFILL_TYPE_DATE))
.put(W3cHints.HONORIFIC_PREFIX, new AutofillHintProperties(
W3cHints.HONORIFIC_PREFIX, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
W3cHints.HONORIFIC_PREFIX);
CharSequence[] examplePrefixes = {"Miss", "Ms.", "Mr.", "Mx.",
"Sr.", "Dr.", "Lady", "Lord"};
filledAutofillField.setListValue(examplePrefixes,
seed % examplePrefixes.length);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.GIVEN_NAME, new AutofillHintProperties(W3cHints.GIVEN_NAME,
SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.GIVEN_NAME);
filledAutofillField.setTextValue("name" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ADDITIONAL_NAME, new AutofillHintProperties(
W3cHints.ADDITIONAL_NAME, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ADDITIONAL_NAME);
filledAutofillField.setTextValue("addtlname" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.FAMILY_NAME, new AutofillHintProperties(
W3cHints.FAMILY_NAME, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.FAMILY_NAME);
filledAutofillField.setTextValue("famname" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.HONORIFIC_SUFFIX, new AutofillHintProperties(
W3cHints.HONORIFIC_SUFFIX, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.HONORIFIC_SUFFIX);
CharSequence[] exampleSuffixes = {"san", "kun", "chan", "sama"};
filledAutofillField.setListValue(exampleSuffixes,
seed % exampleSuffixes.length);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.NEW_PASSWORD, new AutofillHintProperties(
W3cHints.NEW_PASSWORD, SaveInfo.SAVE_DATA_TYPE_PASSWORD,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.NEW_PASSWORD);
filledAutofillField.setTextValue("login" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.CURRENT_PASSWORD, new AutofillHintProperties(
View.AUTOFILL_HINT_PASSWORD, SaveInfo.SAVE_DATA_TYPE_PASSWORD,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_PASSWORD);
filledAutofillField.setTextValue("login" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ORGANIZATION_TITLE, new AutofillHintProperties(
W3cHints.ORGANIZATION_TITLE, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ORGANIZATION_TITLE);
filledAutofillField.setTextValue("org" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.ORGANIZATION, new AutofillHintProperties(W3cHints.ORGANIZATION,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ORGANIZATION);
filledAutofillField.setTextValue("org" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.STREET_ADDRESS, new AutofillHintProperties(
W3cHints.STREET_ADDRESS, SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.STREET_ADDRESS);
filledAutofillField.setTextValue(
"" + seed + " Fake Ln, Fake, FA, FAA 10001");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ADDRESS_LINE1, new AutofillHintProperties(W3cHints.ADDRESS_LINE1,
SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ADDRESS_LINE1);
filledAutofillField.setTextValue("" + seed + " Fake Ln");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ADDRESS_LINE2, new AutofillHintProperties(W3cHints.ADDRESS_LINE2,
SaveInfo.SAVE_DATA_TYPE_ADDRESS, PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ADDRESS_LINE2);
filledAutofillField.setTextValue("Apt. " + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ADDRESS_LINE3, new AutofillHintProperties(W3cHints.ADDRESS_LINE3,
SaveInfo.SAVE_DATA_TYPE_ADDRESS, PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ADDRESS_LINE3);
filledAutofillField.setTextValue("FA" + seed + ", FA, FAA");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ADDRESS_LEVEL4, new AutofillHintProperties(
W3cHints.ADDRESS_LEVEL4, SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ADDRESS_LEVEL4);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ADDRESS_LEVEL3, new AutofillHintProperties(
W3cHints.ADDRESS_LEVEL3, SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ADDRESS_LEVEL3);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ADDRESS_LEVEL2, new AutofillHintProperties(
W3cHints.ADDRESS_LEVEL2, SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ADDRESS_LEVEL2);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.ADDRESS_LEVEL1, new AutofillHintProperties(
W3cHints.ADDRESS_LEVEL1, SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.ADDRESS_LEVEL1);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.COUNTRY, new AutofillHintProperties(W3cHints.COUNTRY,
SaveInfo.SAVE_DATA_TYPE_ADDRESS, PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.COUNTRY);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.COUNTRY_NAME, new AutofillHintProperties(W3cHints.COUNTRY_NAME,
SaveInfo.SAVE_DATA_TYPE_ADDRESS, PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.COUNTRY_NAME);
CharSequence[] exampleCountries = {"USA", "Mexico", "Canada"};
filledAutofillField.setListValue(exampleCountries,
seed % exampleCountries.length);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.POSTAL_CODE, new AutofillHintProperties(
View.AUTOFILL_HINT_POSTAL_CODE, SaveInfo.SAVE_DATA_TYPE_ADDRESS,
PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_POSTAL_CODE);
filledAutofillField.setTextValue("" + seed + seed + seed + seed +
seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.CC_NAME, new AutofillHintProperties(W3cHints.CC_NAME,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD,
PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.CC_NAME);
filledAutofillField.setTextValue("firstname" + seed + "lastname" +
seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.CC_GIVEN_NAME, new AutofillHintProperties(W3cHints.CC_GIVEN_NAME,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.CC_GIVEN_NAME);
filledAutofillField.setTextValue("givenname" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.CC_ADDITIONAL_NAME, new AutofillHintProperties(
W3cHints.CC_ADDITIONAL_NAME, SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD,
PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.CC_ADDITIONAL_NAME);
filledAutofillField.setTextValue("addtlname" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.CC_FAMILY_NAME, new AutofillHintProperties(
W3cHints.CC_FAMILY_NAME, SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD,
PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.CC_FAMILY_NAME);
filledAutofillField.setTextValue("familyname" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.CC_NUMBER, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_NUMBER,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
filledAutofillField.setTextValue("" + seed + "234567");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.CC_EXPIRATION, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + seed);
filledAutofillField.setDateValue(calendar.getTimeInMillis());
return filledAutofillField;
}, View.AUTOFILL_TYPE_DATE))
.put(W3cHints.CC_EXPIRATION_MONTH, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH);
CharSequence[] months = monthRange();
filledAutofillField.setListValue(months,
seed % months.length);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.CC_EXPIRATION_YEAR, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR);
Calendar calendar = Calendar.getInstance();
int expYear = calendar.get(Calendar.YEAR) + seed;
calendar.set(Calendar.YEAR, expYear);
filledAutofillField.setDateValue(calendar.getTimeInMillis());
filledAutofillField.setTextValue("" + expYear);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.CC_CSC, new AutofillHintProperties(
View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField = new FilledAutofillField(
View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
filledAutofillField.setTextValue("" + seed + seed + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.CC_TYPE, new AutofillHintProperties(W3cHints.CC_TYPE,
SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD, PARTITION_CREDIT_CARD,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.CC_TYPE);
filledAutofillField.setTextValue("type" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TRANSACTION_CURRENCY, new AutofillHintProperties(
W3cHints.TRANSACTION_CURRENCY, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TRANSACTION_CURRENCY);
CharSequence[] exampleCurrencies = {"USD", "CAD", "KYD", "CRC"};
filledAutofillField.setListValue(exampleCurrencies,
seed % exampleCurrencies.length);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TRANSACTION_AMOUNT, new AutofillHintProperties(
W3cHints.TRANSACTION_AMOUNT, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TRANSACTION_AMOUNT);
filledAutofillField.setTextValue("" + seed * 100);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.LANGUAGE, new AutofillHintProperties(W3cHints.LANGUAGE,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.LANGUAGE);
CharSequence[] exampleLanguages = {"Bulgarian", "Croatian", "Czech",
"Danish", "Dutch", "English", "Estonian"};
filledAutofillField.setListValue(exampleLanguages,
seed % exampleLanguages.length);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.BDAY, new AutofillHintProperties(W3cHints.BDAY,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.BDAY);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) - seed * 10);
calendar.set(Calendar.MONTH, seed % 12);
calendar.set(Calendar.DATE, seed % 27);
filledAutofillField.setDateValue(calendar.getTimeInMillis());
return filledAutofillField;
}, View.AUTOFILL_TYPE_DATE))
.put(W3cHints.BDAY_DAY, new AutofillHintProperties(W3cHints.BDAY_DAY,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.BDAY_DAY);
filledAutofillField.setTextValue("" + seed % 27);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.BDAY_MONTH, new AutofillHintProperties(W3cHints.BDAY_MONTH,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.BDAY_MONTH);
filledAutofillField.setTextValue("" + seed % 12);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.BDAY_YEAR, new AutofillHintProperties(W3cHints.BDAY_YEAR,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.BDAY_YEAR);
int year = Calendar.getInstance().get(Calendar.YEAR) - seed * 10;
filledAutofillField.setTextValue("" + year);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.SEX, new AutofillHintProperties(W3cHints.SEX,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.SEX);
filledAutofillField.setTextValue("Other");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.URL, new AutofillHintProperties(W3cHints.URL,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.URL);
filledAutofillField.setTextValue("http://google.com");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.PHOTO, new AutofillHintProperties(W3cHints.PHOTO,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.PHOTO);
filledAutofillField.setTextValue("photo" + seed + ".jpg");
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.PREFIX_SECTION, new AutofillHintProperties(
W3cHints.PREFIX_SECTION, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.PREFIX_SECTION);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.SHIPPING, new AutofillHintProperties(W3cHints.SHIPPING,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.SHIPPING);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.BILLING, new AutofillHintProperties(W3cHints.BILLING,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_ADDRESS,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.BILLING);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.PREFIX_HOME, new AutofillHintProperties(W3cHints.PREFIX_HOME,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.PREFIX_HOME);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.PREFIX_WORK, new AutofillHintProperties(W3cHints.PREFIX_WORK,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.PREFIX_WORK);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.PREFIX_FAX, new AutofillHintProperties(W3cHints.PREFIX_FAX,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.PREFIX_FAX);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.PREFIX_PAGER, new AutofillHintProperties(W3cHints.PREFIX_PAGER,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.PREFIX_PAGER);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TEL, new AutofillHintProperties(W3cHints.TEL,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TEL);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.TEL_COUNTRY_CODE, new AutofillHintProperties(
W3cHints.TEL_COUNTRY_CODE, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TEL_COUNTRY_CODE);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TEL_NATIONAL, new AutofillHintProperties(W3cHints.TEL_NATIONAL,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TEL_NATIONAL);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TEL_AREA_CODE, new AutofillHintProperties(
W3cHints.TEL_AREA_CODE, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TEL_AREA_CODE);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TEL_LOCAL, new AutofillHintProperties(
W3cHints.TEL_LOCAL, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TEL_LOCAL);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TEL_LOCAL_PREFIX, new AutofillHintProperties(
W3cHints.TEL_LOCAL_PREFIX, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TEL_LOCAL_PREFIX);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TEL_LOCAL_SUFFIX, new AutofillHintProperties(
W3cHints.TEL_LOCAL_SUFFIX, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TEL_LOCAL_SUFFIX);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.TEL_EXTENSION, new AutofillHintProperties(W3cHints.TEL_EXTENSION,
SaveInfo.SAVE_DATA_TYPE_GENERIC, PARTITION_OTHER,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.TEL_EXTENSION);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.put(W3cHints.EMAIL, new AutofillHintProperties(
View.AUTOFILL_HINT_EMAIL_ADDRESS, SaveInfo.SAVE_DATA_TYPE_GENERIC,
PARTITION_EMAIL,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(View.AUTOFILL_HINT_EMAIL_ADDRESS);
filledAutofillField.setTextValue("email" + seed);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT))
.put(W3cHints.IMPP, new AutofillHintProperties(W3cHints.IMPP,
SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS, PARTITION_EMAIL,
(seed) -> {
FilledAutofillField filledAutofillField =
new FilledAutofillField(W3cHints.IMPP);
return filledAutofillField;
}, View.AUTOFILL_TYPE_TEXT, View.AUTOFILL_TYPE_LIST))
.build();
private AutofillHints() {
}
public static boolean isValidTypeForHints(String[] hints, int type) {
if (hints != null) {
for (String hint : hints) {
if (hint != null && sValidHints.containsKey(hint)) {
boolean valid = sValidHints.get(hint).isValidType(type);
if (valid) {
return true;
}
}
}
}
return false;
}
public static boolean isValidHint(String hint) {
return sValidHints.containsKey(hint);
}
public static int getSaveTypeForHints(String[] hints) {
int saveType = 0;
if (hints != null) {
for (String hint : hints) {
if (hint != null && sValidHints.containsKey(hint)) {
saveType |= sValidHints.get(hint).getSaveType();
}
}
}
return saveType;
}
public static FilledAutofillField getFakeField(String hint, int seed) {
return sValidHints.get(hint).generateFakeField(seed);
}
public static FilledAutofillFieldCollection getFakeFieldCollection(int partition, int seed) {
FilledAutofillFieldCollection filledAutofillFieldCollection =
new FilledAutofillFieldCollection();
for (String hint : sValidHints.keySet()) {
if (hint != null && sValidHints.get(hint).getPartition() == partition) {
FilledAutofillField fakeField = getFakeField(hint, seed);
filledAutofillFieldCollection.add(fakeField);
}
}
return filledAutofillFieldCollection;
}
private static String getStoredHintName(String hint) {
return sValidHints.get(hint).getAutofillHint();
}
public static void convertToStoredHintNames(String[] hints) {
for (int i = 0; i < hints.length; i++) {
hints[i] = getStoredHintName(hints[i]);
}
}
private static CharSequence[] dayRange() {
CharSequence[] days = new CharSequence[27];
for (int i = 0; i < days.length; i++) {
days[i] = Integer.toString(i);
}
return days;
}
private static CharSequence[] monthRange() {
CharSequence[] months = new CharSequence[12];
for (int i = 0; i < months.length; i++) {
months[i] = Integer.toString(i);
}
return months;
}
public static String[] filterForSupportedHints(String[] hints) {
String[] filteredHints = new String[hints.length];
int i = 0;
for (String hint : hints) {
if (AutofillHints.isValidHint(hint)) {
filteredHints[i++] = hint;
} else {
Log.w(AutofillHints.class.getName(), "Invalid autofill hint: " + hint);
}
}
if (i == 0) {
return null;
}
String[] finalFilteredHints = new String[i];
System.arraycopy(filteredHints, 0, finalFilteredHints, 0, i);
return finalFilteredHints;
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill;
import com.keepassdroid.model.FilledAutofillField;
interface FakeFieldGenerator {
FilledAutofillField generate(int seed);
}

View File

@@ -0,0 +1,95 @@
/*
* 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.autofill;
import android.app.assist.AssistStructure;
import android.content.IntentSender;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.service.autofill.AutofillService;
import android.service.autofill.FillCallback;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.SaveCallback;
import android.service.autofill.SaveRequest;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.autofill.AutofillId;
import android.widget.RemoteViews;
import com.keepassdroid.AutoFillAuthActivity;
import com.kunzisoft.keepass.R;
import java.util.Arrays;
@RequiresApi(api = Build.VERSION_CODES.O)
public class KeeAutofillService extends AutofillService {
private static final String TAG = "KeeAutofillService";
@Override
public void onFillRequest(@NonNull FillRequest request, @NonNull CancellationSignal cancellationSignal,
@NonNull FillCallback callback) {
AssistStructure structure = request.getFillContexts()
.get(request.getFillContexts().size() - 1).getStructure();
final Bundle clientState = request.getClientState();
cancellationSignal.setOnCancelListener(() ->
Log.e(TAG, "Cancel autofill not implemented in this sample.")
);
// Parse AutoFill data in Activity
StructureParser parser = new StructureParser(getApplicationContext(), structure);
// TODO: try / catch on other places (onSave, auth activity, etc...)
try {
parser.parseForFill();
} catch (SecurityException e) {
// TODO: handle cases where DAL didn't pass by showing a custom UI asking the user
// to confirm the mapping. Might require subclassing SecurityException.
Log.e(TAG, "Security exception handling " + request);
callback.onFailure(e.getMessage());
return;
}
AutofillFieldMetadataCollection autofillFields = parser.getAutofillFields();
FillResponse.Builder responseBuilder = new FillResponse.Builder();
// Check user's settings for authenticating Responses and Datasets.
AutofillId[] autofillIds = autofillFields.getAutofillIds();
if (!Arrays.asList(autofillIds).isEmpty()) {
// If the entire Autofill Response is authenticated, AuthActivity is used
// to generate Response.
IntentSender sender = AutoFillAuthActivity.getAuthIntentSenderForResponse(this);
RemoteViews presentation =
new RemoteViews(getPackageName(), R.layout.autofill_service_unlock);
presentation.setTextViewText(R.id.text, getString(R.string.autofill_sign_in_prompt));
responseBuilder
.setAuthentication(autofillIds, sender, presentation);
callback.onSuccess(responseBuilder.build());
}
}
@Override
public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
// Do nothing if is fill
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.WindowNode;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import com.keepassdroid.autofill.dataSource.SharedPrefsDigitalAssetLinksRepository;
import com.keepassdroid.model.FilledAutofillFieldCollection;
import com.kunzisoft.keepass.R;
/**
* Parser for an AssistStructure object. This is invoked when the Autofill Service receives an
* AssistStructure from the client Activity, representing its View hierarchy. In this sample, it
* parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
final class StructureParser {
private final AutofillFieldMetadataCollection mAutofillFields =
new AutofillFieldMetadataCollection();
private final Context mContext;
private final AssistStructure mStructure;
private FilledAutofillFieldCollection mFilledAutofillFieldCollection;
StructureParser(Context context, AssistStructure structure) {
mContext = context;
mStructure = structure;
}
/**
* Traverse AssistStructure and add ViewNode metadata to a flat list.
*/
public void parseForFill() {
Log.d(getClass().getName(), "Parsing structure for " + mStructure.getActivityComponent());
int nodes = mStructure.getWindowNodeCount();
mFilledAutofillFieldCollection = new FilledAutofillFieldCollection();
StringBuilder webDomain = new StringBuilder();
for (int i = 0; i < nodes; i++) {
WindowNode node = mStructure.getWindowNodeAt(i);
ViewNode view = node.getRootViewNode();
parseLocked(view, webDomain);
}
if (webDomain.length() > 0) {
String packageName = mStructure.getActivityComponent().getPackageName();
boolean valid = SharedPrefsDigitalAssetLinksRepository.getInstance().isValid(mContext,
webDomain.toString(), packageName);
if (!valid) {
throw new SecurityException(mContext.getString(
R.string.invalid_link_association, webDomain, packageName));
}
Log.d(getClass().getName(), "Domain " + webDomain + " is valid for " + packageName);
} else {
Log.d(getClass().getName(), "no web domain");
}
}
private void parseLocked(ViewNode viewNode, StringBuilder validWebDomain) {
String webDomain = viewNode.getWebDomain();
if (webDomain != null) {
Log.d(getClass().getName(),"child web domain: " + webDomain);
if (validWebDomain.length() > 0) {
if (!webDomain.equals(validWebDomain.toString())) {
throw new SecurityException("Found multiple web domains: valid= "
+ validWebDomain + ", child=" + webDomain);
}
} else {
validWebDomain.append(webDomain);
}
}
if (viewNode.getAutofillHints() != null) {
String[] filteredHints = AutofillHints.filterForSupportedHints(
viewNode.getAutofillHints());
if (filteredHints != null && filteredHints.length > 0) {
mAutofillFields.add(new AutofillFieldMetadata(viewNode));
}
}
int childrenSize = viewNode.getChildCount();
if (childrenSize > 0) {
for (int i = 0; i < childrenSize; i++) {
parseLocked(viewNode.getChildAt(i), validWebDomain);
}
}
}
public AutofillFieldMetadataCollection getAutofillFields() {
return mAutofillFields;
}
public FilledAutofillFieldCollection getClientFormData() {
return mFilledAutofillFieldCollection;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill;
public final class W3cHints {
// Supported W3C autofill tokens (https://html.spec.whatwg.org/multipage/forms.html#autofill)
public static final String HONORIFIC_PREFIX = "honorific-prefix";
public static final String NAME = "name";
public static final String GIVEN_NAME = "given-name";
public static final String ADDITIONAL_NAME = "additional-name";
public static final String FAMILY_NAME = "family-name";
public static final String HONORIFIC_SUFFIX = "honorific-suffix";
public static final String USERNAME = "username";
public static final String NEW_PASSWORD = "new-password";
public static final String CURRENT_PASSWORD = "current-password";
public static final String ORGANIZATION_TITLE = "organization-title";
public static final String ORGANIZATION = "organization";
public static final String STREET_ADDRESS = "street-address";
public static final String ADDRESS_LINE1 = "address-line1";
public static final String ADDRESS_LINE2 = "address-line2";
public static final String ADDRESS_LINE3 = "address-line3";
public static final String ADDRESS_LEVEL4 = "address-level4";
public static final String ADDRESS_LEVEL3 = "address-level3";
public static final String ADDRESS_LEVEL2 = "address-level2";
public static final String ADDRESS_LEVEL1 = "address-level1";
public static final String COUNTRY = "country";
public static final String COUNTRY_NAME = "country-name";
public static final String POSTAL_CODE = "postal-code";
public static final String CC_NAME = "cc-name";
public static final String CC_GIVEN_NAME = "cc-given-name";
public static final String CC_ADDITIONAL_NAME = "cc-additional-name";
public static final String CC_FAMILY_NAME = "cc-family-name";
public static final String CC_NUMBER = "cc-number";
public static final String CC_EXPIRATION = "cc-exp";
public static final String CC_EXPIRATION_MONTH = "cc-exp-month";
public static final String CC_EXPIRATION_YEAR = "cc-exp-year";
public static final String CC_CSC = "cc-csc";
public static final String CC_TYPE = "cc-type";
public static final String TRANSACTION_CURRENCY = "transaction-currency";
public static final String TRANSACTION_AMOUNT = "transaction-amount";
public static final String LANGUAGE = "language";
public static final String BDAY = "bday";
public static final String BDAY_DAY = "bday-day";
public static final String BDAY_MONTH = "bday-month";
public static final String BDAY_YEAR = "bday-year";
public static final String SEX = "sex";
public static final String URL = "url";
public static final String PHOTO = "photo";
// Optional W3C prefixes
public static final String PREFIX_SECTION = "section-";
public static final String SHIPPING = "shipping";
public static final String BILLING = "billing";
// W3C prefixes below...
public static final String PREFIX_HOME = "home";
public static final String PREFIX_WORK = "work";
public static final String PREFIX_FAX = "fax";
public static final String PREFIX_PAGER = "pager";
// ... require those suffix
public static final String TEL = "tel";
public static final String TEL_COUNTRY_CODE = "tel-country-code";
public static final String TEL_NATIONAL = "tel-national";
public static final String TEL_AREA_CODE = "tel-area-code";
public static final String TEL_LOCAL = "tel-local";
public static final String TEL_LOCAL_PREFIX = "tel-local-prefix";
public static final String TEL_LOCAL_SUFFIX = "tel-local-suffix";
public static final String TEL_EXTENSION = "tel_extension";
public static final String EMAIL = "email";
public static final String IMPP = "impp";
private W3cHints() {
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill.dataSource;
import android.content.Context;
/**
* Helper format
* <a href="https://developers.google.com/digital-asset-links/">Digital Asset Links</a> needs.
*/
public interface DigitalAssetLinksDataSource {
/**
* Checks if the association between a web domain and a package is valid.
*/
boolean isValid(Context context, String webDomain, String packageName);
/**
* Clears all cached data.
*/
void clear(Context context);
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.autofill.dataSource;
import android.content.Context;
/**
* Singleton repository that caches the result of Digital Asset Links checks.
*/
public class SharedPrefsDigitalAssetLinksRepository implements DigitalAssetLinksDataSource {
private static SharedPrefsDigitalAssetLinksRepository sInstance;
private SharedPrefsDigitalAssetLinksRepository() {
}
public static SharedPrefsDigitalAssetLinksRepository getInstance() {
if (sInstance == null) {
sInstance = new SharedPrefsDigitalAssetLinksRepository();
}
return sInstance;
}
@Override
public boolean isValid(Context context, String webDomain, String packageName) {
// TODO: implement caching. It could cache the whole domain -> (packagename, fingerprint),
// but then either invalidate when the package change or when the DAL association times out
// (the maxAge is part of the API response), or document that a real-life service
// should do that.
return true;
/*
String fingerprint = null;
try {
fingerprint = SecurityHelper.getFingerprint(context, packageName);
} catch (Exception e) {
Log.e(getClass().getName(), "error getting fingerprint for " + packageName);
return false;
}
*/
// TODO Security return SecurityHelper.isValid(webDomain, packageName, fingerprint);
}
@Override
public void clear(Context context) {
// TODO: implement once if caches results or remove from the interface
}
}

View File

@@ -51,6 +51,6 @@ public class StorageAF {
if (!supportsStorageFramework()) { return false; }
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getBoolean(ctx.getString(R.string.saf_key), ctx.getResources().getBoolean(R.bool.saf_default));
return prefs.getBoolean(ctx.getString(R.string.settings_saf_key), ctx.getResources().getBoolean(R.bool.settings_saf_default));
}
}

View File

@@ -58,7 +58,7 @@ public class LoadDB extends RunnableOnFinish {
mKey = key;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
mRememberKeyfile = prefs.getBoolean(ctx.getString(R.string.keyfile_key), ctx.getResources().getBoolean(R.bool.keyfile_default));
mRememberKeyfile = prefs.getBoolean(ctx.getString(R.string.settings_keyfile_key), ctx.getResources().getBoolean(R.bool.settings_keyfile_default));
}
@Override

View File

@@ -27,7 +27,7 @@ import com.keepassdroid.compat.BuildCompat;
public class ReadOnlyDialog extends WarningDialog {
public ReadOnlyDialog(Context context) {
super(context, R.string.show_read_only_warning);
super(context, R.string.settings_show_read_only_warning);
warning = context.getString(R.string.read_only_warning);

View File

@@ -51,14 +51,14 @@ public class RecentFileHistory {
ctx = c.getApplicationContext();
prefs = PreferenceManager.getDefaultSharedPreferences(c);
enabled = prefs.getBoolean(ctx.getString(R.string.recentfile_key), ctx.getResources().getBoolean(R.bool.recentfile_default));
enabled = prefs.getBoolean(ctx.getString(R.string.settings_recentfile_key), ctx.getResources().getBoolean(R.bool.settings_recentfile_default));
listner = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
if (key.equals(ctx.getString(R.string.recentfile_key))) {
enabled = sharedPreferences.getBoolean(ctx.getString(R.string.recentfile_key), ctx.getResources().getBoolean(R.bool.recentfile_default));
if (key.equals(ctx.getString(R.string.settings_recentfile_key))) {
enabled = sharedPreferences.getBoolean(ctx.getString(R.string.settings_recentfile_key), ctx.getResources().getBoolean(R.bool.settings_recentfile_default));
}
}
};

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.model;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillValue;
import com.google.common.base.Preconditions;
import com.google.gson.annotations.Expose;
import com.keepassdroid.autofill.AutofillHints;
import java.util.Arrays;
import static com.keepassdroid.autofill.AutofillHints.convertToStoredHintNames;
import static com.keepassdroid.autofill.AutofillHints.filterForSupportedHints;
/**
* JSON serializable data class containing the same data as an {@link AutofillValue}.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public class FilledAutofillField {
@Expose
private String mTextValue = null;
@Expose
private Long mDateValue = null;
@Expose
private Boolean mToggleValue = null;
//TODO add explicit mListValue
/**
* Does not need to be serialized into persistent storage, so it's not exposed.
*/
private String[] mAutofillHints = null;
public FilledAutofillField(String... hints) {
mAutofillHints = filterForSupportedHints(hints);
convertToStoredHintNames(mAutofillHints);
}
public void setListValue(CharSequence[] autofillOptions, int listValue) {
/* Only set list value when a hint is allowed to store list values. */
Preconditions.checkArgument(
AutofillHints.isValidTypeForHints(mAutofillHints, View.AUTOFILL_TYPE_LIST),
"List is invalid autofill type for hint(s) - %s",
Arrays.toString(mAutofillHints));
if (autofillOptions != null && autofillOptions.length > 0) {
mTextValue = autofillOptions[listValue].toString();
} else {
Log.w(getClass().getName(), "autofillOptions should have at least one entry.");
}
}
public String[] getAutofillHints() {
return mAutofillHints;
}
public String getTextValue() {
return mTextValue;
}
public void setTextValue(CharSequence textValue) {
/* Only set text value when a hint is allowed to store text values. */
Preconditions.checkArgument(
AutofillHints.isValidTypeForHints(mAutofillHints, View.AUTOFILL_TYPE_TEXT),
"Text is invalid autofill type for hint(s) - %s",
Arrays.toString(mAutofillHints));
mTextValue = textValue.toString();
}
public Long getDateValue() {
return mDateValue;
}
public void setDateValue(Long dateValue) {
/* Only set date value when a hint is allowed to store date values. */
Preconditions.checkArgument(
AutofillHints.isValidTypeForHints(mAutofillHints, View.AUTOFILL_TYPE_DATE),
"Date is invalid autofill type for hint(s) - %s"
, Arrays.toString(mAutofillHints));
mDateValue = dateValue;
}
public Boolean getToggleValue() {
return mToggleValue;
}
public void setToggleValue(Boolean toggleValue) {
/* Only set toggle value when a hint is allowed to store toggle values. */
Preconditions.checkArgument(
AutofillHints.isValidTypeForHints(mAutofillHints, View.AUTOFILL_TYPE_TOGGLE),
"Toggle is invalid autofill type for hint(s) - %s",
Arrays.toString(mAutofillHints));
mToggleValue = toggleValue;
}
public boolean isNull() {
return mTextValue == null && mDateValue == null && mToggleValue == null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FilledAutofillField that = (FilledAutofillField) o;
if (mTextValue != null ? !mTextValue.equals(that.mTextValue) : that.mTextValue != null)
return false;
if (mDateValue != null ? !mDateValue.equals(that.mDateValue) : that.mDateValue != null)
return false;
return mToggleValue != null ? mToggleValue.equals(that.mToggleValue) :
that.mToggleValue == null;
}
@Override
public int hashCode() {
int result = mTextValue != null ? mTextValue.hashCode() : 0;
result = 31 * result + (mDateValue != null ? mDateValue.hashCode() : 0);
result = 31 * result + (mToggleValue != null ? mToggleValue.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keepassdroid.model;
import android.os.Build;
import android.service.autofill.Dataset;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import com.google.gson.annotations.Expose;
import com.keepassdroid.autofill.AutofillFieldMetadata;
import com.keepassdroid.autofill.AutofillFieldMetadataCollection;
import com.keepassdroid.autofill.AutofillHints;
import com.keepassdroid.autofill.W3cHints;
import java.util.HashMap;
import java.util.List;
/**
* FilledAutofillFieldCollection is the model that holds all of the data on a client app's page,
* plus the dataset name associated with it.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public final class FilledAutofillFieldCollection {
@Expose
private final HashMap<String, FilledAutofillField> mHintMap;
@Expose
private String mDatasetName;
public FilledAutofillFieldCollection() {
this(null, new HashMap<>());
}
public FilledAutofillFieldCollection(String datasetName, HashMap<String, FilledAutofillField> hintMap) {
mHintMap = hintMap;
mDatasetName = datasetName;
}
private static boolean isW3cSectionPrefix(String hint) {
return hint.startsWith(W3cHints.PREFIX_SECTION);
}
private static boolean isW3cAddressType(String hint) {
switch (hint) {
case W3cHints.SHIPPING:
case W3cHints.BILLING:
return true;
}
return false;
}
private static boolean isW3cTypePrefix(String hint) {
switch (hint) {
case W3cHints.PREFIX_WORK:
case W3cHints.PREFIX_FAX:
case W3cHints.PREFIX_HOME:
case W3cHints.PREFIX_PAGER:
return true;
}
return false;
}
private static boolean isW3cTypeHint(String hint) {
switch (hint) {
case W3cHints.TEL:
case W3cHints.TEL_COUNTRY_CODE:
case W3cHints.TEL_NATIONAL:
case W3cHints.TEL_AREA_CODE:
case W3cHints.TEL_LOCAL:
case W3cHints.TEL_LOCAL_PREFIX:
case W3cHints.TEL_LOCAL_SUFFIX:
case W3cHints.TEL_EXTENSION:
case W3cHints.EMAIL:
case W3cHints.IMPP:
return true;
}
Log.w(FilledAutofillFieldCollection.class.getName(),"Invalid W3C type hint: " + hint);
return false;
}
/**
* Returns the name of the {@link Dataset}.
*/
public String getDatasetName() {
return mDatasetName;
}
/**
* Sets the {@link Dataset} name.
*/
public void setDatasetName(String datasetName) {
mDatasetName = datasetName;
}
/**
* Adds a {@code FilledAutofillField} to the collection, indexed by all of its hints.
*/
public void add(@NonNull FilledAutofillField filledAutofillField) {
String[] autofillHints = filledAutofillField.getAutofillHints();
String nextHint = null;
for (int i = 0; i < autofillHints.length; i++) {
String hint = autofillHints[i];
if (i < autofillHints.length - 1) {
nextHint = autofillHints[i + 1];
}
// First convert the compound W3C autofill hints
if (isW3cSectionPrefix(hint) && i < autofillHints.length - 1) {
hint = autofillHints[++i];
Log.d(getClass().getName(), "Hint is a W3C section prefix; using " + hint + " instead");
if (i < autofillHints.length - 1) {
nextHint = autofillHints[i + 1];
}
}
if (isW3cTypePrefix(hint) && nextHint != null && isW3cTypeHint(nextHint)) {
hint = nextHint;
i++;
Log.d(getClass().getName(), "Hint is a W3C type prefix; using "+ hint +" instead");
}
if (isW3cAddressType(hint) && nextHint != null) {
hint = nextHint;
i++;
Log.d(getClass().getName(), "Hint is a W3C address prefix; using " + hint + " instead");
}
// Then check if the "actual" hint is supported.
if (AutofillHints.isValidHint(hint)) {
mHintMap.put(hint, filledAutofillField);
} else {
Log.e(getClass().getName(), "Invalid hint: " + autofillHints[i]);
}
}
}
/**
* Populates a {@link Dataset.Builder} with appropriate values for each {@link AutofillId}
* in a {@code AutofillFieldMetadataCollection}.
* <p>
* In other words, it constructs an autofill
* {@link Dataset.Builder} by applying saved values (from this {@code FilledAutofillFieldCollection})
* to Views specified in a {@code AutofillFieldMetadataCollection}, which represents the current
* page the user is on.
*/
public boolean applyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection,
Dataset.Builder datasetBuilder) {
boolean setValueAtLeastOnce = false;
List<String> allHints = autofillFieldMetadataCollection.getAllHints();
for (int hintIndex = 0; hintIndex < allHints.size(); hintIndex++) {
String hint = allHints.get(hintIndex);
List<AutofillFieldMetadata> fillableAutofillFields =
autofillFieldMetadataCollection.getFieldsForHint(hint);
if (fillableAutofillFields == null) {
continue;
}
for (int autofillFieldIndex = 0; autofillFieldIndex < fillableAutofillFields.size(); autofillFieldIndex++) {
FilledAutofillField filledAutofillField = mHintMap.get(hint);
if (filledAutofillField == null) {
continue;
}
AutofillFieldMetadata autofillFieldMetadata = fillableAutofillFields.get(autofillFieldIndex);
AutofillId autofillId = autofillFieldMetadata.getId();
int autofillType = autofillFieldMetadata.getAutofillType();
switch (autofillType) {
case View.AUTOFILL_TYPE_LIST:
int listValue = autofillFieldMetadata.getAutofillOptionIndex(filledAutofillField.getTextValue());
if (listValue != -1) {
datasetBuilder.setValue(autofillId, AutofillValue.forList(listValue));
setValueAtLeastOnce = true;
}
break;
case View.AUTOFILL_TYPE_DATE:
Long dateValue = filledAutofillField.getDateValue();
if (dateValue != null) {
datasetBuilder.setValue(autofillId, AutofillValue.forDate(dateValue));
setValueAtLeastOnce = true;
}
break;
case View.AUTOFILL_TYPE_TEXT:
String textValue = filledAutofillField.getTextValue();
if (textValue != null) {
datasetBuilder.setValue(autofillId, AutofillValue.forText(textValue));
setValueAtLeastOnce = true;
}
break;
case View.AUTOFILL_TYPE_TOGGLE:
Boolean toggleValue = filledAutofillField.getToggleValue();
if (toggleValue != null) {
datasetBuilder.setValue(autofillId, AutofillValue.forToggle(toggleValue));
setValueAtLeastOnce = true;
}
break;
case View.AUTOFILL_TYPE_NONE:
default:
Log.w(getClass().getName(), "Invalid autofill type - " + autofillType);
break;
}
}
}
return setValueAtLeastOnce;
}
/**
* Takes in a list of autofill hints (autofillHints), usually associated with a View or set of
* Views. Returns whether any of the filled fields on the page have at least 1 of these
* autofillHints.
*/
public boolean helpsWithHints(List<String> autofillHints) {
for (int i = 0; i < autofillHints.size(); i++) {
String autofillHint = autofillHints.get(i);
if (mHintMap.containsKey(autofillHint) && !mHintMap.get(autofillHint).isNull()) {
return true;
}
}
return false;
}
}

View File

@@ -50,7 +50,7 @@ public class SearchDbHelper {
private boolean omitBackup() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mCtx);
return prefs.getBoolean(mCtx.getString(R.string.omitbackup_key), mCtx.getResources().getBoolean(R.bool.omitbackup_default));
return prefs.getBoolean(mCtx.getString(R.string.settings_omitbackup_key), mCtx.getResources().getBoolean(R.bool.settings_omitbackup_default));
}

View File

@@ -0,0 +1,98 @@
/*
* 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.settings;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.util.Log;
import android.view.autofill.AutofillManager;
import com.kunzisoft.keepass.R;
@RequiresApi(api = Build.VERSION_CODES.O)
public class AutofillPreferenceFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener {
private static final int REQUEST_CODE_SET_DEFAULT = 1;
private AutofillManager mAutofillManager;
private SwitchPreference enablePreference;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.autofill_preferences, rootKey);
mAutofillManager = getActivity().getSystemService(AutofillManager.class);
// add listeners for non-default actions
enablePreference = (SwitchPreference) findPreference(getString(R.string.settings_autofill_enable_key));
}
@Override
public void onResume() {
super.onResume();
enablePreference.setOnPreferenceClickListener(null);
if (mAutofillManager != null && mAutofillManager.hasEnabledAutofillServices())
enablePreference.setChecked(mAutofillManager.hasEnabledAutofillServices());
enablePreference.setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.getKey().equals(getString(R.string.settings_autofill_enable_key))) {
setService(((SwitchPreference) preference).isChecked());
}
return false;
}
private void setService(boolean enableService) {
if (enableService) {
startEnableService();
} else {
disableService();
}
}
private void disableService() {
if (mAutofillManager != null && mAutofillManager.hasEnabledAutofillServices()) {
mAutofillManager.disableAutofillServices();
} else {
Log.d(getClass().getName(), "Sample service already disabled.");
}
}
private void startEnableService() {
if (mAutofillManager != null && !mAutofillManager.hasEnabledAutofillServices()) {
Intent intent = new Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE);
intent.setData(Uri.parse("package:com.example.android.autofill.service"));
Log.d(getClass().getName(), "enableService(): intent="+ intent);
startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT);
} else {
Log.d(getClass().getName(), "Sample service already enabled.");
}
}
}

View File

@@ -1,6 +1,8 @@
package com.keepassdroid.settings;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
@@ -29,14 +31,20 @@ public class MainPreferenceFragment extends PreferenceFragmentCompat implements
setPreferencesFromResource(R.xml.preferences, rootKey);
// add listeners for non-default actions
Preference preference = findPreference(getString(R.string.app_key));
Preference preference = findPreference(getString(R.string.settings_app_key));
preference.setOnPreferenceClickListener(this);
preference = findPreference(getString(R.string.db_key));
preference = findPreference(getString(R.string.settings_db_key));
Database db = App.getDB();
if (!(db.Loaded() && db.pm.appSettingsEnabled())) {
Preference dbSettings = findPreference(getString(R.string.db_key));
dbSettings.setEnabled(false);
preference.setEnabled(false);
} else {
preference.setOnPreferenceClickListener(this);
}
preference = findPreference(getString(R.string.settings_autofill_key));
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
preference.setEnabled(false);
} else {
preference.setOnPreferenceClickListener(this);
}
@@ -45,14 +53,19 @@ public class MainPreferenceFragment extends PreferenceFragmentCompat implements
@Override
public boolean onPreferenceClick(Preference preference) {
// here you should use the same keys as you used in the xml-file
if (preference.getKey().equals(getString(R.string.app_key))) {
if (preference.getKey().equals(getString(R.string.settings_app_key))) {
mCallback.onNestedPreferenceSelected(NestedSettingsFragment.NESTED_SCREEN_APP_KEY);
}
if (preference.getKey().equals(getString(R.string.db_key))) {
if (preference.getKey().equals(getString(R.string.settings_db_key))) {
mCallback.onNestedPreferenceSelected(NestedSettingsFragment.NESTED_SCREEN_DB_KEY);
}
if (preference.getKey().equals(getString(R.string.settings_autofill_key))) {
Intent intent = new Intent(getContext(), SettingsAutofillActivity.class);
getActivity().startActivity(intent);
}
return false;
}

View File

@@ -55,47 +55,38 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat {
case NESTED_SCREEN_APP_KEY:
setPreferencesFromResource(R.xml.app_preferences, rootKey);
Preference keyFile = findPreference(getString(R.string.keyfile_key));
keyFile.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
Preference keyFile = findPreference(getString(R.string.settings_keyfile_key));
keyFile.setOnPreferenceChangeListener((preference, newValue) -> {
Boolean value = (Boolean) newValue;
public boolean onPreferenceChange(Preference preference, Object newValue) {
Boolean value = (Boolean) newValue;
if (!value) {
App.getFileHistory().deleteAllKeys();
}
return true;
if (!value) {
App.getFileHistory().deleteAllKeys();
}
return true;
});
Preference recentHistory = findPreference(getString(R.string.recentfile_key));
recentHistory.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
Preference recentHistory = findPreference(getString(R.string.settings_recentfile_key));
recentHistory.setOnPreferenceChangeListener((preference, newValue) -> {
Boolean value = (Boolean) newValue;
public boolean onPreferenceChange(Preference preference, Object newValue) {
Boolean value = (Boolean) newValue;
if (value == null) {
value = true;
}
if (!value) {
App.getFileHistory().deleteAll();
}
return true;
if (value == null) {
value = true;
}
if (!value) {
App.getFileHistory().deleteAll();
}
return true;
});
Preference stylePreference = findPreference(getString(R.string.setting_style_key));
stylePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String styleString = (String) newValue;
Stylish.assignStyle(getActivity(), styleString);
getActivity().recreate();
return true;
}
Preference stylePreference = findPreference(getString(R.string.settings_style_key));
stylePreference.setOnPreferenceChangeListener((preference, newValue) -> {
String styleString = (String) newValue;
Stylish.assignStyle(getActivity(), styleString);
getActivity().recreate();
return true;
});
break;
@@ -106,18 +97,15 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat {
Database db = App.getDB();
if (db.Loaded() && db.pm.appSettingsEnabled()) {
Preference rounds = findPreference(getString(R.string.rounds_key));
rounds.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
setRounds(App.getDB(), preference);
return true;
}
Preference rounds = findPreference(getString(R.string.settings_rounds_key));
rounds.setOnPreferenceChangeListener((preference, newValue) -> {
setRounds(App.getDB(), preference);
return true;
});
setRounds(db, rounds);
Preference algorithm = findPreference(getString(R.string.algorithm_key));
Preference algorithm = findPreference(getString(R.string.settings_algorithm_key));
setAlgorithm(db, algorithm);
} else {

View File

@@ -25,10 +25,10 @@ import android.preference.PreferenceManager;
import com.kunzisoft.keepass.R;
public class PrefsUtil {
public class PreferencesUtil {
public static float getListTextSize(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return Float.parseFloat(prefs.getString(ctx.getString(R.string.list_size_key), ctx.getString(R.string.list_size_default)));
return Float.parseFloat(prefs.getString(ctx.getString(R.string.settings_list_size_key), ctx.getString(R.string.list_size_default)));
}
}

View File

@@ -57,7 +57,7 @@ public class SettingsActivity extends StylishActivity implements MainPreferenceF
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar);
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar = findViewById(R.id.toolbar);
toolbar.setTitle(R.string.settings);
setSupportActionBar(toolbar);
assert getSupportActionBar() != null;

View File

@@ -0,0 +1,61 @@
/*
* 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.settings;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import com.kunzisoft.keepass.R;
@RequiresApi(api = Build.VERSION_CODES.O)
public class SettingsAutofillActivity extends SettingsActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitle(R.string.menu_autofill_settings);
setSupportActionBar(toolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new AutofillPreferenceFragment())
.commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch ( item.getItemId() ) {
case android.R.id.home:
onBackPressed();
break;
}
return super.onOptionsItemSelected(item);
}
}

View File

@@ -14,7 +14,7 @@ public class Stylish {
private static String themeString;
public static void init(Context context) {
stylishPrefKey = context.getString(R.string.setting_style_key);
stylishPrefKey = context.getString(R.string.settings_style_key);
Log.d(Stylish.class.getName(), "Attatching to " + context.getPackageName());
themeString = PreferenceManager.getDefaultSharedPreferences(context).getString(stylishPrefKey, context.getString(R.string.list_style_name_light));
}

View File

@@ -39,7 +39,7 @@ public class TimeoutHelper {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(act);
SharedPreferences.Editor edit = prefs.edit();
edit.putLong(act.getString(R.string.timeout_key), time);
edit.putLong(act.getString(R.string.settings_timeout_key), time);
EditorCompat.apply(edit);
@@ -59,14 +59,14 @@ public class TimeoutHelper {
long cur_time = System.currentTimeMillis();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(act);
long timeout_start = prefs.getLong(act.getString(R.string.timeout_key), -1);
long timeout_start = prefs.getLong(act.getString(R.string.settings_timeout_key), -1);
// The timeout never started
if (timeout_start == -1) {
return;
}
String sTimeout = prefs.getString(act.getString(R.string.app_timeout_key), act.getString(R.string.clipboard_timeout_default));
String sTimeout = prefs.getString(act.getString(R.string.settings_app_timeout_key), act.getString(R.string.clipboard_timeout_default));
long timeout;
try {
timeout = Long.parseLong(sTimeout);

View File

@@ -28,7 +28,7 @@ public class Timeout {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
String sTimeout = prefs.getString(ctx.getString(R.string.app_timeout_key), ctx.getString(R.string.clipboard_timeout_default));
String sTimeout = prefs.getString(ctx.getString(R.string.settings_app_timeout_key), ctx.getString(R.string.clipboard_timeout_default));
long timeout;
try {

View File

@@ -36,7 +36,7 @@ import com.keepassdroid.ProgressTask;
import com.keepassdroid.app.App;
import com.keepassdroid.database.PwEntry;
import com.keepassdroid.database.edit.DeleteEntry;
import com.keepassdroid.settings.PrefsUtil;
import com.keepassdroid.settings.PreferencesUtil;
public class PwEntryView extends ClickView {
@@ -58,7 +58,7 @@ public class PwEntryView extends ClickView {
View ev = View.inflate(mAct, R.layout.list_entries_entry, null);
mTv = (TextView) ev.findViewById(R.id.entry_text);
mTv.setTextSize(PrefsUtil.getListTextSize(act));
mTv.setTextSize(PreferencesUtil.getListTextSize(act));
populateView(ev, pw, pos);

View File

@@ -34,7 +34,7 @@ import com.keepassdroid.GroupBaseActivity;
import com.keepassdroid.app.App;
import com.keepassdroid.database.PwGroup;
import com.keepassdroid.database.PwGroupV3;
import com.keepassdroid.settings.PrefsUtil;
import com.keepassdroid.settings.PreferencesUtil;
public class PwGroupView extends ClickView {
@@ -60,7 +60,7 @@ public class PwGroupView extends ClickView {
View gv = View.inflate(act, R.layout.list_entries_group, null);
mTv = (TextView) gv.findViewById(R.id.group_text);
float size = PrefsUtil.getListTextSize(act);
float size = PreferencesUtil.getListTextSize(act);
mTv.setTextSize(size);
TextView label = (TextView) gv.findViewById(R.id.group_label);

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/iconPreferenceColor"
android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
</vector>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?><!--
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="horizontal">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingRight="12dp"
android:paddingEnd="12dp"
android:paddingLeft="12dp"
android:paddingStart="12dp"
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_lock_white_24dp"
android:tint="?attr/colorPrimary"/>
</LinearLayout>

View File

@@ -1,4 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 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/>.
-->
<resources>
<bool name="saf_default">true</bool>
<bool name="settings_saf_default">true</bool>
</resources>

View File

@@ -1,4 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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/>.
-->
<resources>
<attr name="toolbarAppearance" format="reference" />
<attr name="toolbarPopupAppearance" format="reference" />

View File

@@ -32,31 +32,34 @@
<integer name="default_password_length" translatable="false">8</integer>
<!-- Preference settings -->
<string name="algorithm_key" translatable="false">algorithm</string>
<string name="app_key" translatable="false">app</string>
<string name="app_timeout_key" translatable="false">app_timeout_key</string>
<string name="clipboard_timeout_key" translatable="false">clip_timeout_key</string>
<string name="db_key" translatable="false">db</string>
<string name="rounds_key" translatable="false">rounds</string>
<string name="keyfile_key" translatable="false">keyfile</string>
<string name="maskpass_key" translatable="false">maskpass</string>
<string name="omitbackup_key" translatable="false">omitbackup</string>
<string name="recentfile_key" translatable="false">recentfile</string>
<string name="list_size_key" translatable="false">list_size</string>
<string name="show_beta_warning" translatable="false">show_beta_warning</string>
<string name="show_read_only_warning" translatable="false">show_read_only_warning</string>
<string name="sort_key" translatable="false">sort_key</string>
<string name="timeout_key" translatable="false">timeout_key</string>
<string name="saf_key" translatable="false">storage_access_framework_key</string>
<string name="setting_style_key" translatable="false">setting_style_key</string>
<string name="settings_app_key" translatable="false">settings_app_key</string>
<string name="settings_db_key" translatable="false">settings_db_key</string>
<string name="settings_autofill_key" translatable="false">settings_autofill_key</string>
<string name="settings_algorithm_key" translatable="false">algorithm</string>
<string name="settings_app_timeout_key" translatable="false">app_timeout_key</string>
<string name="settings_clipboard_timeout_key" translatable="false">clip_timeout_key</string>
<string name="settings_rounds_key" translatable="false">rounds</string>
<string name="settings_keyfile_key" translatable="false">keyfile</string>
<string name="settings_maskpass_key" translatable="false">maskpass</string>
<string name="settings_omitbackup_key" translatable="false">omitbackup</string>
<string name="settings_recentfile_key" translatable="false">recentfile</string>
<string name="settings_list_size_key" translatable="false">list_size</string>
<string name="settings_show_beta_warning" translatable="false">show_beta_warning</string>
<string name="settings_show_read_only_warning" translatable="false">show_read_only_warning</string>
<string name="settings_sort_key" translatable="false">sort_key</string>
<string name="settings_timeout_key" translatable="false">timeout_key</string>
<string name="settings_saf_key" translatable="false">storage_access_framework_key</string>
<string name="settings_style_key" translatable="false">settings_style_key</string>
<string name="settings_autofill_enable_key" translatable="false">settings_autofill_enable_key</string>
<bool name="settings_maskpass_default" translatable="false">true</bool>
<bool name="settings_keyfile_default" translatable="false">true</bool>
<bool name="settings_sort_default" translatable="false">true</bool>
<bool name="settings_omitbackup_default" translatable="false">true</bool>
<bool name="settings_recentfile_default" translatable="false">true</bool>
<bool name="settings_saf_default" translatable="false">false</bool>
<bool name="settings_autofill_enable_default" translatable="false">false</bool>
<bool name="maskpass_default" translatable="false">true</bool>
<bool name="keyfile_default" translatable="false">true</bool>
<bool name="sort_default" translatable="false">true</bool>
<bool name="omitbackup_default" translatable="false">true</bool>
<bool name="recentfile_default" translatable="false">true</bool>
<bool name="saf_default" translatable="false">false</bool>
<string name="clipboard_timeout_default" translatable="false">300000</string>
<string-array name="clipboard_timeout_values">
<item translatable="false">30000</item>

View File

@@ -135,6 +135,7 @@
<string name="settings">Settings</string>
<string name="menu_app_settings">Application settings</string>
<string name="menu_db_settings">Database settings</string>
<string name="menu_autofill_settings">Autofill settings</string>
<string name="menu_delete">Delete</string>
<string name="menu_donate">Donate</string>
<string name="menu_edit">Edit</string>
@@ -202,6 +203,11 @@
<string name="history">History</string>
<string name="appearance">Appearance</string>
<string name="general">General</string>
<string name="autofill">Autofill</string>
<string name="autofill_service_name">KeepassDX Autofill Service</string>
<string name="autofill_sign_in_prompt">Tap to sign in.</string>
<string name="set_autofill_service">Set default Autofill service</string>
<string name="invalid_link_association">Could not associate web domain %1$s with app %2$s</string>
<string-array name="clipboard_timeout_options">
<item>30 seconds</item>

View File

@@ -24,7 +24,7 @@
android:title="@string/general">
<ListPreference
android:key="@string/clipboard_timeout_key"
android:key="@string/settings_clipboard_timeout_key"
android:title="@string/clipboard_timeout"
android:summary="@string/clipboard_timeout_summary"
android:entries="@array/clipboard_timeout_options"
@@ -32,7 +32,7 @@
android:dialogTitle="@string/clipboard_timeout"
android:defaultValue="@string/clipboard_timeout_default"/>
<ListPreference
android:key="@string/app_timeout_key"
android:key="@string/settings_app_timeout_key"
android:title="@string/app_timeout"
android:summary="@string/app_timeout_summary"
android:entries="@array/clipboard_timeout_options"
@@ -40,20 +40,20 @@
android:dialogTitle="@string/app_timeout"
android:defaultValue="@string/clipboard_timeout_default"/>
<CheckBoxPreference
android:key="@string/maskpass_key"
android:key="@string/settings_maskpass_key"
android:title="@string/maskpass_title"
android:summary="@string/maskpass_summary"
android:defaultValue="@bool/maskpass_default"/>
android:defaultValue="@bool/settings_maskpass_default"/>
<CheckBoxPreference
android:summary="@string/omitbackup_summary"
android:defaultValue="@bool/omitbackup_default"
android:defaultValue="@bool/settings_omitbackup_default"
android:title="@string/omitbackup_title"
android:key="@string/omitbackup_key"/>
android:key="@string/settings_omitbackup_key"/>
<CheckBoxPreference
android:summary="@string/use_saf_summary"
android:defaultValue="@bool/saf_default"
android:defaultValue="@bool/settings_saf_default"
android:title="@string/use_saf_title"
android:key="@string/saf_key"/>
android:key="@string/settings_saf_key"/>
</PreferenceCategory>
@@ -62,15 +62,15 @@
<CheckBoxPreference
android:summary="@string/recentfile_summary"
android:defaultValue="@bool/recentfile_default"
android:defaultValue="@bool/settings_recentfile_default"
android:title="@string/recentfile_title"
android:key="@string/recentfile_key"/>
android:key="@string/settings_recentfile_key"/>
<CheckBoxPreference
android:key="@string/keyfile_key"
android:key="@string/settings_keyfile_key"
android:title="@string/remember_keyfile_title"
android:summary="@string/remember_keyfile_summary"
android:dependency="@string/recentfile_key"
android:defaultValue="@bool/keyfile_default"/>
android:dependency="@string/settings_recentfile_key"
android:defaultValue="@bool/settings_keyfile_default"/>
</PreferenceCategory>
@@ -80,12 +80,12 @@
<ListPreference
android:title="@string/style_choose_title"
android:summary="@string/style_choose_summary"
android:key="@string/setting_style_key"
android:key="@string/settings_style_key"
android:defaultValue="@string/list_style_name_light"
android:entries="@array/list_style_names"
android:entryValues="@array/list_style_values" />
<ListPreference
android:key="@string/list_size_key"
android:key="@string/settings_list_size_key"
android:title="@string/list_size_title"
android:summary="@string/list_size_summary"
android:entries="@array/list_size_options"

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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/>.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:title="@string/set_autofill_service"
android:defaultValue="@bool/settings_autofill_enable_default"
android:key="@string/settings_autofill_enable_key"/>
</PreferenceScreen>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?><!--
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<!--
Attributes for the AutoFill service that tell the framework what will act as the Autofill service's
Settings Activity. This is pointed to in the service's meta-data in the application's manifest.
-->
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.keepassdroid.settings.SettingsAutofillActivity" />

View File

@@ -24,11 +24,11 @@
android:title="@string/general">
<Preference
android:key="@string/algorithm_key"
android:key="@string/settings_algorithm_key"
android:title="@string/algorithm"
android:enabled="false"/>
<com.keepassdroid.settings.RoundsPreference
android:key="@string/rounds_key"
android:key="@string/settings_rounds_key"
android:persistent="false"
android:title="@string/rounds"
android:dialogLayout="@layout/database_settings"

View File

@@ -23,18 +23,27 @@
<PreferenceCategory
android:title="@string/application">
<Preference
android:key="@string/app_key"
android:key="@string/settings_app_key"
android:title="@string/menu_app_settings"
android:icon="@drawable/ic_phone_android_white_24dp"
android:icon="@drawable/ic_phone_android_prefs_24dp"
android:persistent="false" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/database">
<Preference
android:key="@string/db_key"
android:key="@string/settings_db_key"
android:title="@string/menu_db_settings"
android:icon="@drawable/ic_data_usage_white_24dp"
android:icon="@drawable/ic_data_usage_prefs_24dp"
android:persistent="false" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/autofill">
<Preference
android:key="@string/settings_autofill_key"
android:title="@string/menu_autofill_settings"
android:icon="@drawable/ic_content_paste_prefs_24dp"
android:persistent="false" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -8,7 +8,7 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
classpath 'com.android.tools.build:gradle:3.0.1'
}
}