dirs = [
- 'main'] // main sample code; look here for the interesting stuff.
+ 'main'] // main sample code; look here for the interesting stuff.
android {
- compileSdkVersion 26
- buildToolsVersion "26.0.1"
+ compileSdkVersion "android-P"
defaultConfig {
minSdkVersion 26
- targetSdkVersion 26
- jackOptions {
- enabled true
- }
+ targetSdkVersion 28
}
compileOptions {
@@ -44,4 +29,19 @@ android {
androidTest.setRoot('tests')
androidTest.java.srcDirs = ['tests/src']
}
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation "com.android.support:support-v4:28.0.0-alpha1"
+ implementation "com.android.support:support-v13:28.0.0-alpha1"
+ implementation "com.android.support:cardview-v7:28.0.0-alpha1"
+ implementation "com.android.support:appcompat-v7:28.0.0-alpha1"
+ implementation 'com.android.support:design:28.0.0-alpha1'
+ implementation 'com.android.support.constraint:constraint-layout:1.0.2'
+ implementation group: 'com.google.guava', name: 'guava', version: '22.0-android'
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
index 7d075ab9917d91e3b68e78ad367d5ecdae50780b..a837128941ce0373a81dcfec8c893c62587c3364 100644
--- a/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
@@ -14,11 +14,11 @@
limitations under the License.
-->
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/BaseMainFragment.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/BaseMainFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..5be875cd507027a2b6c9bbd7f655b372f33c1b39
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/BaseMainFragment.java
@@ -0,0 +1,24 @@
+/*
+ * 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.example.android.autofill.app;
+
+import android.support.annotation.StringRes;
+import android.support.v4.app.Fragment;
+
+public abstract class BaseMainFragment extends Fragment {
+ public abstract @StringRes int getPageTitleResId();
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/MainActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/MainActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c8915bb51d872ced3baebf476dc06f96784a024
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/MainActivity.java
@@ -0,0 +1,78 @@
+/*
+ * 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.example.android.autofill.app;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TabLayout;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import com.example.android.autofill.app.commoncases.CommonCasesFragment;
+import com.example.android.autofill.app.edgecases.EdgeCasesFragment;
+
+/**
+ * This is used to launch sample activities that showcase autofill.
+ */
+public class MainActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ ViewPager viewPager = findViewById(R.id.pager);
+ PagerAdapter pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(), this);
+ viewPager.setAdapter(pagerAdapter);
+ TabLayout tabLayout = findViewById(R.id.sliding_tabs);
+ tabLayout.setupWithViewPager(viewPager);
+ }
+
+ /**
+ * A simple pager adapter that holds 2 Fragments.
+ */
+ private static class ScreenSlidePagerAdapter extends FragmentPagerAdapter {
+ private BaseMainFragment[] fragments = new BaseMainFragment[]{new CommonCasesFragment(),
+ new EdgeCasesFragment()};
+
+ private Context mContext;
+
+ public ScreenSlidePagerAdapter(FragmentManager fm, Context context) {
+ super(fm);
+ mContext = context;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return fragments[position];
+ }
+
+ @Override
+ public int getCount() {
+ return fragments.length;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mContext.getString(fragments[position].getPageTitleResId());
+ }
+ }
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/Util.java
similarity index 96%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/Util.java
index b7de476d7fffb72e372cd540e3818c14fca93c49..288a908b348102f781c9dfde0ae2b0627187d683 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/Util.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework;
+package com.example.android.autofill.app;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
@@ -27,7 +27,7 @@ import android.view.autofill.AutofillValue;
import java.util.Arrays;
import java.util.Set;
-public final class CommonUtil {
+public final class Util {
public static final String TAG = "AutofillSample";
public static final boolean DEBUG = true;
@@ -60,7 +60,7 @@ public final class CommonUtil {
return builder.toString();
}
- public static String getTypeAsString(int type) {
+ public static String getAutofillTypeAsString(int type) {
switch (type) {
case View.AUTOFILL_TYPE_TEXT:
return "TYPE_TEXT";
@@ -131,7 +131,7 @@ public final class CommonUtil {
String[] afHints = node.getAutofillHints();
CharSequence[] options = node.getAutofillOptions();
- builder.append(prefix).append("afType: ").append(getTypeAsString(node.getAutofillType()))
+ builder.append(prefix).append("afType: ").append(getAutofillTypeAsString(node.getAutofillType()))
.append("\tafValue:")
.append(getAutofillValueAndTypeAsString(node.getAutofillValue()))
.append("\tafOptions:").append(options == null ? "N/A" : Arrays.toString(options))
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/WelcomeActivity.java
similarity index 94%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/WelcomeActivity.java
index 13eb939b0d7af369d4878982f1b7b8149dcdd6b7..5f7b117d55750f54c099cfcfdf10d4eba88593ea 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/WelcomeActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app;
import android.content.Context;
import android.content.Intent;
@@ -22,8 +22,6 @@ import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
-import com.example.android.autofillframework.R;
-
import static java.lang.Math.toIntExact;
public class WelcomeActivity extends AppCompatActivity {
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CommonCasesFragment.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CommonCasesFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..ebdb3fc9a5b188e544056b3d8397e9a514a9cae0
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CommonCasesFragment.java
@@ -0,0 +1,39 @@
+/*
+* 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.example.android.autofill.app.commoncases;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.example.android.autofill.app.BaseMainFragment;
+import com.example.android.autofill.app.R;
+
+public class CommonCasesFragment extends BaseMainFragment {
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_common_cases, container, false);
+ }
+
+ @Override
+ public int getPageTitleResId() {
+ return R.string.common_cases_page_title;
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardCompoundViewActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardCompoundViewActivity.java
similarity index 85%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardCompoundViewActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardCompoundViewActivity.java
index 05f57d03424f39bc21e8946c9119fb04bbce07b8..b8df27d8458cb73dfed8df26ad25b1167150eb0a 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardCompoundViewActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardCompoundViewActivity.java
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+
+package com.example.android.autofill.app.commoncases;
import android.content.Context;
import android.content.Intent;
@@ -24,7 +25,9 @@ import android.view.View;
import android.view.autofill.AutofillManager;
import android.widget.EditText;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
+import com.example.android.autofill.app.view.autofillable.CreditCardExpirationDateCompoundView;
public class CreditCardCompoundViewActivity extends AppCompatActivity {
@@ -32,11 +35,6 @@ public class CreditCardCompoundViewActivity extends AppCompatActivity {
private EditText mCcExpNumber;
private EditText mCcSecurityCode;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, CreditCardCompoundViewActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -53,7 +51,10 @@ public class CreditCardCompoundViewActivity extends AppCompatActivity {
findViewById(R.id.clearButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- getSystemService(AutofillManager.class).cancel();
+ AutofillManager afm = getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.cancel();
+ }
resetFields();
}
});
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardDatePickerActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardDatePickerActivity.java
similarity index 85%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardDatePickerActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardDatePickerActivity.java
index 940f5e4848d28feb933ce299ffa9454691715d53..4a15886325a39a00009a491ed126d9c95d831f99 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardDatePickerActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardDatePickerActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.commoncases;
import android.content.Context;
import android.content.Intent;
@@ -25,9 +25,11 @@ import android.view.View;
import android.view.autofill.AutofillManager;
import android.widget.EditText;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
+import com.example.android.autofill.app.view.autofillable.CreditCardExpirationDatePickerView;
-import static com.example.android.autofillframework.CommonUtil.TAG;
+import static com.example.android.autofill.app.Util.TAG;
public class CreditCardDatePickerActivity extends AppCompatActivity {
@@ -35,11 +37,6 @@ public class CreditCardDatePickerActivity extends AppCompatActivity {
private EditText mCcExpNumber;
private EditText mCcSecurityCode;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, CreditCardDatePickerActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -56,7 +53,10 @@ public class CreditCardDatePickerActivity extends AppCompatActivity {
findViewById(R.id.clearButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- getSystemService(AutofillManager.class).cancel();
+ AutofillManager afm = getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.cancel();
+ }
resetFields();
}
});
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardSpinnersActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardSpinnersActivity.java
similarity index 93%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardSpinnersActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardSpinnersActivity.java
index af1bed5b71b3fea1200c5eb38178017117b7615e..bab5379863e9e83d120b8aab236b597fb8be4a22 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardSpinnersActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/CreditCardSpinnersActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.commoncases;
import android.content.Context;
import android.content.Intent;
@@ -25,7 +25,8 @@ import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
import java.util.Calendar;
@@ -41,11 +42,6 @@ public class CreditCardSpinnersActivity extends AppCompatActivity {
private EditText mCcCardNumber;
private EditText mCcSecurityCode;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, CreditCardSpinnersActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -98,7 +94,10 @@ public class CreditCardSpinnersActivity extends AppCompatActivity {
findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- getSystemService(AutofillManager.class).cancel();
+ AutofillManager afm = getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.cancel();
+ }
resetFields();
}
});
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/EmailComposeActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/EmailComposeActivity.java
similarity index 83%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/EmailComposeActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/EmailComposeActivity.java
index 957d7aad9be904790d9871724e3e72addaa96832..94a001942047779a0b4df9097280b4139f594658 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/EmailComposeActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/EmailComposeActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.commoncases;
import android.content.Context;
import android.content.Intent;
@@ -22,15 +22,11 @@ import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
public class EmailComposeActivity extends AppCompatActivity {
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, EmailComposeActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/StandardAutoCompleteSignInActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/StandardAutoCompleteSignInActivity.java
similarity index 90%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/StandardAutoCompleteSignInActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/StandardAutoCompleteSignInActivity.java
index 5fbdd346a5294f53d1eb087e5c59317cb37c1103..089c6b2d597789affe7086e7983fad25b7bdb308 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/StandardAutoCompleteSignInActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/StandardAutoCompleteSignInActivity.java
@@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.commoncases;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
@@ -27,9 +28,10 @@ import android.widget.AutoCompleteTextView;
import android.widget.TextView;
import android.widget.Toast;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
-import static com.example.android.autofillframework.CommonUtil.TAG;
+import static com.example.android.autofill.app.Util.TAG;
public class StandardAutoCompleteSignInActivity extends AppCompatActivity {
private AutoCompleteTextView mUsernameAutoCompleteField;
@@ -40,11 +42,6 @@ public class StandardAutoCompleteSignInActivity extends AppCompatActivity {
private AutofillManager.AutofillCallback mAutofillCallback;
private AutofillManager mAutofillManager;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, StandardAutoCompleteSignInActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -64,6 +61,10 @@ public class StandardAutoCompleteSignInActivity extends AppCompatActivity {
mClearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
+ AutofillManager afm = getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.cancel();
+ }
resetFields();
}
});
@@ -118,8 +119,7 @@ public class StandardAutoCompleteSignInActivity extends AppCompatActivity {
private class MyAutofillCallback extends AutofillManager.AutofillCallback {
@Override
- public void onAutofillEvent(View view, int event) {
- super.onAutofillEvent(view, event);
+ public void onAutofillEvent(@NonNull View view, int event) {
if (view instanceof AutoCompleteTextView) {
switch (event) {
case AutofillManager.AutofillCallback.EVENT_INPUT_UNAVAILABLE:
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/StandardSignInActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/StandardSignInActivity.java
similarity index 87%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/StandardSignInActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/StandardSignInActivity.java
index 9038e3da08c386e3286da8fb22d922cc18f08470..c333bce1f51ddc978b25a7cd3e21223d4dba6c68 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/StandardSignInActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/StandardSignInActivity.java
@@ -13,28 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.commoncases;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
+import android.view.autofill.AutofillManager;
import android.widget.EditText;
import android.widget.Toast;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
public class StandardSignInActivity extends AppCompatActivity {
private EditText mUsernameEditText;
private EditText mPasswordEditText;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, StandardSignInActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -51,6 +48,10 @@ public class StandardSignInActivity extends AppCompatActivity {
findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
+ AutofillManager afm = getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.cancel();
+ }
resetFields();
}
});
@@ -82,6 +83,6 @@ public class StandardSignInActivity extends AppCompatActivity {
* authenticate users.
*/
public boolean isValidCredentials(String username, String password) {
- return username != null && password != null && username.equals(password);
+ return username != null && password != null && username.equalsIgnoreCase(password);
}
-}
\ No newline at end of file
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/VirtualSignInActivity.java
similarity index 92%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/VirtualSignInActivity.java
index 30e25397988e2da9c47cc69e975389c3ff5e2463..baafbf75b28eb4f793451c7dd1fcb659f83d8be2 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/VirtualSignInActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.commoncases;
import android.content.Context;
import android.content.Intent;
@@ -23,7 +23,9 @@ import android.view.View;
import android.view.autofill.AutofillManager;
import android.widget.Toast;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
+import com.example.android.autofill.app.view.autofillable.CustomVirtualView;
/**
* Activity that uses a virtual views for Username/Password text fields.
@@ -35,11 +37,6 @@ public class VirtualSignInActivity extends AppCompatActivity {
private CustomVirtualView.Line mUsernameLine;
private CustomVirtualView.Line mPasswordLine;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, VirtualSignInActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WebViewSignInActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/WebViewSignInActivity.java
similarity index 80%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WebViewSignInActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/WebViewSignInActivity.java
index 5eb0b50f5d2529071b7c7ae196e3ac9eed6d7387..9bd09f79fae6833432432e1355982e59116594d3 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/WebViewSignInActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/WebViewSignInActivity.java
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+
+package com.example.android.autofill.app.commoncases;
import android.content.Context;
import android.content.Intent;
@@ -24,18 +25,13 @@ import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
-import static com.example.android.autofillframework.CommonUtil.DEBUG;
-import static com.example.android.autofillframework.CommonUtil.TAG;
+import static com.example.android.autofill.app.Util.DEBUG;
+import static com.example.android.autofill.app.Util.TAG;
public class WebViewSignInActivity extends AppCompatActivity {
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, WebViewSignInActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/AbstractMultipleStepsActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/AbstractMultipleStepsActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..2baf3351e9501a13c65e619cd6fe06365dc1b163
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/AbstractMultipleStepsActivity.java
@@ -0,0 +1,174 @@
+/*
+ * 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.example.android.autofill.app.edgecases;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.example.android.autofill.app.R;
+
+import java.util.Map;
+
+import static com.example.android.autofill.app.Util.DEBUG;
+import static com.example.android.autofill.app.Util.TAG;
+
+/**
+ * Activity that emulates a multiple-steps wizard activity, where each step shows just one
+ * label and input.
+ *
+ *
Its's useful to verify how an autofill service handles account creation that takes multiple
+ * steps.
+ */
+
+/*
+ * TODO list
+ * - use ConstraintLayout
+ * - use Fragments instead of replacing views directly
+ * - use custom view and/or layout xml for mSteps
+ */
+abstract class AbstractMultipleStepsActivity extends AppCompatActivity {
+
+ private TextView mStatus;
+ private ViewGroup mContainer;
+
+ private Button mPrev;
+ private Button mNext;
+ private Button mFinish;
+
+ private int mCurrentStep;
+ private boolean mFinished;
+
+ private LinearLayout[] mSteps;
+
+ /**
+ * Gets the mapping from resource id to autofill hints.
+ */
+ protected abstract Map getStepsMap();
+
+ @Override
+ protected final void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.multiple_steps_activity);
+
+ mStatus = findViewById(R.id.status);
+ mContainer = findViewById(R.id.container);
+ mPrev = findViewById(R.id.prev);
+ mNext = findViewById(R.id.next);
+ mFinish = findViewById(R.id.finish);
+
+ View.OnClickListener onClickListener = (v) -> {
+ if (v == mPrev) {
+ showStep(mCurrentStep - 1);
+ } else if (v == mNext) {
+ showStep(mCurrentStep + 1);
+ } else {
+ finishIt();
+ }
+ };
+ mPrev.setOnClickListener(onClickListener);
+ mNext.setOnClickListener(onClickListener);
+ mFinish.setOnClickListener(onClickListener);
+
+ Map stepsMap = getStepsMap();
+ if (DEBUG) debug("onCreate(): steps=%s", stepsMap);
+ initializeSteps(stepsMap);
+
+ showStep(0);
+ }
+
+ private void showStep(int i) {
+ if (mFinished || i < 0 || i >= mSteps.length) {
+ warn("Invalid step: %d (finished=%s, range=[%d,%d])",
+ mFinished, i, 0, mSteps.length - 1);
+ return;
+ }
+ View step = mSteps[i];
+ mStatus.setText(getString(R.string.message_showing_step, i));
+ if (DEBUG) debug("Showing step %d", i);
+ if (mContainer.getChildCount() > 0) {
+ mContainer.removeViewAt(0);
+ }
+ mContainer.addView(step);
+ mCurrentStep = i;
+
+ mPrev.setEnabled(mCurrentStep != 0);
+ mNext.setEnabled(mCurrentStep != mSteps.length - 1);
+ }
+
+ private void updateButtons() {
+ mPrev.setEnabled(!mFinished && mCurrentStep != 0);
+ mNext.setEnabled(!mFinished && mCurrentStep != mSteps.length - 1);
+ mFinish.setEnabled(!mFinished);
+ }
+
+ private void finishIt() {
+ StringBuilder message = new StringBuilder(getString(R.string.message_finished))
+ .append("\n\n");
+ for (int i = 0; i < mSteps.length; i++) {
+ TextView label = (TextView) mSteps[i].getChildAt(0);
+ EditText input = (EditText) mSteps[i].getChildAt(1);
+ message.append(getString(R.string.message_step_description, label.getText(), input.getText()))
+ .append('\n');
+ }
+ mStatus.setText(message.toString());
+ mContainer.removeAllViews();
+ mFinished = true;
+ updateButtons();
+ }
+
+ private void initializeSteps(Map stepsMap) {
+ mSteps = new LinearLayout[stepsMap.size()];
+ int i = 0;
+ for (Map.Entry entry : stepsMap.entrySet()) {
+ int labelId = entry.getKey();
+ String autofillHints = entry.getValue();
+ if (DEBUG) debug("step %d: %s->%s", i, getString(labelId), autofillHints);
+ mSteps[i++] = newStep(labelId, autofillHints);
+ }
+ }
+
+ private LinearLayout newStep(int labelId, String autofillHints) {
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.HORIZONTAL);
+
+ TextView label = new TextView(this);
+ label.setText(labelId);
+ layout.addView(label);
+
+ EditText input = new EditText(this);
+ input.setAutofillHints(autofillHints);
+ input.setWidth(500); // TODO: proper size
+ layout.addView(input);
+
+ return layout;
+ }
+
+ protected void debug(String fmt, Object... args) {
+ Log.d(TAG, getLocalClassName() + "." + String.format(fmt, args));
+ }
+
+ protected void warn(String fmt, Object... args) {
+ Log.w(TAG, getLocalClassName() + "." + String.format(fmt, args));
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/CreditCardActivity.java
similarity index 88%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/CreditCardActivity.java
index 83e8614af80438c86c6a819cecc75f97801e051e..3e68a012af1ab8deb02b11af543c8ede3f464485 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/CreditCardActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.edgecases;
import android.content.Context;
import android.content.Intent;
@@ -24,7 +24,8 @@ import android.view.View;
import android.view.autofill.AutofillManager;
import android.widget.EditText;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
public class CreditCardActivity extends AppCompatActivity {
@@ -34,11 +35,6 @@ public class CreditCardActivity extends AppCompatActivity {
private EditText mCcNumber;
private EditText mCcSecurityCode;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, CreditCardActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -57,7 +53,10 @@ public class CreditCardActivity extends AppCompatActivity {
findViewById(R.id.clearButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- getSystemService(AutofillManager.class).cancel();
+ AutofillManager afm = getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.cancel();
+ }
resetFields();
}
});
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardAntiPatternActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/CreditCardAntiPatternActivity.java
similarity index 87%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardAntiPatternActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/CreditCardAntiPatternActivity.java
index a7c14290fee753e42aaa8064b8b539afe4914c5a..edffcc054bd130d575ef5b9168952dba78828077 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardAntiPatternActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/CreditCardAntiPatternActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.edgecases;
import android.content.Context;
import android.content.Intent;
@@ -24,7 +24,8 @@ import android.view.View;
import android.view.autofill.AutofillManager;
import android.widget.EditText;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
public class CreditCardAntiPatternActivity extends AppCompatActivity {
@@ -32,11 +33,6 @@ public class CreditCardAntiPatternActivity extends AppCompatActivity {
private EditText mCcExpNumber;
private EditText mCcSecurityCode;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, CreditCardAntiPatternActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -53,7 +49,10 @@ public class CreditCardAntiPatternActivity extends AppCompatActivity {
findViewById(R.id.clearButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- getSystemService(AutofillManager.class).cancel();
+ AutofillManager afm = getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.cancel();
+ }
resetFields();
}
});
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/EdgeCasesFragment.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/EdgeCasesFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..917be103dd1c4f7f300d34ffa7ad9b45ed839f19
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/EdgeCasesFragment.java
@@ -0,0 +1,40 @@
+/*
+* 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.example.android.autofill.app.edgecases;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.example.android.autofill.app.BaseMainFragment;
+import com.example.android.autofill.app.R;
+
+public class EdgeCasesFragment extends BaseMainFragment {
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_edge_cases, container, false);
+ }
+
+ @Override
+ public int getPageTitleResId() {
+ return R.string.edge_cases_page_title;
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MultiplePartitionsActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultiplePartitionsActivity.java
similarity index 91%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MultiplePartitionsActivity.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultiplePartitionsActivity.java
index 319ed192c12d207bccd8fc4093d66607f6dc360d..9e09b45b0bbf281b2cc9f9a1b56c3140f46f5eeb 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MultiplePartitionsActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultiplePartitionsActivity.java
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.edgecases;
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
@@ -23,8 +22,10 @@ import android.view.View;
import android.view.autofill.AutofillManager;
import android.widget.Toast;
-import com.example.android.autofillframework.CommonUtil;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.Util;
+import com.example.android.autofill.app.view.autofillable.CustomVirtualView;
+import com.example.android.autofill.app.view.autofillable.ScrollableCustomVirtualView;
/**
* Activity used to demonstrated safe partitioning of data.
@@ -46,24 +47,14 @@ public class MultiplePartitionsActivity extends AppCompatActivity {
private ScrollableCustomVirtualView mCustomVirtualView;
private AutofillManager mAutofillManager;
-
private CustomVirtualView.Partition mCredentialsPartition;
private CustomVirtualView.Partition mCcPartition;
- public static Intent getStartActivityIntent(Context context) {
- Intent intent = new Intent(context, MultiplePartitionsActivity.class);
- return intent;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
setContentView(R.layout.multiple_partitions_activity);
-
mCustomVirtualView = findViewById(R.id.custom_view);
-
-
mCredentialsPartition =
mCustomVirtualView.addPartition(getString(R.string.partition_credentials));
mCredentialsPartition.addLine("username", View.AUTOFILL_TYPE_TEXT,
@@ -72,7 +63,6 @@ public class MultiplePartitionsActivity extends AppCompatActivity {
mCredentialsPartition.addLine("password", View.AUTOFILL_TYPE_TEXT,
getString(R.string.password_label),
" ", true, View.AUTOFILL_HINT_PASSWORD);
-
int ccExpirationType = View.AUTOFILL_TYPE_DATE;
// TODO: add a checkbox to switch between text / date instead
Intent intent = getIntent();
@@ -81,12 +71,11 @@ public class MultiplePartitionsActivity extends AppCompatActivity {
if (newType != -1) {
ccExpirationType = newType;
String typeMessage = getString(R.string.message_credit_card_expiration_type,
- CommonUtil.getTypeAsString(ccExpirationType));
+ Util.getAutofillTypeAsString(ccExpirationType));
// TODO: display type in a header or proper status widget
Toast.makeText(getApplicationContext(), typeMessage, Toast.LENGTH_LONG).show();
}
}
-
mCcPartition = mCustomVirtualView.addPartition(getString(R.string.partition_credit_card));
mCcPartition.addLine("ccNumber", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_number_label),
@@ -106,7 +95,7 @@ public class MultiplePartitionsActivity extends AppCompatActivity {
mCcPartition.addLine("ccSecurityCode", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_security_code_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
-
+ mAutofillManager = getSystemService(AutofillManager.class);
findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -115,7 +104,6 @@ public class MultiplePartitionsActivity extends AppCompatActivity {
mAutofillManager.cancel();
}
});
- mAutofillManager = getSystemService(AutofillManager.class);
}
private void resetFields() {
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultipleStepsCreditCardActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultipleStepsCreditCardActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..804b77f227fdc95d2a9370c87b5d629d1c1b81f2
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultipleStepsCreditCardActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.example.android.autofill.app.edgecases;
+
+import android.view.View;
+
+import com.example.android.autofill.app.R;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class MultipleStepsCreditCardActivity extends AbstractMultipleStepsActivity {
+
+ @Override
+ protected Map getStepsMap() {
+ LinkedHashMap steps = new LinkedHashMap<>(4);
+ steps.put(R.string.credit_card_number_label,
+ View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
+ steps.put(R.string.credit_card_expiration_month_label,
+ View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH);
+ steps.put(R.string.credit_card_expiration_year_label,
+ View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR);
+ steps.put(R.string.credit_card_security_code_abbrev_label,
+ View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
+ return ImmutableMap.copyOf(steps);
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultipleStepsSignInActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultipleStepsSignInActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec45a9c01c46c5b7c86bb6f39b7904002c3335ff
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/edgecases/MultipleStepsSignInActivity.java
@@ -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.example.android.autofill.app.edgecases;
+
+import android.view.View;
+
+import com.example.android.autofill.app.R;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class MultipleStepsSignInActivity extends AbstractMultipleStepsActivity {
+
+ @Override
+ protected Map getStepsMap() {
+ LinkedHashMap steps = new LinkedHashMap<>(2);
+ steps.put(R.string.username_label, View.AUTOFILL_HINT_USERNAME);
+ steps.put(R.string.password_label, View.AUTOFILL_HINT_PASSWORD);
+ return ImmutableMap.copyOf(steps);
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDateCompoundView.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CreditCardExpirationDateCompoundView.java
similarity index 96%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDateCompoundView.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CreditCardExpirationDateCompoundView.java
index afa105e0860db5f63523d402333da9ddbf8fb00f..1cee75a94a6466edf5cc540601daeaef1dc0005c 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDateCompoundView.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CreditCardExpirationDateCompoundView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.view.autofillable;
import android.content.Context;
import android.support.annotation.NonNull;
@@ -29,11 +29,11 @@ import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.Spinner;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
import java.util.Calendar;
-import static com.example.android.autofillframework.CommonUtil.TAG;
+import static com.example.android.autofill.app.Util.TAG;
/**
* A custom view that represents a {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE} using
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDatePickerView.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CreditCardExpirationDatePickerView.java
similarity index 95%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDatePickerView.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CreditCardExpirationDatePickerView.java
index e8385fbc0c48b470d71c28f62a061d1b1cc36780..870825a2643dee33afa8e325014e9b322e2881f3 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDatePickerView.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CreditCardExpirationDatePickerView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.view.autofillable;
import android.app.DatePickerDialog;
import android.app.Dialog;
@@ -30,15 +30,14 @@ import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillValue;
import android.widget.DatePicker;
-import android.widget.EditText;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
import java.util.Calendar;
import java.util.Date;
-import static com.example.android.autofillframework.CommonUtil.DEBUG;
-import static com.example.android.autofillframework.CommonUtil.TAG;
+import static com.example.android.autofill.app.Util.DEBUG;
+import static com.example.android.autofill.app.Util.TAG;
/**
* A custom view that represents a {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE} using
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CustomVirtualView.java
similarity index 95%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CustomVirtualView.java
index f6fb3a522571fc5b5c9bb912177c53ba5929d006..6c6e1254e7b08e18fbbfa32af9d8c86ba004835e 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/CustomVirtualView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.view.autofillable;
import android.content.Context;
import android.content.res.TypedArray;
@@ -38,8 +38,8 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
-import com.example.android.autofillframework.CommonUtil;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.Util;
import com.google.common.base.Preconditions;
import java.text.DateFormat;
@@ -47,7 +47,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
-import static com.example.android.autofillframework.CommonUtil.bundleToString;
+import static com.example.android.autofill.app.Util.bundleToString;
/**
* A custom View with a virtual structure for fields supporting {@link View#getAutofillHints()}
@@ -56,6 +56,16 @@ public class CustomVirtualView extends View {
protected static final boolean DEBUG = true;
protected static final boolean VERBOSE = false;
+
+ /**
+ * When set, it notifies AutofillManager of focus change as the view scrolls, so the
+ * autofill UI is continually drawn.
+ *
+ *
This is janky and incompatible with the way the autofill UI works on native views, but
+ * it's a cool experiment!
+ */
+ private static final boolean DRAW_AUTOFILL_UI_AFTER_SCROLL = false;
+
private static final String TAG = "CustomView";
private static final int DEFAULT_TEXT_HEIGHT_DP = 34;
private static final int VERTICAL_GAP = 10;
@@ -159,7 +169,7 @@ public class CustomVirtualView extends View {
// Check if the type was properly set by the autofill service
if (DEBUG) {
Log.d(TAG, "Validating " + i
- + ": expectedType=" + CommonUtil.getTypeAsString(item.type)
+ + ": expectedType=" + Util.getAutofillTypeAsString(item.type)
+ "(" + item.type + "), value=" + value);
}
boolean valid = false;
@@ -240,6 +250,10 @@ public class CustomVirtualView extends View {
if (VERBOSE) Log.v(TAG, "setBounds(" + x + ", " + y + "): " + line.mBounds);
canvas.drawText(writeText, x, y, mTextPaint);
y += mLineLength;
+
+ if (DRAW_AUTOFILL_UI_AFTER_SCROLL) {
+ line.notifyFocusChanged();
+ }
}
}
@@ -340,7 +354,7 @@ public class CustomVirtualView extends View {
public String toString() {
return id + "/" + idEntry + ": "
+ (type == AUTOFILL_TYPE_DATE ? date : text) // TODO: use DateFormat for date
- + " (" + CommonUtil.getTypeAsString(type) + ")"
+ + " (" + Util.getAutofillTypeAsString(type) + ")"
+ (editable ? " (editable)" : " (read-only)"
+ (sanitized ? " (sanitized)" : " (sensitive"))
+ (hints == null ? " (no hints)" : " ( " + Arrays.toString(hints) + ")");
@@ -440,7 +454,11 @@ public class CustomVirtualView extends View {
private void changeFocus(boolean focused) {
mFieldTextItem.focused = focused;
- if (focused) {
+ notifyFocusChanged();
+ }
+
+ void notifyFocusChanged() {
+ if (mFieldTextItem.focused) {
Rect absBounds = getAbsCoordinates();
if (DEBUG) {
Log.d(TAG, "focus gained on " + mFieldTextItem.id + "; absBounds=" + absBounds);
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/ScrollableCustomVirtualView.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/ScrollableCustomVirtualView.java
similarity index 98%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/ScrollableCustomVirtualView.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/ScrollableCustomVirtualView.java
index 0ce5036deffbacb6fa764d2c1a1e11d44b4a5040..cdc9d98980e3b3657ce53915a4c01dfd759139a0 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/ScrollableCustomVirtualView.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/autofillable/ScrollableCustomVirtualView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.view.autofillable;
import android.content.Context;
import android.support.annotation.Nullable;
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/InfoButton.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/InfoButton.java
similarity index 94%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/InfoButton.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/InfoButton.java
index d5811e116932a47971da6479ec466d209f422d0c..92a221fc35cdd41e8e786e02edaa30da7aa9c185 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/InfoButton.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/InfoButton.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.view.widget;
import android.content.Context;
import android.content.res.TypedArray;
@@ -22,7 +22,7 @@ import android.support.v7.widget.AppCompatImageButton;
import android.util.AttributeSet;
import android.view.View;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
public class InfoButton extends AppCompatImageButton {
public InfoButton(Context context) {
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/NavigationItem.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/NavigationItem.java
similarity index 72%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/NavigationItem.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/NavigationItem.java
index baeef683b390f57dc9aeb98a4a075bd6bb81c1e2..9fea6bf499ec2e2e82355068a9ecc196afcb0901 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/NavigationItem.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/NavigationItem.java
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.app;
+package com.example.android.autofill.app.view.widget;
import android.content.Context;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
@@ -25,16 +26,17 @@ import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
-import com.example.android.autofillframework.R;
+import com.example.android.autofill.app.R;
-public class NavigationItem extends FrameLayout {
- CardView mCardView;
+import static com.example.android.autofill.app.Util.TAG;
+public class NavigationItem extends FrameLayout {
public NavigationItem(Context context) {
this(context, null);
}
@@ -56,23 +58,30 @@ public class NavigationItem extends FrameLayout {
String infoText = typedArray.getString(R.styleable.NavigationItem_infoText);
Drawable logoDrawable = typedArray.getDrawable(R.styleable.NavigationItem_itemLogo);
@ColorRes int colorRes = typedArray.getResourceId(R.styleable.NavigationItem_imageColor, 0);
+ String launchingActivityName = typedArray.getString(R.styleable.NavigationItem_destinationActivityName);
int imageColor = ContextCompat.getColor(getContext(), colorRes);
typedArray.recycle();
View rootView = LayoutInflater.from(context).inflate(R.layout.navigation_item, this);
- if (logoDrawable != null) {
- logoDrawable.setColorFilter(imageColor, PorterDuff.Mode.SRC_IN);
- }
TextView buttonLabel = rootView.findViewById(R.id.buttonLabel);
buttonLabel.setText(labelText);
- buttonLabel.setCompoundDrawablesRelativeWithIntrinsicBounds(logoDrawable, null,
- null, null);
+ if (logoDrawable != null) {
+ Drawable mutatedLogoDrawable = logoDrawable.mutate();
+ mutatedLogoDrawable.setColorFilter(imageColor, PorterDuff.Mode.SRC_IN);
+ buttonLabel.setCompoundDrawablesRelativeWithIntrinsicBounds(mutatedLogoDrawable, null,
+ null, null);
+ }
InfoButton infoButton = rootView.findViewById(R.id.infoButton);
infoButton.setInfoText(infoText);
infoButton.setColorFilter(imageColor);
- mCardView = rootView.findViewById(R.id.cardView);
- }
-
- public void setNavigationButtonClickListener(@Nullable OnClickListener l) {
- mCardView.setOnClickListener(l);
+ CardView outerView = rootView.findViewById(R.id.cardView);
+ outerView.setOnClickListener((view) -> {
+ if (launchingActivityName != null) {
+ Intent intent = new Intent();
+ intent.setClassName(getContext().getPackageName(), launchingActivityName);
+ context.startActivity(intent);
+ } else {
+ Log.w(TAG, "Launching Activity name not set.");
+ }
+ });
}
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java
deleted file mode 100644
index 0e139f9ffb841f47593fb2ba38d5e93d170aee22..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.example.android.autofillframework.app;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.view.View;
-
-import com.example.android.autofillframework.R;
-
-/**
- * This is used to launch sample activities that showcase autofill.
- */
-public class MainActivity extends AppCompatActivity {
-
- private static final String TAG = "MainActivity";
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (launchTrampolineActivity()) {
- return;
- }
-
- setContentView(R.layout.activity_main);
- NavigationItem loginEditTexts = findViewById(R.id.standardViewSignInButton);
- NavigationItem loginCustomVirtual = findViewById(R.id.virtualViewSignInButton);
- NavigationItem creditCard = findViewById(R.id.creditCardButton);
- NavigationItem creditCardSpinners = findViewById(R.id.creditCardSpinnersButton);
- NavigationItem loginAutoComplete = findViewById(R.id.standardLoginWithAutoCompleteButton);
- NavigationItem emailCompose = findViewById(R.id.emailComposeButton);
- NavigationItem creditCardCompoundView = findViewById(R.id.creditCardCompoundViewButton);
- NavigationItem creditCardDatePicker = findViewById(R.id.creditCardDatePickerButton);
- NavigationItem creditCardAntiPatternPicker = findViewById(R.id.creditCardAntiPatternButton);
- NavigationItem multiplePartitions = findViewById(R.id.multiplePartitionsButton);
- NavigationItem loginWebView = findViewById(R.id.webviewSignInButton);
- loginEditTexts.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(StandardSignInActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- loginCustomVirtual.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(VirtualSignInActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- creditCard.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(CreditCardActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- creditCardSpinners.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(CreditCardSpinnersActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- loginAutoComplete.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(StandardAutoCompleteSignInActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- emailCompose.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(EmailComposeActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- creditCardCompoundView.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(CreditCardCompoundViewActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- creditCardDatePicker.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(CreditCardDatePickerActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- creditCardAntiPatternPicker.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- startActivity(CreditCardAntiPatternActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- multiplePartitions.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(MultiplePartitionsActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- loginWebView.setNavigationButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- startActivity(WebViewSignInActivity.getStartActivityIntent(MainActivity.this));
- }
- });
- }
-
- private boolean launchTrampolineActivity() {
- Intent intent = getIntent();
- if (intent != null) {
- String target = intent.getStringExtra("target");
- if (target != null) {
- Log.i(TAG, "trampolining into " + target + " instead");
- try {
- Intent newIntent = new Intent(this,
- Class.forName("com.example.android.autofillframework." + target));
- newIntent.putExtras(intent);
- newIntent.removeExtra("target");
- getApplicationContext().startActivity(newIntent);
- finish();
- return true;
- } catch (Exception e) {
- Log.e(TAG, "Error launching " + target, e);
- }
- }
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java
deleted file mode 100644
index 631cc0a929f4af40c8bb591d04dbfb6f615a8b2b..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-import android.app.PendingIntent;
-import android.app.assist.AssistStructure;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.service.autofill.Dataset;
-import android.service.autofill.FillResponse;
-import android.support.annotation.Nullable;
-import android.support.v7.app.AppCompatActivity;
-import android.text.Editable;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.EditText;
-import android.widget.Toast;
-
-import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-import com.example.android.autofillframework.multidatasetservice.settings.MyPreferences;
-
-import java.util.HashMap;
-
-import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE;
-import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT;
-import static com.example.android.autofillframework.CommonUtil.EXTRA_DATASET_NAME;
-import static com.example.android.autofillframework.CommonUtil.EXTRA_FOR_RESPONSE;
-import static com.example.android.autofillframework.CommonUtil.TAG;
-
-/**
- * This Activity controls the UI for logging in to the Autofill service.
- * It is launched when an Autofill Response or specific Dataset within the Response requires
- * authentication to access. It bundles the result in an Intent.
- */
-public class AuthActivity extends AppCompatActivity {
-
- // Unique id for dataset intents.
- private static int sDatasetPendingIntentId = 0;
-
- private EditText mMasterPassword;
- private Intent mReplyIntent;
-
- static IntentSender getAuthIntentSenderForResponse(Context context) {
- final Intent intent = new Intent(context, AuthActivity.class);
- return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT)
- .getIntentSender();
- }
-
- static IntentSender getAuthIntentSenderForDataset(Context context, String datasetName) {
- final Intent intent = new Intent(context, AuthActivity.class);
- intent.putExtra(EXTRA_DATASET_NAME, datasetName);
- intent.putExtra(EXTRA_FOR_RESPONSE, false);
- return PendingIntent.getActivity(context, ++sDatasetPendingIntentId, intent,
- PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.multidataset_service_auth_activity);
- mMasterPassword = findViewById(R.id.master_password);
- findViewById(R.id.login).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- login();
- }
-
- });
- findViewById(R.id.cancel).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- onFailure();
- AuthActivity.this.finish();
- }
- });
- }
-
- private void login() {
- Editable password = mMasterPassword.getText();
- if (password.toString()
- .equals(MyPreferences.getInstance(AuthActivity.this).getMasterPassword())) {
- onSuccess();
- } else {
- Toast.makeText(this, "Password incorrect", Toast.LENGTH_SHORT).show();
- onFailure();
- }
- finish();
- }
-
- @Override
- public void finish() {
- if (mReplyIntent != null) {
- setResult(RESULT_OK, mReplyIntent);
- } else {
- setResult(RESULT_CANCELED);
- }
- super.finish();
- }
-
- private void onFailure() {
- Log.w(TAG, "Failed auth.");
- mReplyIntent = null;
- }
-
- private void onSuccess() {
- Intent intent = getIntent();
- boolean forResponse = intent.getBooleanExtra(EXTRA_FOR_RESPONSE, true);
- AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
- StructureParser parser = new StructureParser(getApplicationContext(), structure);
- parser.parseForFill();
- AutofillFieldMetadataCollection autofillFields = parser.getAutofillFields();
- int saveTypes = autofillFields.getSaveType();
- mReplyIntent = new Intent();
- HashMap clientFormDataMap =
- SharedPrefsAutofillRepository.getInstance().getFilledAutofillFieldCollection
- (this, autofillFields.getFocusedHints(), autofillFields.getAllHints());
- if (forResponse) {
- setResponseIntent(AutofillHelper.newResponse
- (this, false, autofillFields, clientFormDataMap));
- } else {
- String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME);
- setDatasetIntent(AutofillHelper.newDataset
- (this, autofillFields, clientFormDataMap.get(datasetName), false));
- }
- }
-
- private void setResponseIntent(FillResponse fillResponse) {
- mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);
- }
-
- private void setDatasetIntent(Dataset dataset) {
- mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillFieldMetadata.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillFieldMetadata.java
deleted file mode 100644
index 1e8427db0b2f5289df4e7c511d73433f02e5052c..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillFieldMetadata.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-import android.app.assist.AssistStructure.ViewNode;
-import android.view.autofill.AutofillId;
-
-import static com.example.android.autofillframework.multidatasetservice.AutofillHints.convertToStoredHintNames;
-import static com.example.android.autofillframework.multidatasetservice.AutofillHints.filterForSupportedHints;
-
-/**
- * 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.
- */
-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 = filterForSupportedHints(view.getAutofillHints());
- if (hints != null) {
- 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;
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillFieldMetadataCollection.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillFieldMetadataCollection.java
deleted file mode 100644
index 59b8fcd5d8e6e5a31fc7ab678d2e0dce55dc400b..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillFieldMetadataCollection.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-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 mAutofillIds = new ArrayList<>();
- private final HashMap> mAutofillHintsToFieldsMap = new HashMap<>();
- private final List mAllAutofillHints = new ArrayList<>();
- private final List 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 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 getFieldsForHint(String hint) {
- return mAutofillHintsToFieldsMap.get(hint);
- }
-
- public List getFocusedHints() {
- return mFocusedAutofillHints;
- }
-
- public List getAllHints() {
- return mAllAutofillHints;
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java
deleted file mode 100644
index 4c0f173e07943a97c44939a0d258eab299e0820f..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-import android.content.Context;
-import android.content.IntentSender;
-import android.service.autofill.Dataset;
-import android.service.autofill.FillResponse;
-import android.service.autofill.SaveInfo;
-import android.support.annotation.DrawableRes;
-import android.util.Log;
-import android.view.autofill.AutofillId;
-import android.widget.RemoteViews;
-
-import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-
-import java.util.HashMap;
-import java.util.Set;
-
-import static com.example.android.autofillframework.CommonUtil.TAG;
-
-/**
- * This is a class containing helper methods for building Autofill Datasets and Responses.
- */
-public final class AutofillHelper {
-
- private AutofillHelper() {
- throw new UnsupportedOperationException("provide static methods only");
- }
-
- /**
- * Wraps autofill data in a LoginCredential Dataset object which can then be sent back to the
- * client View.
- */
- public static Dataset newDataset(Context context,
- AutofillFieldMetadataCollection autofillFields,
- FilledAutofillFieldCollection filledAutofillFieldCollection, boolean datasetAuth) {
- String datasetName = filledAutofillFieldCollection.getDatasetName();
- if (datasetName != null) {
- Dataset.Builder datasetBuilder;
- if (datasetAuth) {
- datasetBuilder = new Dataset.Builder
- (newRemoteViews(context.getPackageName(), datasetName,
- R.drawable.ic_lock_black_24dp));
- IntentSender sender =
- AuthActivity.getAuthIntentSenderForDataset(context, datasetName);
- datasetBuilder.setAuthentication(sender);
- } else {
- datasetBuilder = new Dataset.Builder
- (newRemoteViews(context.getPackageName(), datasetName,
- R.drawable.ic_person_black_24dp));
- }
- boolean setValueAtLeastOnce =
- filledAutofillFieldCollection.applyToFields(autofillFields, datasetBuilder);
- if (setValueAtLeastOnce) {
- return datasetBuilder.build();
- }
- }
- return null;
- }
-
- public static RemoteViews newRemoteViews(String packageName, String remoteViewsText,
- @DrawableRes int drawableId) {
- RemoteViews presentation =
- new RemoteViews(packageName, R.layout.multidataset_service_list_item);
- presentation.setTextViewText(R.id.text, remoteViewsText);
- presentation.setImageViewResource(R.id.icon, drawableId);
- return presentation;
- }
-
- /**
- * Wraps autofill data in a Response object (essentially a series of Datasets) which can then
- * be sent back to the client View.
- */
- public static FillResponse newResponse(Context context,
- boolean datasetAuth, AutofillFieldMetadataCollection autofillFields,
- HashMap clientFormDataMap) {
- FillResponse.Builder responseBuilder = new FillResponse.Builder();
- if (clientFormDataMap != null) {
- Set datasetNames = clientFormDataMap.keySet();
- for (String datasetName : datasetNames) {
- FilledAutofillFieldCollection filledAutofillFieldCollection =
- clientFormDataMap.get(datasetName);
- if (filledAutofillFieldCollection != null) {
- Dataset dataset = newDataset(context, autofillFields,
- filledAutofillFieldCollection, datasetAuth);
- if (dataset != null) {
- responseBuilder.addDataset(dataset);
- }
- }
- }
- }
- if (autofillFields.getSaveType() != 0) {
- AutofillId[] autofillIds = autofillFields.getAutofillIds();
- responseBuilder.setSaveInfo
- (new SaveInfo.Builder(autofillFields.getSaveType(), autofillIds).build());
- return responseBuilder.build();
- } else {
- Log.d(TAG, "These fields are not meant to be saved by autofill.");
- return null;
- }
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHints.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHints.java
deleted file mode 100644
index af42b1089571a0a3ed6170a7f37f74d99ba86e39..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHints.java
+++ /dev/null
@@ -1,774 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-import android.service.autofill.SaveInfo;
-import android.util.Log;
-import android.view.View;
-
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillField;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-import com.google.common.collect.ImmutableMap;
-
-import java.util.Calendar;
-
-import static com.example.android.autofillframework.CommonUtil.TAG;
-
-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 sValidHints =
- new ImmutableMap.Builder()
- .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.d(TAG, "Invalid autofill hint: " + hint);
- }
- }
- if (i == 0) {
- return null;
- }
- String[] finalFilteredHints = new String[i];
- System.arraycopy(filteredHints, 0, finalFilteredHints, 0, i);
- return finalFilteredHints;
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
deleted file mode 100644
index ac0302210e11863391655b270fa6168701a46780..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-import android.app.assist.AssistStructure;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.service.autofill.AutofillService;
-import android.service.autofill.FillCallback;
-import android.service.autofill.FillContext;
-import android.service.autofill.FillRequest;
-import android.service.autofill.FillResponse;
-import android.service.autofill.SaveCallback;
-import android.service.autofill.SaveRequest;
-import android.util.Log;
-import android.view.autofill.AutofillId;
-import android.widget.RemoteViews;
-
-import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository;
-import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsPackageVerificationRepository;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-import com.example.android.autofillframework.multidatasetservice.settings.MyPreferences;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-import static com.example.android.autofillframework.CommonUtil.TAG;
-import static com.example.android.autofillframework.CommonUtil.VERBOSE;
-import static com.example.android.autofillframework.CommonUtil.bundleToString;
-import static com.example.android.autofillframework.CommonUtil.dumpStructure;
-
-public class MyAutofillService extends AutofillService {
-
- @Override
- public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
- FillCallback callback) {
- AssistStructure structure = request.getFillContexts()
- .get(request.getFillContexts().size() - 1).getStructure();
- String packageName = structure.getActivityComponent().getPackageName();
- if (!SharedPrefsPackageVerificationRepository.getInstance()
- .putPackageSignatures(getApplicationContext(), packageName)) {
- callback.onFailure(
- getApplicationContext().getString(R.string.invalid_package_signature));
- return;
- }
- final Bundle data = request.getClientState();
- if (VERBOSE) {
- Log.v(TAG, "onFillRequest(): data=" + bundleToString(data));
- dumpStructure(structure);
- }
-
- cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
- @Override
- public void onCancel() {
- Log.w(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.w(TAG, "Security exception handling " + request, e);
- callback.onFailure(e.getMessage());
- return;
- }
- AutofillFieldMetadataCollection autofillFields = parser.getAutofillFields();
- FillResponse.Builder responseBuilder = new FillResponse.Builder();
- // Check user's settings for authenticating Responses and Datasets.
- boolean responseAuth = MyPreferences.getInstance(this).isResponseAuth();
- AutofillId[] autofillIds = autofillFields.getAutofillIds();
- if (responseAuth && !Arrays.asList(autofillIds).isEmpty()) {
- // If the entire Autofill Response is authenticated, AuthActivity is used
- // to generate Response.
- IntentSender sender = AuthActivity.getAuthIntentSenderForResponse(this);
- RemoteViews presentation = AutofillHelper
- .newRemoteViews(getPackageName(), getString(R.string.autofill_sign_in_prompt),
- R.drawable.ic_lock_black_24dp);
- responseBuilder
- .setAuthentication(autofillIds, sender, presentation);
- callback.onSuccess(responseBuilder.build());
- } else {
- boolean datasetAuth = MyPreferences.getInstance(this).isDatasetAuth();
- HashMap clientFormDataMap =
- SharedPrefsAutofillRepository.getInstance().getFilledAutofillFieldCollection(
- this, autofillFields.getFocusedHints(), autofillFields.getAllHints());
- FillResponse response = AutofillHelper.newResponse
- (this, datasetAuth, autofillFields, clientFormDataMap);
- callback.onSuccess(response);
- }
- }
-
- @Override
- public void onSaveRequest(SaveRequest request, SaveCallback callback) {
- List context = request.getFillContexts();
- final AssistStructure structure = context.get(context.size() - 1).getStructure();
- String packageName = structure.getActivityComponent().getPackageName();
- if (!SharedPrefsPackageVerificationRepository.getInstance()
- .putPackageSignatures(getApplicationContext(), packageName)) {
- callback.onFailure(
- getApplicationContext().getString(R.string.invalid_package_signature));
- return;
- }
- final Bundle data = request.getClientState();
- if (VERBOSE) {
- Log.v(TAG, "onSaveRequest(): data=" + bundleToString(data));
- dumpStructure(structure);
- }
- StructureParser parser = new StructureParser(getApplicationContext(), structure);
- parser.parseForSave();
- FilledAutofillFieldCollection filledAutofillFieldCollection = parser.getClientFormData();
- SharedPrefsAutofillRepository.getInstance()
- .saveFilledAutofillFieldCollection(this, filledAutofillFieldCollection);
- }
-
- @Override
- public void onConnected() {
- Log.d(TAG, "onConnected");
- }
-
- @Override
- public void onDisconnected() {
- Log.d(TAG, "onDisconnected");
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/SecurityHelper.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/SecurityHelper.java
deleted file mode 100644
index f18b422ead19c972525bfaef767b89cd2549806e..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/SecurityHelper.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.Signature;
-import android.os.AsyncTask;
-import android.util.Log;
-
-import com.google.common.net.InternetDomainName;
-
-import org.json.JSONObject;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.security.MessageDigest;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-
-import static com.example.android.autofillframework.CommonUtil.DEBUG;
-import static com.example.android.autofillframework.CommonUtil.TAG;
-import static com.example.android.autofillframework.CommonUtil.VERBOSE;
-
-/**
- * Helper class for security checks.
- */
-public final class SecurityHelper {
-
- private static final String REST_TEMPLATE =
- "https://digitalassetlinks.googleapis.com/v1/assetlinks:check?"
- + "source.web.site=%s&relation=delegate_permission/%s"
- + "&target.android_app.package_name=%s"
- + "&target.android_app.certificate.sha256_fingerprint=%s";
-
- private static final String PERMISSION_GET_LOGIN_CREDS = "common.get_login_creds";
- private static final String PERMISSION_HANDLE_ALL_URLS = "common.handle_all_urls";
-
- private SecurityHelper() {
- throw new UnsupportedOperationException("provides static methods only");
- }
-
- private static boolean isValidSync(String webDomain, String permission, String packageName,
- String fingerprint) {
- if (DEBUG) Log.d(TAG, "validating domain " + webDomain + " for pkg " + packageName
- + " and fingerprint " + fingerprint + " for permission" + permission);
- if (!webDomain.startsWith("http:") && !webDomain.startsWith("https:")) {
- // Unfortunately AssistStructure.ViewNode does not tell what the domain is, so let's
- // assume it's https
- webDomain = "https://" + webDomain;
- }
-
- String restUrl =
- String.format(REST_TEMPLATE, webDomain, permission, packageName, fingerprint);
- if (DEBUG) Log.d(TAG, "DAL REST request: " + restUrl);
-
- HttpURLConnection urlConnection = null;
- StringBuilder output = new StringBuilder();
- try {
- URL url = new URL(restUrl);
- urlConnection = (HttpURLConnection) url.openConnection();
- try (BufferedReader reader = new BufferedReader(
- new InputStreamReader(urlConnection.getInputStream()))) {
- String line = null;
- while ((line = reader.readLine()) != null) {
- output.append(line);
- }
- }
- String response = output.toString();
- if (VERBOSE) Log.v(TAG, "DAL REST Response: " + response);
-
- JSONObject jsonObject = new JSONObject(response);
- boolean valid = jsonObject.optBoolean("linked", false);
- if (DEBUG) Log.d(TAG, "Valid: " + valid);
-
- return valid;
- } catch (Exception e) {
- throw new RuntimeException("Failed to validate", e);
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- }
-
- }
-
- private static boolean isValidSync(String webDomain, String packageName, String fingerprint) {
- boolean isValid =
- isValidSync(webDomain, PERMISSION_GET_LOGIN_CREDS, packageName, fingerprint);
- if (!isValid) {
- // Ideally we should only check for the get_login_creds, but not all domains set
- // it yet, so validating for handle_all_urls gives a higher coverage.
- if (DEBUG) {
- Log.d(TAG, PERMISSION_GET_LOGIN_CREDS + " validation failed; trying "
- + PERMISSION_HANDLE_ALL_URLS);
- }
- isValid = isValidSync(webDomain, PERMISSION_HANDLE_ALL_URLS, packageName, fingerprint);
- }
- return isValid;
- }
-
- public static String getCanonicalDomain(String domain) {
- InternetDomainName idn = InternetDomainName.from(domain);
- while (idn != null && !idn.isTopPrivateDomain()) {
- idn = idn.parent();
- }
- return idn == null ? null : idn.toString();
- }
-
- public static boolean isValid(String webDomain, String packageName, String fingerprint) {
- String canonicalDomain = getCanonicalDomain(webDomain);
- if (DEBUG) Log.d(TAG, "validating domain " + canonicalDomain + " (" + webDomain
- + ") for pkg " + packageName + " and fingerprint " + fingerprint);
- final String fullDomain;
- if (!webDomain.startsWith("http:") && !webDomain.startsWith("https:")) {
- // Unfortunately AssistStructure.ViewNode does not tell what the domain is, so let's
- // assume it's https
- fullDomain = "https://" + canonicalDomain;
- } else {
- fullDomain = canonicalDomain;
- }
-
- // TODO: use the DAL Java API or a better REST alternative like Volley
- // and/or document it should not block until it returns (for example, the server could
- // start parsing the structure while it waits for the result.
- AsyncTask task = new AsyncTask() {
- @Override
- protected Boolean doInBackground(String... strings) {
- return isValidSync(fullDomain, packageName, fingerprint);
- }
- };
- try {
- return task.execute((String[]) null).get();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- Log.w(TAG, "Thread interrupted");
- } catch (Exception e) {
- Log.w(TAG, "Async task failed", e);
- }
- return false;
- }
-
- /**
- * Gets the fingerprint of the signed certificate of a package.
- */
- public static String getFingerprint(Context context, String packageName) throws Exception {
- PackageManager pm = context.getPackageManager();
- PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
- Signature[] signatures = packageInfo.signatures;
- if (signatures.length != 1) {
- throw new SecurityException(packageName + " has " + signatures.length + " signatures");
- }
- byte[] cert = signatures[0].toByteArray();
- try (InputStream input = new ByteArrayInputStream(cert)) {
- CertificateFactory factory = CertificateFactory.getInstance("X509");
- X509Certificate x509 = (X509Certificate) factory.generateCertificate(input);
- MessageDigest md = MessageDigest.getInstance("SHA256");
- byte[] publicKey = md.digest(x509.getEncoded());
- return toHexFormat(publicKey);
- }
- }
-
- private static String toHexFormat(byte[] bytes) {
- StringBuilder builder = new StringBuilder(bytes.length * 2);
- for (int i = 0; i < bytes.length; i++) {
- String hex = Integer.toHexString(bytes[i]);
- int length = hex.length();
- if (length == 1) {
- hex = "0" + hex;
- }
- if (length > 2) {
- hex = hex.substring(length - 2, length);
- }
- builder.append(hex.toUpperCase());
- if (i < (bytes.length - 1)) {
- builder.append(':');
- }
- }
- return builder.toString();
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/StructureParser.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/StructureParser.java
deleted file mode 100644
index 030780f9c7bb16af1c5bed69d8af6efa7b619eeb..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/StructureParser.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-import android.app.assist.AssistStructure;
-import android.app.assist.AssistStructure.ViewNode;
-import android.app.assist.AssistStructure.WindowNode;
-import android.content.Context;
-import android.util.Log;
-import android.view.autofill.AutofillValue;
-
-import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsDigitalAssetLinksRepository;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillField;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-
-import static com.example.android.autofillframework.CommonUtil.DEBUG;
-import static com.example.android.autofillframework.CommonUtil.TAG;
-
-/**
- * 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.
- */
-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;
- }
-
- public void parseForFill() {
- parse(true);
- }
-
- public void parseForSave() {
- parse(false);
- }
-
- /**
- * Traverse AssistStructure and add ViewNode metadata to a flat list.
- */
- private void parse(boolean forFill) {
- if (DEBUG) Log.d(TAG, "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(forFill, 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));
- }
- if (DEBUG) Log.d(TAG, "Domain " + webDomain + " is valid for " + packageName);
- } else {
- if (DEBUG) Log.d(TAG, "no web domain");
- }
- }
-
- private void parseLocked(boolean forFill, ViewNode viewNode, StringBuilder validWebDomain) {
- String webDomain = viewNode.getWebDomain();
- if (webDomain != null) {
- if (DEBUG) Log.d(TAG, "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) {
- if (forFill) {
- mAutofillFields.add(new AutofillFieldMetadata(viewNode));
- } else {
- FilledAutofillField filledAutofillField =
- new FilledAutofillField(viewNode.getAutofillHints());
- AutofillValue autofillValue = viewNode.getAutofillValue();
- if (autofillValue.isText()) {
- // Using toString of AutofillValue.getTextValue in order to save it to
- // SharedPreferences.
- filledAutofillField.setTextValue(autofillValue.getTextValue().toString());
- } else if (autofillValue.isDate()) {
- filledAutofillField.setDateValue(autofillValue.getDateValue());
- } else if (autofillValue.isList()) {
- filledAutofillField.setListValue(viewNode.getAutofillOptions(),
- autofillValue.getListValue());
- }
- mFilledAutofillFieldCollection.add(filledAutofillField);
- }
- }
- }
- int childrenSize = viewNode.getChildCount();
- if (childrenSize > 0) {
- for (int i = 0; i < childrenSize; i++) {
- parseLocked(forFill, viewNode.getChildAt(i), validWebDomain);
- }
- }
- }
-
- public AutofillFieldMetadataCollection getAutofillFields() {
- return mAutofillFields;
- }
-
- public FilledAutofillFieldCollection getClientFormData() {
- return mFilledAutofillFieldCollection;
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/W3cHints.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/W3cHints.java
deleted file mode 100644
index 14085544992b7c8877953e0083f220d41480ea3a..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/W3cHints.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice;
-
-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() {
- }
-}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java
deleted file mode 100644
index b92a7365df5b1b26891be39810138dc23205d82f..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice.datasource;
-
-import android.content.Context;
-
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-
-import java.util.HashMap;
-import java.util.List;
-
-public interface AutofillDataSource {
-
- /**
- * Gets saved FilledAutofillFieldCollection that contains some objects that can autofill fields
- * with these {@code autofillHints}.
- */
- HashMap getFilledAutofillFieldCollection(Context context,
- List focusedAutofillHints, List allAutofillHints);
-
- /**
- * Stores a collection of Autofill fields.
- */
- void saveFilledAutofillFieldCollection(Context context,
- FilledAutofillFieldCollection filledAutofillFieldCollection);
-
- /**
- * Clears all data.
- */
- void clear(Context context);
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java
deleted file mode 100644
index 91a1923e0fc4916c1a085743989eaf7709c36090..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice.datasource;
-
-import android.content.Context;
-import android.util.ArraySet;
-
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Singleton autofill data repository that stores autofill fields to SharedPreferences.
- *
- * Disclaimer : you should not store sensitive fields like user data unencrypted.
- * This is done here only for simplicity and learning purposes.
- */
-public class SharedPrefsAutofillRepository implements AutofillDataSource {
- private static final String SHARED_PREF_KEY = "com.example.android.autofillframework"
- + ".multidatasetservice.datasource.AutofillDataSource";
- private static final String CLIENT_FORM_DATA_KEY = "loginCredentialDatasets";
- private static final String DATASET_NUMBER_KEY = "datasetNumber";
- private static SharedPrefsAutofillRepository sInstance;
-
- private SharedPrefsAutofillRepository() {
- }
-
- public static SharedPrefsAutofillRepository getInstance() {
- if (sInstance == null) {
- sInstance = new SharedPrefsAutofillRepository();
- }
- return sInstance;
- }
-
- @Override
- public HashMap getFilledAutofillFieldCollection(
- Context context, List focusedAutofillHints, List allAutofillHints) {
- boolean hasDataForFocusedAutofillHints = false;
- HashMap clientFormDataMap = new HashMap<>();
- Set clientFormDataStringSet = getAllAutofillDataStringSet(context);
- for (String clientFormDataString : clientFormDataStringSet) {
- Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
- FilledAutofillFieldCollection filledAutofillFieldCollection =
- gson.fromJson(clientFormDataString, FilledAutofillFieldCollection.class);
- if (filledAutofillFieldCollection != null) {
- if (filledAutofillFieldCollection.helpsWithHints(focusedAutofillHints)) {
- // Saved data has data relevant to at least 1 of the hints associated with the
- // View in focus.
- hasDataForFocusedAutofillHints = true;
- }
- if (filledAutofillFieldCollection.helpsWithHints(allAutofillHints)) {
- // Saved data has data relevant to at least 1 of these hints associated with any
- // of the Views in the hierarchy.
- clientFormDataMap.put(filledAutofillFieldCollection.getDatasetName(),
- filledAutofillFieldCollection);
- }
- }
- }
- if (hasDataForFocusedAutofillHints) {
- return clientFormDataMap;
- } else {
- return null;
- }
- }
-
- @Override
- public void saveFilledAutofillFieldCollection(Context context,
- FilledAutofillFieldCollection filledAutofillFieldCollection) {
- String datasetName = "dataset-" + getDatasetNumber(context);
- filledAutofillFieldCollection.setDatasetName(datasetName);
- Set allAutofillData = getAllAutofillDataStringSet(context);
- Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
- allAutofillData.add(gson.toJson(filledAutofillFieldCollection));
- saveAllAutofillDataStringSet(context, allAutofillData);
- incrementDatasetNumber(context);
- }
-
- @Override
- public void clear(Context context) {
- context.getApplicationContext()
- .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
- .edit()
- .remove(CLIENT_FORM_DATA_KEY)
- .remove(DATASET_NUMBER_KEY)
- .apply();
- }
-
- private Set getAllAutofillDataStringSet(Context context) {
- return context.getApplicationContext()
- .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
- .getStringSet(CLIENT_FORM_DATA_KEY, new ArraySet());
- }
-
- private void saveAllAutofillDataStringSet(Context context,
- Set allAutofillDataStringSet) {
- context.getApplicationContext()
- .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
- .edit()
- .putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet)
- .apply();
- }
-
- /**
- * For simplicity, datasets will be named in the form "dataset-X" where X means
- * this was the Xth dataset saved.
- */
- private int getDatasetNumber(Context context) {
- return context.getApplicationContext()
- .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
- .getInt(DATASET_NUMBER_KEY, 0);
- }
-
- /**
- * Every time a dataset is saved, this should be called to increment the dataset number.
- * (only important for this service's dataset naming scheme).
- */
- private void incrementDatasetNumber(Context context) {
- context.getApplicationContext()
- .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
- .edit()
- .putInt(DATASET_NUMBER_KEY, getDatasetNumber(context) + 1)
- .apply();
- }
-}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsDigitalAssetLinksRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsDigitalAssetLinksRepository.java
deleted file mode 100644
index a8124a1c446aed2cd98cfc0bb7f718669511053c..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsDigitalAssetLinksRepository.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice.datasource;
-
-import android.content.Context;
-import android.util.Log;
-
-import com.example.android.autofillframework.multidatasetservice.SecurityHelper;
-
-import static com.example.android.autofillframework.CommonUtil.TAG;
-
-/**
- * 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.
-
- String fingerprint = null;
- try {
- fingerprint = SecurityHelper.getFingerprint(context, packageName);
- } catch (Exception e) {
- Log.w(TAG, "error getting fingerprint for " + packageName, e);
- return false;
- }
- return SecurityHelper.isValid(webDomain, packageName, fingerprint);
- }
-
- @Override
- public void clear(Context context) {
- // TODO: implement once if caches results or remove from the interface
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java
deleted file mode 100644
index aa467786ed0e9f769b82dd9cdb2eb55f1349616d..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice.datasource;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-
-import com.example.android.autofillframework.multidatasetservice.SecurityHelper;
-
-import static com.example.android.autofillframework.CommonUtil.TAG;
-
-public class SharedPrefsPackageVerificationRepository implements PackageVerificationDataSource {
-
- private static final String SHARED_PREF_KEY = "com.example.android.autofillframework"
- + ".multidatasetservice.datasource.PackageVerificationDataSource";
- private static PackageVerificationDataSource sInstance;
-
- private SharedPrefsPackageVerificationRepository() {
- }
-
- public static PackageVerificationDataSource getInstance() {
- if (sInstance == null) {
- sInstance = new SharedPrefsPackageVerificationRepository();
- }
- return sInstance;
- }
-
- @Override
- public void clear(Context context) {
- context.getApplicationContext().getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
- .edit()
- .clear()
- .apply();
- }
-
- @Override
- public boolean putPackageSignatures(Context context, String packageName) {
- String hash;
- try {
- hash = SecurityHelper.getFingerprint(context, packageName);
- Log.d(TAG, "Hash for " + packageName + ": " + hash);
- } catch (Exception e) {
- Log.w(TAG, "Error getting hash for " + packageName + ": " + e);
- return false;
- }
-
- if (!containsSignatureForPackage(context, packageName)) {
- // Storage does not yet contain signature for this package name.
- context.getApplicationContext()
- .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
- .edit()
- .putString(packageName, hash)
- .apply();
- return true;
- }
- return containsMatchingSignatureForPackage(context, packageName, hash);
- }
-
- private boolean containsSignatureForPackage(Context context, String packageName) {
- SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
- SHARED_PREF_KEY, Context.MODE_PRIVATE);
- return prefs.contains(packageName);
- }
-
- private boolean containsMatchingSignatureForPackage(Context context, String packageName,
- String hash) {
- SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
- SHARED_PREF_KEY, Context.MODE_PRIVATE);
- return hash.equals(prefs.getString(packageName, null));
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillField.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillField.java
deleted file mode 100644
index dfaa1e7816a0e1ccd888bad255ccb5eeba942ec6..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillField.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice.model;
-
-import android.util.Log;
-import android.view.View;
-import android.view.autofill.AutofillValue;
-
-import com.example.android.autofillframework.multidatasetservice.AutofillHints;
-import com.google.common.base.Preconditions;
-import com.google.gson.annotations.Expose;
-
-import java.util.Arrays;
-
-import static com.example.android.autofillframework.CommonUtil.TAG;
-import static com.example.android.autofillframework.multidatasetservice.AutofillHints.convertToStoredHintNames;
-import static com.example.android.autofillframework.multidatasetservice.AutofillHints.filterForSupportedHints;
-
-/**
- * JSON serializable data class containing the same data as an {@link AutofillValue}.
- */
-public class FilledAutofillField {
- @Expose
- private String mTextValue = null;
- @Expose
- private Long mDateValue = null;
- @Expose
- private Boolean mToggleValue = null;
-
- /**
- * 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(TAG, "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;
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java
deleted file mode 100644
index 05956ec9489c73f8fffce3225b5c542f81fd1ea9..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice.model;
-
-import android.service.autofill.Dataset;
-import android.support.annotation.NonNull;
-import android.util.Log;
-import android.view.View;
-import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillValue;
-
-import com.example.android.autofillframework.multidatasetservice.AutofillFieldMetadata;
-import com.example.android.autofillframework.multidatasetservice.AutofillFieldMetadataCollection;
-import com.example.android.autofillframework.multidatasetservice.AutofillHints;
-import com.example.android.autofillframework.multidatasetservice.W3cHints;
-import com.google.gson.annotations.Expose;
-
-import java.util.HashMap;
-import java.util.List;
-
-import static com.example.android.autofillframework.CommonUtil.DEBUG;
-import static com.example.android.autofillframework.CommonUtil.TAG;
-
-/**
- * FilledAutofillFieldCollection is the model that holds all of the data on a client app's page,
- * plus the dataset name associated with it.
- */
-public final class FilledAutofillFieldCollection {
- @Expose
- private final HashMap mHintMap;
- @Expose
- private String mDatasetName;
-
- public FilledAutofillFieldCollection() {
- this(null, new HashMap());
- }
-
- public FilledAutofillFieldCollection(String datasetName, HashMap 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(TAG, "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];
- if (DEBUG) Log.d(TAG, "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++;
- if (DEBUG) Log.d(TAG, "Hint is a W3C type prefix; using " + hint + " instead");
- }
- if (isW3cAddressType(hint) && nextHint != null) {
- hint = nextHint;
- i++;
- if (DEBUG) Log.d(TAG, "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(TAG, "Invalid hint: " + autofillHints[i]);
- }
- }
- }
-
- /**
- * Populates a {@link Dataset.Builder} with appropriate values for each {@link AutofillId}
- * in a {@code AutofillFieldMetadataCollection}.
- *
- * 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 allHints = autofillFieldMetadataCollection.getAllHints();
- for (int hintIndex = 0; hintIndex < allHints.size(); hintIndex++) {
- String hint = allHints.get(hintIndex);
- List 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(TAG, "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 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;
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
deleted file mode 100644
index 6ca59601b03d17b5c8c2feb055283e754af4c838..0000000000000000000000000000000000000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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.example.android.autofillframework.multidatasetservice.settings;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.NumberPicker;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.multidatasetservice.AutofillHints;
-import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository;
-import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsPackageVerificationRepository;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-
-public class SettingsActivity extends AppCompatActivity {
- private static final String TAG = "SettingsActivity";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.multidataset_service_settings_activity);
- final MyPreferences preferences = MyPreferences.getInstance(this);
- setupSettingsSwitch(R.id.settings_auth_responses_container,
- R.id.settings_auth_responses_label,
- R.id.settings_auth_responses_switch,
- preferences.isResponseAuth(),
- (compoundButton, isResponseAuth) -> preferences.setResponseAuth(isResponseAuth));
- setupSettingsSwitch(R.id.settings_auth_datasets_container,
- R.id.settings_auth_datasets_label,
- R.id.settings_auth_datasets_switch,
- preferences.isDatasetAuth(),
- (compoundButton, isDatasetAuth) -> preferences.setDatasetAuth(isDatasetAuth));
- setupSettingsButton(R.id.settings_add_data_container,
- R.id.settings_add_data_label,
- R.id.settings_add_data_icon,
- (view) -> buildAddDataDialog().show());
- setupSettingsButton(R.id.settings_clear_data_container,
- R.id.settings_clear_data_label,
- R.id.settings_clear_data_icon,
- (view) -> buildClearDataDialog().show());
- setupSettingsButton(R.id.settings_auth_credentials_container,
- R.id.settings_auth_credentials_label,
- R.id.settings_auth_credentials_icon,
- (view) -> {
- if (preferences.getMasterPassword() != null) {
- buildCurrentCredentialsDialog().show();
- } else {
- buildNewCredentialsDialog().show();
- }
- });
- }
-
- private AlertDialog buildClearDataDialog() {
- return new AlertDialog.Builder(SettingsActivity.this)
- .setMessage(R.string.settings_clear_data_confirmation)
- .setTitle(R.string.settings_clear_data_confirmation_title)
- .setNegativeButton(R.string.cancel, null)
- .setPositiveButton(R.string.ok, (dialog, which) -> {
- SharedPrefsAutofillRepository.getInstance().clear(SettingsActivity.this);
- SharedPrefsPackageVerificationRepository.getInstance()
- .clear(SettingsActivity.this);
- MyPreferences.getInstance(SettingsActivity.this).clearCredentials();
- dialog.dismiss();
- })
- .create();
- }
-
- private AlertDialog buildAddDataDialog() {
- NumberPicker numberOfDatasetsPicker = LayoutInflater
- .from(SettingsActivity.this)
- .inflate(R.layout.multidataset_service_settings_add_data_dialog, null)
- .findViewById(R.id.number_of_datasets_picker);
- numberOfDatasetsPicker.setMinValue(0);
- numberOfDatasetsPicker.setMaxValue(10);
- numberOfDatasetsPicker.setWrapSelectorWheel(false);
- return new AlertDialog.Builder(SettingsActivity.this)
- .setTitle(R.string.settings_add_data_title)
- .setNegativeButton(R.string.cancel, null)
- .setMessage(R.string.settings_select_number_of_datasets)
- .setView(numberOfDatasetsPicker)
- .setPositiveButton(R.string.ok, (dialog, which) -> {
- int numOfDatasets = numberOfDatasetsPicker.getValue();
- boolean success = buildAndSaveMockedAutofillFieldCollection(
- SettingsActivity.this, numOfDatasets);
- dialog.dismiss();
- if (success) {
- Snackbar.make(SettingsActivity.this.findViewById(R.id.settings_layout),
- SettingsActivity.this.getResources().getQuantityString(
- R.plurals.settings_add_data_success, numOfDatasets,
- numOfDatasets),
- Snackbar.LENGTH_SHORT).show();
- }
- })
- .create();
- }
-
- /**
- * Builds mock autofill data and saves it to repository.
- */
- private boolean buildAndSaveMockedAutofillFieldCollection(Context context, int numOfDatasets) {
- if (numOfDatasets < 0 || numOfDatasets > 10) {
- Log.w(TAG, "Number of Datasets out of range.");
- return false;
- }
- for (int i = 0; i < numOfDatasets * 2; i += 2) {
- for (int partition : AutofillHints.PARTITIONS) {
- FilledAutofillFieldCollection filledAutofillFieldCollection =
- AutofillHints.getFakeFieldCollection(partition, i);
- SharedPrefsAutofillRepository.getInstance().saveFilledAutofillFieldCollection(
- context, filledAutofillFieldCollection);
- }
- }
- return true;
- }
-
- private AlertDialog.Builder prepareCredentialsDialog() {
- return new AlertDialog.Builder(SettingsActivity.this)
- .setTitle(R.string.settings_auth_change_credentials_title)
- .setNegativeButton(R.string.cancel, null);
- }
-
- private AlertDialog buildCurrentCredentialsDialog() {
- final EditText currentPasswordField = LayoutInflater
- .from(SettingsActivity.this)
- .inflate(R.layout.multidataset_service_settings_authentication_dialog, null)
- .findViewById(R.id.master_password_field);
- return prepareCredentialsDialog()
- .setMessage(R.string.settings_auth_enter_current_password)
- .setView(currentPasswordField)
- .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String password = currentPasswordField.getText().toString();
- if (MyPreferences.getInstance(SettingsActivity.this).getMasterPassword()
- .equals(password)) {
- buildNewCredentialsDialog().show();
- dialog.dismiss();
- }
- }
- })
- .create();
- }
-
- private AlertDialog buildNewCredentialsDialog() {
- final EditText newPasswordField = LayoutInflater
- .from(SettingsActivity.this)
- .inflate(R.layout.multidataset_service_settings_authentication_dialog, null)
- .findViewById(R.id.master_password_field);
- return prepareCredentialsDialog()
- .setMessage(R.string.settings_auth_enter_new_password)
- .setView(newPasswordField)
- .setPositiveButton(R.string.ok, (dialog, which) -> {
- String password = newPasswordField.getText().toString();
- MyPreferences.getInstance(SettingsActivity.this).setMasterPassword(password);
- dialog.dismiss();
- })
- .create();
- }
-
- private void setupSettingsSwitch(int containerId, int labelId, int switchId, boolean checked,
- CompoundButton.OnCheckedChangeListener checkedChangeListener) {
- ViewGroup container = findViewById(containerId);
- String switchLabel = ((TextView) container.findViewById(labelId)).getText().toString();
- final Switch switchView = container.findViewById(switchId);
- switchView.setContentDescription(switchLabel);
- switchView.setChecked(checked);
- container.setOnClickListener((view) -> switchView.performClick());
- switchView.setOnCheckedChangeListener(checkedChangeListener);
- }
-
- private void setupSettingsButton(int containerId, int labelId, int imageViewId,
- final View.OnClickListener onClickListener) {
- ViewGroup container = findViewById(containerId);
- TextView buttonLabel = container.findViewById(labelId);
- String buttonLabelText = buttonLabel.getText().toString();
- ImageView imageView = container.findViewById(imageViewId);
- imageView.setContentDescription(buttonLabelText);
- container.setOnClickListener(onClickListener);
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_autocomplete_logo_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_autocomplete_logo_24dp.xml
index 80f30a2f520665ab194da914d9ab0c5f4ef19075..18dc280b8ec88438d1da53694b77206f16e3caab 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_autocomplete_logo_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_autocomplete_logo_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_custom_virtual_logo_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_custom_virtual_logo_24dp.xml
index 3a8ee3b2d042a4b8c83281d2ee4d64f9cde19172..f230e2680b09f811c40a54fab979d24290f149fd 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_custom_virtual_logo_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_custom_virtual_logo_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M13,7h-2v2h2L13,7zM13,11h-2v2h2v-2zM17,11h-2v2h2v-2zM3,3v18h18L21,3L3,3zM19,19L5,19L5,5h14v14zM13,15h-2v2h2v-2zM9,11L7,11v2h2v-2z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_disabled_black_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_disabled_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..60e4cbd38f40d5f274de2003b4a76e09063b18bb
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_disabled_black_24dp.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_edittexts_logo_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_edittexts_logo_24dp.xml
index 17e403d9285267c8ddb8b2cd8202c1fefd698b81..b260df239ec46b7b1047ac10197b33013663e4f6 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_edittexts_logo_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_edittexts_logo_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M4,9h16v2L4,11zM4,13h10v2L4,15z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_email_black_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_email_black_24dp.xml
index 174c1272545aee32679aa9a8abdfdc075e459f63..2c31d6861f49859496afe3ef3fbf3bc57ce262bc 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_email_black_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_email_black_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_info_black_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_info_black_24dp.xml
index c297121b9ece211313fec3f42ba5a24ac2b93c89..a3dba1d9fe18d5ee2105317ba40f1a38873010be 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_info_black_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_info_black_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_person_black_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_person_black_24dp.xml
index 6534d9f0ec4a2ecb5fee31815134e0202cc5b171..032db1299bd28b612630c3fac06e48c94f48a2d1 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_person_black_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_person_black_24dp.xml
@@ -13,8 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
-
+
+
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_send_white_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_send_white_24dp.xml
index f614267aea99b05c7b64f6fff8f39d9026617626..5e92b26679cf275e43675012fc26635b9a8200d2 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_send_white_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_send_white_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_spinners_logo_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_spinners_logo_24dp.xml
index 5fb27a24a6ca393c03ad9ac1e6a882b28ddfe0cf..ea91d03150acf9eb35af6e3342a2a4c6484b2d20 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_spinners_logo_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_spinners_logo_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M9,11L7,11v2h2v-2zM13,11h-2v2h2v-2zM17,11h-2v2h2v-2zM19,4h-1L18,2h-2v2L8,4L8,2L6,2v2L5,4c-1.11,0 -1.99,0.9 -1.99,2L3,20c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM19,20L5,20L5,9h14v11z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_view_module_black_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_view_module_black_24dp.xml
index ab36b07663454c718b2be5c464b564478de26970..bb739bb31c18c0caea582a116fa07433b8933e9f 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_view_module_black_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_view_module_black_24dp.xml
@@ -1,9 +1,9 @@
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M4,11h5L9,5L4,5v6zM4,18h5v-6L4,12v6zM10,18h5v-6h-5v6zM16,18h5v-6h-5v6zM10,11h5L15,5h-5v6zM16,5v6h5L21,5h-5z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_web_black_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_web_black_24dp.xml
index 48e7b7bab02058a5f5f36fe339caf0f83b77390a..d5e35bfd035b29ab912444c2f1b04b6cc828213d 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_web_black_24dp.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_web_black_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM15,18L4,18v-4h11v4zM15,13L4,13L4,9h11v4zM20,18h-4L16,9h4v9z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/activity_main.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/activity_main.xml
index 687308ea0d7465aaff8d07e1ff00e93514870f10..ece13e9d03ae7c19284f77a04fd007d829e98348 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/activity_main.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/activity_main.xml
@@ -13,118 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
+ app:tabMode="fixed" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/cc_exp_date.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/cc_exp_date.xml
index a4dd5377215a08b8794f1dbd3a4a8193d0fa099b..aebe9f833082345a111b4560288792a3f6fac9cd 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/cc_exp_date.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/cc_exp_date.xml
@@ -14,7 +14,7 @@
* limitations under the License.
-->
+ xmlns:app="http://schemas.android.com/apk/res-auto">
-
-
-
-
-
-
-
-
-
-
@@ -124,8 +123,8 @@
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:autofillHints="creditCardSecurityCode"
- android:inputType="number"
android:ems="6"
+ android:inputType="number"
app:layout_constraintBottom_toBottomOf="@+id/creditCardSecurityCodeLabel"
app:layout_constraintStart_toEndOf="@+id/creditCardSecurityCodeLabel"
app:layout_constraintTop_toTopOf="@+id/creditCardSecurityCodeLabel" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_spinners_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_spinners_activity.xml
index 1034ca2cbd4e2f24af9e17e9c3e250a78b4ac135..6c1bfef45c21c1ce226c458c77cf502e449ea110 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_spinners_activity.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_spinners_activity.xml
@@ -1,5 +1,4 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/fragment_edge_cases.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/fragment_edge_cases.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ab7c32aa623f007b851dc336e296bcbdbb19e2c4
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/fragment_edge_cases.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml
index ce70e1d006f9133556de72dd99521f2a9c8dc39a..1cb4232a42635d185c2b85f7e5a7b662498b6536 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml
@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
-
\ No newline at end of file
+ android:id="@+id/webview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/login_with_autocomplete_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/login_with_autocomplete_activity.xml
index 23375da96700ce87e5e66e4fbb29b2f65ba25ead..d9f89e7df2e587562d0fda951215a7944030b133 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/login_with_autocomplete_activity.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/login_with_autocomplete_activity.xml
@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/multiple_partitions_header" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/navigation_button.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/navigation_button.xml
index e4c91ec09d28e00b34e3336252f889155923bf3b..84e27be448d913469aedd9c77c22c77f5a5bef6a 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/navigation_button.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/navigation_button.xml
@@ -1,5 +1,4 @@
-
-
+ xmlns:tools="http://schemas.android.com/tools">
-
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
-
-
-
-
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/welcome_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/welcome_activity.xml
index 60d6e1fcb21a0dbbf465fa295bd9338b7cfdc1e7..a400a84c8e785a22a524c89d6e5b3da87b44407b 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/welcome_activity.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/welcome_activity.xml
@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/values/attrs.xml b/input/autofill/AutofillFramework/Application/src/main/res/values/attrs.xml
index 23bf5cff3de4234f4c075f1e8cee5c6b86bc4f45..bfadd1a1a71f35ef0812d825da30d0b69c5449ea 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/values/attrs.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/values/attrs.xml
@@ -1,5 +1,4 @@
-
-
+
+
+
+
+
+
Credentials
Credit Card
@@ -155,10 +138,24 @@
INVALID
Representing expiration dates as %1$s
+ Finished
+ Showing step %1$d
+ %1$s: %2$s
+
+ Prev
+ Next
+ Finish
+
- Automatically return to main page in %d second.
- Automatically return to main page in %d seconds.
+
+
+ - user-1
+ - user-2
+
+
- 1
- 2
@@ -204,9 +201,4 @@
- 27
-
- - user-1
- - user-2
-
-
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/values/styles.xml b/input/autofill/AutofillFramework/Application/src/main/res/values/styles.xml
index c44f9991adc6d062e31cc09935232e0bde45e7ec..39e5ba88e4c8d6ee2c8f77410651b41e93469c81 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/values/styles.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/values/styles.xml
@@ -15,33 +15,6 @@
-->
-
-
-
-
-
-
-
-
diff --git a/input/autofill/AutofillFramework/afservice/.gitignore b/input/autofill/AutofillFramework/afservice/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..796b96d1c402326528b4ba3c12ee9d92d0e212e9
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/input/autofill/AutofillFramework/afservice/build.gradle b/input/autofill/AutofillFramework/afservice/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..f61420770357e38858a08ae45f2edb977fd31455
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/build.gradle
@@ -0,0 +1,51 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion "android-P"
+
+ defaultConfig {
+ minSdkVersion 26
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ debug {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation 'com.android.support:appcompat-v7:28.0.0-alpha1'
+ implementation "android.arch.persistence.room:runtime:1.0.0"
+ annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'com.android.support:design:28.0.0-alpha1'
+ implementation 'com.android.support.constraint:constraint-layout:1.0.2'
+ implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
+ implementation 'com.squareup.retrofit2:retrofit:2.3.0'
+ implementation group: 'com.google.guava', name: 'guava', version: '22.0-android'
+ implementation "com.android.support.test.espresso:espresso-idling-resource:3.0.1"
+ implementation "com.google.code.findbugs:jsr305:3.0.2"
+
+ androidTestImplementation "junit:junit:4.12"
+ androidTestImplementation ("com.android.support.test.espresso:espresso-core:3.0.1")
+ androidTestImplementation "com.android.support.test.espresso:espresso-contrib:3.0.1"
+ androidTestImplementation "com.android.support.test.espresso:espresso-intents:3.0.1"
+ androidTestImplementation "com.android.support.test.espresso.idling:idling-concurrent:3.0.1"
+
+}
diff --git a/input/autofill/AutofillFramework/afservice/proguard-rules.pro b/input/autofill/AutofillFramework/afservice/proguard-rules.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a313042ca3a7198bccdb25e7c101c6b2211afee9
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/proguard-rules.pro
@@ -0,0 +1,24 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+-keep class com.google.common.base.Preconditions { *; }
+-keep class android.arch.** { *; }
+-keep com.example.android.autofill.service.** { *; }
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/AutofillDaoTest.java b/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/AutofillDaoTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c76ba435b64e5f8d505d51bca9cd4b1edf7960af
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/AutofillDaoTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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.example.android.autofill.service.data.source.local;
+
+import android.arch.persistence.room.Room;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.model.AutofillDataset;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.google.common.collect.ImmutableList;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.hasSize;
+
+@RunWith(AndroidJUnit4.class)
+public class AutofillDaoTest {
+ private final AutofillDataset mDataset =
+ new AutofillDataset(UUID.randomUUID().toString(),
+ "dataset-1", InstrumentationRegistry.getContext().getPackageName());
+ private final FilledAutofillField mUsernameField =
+ new FilledAutofillField(mDataset.getId(), View.AUTOFILL_HINT_USERNAME, "login");
+ private final FilledAutofillField mPasswordField =
+ new FilledAutofillField(mDataset.getId(), View.AUTOFILL_HINT_PASSWORD, "password");
+
+ private AutofillDatabase mDatabase;
+
+ @Before
+ public void setup() {
+ // using an in-memory database because the information stored here disappears when the
+ // process is killed
+ mDatabase = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),
+ AutofillDatabase.class).build();
+
+ }
+
+ @After
+ public void closeDb() {
+ mDatabase.close();
+ }
+
+ @Test
+ public void insertFilledAutofillFieldAndGet() {
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields =
+ new DatasetWithFilledAutofillFields();
+ datasetWithFilledAutofillFields.autofillDataset = mDataset;
+ datasetWithFilledAutofillFields.filledAutofillFields =
+ Arrays.asList(mUsernameField, mPasswordField);
+ datasetWithFilledAutofillFields.filledAutofillFields
+ .sort(Comparator.comparing(FilledAutofillField::getFieldTypeName));
+
+ // When inserting a page's autofill fields.
+ mDatabase.autofillDao().insertAutofillDataset(mDataset);
+ mDatabase.autofillDao().insertFilledAutofillFields(
+ datasetWithFilledAutofillFields.filledAutofillFields);
+
+ // Represents all hints of all fields on page.
+ List allHints = ImmutableList.of(View.AUTOFILL_HINT_USERNAME,
+ View.AUTOFILL_HINT_PASSWORD);
+ List loadedDatasets = mDatabase.autofillDao()
+ .getDatasets(allHints);
+ loadedDatasets.get(0).filledAutofillFields.sort(
+ Comparator.comparing(FilledAutofillField::getFieldTypeName));
+ assertThat(loadedDatasets, contains(datasetWithFilledAutofillFields));
+ assertThat(loadedDatasets, hasSize(1));
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/LocalDataSourceTest.java b/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/LocalDataSourceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f445c541f33e965f25c34b0c1d8cf0b1abbfca5
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/LocalDataSourceTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.example.android.autofill.service.data.source.local;
+
+import android.arch.persistence.room.Room;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.util.SingleExecutors;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LocalDataSourceTest {
+
+ private LocalAutofillDataSource mLocalDataSource;
+ private AutofillDatabase mDatabase;
+
+ @Before
+ public void setup() {
+ // using an in-memory database for testing, since it doesn't survive killing the process
+ mDatabase = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),
+ AutofillDatabase.class)
+ .build();
+ AutofillDao tasksDao = mDatabase.autofillDao();
+ SharedPreferences sharedPreferences = InstrumentationRegistry.getContext()
+ .getSharedPreferences(LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ // Make sure that we're not keeping a reference to the wrong instance.
+ LocalAutofillDataSource.clearInstance();
+ mLocalDataSource = LocalAutofillDataSource.getInstance(sharedPreferences,
+ tasksDao, new SingleExecutors());
+ }
+
+ @After
+ public void cleanUp() {
+ try {
+ mDatabase.close();
+ } finally {
+ LocalAutofillDataSource.clearInstance();
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/util/SingleExecutors.java b/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/util/SingleExecutors.java
new file mode 100644
index 0000000000000000000000000000000000000000..baac2b1962e6702207e1190097d162da0f775d8d
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/util/SingleExecutors.java
@@ -0,0 +1,30 @@
+/*
+ * 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.example.android.autofill.service.util;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Allow instant execution of tasks.
+ */
+public final class SingleExecutors extends AppExecutors {
+ private static Executor sInstance = Runnable::run;
+
+ public SingleExecutors() {
+ super(sInstance, sInstance, sInstance);
+ }
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b02a591d5e1369e7247a88eb764816869c6c5f8a
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AuthActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AuthActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1660b7c43bc53a118d09f826feaa43d3af0a30e
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AuthActivity.java
@@ -0,0 +1,227 @@
+/*
+ * 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.example.android.autofill.service;
+
+import android.app.PendingIntent;
+import android.app.assist.AssistStructure;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.text.Editable;
+import android.widget.EditText;
+import android.widget.RemoteViews;
+import android.widget.Toast;
+
+import com.example.android.autofill.service.data.ClientViewMetadata;
+import com.example.android.autofill.service.data.ClientViewMetadataBuilder;
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.adapter.DatasetAdapter;
+import com.example.android.autofill.service.data.adapter.ResponseAdapter;
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource;
+import com.example.android.autofill.service.data.source.local.DigitalAssetLinksRepository;
+import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.settings.MyPreferences;
+import com.example.android.autofill.service.util.AppExecutors;
+import com.google.gson.GsonBuilder;
+
+import java.util.HashMap;
+import java.util.List;
+
+import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE;
+import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT;
+import static com.example.android.autofill.service.util.Util.EXTRA_DATASET_NAME;
+import static com.example.android.autofill.service.util.Util.EXTRA_FOR_RESPONSE;
+import static com.example.android.autofill.service.util.Util.logw;
+
+
+/**
+ * This Activity controls the UI for logging in to the Autofill service.
+ * It is launched when an Autofill Response or specific Dataset within the Response requires
+ * authentication to access. It bundles the result in an Intent.
+ */
+public class AuthActivity extends AppCompatActivity {
+
+ // Unique id for dataset intents.
+ private static int sDatasetPendingIntentId = 0;
+
+ private LocalAutofillDataSource mLocalAutofillDataSource;
+ private DigitalAssetLinksRepository mDalRepository;
+ private EditText mMasterPassword;
+ private DatasetAdapter mDatasetAdapter;
+ private ResponseAdapter mResponseAdapter;
+ private ClientViewMetadata mClientViewMetadata;
+ private String mPackageName;
+ private Intent mReplyIntent;
+ private MyPreferences mPreferences;
+
+ public static IntentSender getAuthIntentSenderForResponse(Context context) {
+ final Intent intent = new Intent(context, AuthActivity.class);
+ return PendingIntent.getActivity(context, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
+ }
+
+ public static IntentSender getAuthIntentSenderForDataset(Context originContext,
+ String datasetName) {
+ Intent intent = new Intent(originContext, AuthActivity.class);
+ intent.putExtra(EXTRA_DATASET_NAME, datasetName);
+ intent.putExtra(EXTRA_FOR_RESPONSE, false);
+ return PendingIntent.getActivity(originContext, ++sDatasetPendingIntentId, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.multidataset_service_auth_activity);
+ SharedPreferences sharedPreferences =
+ getSharedPreferences(LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ DefaultFieldTypesSource defaultFieldTypesSource =
+ DefaultFieldTypesLocalJsonSource.getInstance(getResources(),
+ new GsonBuilder().create());
+ AutofillDao autofillDao = AutofillDatabase.getInstance(this,
+ defaultFieldTypesSource, new AppExecutors()).autofillDao();
+ mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(sharedPreferences,
+ autofillDao, new AppExecutors());
+ mDalRepository = DigitalAssetLinksRepository.getInstance(getPackageManager());
+ mMasterPassword = findViewById(R.id.master_password);
+ mPackageName = getPackageName();
+ mPreferences = MyPreferences.getInstance(this);
+ findViewById(R.id.login).setOnClickListener((view) -> login());
+ findViewById(R.id.cancel).setOnClickListener((view) -> {
+ onFailure();
+ AuthActivity.this.finish();
+ });
+ }
+
+ private void login() {
+ Editable password = mMasterPassword.getText();
+ String correctPassword = MyPreferences.getInstance(AuthActivity.this).getMasterPassword();
+ if (password.toString().equals(correctPassword)) {
+ onSuccess();
+ } else {
+ Toast.makeText(this, "Password incorrect", Toast.LENGTH_SHORT).show();
+ onFailure();
+ }
+ }
+
+ @Override
+ public void finish() {
+ if (mReplyIntent != null) {
+ setResult(RESULT_OK, mReplyIntent);
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+ super.finish();
+ }
+
+ private void onFailure() {
+ logw("Failed auth.");
+ mReplyIntent = null;
+ }
+
+ private void onSuccess() {
+ Intent intent = getIntent();
+ boolean forResponse = intent.getBooleanExtra(EXTRA_FOR_RESPONSE, true);
+ AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
+ ClientParser clientParser = new ClientParser(structure);
+ mReplyIntent = new Intent();
+ mLocalAutofillDataSource.getFieldTypeByAutofillHints(
+ new DataCallback>() {
+ @Override
+ public void onLoaded(HashMap fieldTypesByAutofillHint) {
+ ClientViewMetadataBuilder builder = new ClientViewMetadataBuilder(clientParser,
+ fieldTypesByAutofillHint);
+ mClientViewMetadata = builder.buildClientViewMetadata();
+ mDatasetAdapter = new DatasetAdapter(clientParser);
+ mResponseAdapter = new ResponseAdapter(AuthActivity.this,
+ mClientViewMetadata, mPackageName, mDatasetAdapter);
+ if (forResponse) {
+ fetchAllDatasetsAndSetIntent(fieldTypesByAutofillHint);
+ } else {
+ String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME);
+ fetchDatasetAndSetIntent(fieldTypesByAutofillHint, datasetName);
+ }
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+ private void fetchDatasetAndSetIntent(
+ HashMap fieldTypesByAutofillHint, String datasetName) {
+ mLocalAutofillDataSource.getAutofillDataset(mClientViewMetadata.getAllHints(),
+ datasetName, new DataCallback() {
+ @Override
+ public void onLoaded(DatasetWithFilledAutofillFields dataset) {
+ String datasetName = dataset.autofillDataset.getDatasetName();
+ RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(
+ mPackageName, datasetName);
+ setDatasetIntent(mDatasetAdapter.buildDataset(fieldTypesByAutofillHint,
+ dataset, remoteViews));
+ finish();
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+ logw(msg, params);
+ finish();
+ }
+ });
+ }
+
+ private void fetchAllDatasetsAndSetIntent(
+ HashMap fieldTypesByAutofillHint) {
+ mLocalAutofillDataSource.getAutofillDatasets(mClientViewMetadata.getAllHints(),
+ new DataCallback>() {
+ @Override
+ public void onLoaded(List datasets) {
+ boolean datasetAuth = mPreferences.isDatasetAuth();
+ FillResponse fillResponse = mResponseAdapter.buildResponse(
+ fieldTypesByAutofillHint, datasets, datasetAuth);
+ setResponseIntent(fillResponse);
+ finish();
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+ logw(msg, params);
+ finish();
+ }
+ });
+ }
+
+ private void setResponseIntent(FillResponse fillResponse) {
+ mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);
+ }
+
+ private void setDatasetIntent(Dataset dataset) {
+ mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHintProperties.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHintProperties.java
similarity index 84%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHintProperties.java
rename to input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHintProperties.java
index 5a793774136e23c8df3669c76891ce79e94efd4e..8314eb304cda6aea22b2b35d2870b0cb337b6aec 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHintProperties.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHintProperties.java
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.multidatasetservice;
+package com.example.android.autofill.service;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillField;
+import android.view.View;
+
+import com.example.android.autofill.service.model.FilledAutofillField;
import java.util.Arrays;
import java.util.HashSet;
@@ -44,8 +46,8 @@ public final class AutofillHintProperties {
/**
* Generates dummy autofill field data that is relevant to the autofill hint.
*/
- public FilledAutofillField generateFakeField(int seed) {
- return mFakeFieldGenerator.generate(seed);
+ public FilledAutofillField generateFakeField(int seed, String datasetId) {
+ return mFakeFieldGenerator.generate(seed, datasetId);
}
/**
@@ -77,10 +79,14 @@ public final class AutofillHintProperties {
/**
* 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}.
+ * with the hint {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}, should
+ * only be stored as {@link View#AUTOFILL_TYPE_DATE}.
*/
public boolean isValidType(int type) {
return mValidTypes.contains(type);
}
+
+ public Set getTypes() {
+ return mValidTypes;
+ }
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHints.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHints.java
new file mode 100644
index 0000000000000000000000000000000000000000..d43071f203a8ebb1750ba456b9eadc92f296a687
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHints.java
@@ -0,0 +1,175 @@
+/*
+ * 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.example.android.autofill.service;
+
+import android.support.annotation.NonNull;
+
+import com.example.android.autofill.service.model.FakeData;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+import static com.example.android.autofill.service.util.Util.logd;
+import static com.example.android.autofill.service.util.Util.logw;
+import static java.util.stream.Collectors.toList;
+
+public final class AutofillHints {
+ public static final int PARTITION_ALL = -1;
+ 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
+ };
+
+ private AutofillHints() {
+ }
+
+ public static FilledAutofillField generateFakeField(
+ FieldTypeWithHeuristics fieldTypeWithHeuristics, String packageName, int seed,
+ String datasetId) {
+ FakeData fakeData = fieldTypeWithHeuristics.fieldType.getFakeData();
+ String fieldTypeName = fieldTypeWithHeuristics.fieldType.getTypeName();
+ String text = null;
+ Long date = null;
+ Boolean toggle = null;
+ if (fakeData.strictExampleSet != null && fakeData.strictExampleSet.strings != null &&
+ fakeData.strictExampleSet.strings.size() > 0 &&
+ !fakeData.strictExampleSet.strings.get(0).isEmpty()) {
+ List choices = fakeData.strictExampleSet.strings;
+ text = choices.get(seed % choices.size());
+ } else if (fakeData.textTemplate != null) {
+ text = fakeData.textTemplate.replace("seed", "" + seed)
+ .replace("curr_time", "" + Calendar.getInstance().getTimeInMillis());
+ } else if (fakeData.dateTemplate != null) {
+ if (fakeData.dateTemplate.contains("curr_time")) {
+ date = Calendar.getInstance().getTimeInMillis();
+ }
+ }
+ return new FilledAutofillField(datasetId, fieldTypeName, text, date, toggle);
+ }
+
+ public static String getFieldTypeNameFromAutofillHints(
+ HashMap fieldTypesByAutofillHint,
+ @NonNull List hints) {
+ return getFieldTypeNameFromAutofillHints(fieldTypesByAutofillHint, hints, PARTITION_ALL);
+ }
+
+ public static String getFieldTypeNameFromAutofillHints(
+ HashMap fieldTypesByAutofillHint,
+ @NonNull List hints, int partition) {
+ List fieldTypeNames = removePrefixes(hints)
+ .stream()
+ .filter(fieldTypesByAutofillHint::containsKey)
+ .map(fieldTypesByAutofillHint::get)
+ .filter(Objects::nonNull)
+ .filter((fieldTypeWithHints) ->
+ matchesPartition(fieldTypeWithHints.fieldType.getPartition(), partition))
+ .map(FieldTypeWithHeuristics::getFieldType).map(FieldType::getTypeName)
+ .collect(toList());
+ if (fieldTypeNames != null && fieldTypeNames.size() > 0) {
+ return fieldTypeNames.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ public static boolean matchesPartition(int partition, int otherPartition) {
+ return partition == PARTITION_ALL || otherPartition == PARTITION_ALL ||
+ partition == otherPartition;
+ }
+
+ private static List removePrefixes(@NonNull List hints) {
+ List hintsWithoutPrefixes = new ArrayList<>();
+ String nextHint = null;
+ for (int i = 0; i < hints.size(); i++) {
+ String hint = hints.get(i);
+ if (i < hints.size() - 1) {
+ nextHint = hints.get(i + 1);
+ }
+ // First convert the compound W3C autofill hints
+ if (isW3cSectionPrefix(hint) && i < hints.size() - 1) {
+ i++;
+ hint = hints.get(i);
+ logd("Hint is a W3C section prefix; using %s instead", hint);
+ if (i < hints.size() - 1) {
+ nextHint = hints.get(i + 1);
+ }
+ }
+ if (isW3cTypePrefix(hint) && nextHint != null && isW3cTypeHint(nextHint)) {
+ hint = nextHint;
+ i++;
+ logd("Hint is a W3C type prefix; using %s instead", hint);
+ }
+ if (isW3cAddressType(hint) && nextHint != null) {
+ hint = nextHint;
+ i++;
+ logd("Hint is a W3C address prefix; using %s instead", hint);
+ }
+ hintsWithoutPrefixes.add(hint);
+ }
+ return hintsWithoutPrefixes;
+ }
+
+ private static boolean isW3cSectionPrefix(@NonNull String hint) {
+ return hint.startsWith(W3cHints.PREFIX_SECTION);
+ }
+
+ private static boolean isW3cAddressType(@NonNull String hint) {
+ switch (hint) {
+ case W3cHints.SHIPPING:
+ case W3cHints.BILLING:
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isW3cTypePrefix(@NonNull 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(@NonNull 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;
+ }
+ logw("Invalid W3C type hint: %s", hint);
+ return false;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ClientParser.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ClientParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..048c0018efd2060d6d3c309397334f1b9ca57ee4
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ClientParser.java
@@ -0,0 +1,72 @@
+/*
+ * 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.example.android.autofill.service;
+
+import android.app.assist.AssistStructure;
+import android.support.annotation.NonNull;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+import static android.app.assist.AssistStructure.ViewNode;
+
+/**
+ * Wrapper for {@link AssistStructure} to make it easy to parse.
+ */
+public final class ClientParser {
+ private final List mStructures;
+
+ public ClientParser(@NonNull List structures) {
+ Preconditions.checkNotNull(structures);
+ mStructures = structures;
+ }
+
+ public ClientParser(@NonNull AssistStructure structure) {
+ this(ImmutableList.of(structure));
+ }
+
+ /**
+ * Traverses through the {@link AssistStructure} and does something at each {@link ViewNode}.
+ *
+ * @param processor contains action to be performed on each {@link ViewNode}.
+ */
+ public void parse(NodeProcessor processor) {
+ for (AssistStructure structure : mStructures) {
+ int nodes = structure.getWindowNodeCount();
+ for (int i = 0; i < nodes; i++) {
+ AssistStructure.ViewNode viewNode = structure.getWindowNodeAt(i).getRootViewNode();
+ traverseRoot(viewNode, processor);
+ }
+ }
+ }
+
+ private void traverseRoot(AssistStructure.ViewNode viewNode, NodeProcessor processor) {
+ processor.processNode(viewNode);
+ int childrenSize = viewNode.getChildCount();
+ if (childrenSize > 0) {
+ for (int i = 0; i < childrenSize; i++) {
+ traverseRoot(viewNode.getChildAt(i), processor);
+ }
+ }
+ }
+
+ public interface NodeProcessor {
+ void processNode(ViewNode node);
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/FakeFieldGenerator.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/FakeFieldGenerator.java
similarity index 76%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/FakeFieldGenerator.java
rename to input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/FakeFieldGenerator.java
index 383de216a8a85dc0591c0bef038e47fd1c285d9f..8d0754a94a6167c4a9181578546024b738179389 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/FakeFieldGenerator.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/FakeFieldGenerator.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.multidatasetservice;
+package com.example.android.autofill.service;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillField;
+import com.example.android.autofill.service.model.FilledAutofillField;
interface FakeFieldGenerator {
- FilledAutofillField generate(int seed);
+ FilledAutofillField generate(int seed, String datasetId);
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9acaa5a437d9cbda304e3c904b95045b36b83c9
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualActivity.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.assist.AssistStructure;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.DividerItemDecoration;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.autofill.service.data.ClientViewMetadata;
+import com.example.android.autofill.service.data.ClientViewMetadataBuilder;
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.adapter.DatasetAdapter;
+import com.example.android.autofill.service.data.adapter.ResponseAdapter;
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource;
+import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.model.AutofillDataset;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.settings.MyPreferences;
+import com.example.android.autofill.service.util.AppExecutors;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.GsonBuilder;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE;
+import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT;
+import static com.example.android.autofill.service.util.Util.logd;
+
+/**
+ * When the user long-presses on an autofillable field and selects "Autofill", this activity is
+ * launched to allow the user to select the dataset.
+ */
+public class ManualActivity extends AppCompatActivity {
+
+ private static final int RC_SELECT_FIELD = 1;
+
+ // Unique id for dataset intents.
+ private static int sDatasetPendingIntentId = 0;
+
+ private LocalAutofillDataSource mLocalAutofillDataSource;
+ private DatasetAdapter mDatasetAdapter;
+ private ResponseAdapter mResponseAdapter;
+ private ClientViewMetadata mClientViewMetadata;
+ private String mPackageName;
+ private Intent mReplyIntent;
+ private MyPreferences mPreferences;
+ private List mAllDatasets;
+ private RecyclerView mRecyclerView;
+
+ public static IntentSender getManualIntentSenderForResponse(Context context) {
+ final Intent intent = new Intent(context, ManualActivity.class);
+ return PendingIntent.getActivity(context, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.multidataset_service_manual_activity);
+ SharedPreferences sharedPreferences =
+ getSharedPreferences(LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ DefaultFieldTypesSource defaultFieldTypesSource =
+ DefaultFieldTypesLocalJsonSource.getInstance(getResources(),
+ new GsonBuilder().create());
+ AutofillDao autofillDao = AutofillDatabase.getInstance(this,
+ defaultFieldTypesSource, new AppExecutors()).autofillDao();
+ mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(sharedPreferences,
+ autofillDao, new AppExecutors());
+ mPackageName = getPackageName();
+ mPreferences = MyPreferences.getInstance(this);
+ mRecyclerView = findViewById(R.id.suggestionsList);
+ mRecyclerView.addItemDecoration(new DividerItemDecoration(this, VERTICAL));
+ mLocalAutofillDataSource.getAllAutofillDatasets(
+ new DataCallback>() {
+ @Override
+ public void onLoaded(List datasets) {
+ mAllDatasets = datasets;
+ buildAdapter();
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+ private void buildAdapter() {
+ List datasetIds = new ArrayList<>();
+ List datasetNames = new ArrayList<>();
+ List> allFieldTypes = new ArrayList<>();
+ for (DatasetWithFilledAutofillFields dataset : mAllDatasets) {
+ String datasetName = dataset.autofillDataset.getDatasetName();
+ String datasetId = dataset.autofillDataset.getId();
+ List fieldTypes = new ArrayList<>();
+ for (FilledAutofillField filledAutofillField : dataset.filledAutofillFields) {
+ fieldTypes.add(filledAutofillField.getFieldTypeName());
+ }
+ datasetIds.add(datasetId);
+ datasetNames.add(datasetName);
+ allFieldTypes.add(fieldTypes);
+ }
+ AutofillDatasetsAdapter adapter = new AutofillDatasetsAdapter(datasetIds, datasetNames,
+ allFieldTypes, this);
+ mRecyclerView.setAdapter(adapter);
+ }
+
+ @Override
+ public void finish() {
+ if (mReplyIntent != null) {
+ setResult(RESULT_OK, mReplyIntent);
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+ super.finish();
+ }
+
+ private void onFieldSelected(FilledAutofillField field, FieldType fieldType) {
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields = new DatasetWithFilledAutofillFields();
+ String newDatasetId = UUID.randomUUID().toString();
+ FilledAutofillField copyOfField = new FilledAutofillField(newDatasetId,
+ field.getFieldTypeName(), field.getTextValue(), field.getDateValue(),
+ field.getToggleValue());
+ String datasetName = "dataset-manual";
+ AutofillDataset autofillDataset = new AutofillDataset(newDatasetId, datasetName, mPackageName);
+ datasetWithFilledAutofillFields.filledAutofillFields = ImmutableList.of(copyOfField);
+ datasetWithFilledAutofillFields.autofillDataset = autofillDataset;
+ Intent intent = getIntent();
+ AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
+ ClientParser clientParser = new ClientParser(structure);
+ mReplyIntent = new Intent();
+ mLocalAutofillDataSource.getFieldTypeByAutofillHints(
+ new DataCallback>() {
+ @Override
+ public void onLoaded(HashMap fieldTypesByAutofillHint) {
+ ClientViewMetadataBuilder builder = new ClientViewMetadataBuilder(clientParser,
+ fieldTypesByAutofillHint);
+ mClientViewMetadata = builder.buildClientViewMetadata();
+ mDatasetAdapter = new DatasetAdapter(clientParser);
+ mResponseAdapter = new ResponseAdapter(ManualActivity.this,
+ mClientViewMetadata, mPackageName, mDatasetAdapter);
+ FillResponse fillResponse = mResponseAdapter.buildResponseForFocusedNode(
+ datasetName, field, fieldType);
+ setResponseIntent(fillResponse);
+ finish();
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode != RC_SELECT_FIELD || resultCode != RESULT_OK) {
+ logd("Ignoring requestCode == %d | resultCode == %d", requestCode,
+ resultCode);
+ return;
+ }
+ String datasetId = data.getStringExtra(ManualFieldPickerActivity.EXTRA_SELECTED_FIELD_DATASET_ID);
+ String fieldTypeName = data.getStringExtra(ManualFieldPickerActivity.EXTRA_SELECTED_FIELD_TYPE_NAME);
+ mLocalAutofillDataSource.getFilledAutofillField(datasetId, fieldTypeName, new DataCallback() {
+ @Override
+ public void onLoaded(FilledAutofillField field) {
+ mLocalAutofillDataSource.getFieldType(field.getFieldTypeName(), new DataCallback() {
+ @Override
+ public void onLoaded(FieldType fieldType) {
+ onFieldSelected(field, fieldType);
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+
+ private void updateHeuristics() {
+// TODO: update heuristics in data source; something like:
+// mLocalAutofillDataSource.getAutofillDataset(mClientViewMetadata.getAllHints(),
+// datasetName, new DataCallback() {
+// @Override
+// public void onLoaded(DatasetWithFilledAutofillFields dataset) {
+// String datasetName = dataset.autofillDataset.getDatasetName();
+// RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(
+// mPackageName, datasetName);
+// setDatasetIntent(mDatasetAdapter.buildDataset(fieldTypesByAutofillHint,
+// dataset, remoteViews));
+// finish();
+// }
+//
+// @Override
+// public void onDataNotAvailable(String msg, Object... params) {
+// logw(msg, params);
+// finish();
+// }
+// });
+ }
+
+ private void setResponseIntent(FillResponse fillResponse) {
+ mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);
+ }
+
+ private void setDatasetIntent(Dataset dataset) {
+ mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
+ }
+
+ /**
+ * Adapter for the {@link RecyclerView} that holds a list of datasets.
+ */
+ private static class AutofillDatasetsAdapter extends RecyclerView.Adapter {
+
+ private final List mDatasetIds;
+ private final List mDatasetNames;
+ private final List> mFieldTypes;
+ private final Activity mActivity;
+
+ AutofillDatasetsAdapter(List datasetIds, List datasetNames,
+ List> fieldTypes, Activity activity) {
+ mDatasetIds = datasetIds;
+ mDatasetNames = datasetNames;
+ mFieldTypes = fieldTypes;
+ mActivity = activity;
+ }
+
+ @Override
+ public DatasetViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return DatasetViewHolder.newInstance(parent, mActivity);
+ }
+
+ @Override
+ public void onBindViewHolder(final DatasetViewHolder holder, final int position) {
+ holder.bind(mDatasetIds.get(position), mDatasetNames.get(position),
+ mFieldTypes.get(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return mDatasetNames.size();
+ }
+ }
+
+ /**
+ * Contains views needed in each row of the list of datasets.
+ */
+ private static class DatasetViewHolder extends RecyclerView.ViewHolder {
+ private final View mRootView;
+ private final TextView mDatasetNameText;
+ private final TextView mFieldTypesText;
+ private final Activity mActivity;
+
+ public DatasetViewHolder(View itemView, Activity activity) {
+ super(itemView);
+ mRootView = itemView;
+ mDatasetNameText = itemView.findViewById(R.id.datasetName);
+ mFieldTypesText = itemView.findViewById(R.id.fieldTypes);
+ mActivity = activity;
+ }
+
+ public static DatasetViewHolder newInstance(ViewGroup parent, Activity activity) {
+ return new DatasetViewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.dataset_suggestion, parent, false), activity);
+ }
+
+ public void bind(String datasetId, String datasetName, List fieldTypes) {
+ mDatasetNameText.setText(datasetName);
+ String firstFieldType = null;
+ String secondFieldType = null;
+ int numOfFieldTypes = 0;
+ if (fieldTypes != null) {
+ numOfFieldTypes = fieldTypes.size();
+ if (numOfFieldTypes > 0) {
+ firstFieldType = fieldTypes.get(0);
+ }
+ if (numOfFieldTypes > 1) {
+ secondFieldType = fieldTypes.get(1);
+ }
+ }
+ String fieldTypesString;
+ if (numOfFieldTypes == 1) {
+ fieldTypesString = "Contains data for " + firstFieldType + ".";
+ } else if (numOfFieldTypes == 2) {
+ fieldTypesString = "Contains data for " + firstFieldType + " and " + secondFieldType + ".";
+ } else if (numOfFieldTypes > 2) {
+ fieldTypesString = "Contains data for " + firstFieldType + ", " + secondFieldType + ", and more.";
+ } else {
+ fieldTypesString = "Ignore: Contains no data.";
+ }
+ mFieldTypesText.setText(fieldTypesString);
+ mRootView.setOnClickListener((view) -> {
+ Intent intent = ManualFieldPickerActivity.getIntent(mActivity, datasetId);
+ mActivity.startActivityForResult(intent, RC_SELECT_FIELD);
+ });
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualFieldPickerActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualFieldPickerActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..608ac953b7d3b850dc89b22fca46c6b40295f7b7
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualFieldPickerActivity.java
@@ -0,0 +1,160 @@
+/*
+ * 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.example.android.autofill.service;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.DividerItemDecoration;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource;
+import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.util.AppExecutors;
+import com.google.gson.GsonBuilder;
+
+import java.util.List;
+
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+public class ManualFieldPickerActivity extends AppCompatActivity {
+ private static final String EXTRA_DATASET_ID = "extra_dataset_id";
+ public static final String EXTRA_SELECTED_FIELD_DATASET_ID = "selected_field_dataset_id";
+ public static final String EXTRA_SELECTED_FIELD_TYPE_NAME = "selected_field_type_name";
+
+ private LocalAutofillDataSource mLocalAutofillDataSource;
+
+ private RecyclerView mRecyclerView;
+ private TextView mListTitle;
+ private DatasetWithFilledAutofillFields mDataset;
+
+ public static Intent getIntent(Context originContext, String datasetId) {
+ Intent intent = new Intent(originContext, ManualFieldPickerActivity.class);
+ intent.putExtra(EXTRA_DATASET_ID, datasetId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_field_picker);
+ SharedPreferences sharedPreferences = getSharedPreferences(
+ LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ DefaultFieldTypesSource defaultFieldTypesSource =
+ DefaultFieldTypesLocalJsonSource.getInstance(getResources(),
+ new GsonBuilder().create());
+ AutofillDao autofillDao = AutofillDatabase.getInstance(this,
+ defaultFieldTypesSource, new AppExecutors()).autofillDao();
+ String datasetId = getIntent().getStringExtra(EXTRA_DATASET_ID);
+ mRecyclerView = findViewById(R.id.fieldsList);
+ mRecyclerView.addItemDecoration(new DividerItemDecoration(this, VERTICAL));
+ mListTitle = findViewById(R.id.listTitle);
+ mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(sharedPreferences,
+ autofillDao, new AppExecutors());
+ mLocalAutofillDataSource.getAutofillDatasetWithId(datasetId,
+ new DataCallback() {
+ @Override
+ public void onLoaded(DatasetWithFilledAutofillFields dataset) {
+ mDataset = dataset;
+ if (mDataset != null) {
+ onLoadedDataset();
+ }
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+ public void onSelectedDataset(FilledAutofillField field) {
+ Intent data = new Intent()
+ .putExtra(EXTRA_SELECTED_FIELD_DATASET_ID, field.getDatasetId())
+ .putExtra(EXTRA_SELECTED_FIELD_TYPE_NAME, field.getFieldTypeName());
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ public void onLoadedDataset() {
+ FieldsAdapter fieldsAdapter = new FieldsAdapter(this, mDataset.filledAutofillFields);
+ mRecyclerView.setAdapter(fieldsAdapter);
+ mListTitle.setText(getString(R.string.manual_data_picker_title,
+ mDataset.autofillDataset.getDatasetName()));
+ }
+
+ private static class FieldsAdapter extends RecyclerView.Adapter {
+ private final Activity mActivity;
+ private final List mFields;
+
+ public FieldsAdapter(Activity activity, List fields) {
+ mActivity = activity;
+ mFields = fields;
+ }
+
+ @Override
+ public FieldViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new FieldViewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.dataset_field, parent, false), mActivity);
+ }
+
+ @Override
+ public void onBindViewHolder(FieldViewHolder holder, int position) {
+ FilledAutofillField field = mFields.get(position);
+ holder.bind(field);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mFields.size();
+ }
+ }
+
+ private static class FieldViewHolder extends RecyclerView.ViewHolder {
+ private final View mRootView;
+ private final TextView mFieldTypeText;
+ private final Activity mActivity;
+
+ public FieldViewHolder(View itemView, Activity activity) {
+ super(itemView);
+ mRootView = itemView;
+ mFieldTypeText = itemView.findViewById(R.id.fieldType);
+ mActivity = activity;
+ }
+
+ public void bind(FilledAutofillField field) {
+ mFieldTypeText.setText(field.getFieldTypeName());
+ mRootView.setOnClickListener((view) -> {
+ ((ManualFieldPickerActivity) mActivity).onSelectedDataset(field);
+ });
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/MyAutofillService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/MyAutofillService.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9cdd6caaec2ac300eaaa042fb3dec2b0810b0f1
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/MyAutofillService.java
@@ -0,0 +1,282 @@
+/*
+ * 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.example.android.autofill.service;
+
+import android.app.assist.AssistStructure;
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.os.CancellationSignal;
+import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
+import android.service.autofill.FillContext;
+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.view.autofill.AutofillManager;
+import android.widget.RemoteViews;
+
+import com.example.android.autofill.service.data.AutofillDataBuilder;
+import com.example.android.autofill.service.data.ClientAutofillDataBuilder;
+import com.example.android.autofill.service.data.ClientViewMetadata;
+import com.example.android.autofill.service.data.ClientViewMetadataBuilder;
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.adapter.DatasetAdapter;
+import com.example.android.autofill.service.data.adapter.ResponseAdapter;
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.data.source.PackageVerificationDataSource;
+import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource;
+import com.example.android.autofill.service.data.source.local.DigitalAssetLinksRepository;
+import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
+import com.example.android.autofill.service.data.source.local.SharedPrefsPackageVerificationRepository;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.model.DalCheck;
+import com.example.android.autofill.service.model.DalInfo;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.settings.MyPreferences;
+import com.example.android.autofill.service.util.AppExecutors;
+import com.example.android.autofill.service.util.Util;
+import com.google.gson.GsonBuilder;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement;
+import static com.example.android.autofill.service.util.Util.bundleToString;
+import static com.example.android.autofill.service.util.Util.dumpStructure;
+import static com.example.android.autofill.service.util.Util.logVerboseEnabled;
+import static com.example.android.autofill.service.util.Util.logd;
+import static com.example.android.autofill.service.util.Util.loge;
+import static com.example.android.autofill.service.util.Util.logv;
+import static com.example.android.autofill.service.util.Util.logw;
+import static java.util.stream.Collectors.toList;
+
+public class MyAutofillService extends AutofillService {
+
+ private LocalAutofillDataSource mLocalAutofillDataSource;
+ private DigitalAssetLinksRepository mDalRepository;
+ private PackageVerificationDataSource mPackageVerificationRepository;
+ private AutofillDataBuilder mAutofillDataBuilder;
+ private ResponseAdapter mResponseAdapter;
+ private ClientViewMetadata mClientViewMetadata;
+ private MyPreferences mPreferences;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mPreferences = MyPreferences.getInstance(this);
+ Util.setLoggingLevel(mPreferences.getLoggingLevel());
+ SharedPreferences localAfDataSourceSharedPrefs =
+ getSharedPreferences(LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ DefaultFieldTypesSource defaultFieldTypesSource =
+ DefaultFieldTypesLocalJsonSource.getInstance(getResources(),
+ new GsonBuilder().create());
+ AutofillDao autofillDao = AutofillDatabase.getInstance(this,
+ defaultFieldTypesSource, new AppExecutors()).autofillDao();
+ mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(localAfDataSourceSharedPrefs,
+ autofillDao, new AppExecutors());
+ mDalRepository = DigitalAssetLinksRepository.getInstance(getPackageManager());
+ mPackageVerificationRepository = SharedPrefsPackageVerificationRepository.getInstance(this);
+ }
+
+ @Override
+ public void onFillRequest(@NonNull FillRequest request,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback) {
+ List fillContexts = request.getFillContexts();
+ List structures =
+ fillContexts.stream().map(FillContext::getStructure).collect(toList());
+ AssistStructure latestStructure = fillContexts.get(fillContexts.size() - 1).getStructure();
+ ClientParser parser = new ClientParser(structures);
+
+ // Check user's settings for authenticating Responses and Datasets.
+ boolean responseAuth = mPreferences.isResponseAuth();
+ boolean datasetAuth = mPreferences.isDatasetAuth();
+ boolean manual = (request.getFlags() & FillRequest.FLAG_MANUAL_REQUEST) != 0;
+ mLocalAutofillDataSource.getFieldTypeByAutofillHints(
+ new DataCallback>() {
+ @Override
+ public void onLoaded(HashMap fieldTypesByAutofillHint) {
+ DatasetAdapter datasetAdapter = new DatasetAdapter(parser);
+ ClientViewMetadataBuilder clientViewMetadataBuilder =
+ new ClientViewMetadataBuilder(parser, fieldTypesByAutofillHint);
+ mClientViewMetadata = clientViewMetadataBuilder.buildClientViewMetadata();
+ mResponseAdapter = new ResponseAdapter(MyAutofillService.this,
+ mClientViewMetadata, getPackageName(), datasetAdapter);
+ String packageName = latestStructure.getActivityComponent().getPackageName();
+ if (!mPackageVerificationRepository.putPackageSignatures(packageName)) {
+ callback.onFailure(getString(R.string.invalid_package_signature));
+ return;
+ }
+ if (logVerboseEnabled()) {
+ logv("onFillRequest(): clientState=%s",
+ bundleToString(request.getClientState()));
+ dumpStructure(latestStructure);
+ }
+ cancellationSignal.setOnCancelListener(() ->
+ logw("Cancel autofill not implemented in this sample.")
+ );
+ fetchDataAndGenerateResponse(fieldTypesByAutofillHint, responseAuth,
+ datasetAuth, manual, callback);
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+ private void fetchDataAndGenerateResponse(
+ HashMap fieldTypesByAutofillHint, boolean responseAuth,
+ boolean datasetAuth, boolean manual, FillCallback callback) {
+ if (responseAuth) {
+ // If the entire Autofill Response is authenticated, AuthActivity is used
+ // to generate Response.
+ IntentSender sender = AuthActivity.getAuthIntentSenderForResponse(this);
+ RemoteViews remoteViews = RemoteViewsHelper.viewsWithAuth(getPackageName(),
+ getString(R.string.autofill_sign_in_prompt));
+ FillResponse response = mResponseAdapter.buildResponse(sender, remoteViews);
+ if (response != null) {
+ callback.onSuccess(response);
+ }
+ } else {
+ mLocalAutofillDataSource.getAutofillDatasets(mClientViewMetadata.getAllHints(),
+ new DataCallback>() {
+ @Override
+ public void onLoaded(List datasets) {
+ if ((datasets == null || datasets.isEmpty()) && manual) {
+ IntentSender sender = ManualActivity
+ .getManualIntentSenderForResponse(MyAutofillService.this);
+ RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(
+ getPackageName(),
+ getString(R.string.autofill_manual_prompt));
+ FillResponse response = mResponseAdapter.buildManualResponse(sender,
+ remoteViews);
+ if (response != null) {
+ callback.onSuccess(response);
+ }
+ } else {
+ FillResponse response = mResponseAdapter.buildResponse(
+ fieldTypesByAutofillHint, datasets, datasetAuth);
+ callback.onSuccess(response);
+ }
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+ logw(msg, params);
+ callback.onFailure(String.format(msg, params));
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
+ List fillContexts = request.getFillContexts();
+ List structures =
+ fillContexts.stream().map(FillContext::getStructure).collect(toList());
+ AssistStructure latestStructure = fillContexts.get(fillContexts.size() - 1).getStructure();
+ ClientParser parser = new ClientParser(structures);
+ mLocalAutofillDataSource.getFieldTypeByAutofillHints(
+ new DataCallback>() {
+ @Override
+ public void onLoaded(
+ HashMap fieldTypesByAutofillHint) {
+ mAutofillDataBuilder = new ClientAutofillDataBuilder(
+ fieldTypesByAutofillHint, getPackageName(), parser);
+ ClientViewMetadataBuilder clientViewMetadataBuilder =
+ new ClientViewMetadataBuilder(parser, fieldTypesByAutofillHint);
+ mClientViewMetadata = clientViewMetadataBuilder.buildClientViewMetadata();
+ String packageName = latestStructure.getActivityComponent().getPackageName();
+ if (!mPackageVerificationRepository.putPackageSignatures(packageName)) {
+ callback.onFailure(getString(R.string.invalid_package_signature));
+ return;
+ }
+ if (logVerboseEnabled()) {
+ logv("onSaveRequest(): clientState=%s",
+ bundleToString(request.getClientState()));
+ }
+ dumpStructure(latestStructure);
+ checkWebDomainAndBuildAutofillData(packageName, callback);
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+ loge("Should not happen - could not find field types.");
+ }
+ });
+ }
+
+ private void checkWebDomainAndBuildAutofillData(String packageName, SaveCallback callback) {
+ String webDomain;
+ try {
+ webDomain = mClientViewMetadata.getWebDomain();
+ } catch (SecurityException e) {
+ logw(e.getMessage());
+ callback.onFailure(getString(R.string.security_exception));
+ return;
+ }
+ if (webDomain != null && webDomain.length() > 0) {
+ DalCheckRequirement req = mPreferences.getDalCheckRequirement();
+ mDalRepository.checkValid(req, new DalInfo(webDomain, packageName),
+ new DataCallback() {
+ @Override
+ public void onLoaded(DalCheck dalCheck) {
+ if (dalCheck.linked) {
+ logd("Domain %s is valid for %s", webDomain, packageName);
+ buildAndSaveAutofillData();
+ } else {
+ loge("Could not associate web domain %s with app %s",
+ webDomain, packageName);
+ callback.onFailure(getString(R.string.dal_exception));
+ }
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+ logw(msg, params);
+ callback.onFailure(getString(R.string.dal_exception));
+ }
+ });
+ } else {
+ logd("no web domain");
+ buildAndSaveAutofillData();
+ }
+ }
+
+ private void buildAndSaveAutofillData() {
+ int datasetNumber = mLocalAutofillDataSource.getDatasetNumber();
+ List datasetsWithFilledAutofillFields =
+ mAutofillDataBuilder.buildDatasetsByPartition(datasetNumber);
+ mLocalAutofillDataSource.saveAutofillDatasets(datasetsWithFilledAutofillFields);
+ }
+
+ @Override
+ public void onConnected() {
+ logd("onConnected");
+ }
+
+ @Override
+ public void onDisconnected() {
+ logd("onDisconnected");
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/RemoteViewsHelper.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/RemoteViewsHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..b925b95ac55470e3c6035beba40ee83031bdb931
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/RemoteViewsHelper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.example.android.autofill.service;
+
+import android.support.annotation.DrawableRes;
+import android.widget.RemoteViews;
+
+/**
+ * This is a class containing helper methods for building Autofill Datasets and Responses.
+ */
+public final class RemoteViewsHelper {
+ private RemoteViewsHelper() {
+ }
+
+ public static RemoteViews viewsWithAuth(String packageName, String text) {
+ return simpleRemoteViews(packageName, text, R.drawable.ic_lock_black_24dp);
+ }
+
+ public static RemoteViews viewsWithNoAuth(String packageName, String text) {
+ return simpleRemoteViews(packageName, text, R.drawable.ic_person_black_24dp);
+ }
+
+ private static RemoteViews simpleRemoteViews(String packageName, String remoteViewsText,
+ @DrawableRes int drawableId) {
+ RemoteViews presentation = new RemoteViews(packageName,
+ R.layout.multidataset_service_list_item);
+ presentation.setTextViewText(R.id.text, remoteViewsText);
+ presentation.setImageViewResource(R.id.icon, drawableId);
+ return presentation;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/W3cHints.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/W3cHints.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd8e9457641ae5173b1bf7d4afe4882b4d5f4981
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/W3cHints.java
@@ -0,0 +1,42 @@
+/*
+ * 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.example.android.autofill.service;
+
+public final class W3cHints {
+ // 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() {
+ }
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/AutofillDataBuilder.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/AutofillDataBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0110bdd2a3511f93864238d919cb5a07f432e0c7
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/AutofillDataBuilder.java
@@ -0,0 +1,25 @@
+/*
+ * 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.example.android.autofill.service.data;
+
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+
+import java.util.List;
+
+public interface AutofillDataBuilder {
+ List buildDatasetsByPartition(int datasetNumber);
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientAutofillDataBuilder.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientAutofillDataBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d268337f0e3ae643416d6c36ff80e8591f49a20
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientAutofillDataBuilder.java
@@ -0,0 +1,160 @@
+/*
+ * 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.example.android.autofill.service.data;
+
+import android.app.assist.AssistStructure;
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.autofill.AutofillValue;
+
+import com.example.android.autofill.service.AutofillHints;
+import com.example.android.autofill.service.ClientParser;
+import com.example.android.autofill.service.model.AutofillDataset;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.google.common.collect.ImmutableList;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import static com.example.android.autofill.service.util.Util.loge;
+
+public class ClientAutofillDataBuilder implements AutofillDataBuilder {
+ private final ClientParser mClientParser;
+ private final HashMap mFieldTypesByAutofillHint;
+ private final String mPackageName;
+
+ public ClientAutofillDataBuilder(HashMap fieldTypesByAutofillHint,
+ String packageName, ClientParser clientParser) {
+ mClientParser = clientParser;
+ mFieldTypesByAutofillHint = fieldTypesByAutofillHint;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public List buildDatasetsByPartition(int datasetNumber) {
+ ImmutableList.Builder listBuilder =
+ new ImmutableList.Builder<>();
+ for (int partition : AutofillHints.PARTITIONS) {
+ AutofillDataset autofillDataset = new AutofillDataset(UUID.randomUUID().toString(),
+ "dataset-" + datasetNumber + "." + partition, mPackageName);
+ DatasetWithFilledAutofillFields dataset =
+ buildDatasetForPartition(autofillDataset, partition);
+ if (dataset != null && dataset.filledAutofillFields != null) {
+ listBuilder.add(dataset);
+ }
+ }
+ return listBuilder.build();
+ }
+
+ /**
+ * Parses a client view structure and build a dataset (in the form of a
+ * {@link DatasetWithFilledAutofillFields}) from the view metadata found.
+ */
+ private DatasetWithFilledAutofillFields buildDatasetForPartition(AutofillDataset dataset,
+ int partition) {
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields =
+ new DatasetWithFilledAutofillFields();
+ datasetWithFilledAutofillFields.autofillDataset = dataset;
+ mClientParser.parse((node) ->
+ parseAutofillFields(node, datasetWithFilledAutofillFields, partition)
+ );
+ return datasetWithFilledAutofillFields;
+
+ }
+
+ private void parseAutofillFields(AssistStructure.ViewNode viewNode,
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields, int partition) {
+ String[] hints = viewNode.getAutofillHints();
+ if (hints == null || hints.length == 0) {
+ return;
+ }
+ AutofillValue autofillValue = viewNode.getAutofillValue();
+ String textValue = null;
+ Long dateValue = null;
+ Boolean toggleValue = null;
+ CharSequence[] autofillOptions = null;
+ Integer listIndex = null;
+ if (autofillValue != null) {
+ if (autofillValue.isText()) {
+ // Using toString of AutofillValue.getTextValue in order to save it to
+ // SharedPreferences.
+ textValue = autofillValue.getTextValue().toString();
+ } else if (autofillValue.isDate()) {
+ dateValue = autofillValue.getDateValue();
+ } else if (autofillValue.isList()) {
+ autofillOptions = viewNode.getAutofillOptions();
+ listIndex = autofillValue.getListValue();
+ } else if (autofillValue.isToggle()) {
+ toggleValue = autofillValue.getToggleValue();
+ }
+ }
+ appendViewMetadata(datasetWithFilledAutofillFields,
+ hints, partition, textValue, dateValue, toggleValue,
+ autofillOptions, listIndex);
+ }
+
+ private void appendViewMetadata(@NonNull DatasetWithFilledAutofillFields
+ datasetWithFilledAutofillFields, @NonNull String[] hints, int partition,
+ @Nullable String textValue, @Nullable Long dateValue, @Nullable Boolean toggleValue,
+ @Nullable CharSequence[] autofillOptions, @Nullable Integer listIndex) {
+ for (int i = 0; i < hints.length; i++) {
+ String hint = hints[i];
+ // Then check if the "actual" hint is supported.
+ FieldTypeWithHeuristics fieldTypeWithHeuristics = mFieldTypesByAutofillHint.get(hint);
+ if (fieldTypeWithHeuristics != null) {
+ FieldType fieldType = fieldTypeWithHeuristics.fieldType;
+ if (!AutofillHints.matchesPartition(fieldType.getPartition(), partition)) {
+ continue;
+ }
+ // Only add the field if the hint is supported by the type.
+ if (textValue != null) {
+ if (!fieldType.getAutofillTypes().ints.contains(View.AUTOFILL_TYPE_TEXT)) {
+ loge("Text is invalid type for hint '%s'", hint);
+ }
+ }
+ if (autofillOptions != null && listIndex != null &&
+ autofillOptions.length > listIndex) {
+ if (!fieldType.getAutofillTypes().ints.contains(View.AUTOFILL_TYPE_LIST)) {
+ loge("List is invalid type for hint '%s'", hint);
+ }
+ textValue = autofillOptions[listIndex].toString();
+ }
+ if (dateValue != null) {
+ if (!fieldType.getAutofillTypes().ints.contains(View.AUTOFILL_TYPE_DATE)) {
+ loge("Date is invalid type for hint '%s'", hint);
+ }
+ }
+ if (toggleValue != null) {
+ if (!fieldType.getAutofillTypes().ints.contains(View.AUTOFILL_TYPE_TOGGLE)) {
+ loge("Toggle is invalid type for hint '%s'", hint);
+ }
+ }
+ String datasetId = datasetWithFilledAutofillFields.autofillDataset.getId();
+ datasetWithFilledAutofillFields.add(new FilledAutofillField(datasetId,
+ fieldType.getTypeName(), textValue, dateValue, toggleValue));
+ } else {
+ loge("Invalid hint: %s", hint);
+ }
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadata.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadata.java
new file mode 100644
index 0000000000000000000000000000000000000000..85b789ef7f39a3400bfaa11ab5d4def87ddc6b0c
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadata.java
@@ -0,0 +1,75 @@
+/*
+ * 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.example.android.autofill.service.data;
+
+import android.service.autofill.SaveInfo;
+import android.view.autofill.AutofillId;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * In this simple implementation, the only view data we collect from the client are autofill hints
+ * of the views in the view hierarchy, the corresponding autofill IDs, and the {@link SaveInfo}
+ * based on the hints.
+ */
+public class ClientViewMetadata {
+ private final List mAllHints;
+ private final int mSaveType;
+ private final AutofillId[] mAutofillIds;
+ private final String mWebDomain;
+ private final AutofillId[] mFocusedIds;
+
+ public ClientViewMetadata(List allHints, int saveType, AutofillId[] autofillIds,
+ AutofillId[] focusedIds, String webDomain) {
+ mAllHints = allHints;
+ mSaveType = saveType;
+ mAutofillIds = autofillIds;
+ mWebDomain = webDomain;
+ mFocusedIds = focusedIds;
+ }
+
+ public List getAllHints() {
+ return mAllHints;
+ }
+
+ public AutofillId[] getAutofillIds() {
+ return mAutofillIds;
+ }
+
+ public AutofillId[] getFocusedIds() {
+ return mFocusedIds;
+ }
+
+ public int getSaveType() {
+ return mSaveType;
+ }
+
+ public String getWebDomain() {
+ return mWebDomain;
+ }
+
+ @Override public String toString() {
+ return "ClientViewMetadata{" +
+ "mAllHints=" + mAllHints +
+ ", mSaveType=" + mSaveType +
+ ", mAutofillIds=" + Arrays.toString(mAutofillIds) +
+ ", mWebDomain='" + mWebDomain + '\'' +
+ ", mFocusedIds=" + Arrays.toString(mFocusedIds) +
+ '}';
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadataBuilder.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadataBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..40a67efb8bb7ac1ce04fa3ceecb873767307643a
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadataBuilder.java
@@ -0,0 +1,91 @@
+/*
+ * 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.example.android.autofill.service.data;
+
+
+import android.app.assist.AssistStructure;
+import android.util.MutableInt;
+import android.view.autofill.AutofillId;
+
+import com.example.android.autofill.service.ClientParser;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static com.example.android.autofill.service.util.Util.logd;
+
+public class ClientViewMetadataBuilder {
+ private ClientParser mClientParser;
+ private HashMap mFieldTypesByAutofillHint;
+
+ public ClientViewMetadataBuilder(ClientParser parser,
+ HashMap fieldTypesByAutofillHint) {
+ mClientParser = parser;
+ mFieldTypesByAutofillHint = fieldTypesByAutofillHint;
+ }
+
+ public ClientViewMetadata buildClientViewMetadata() {
+ List allHints = new ArrayList<>();
+ MutableInt saveType = new MutableInt(0);
+ List autofillIds = new ArrayList<>();
+ StringBuilder webDomainBuilder = new StringBuilder();
+ List focusedAutofillIds = new ArrayList<>();
+ mClientParser.parse((node) -> parseNode(node, allHints, saveType, autofillIds, focusedAutofillIds));
+ mClientParser.parse((node) -> parseWebDomain(node, webDomainBuilder));
+ String webDomain = webDomainBuilder.toString();
+ AutofillId[] autofillIdsArray = autofillIds.toArray(new AutofillId[autofillIds.size()]);
+ AutofillId[] focusedIds = focusedAutofillIds.toArray(new AutofillId[focusedAutofillIds.size()]);
+ return new ClientViewMetadata(allHints, saveType.value, autofillIdsArray, focusedIds, webDomain);
+ }
+
+ private void parseWebDomain(AssistStructure.ViewNode viewNode, StringBuilder validWebDomain) {
+ String webDomain = viewNode.getWebDomain();
+ if (webDomain != null) {
+ logd("child web domain: %s", 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);
+ }
+ }
+ }
+
+ private void parseNode(AssistStructure.ViewNode root, List allHints,
+ MutableInt autofillSaveType, List autofillIds,
+ List focusedAutofillIds) {
+ String[] hints = root.getAutofillHints();
+ if (hints != null) {
+ for (String hint : hints) {
+ FieldTypeWithHeuristics fieldTypeWithHints = mFieldTypesByAutofillHint.get(hint);
+ if (fieldTypeWithHints != null && fieldTypeWithHints.fieldType != null) {
+ allHints.add(hint);
+ autofillSaveType.value |= fieldTypeWithHints.fieldType.getSaveInfo();
+ autofillIds.add(root.getAutofillId());
+ }
+ }
+ }
+ if (root.isFocused()) {
+ focusedAutofillIds.add(root.getAutofillId());
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/DataCallback.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/DataCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..e76560284166c1dd1bc9a56fccd92a3e00c403ef
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/DataCallback.java
@@ -0,0 +1,23 @@
+/*
+ * 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.example.android.autofill.service.data;
+
+public interface DataCallback {
+ void onLoaded(T object);
+
+ void onDataNotAvailable(String msg, Object... params);
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/FakeAutofillDataBuilder.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/FakeAutofillDataBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..67ee88034e6c043a91a3bb9bb075a49c87378699
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/FakeAutofillDataBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * 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.example.android.autofill.service.data;
+
+import com.example.android.autofill.service.AutofillHints;
+import com.example.android.autofill.service.model.AutofillDataset;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.UUID;
+
+public class FakeAutofillDataBuilder implements AutofillDataBuilder {
+ private final List mFieldTypesWithHints;
+ private final String mPackageName;
+ private final int mSeed;
+
+ public FakeAutofillDataBuilder(List fieldTypesWithHints,
+ String packageName, int seed) {
+ mFieldTypesWithHints = fieldTypesWithHints;
+ mSeed = seed;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public List buildDatasetsByPartition(int datasetNumber) {
+ ImmutableList.Builder listBuilder =
+ new ImmutableList.Builder<>();
+ for (int partition : AutofillHints.PARTITIONS) {
+ AutofillDataset autofillDataset = new AutofillDataset(UUID.randomUUID().toString(),
+ "dataset-" + datasetNumber + "." + partition, mPackageName);
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields =
+ buildCollectionForPartition(autofillDataset, partition);
+ if (datasetWithFilledAutofillFields != null &&
+ datasetWithFilledAutofillFields.filledAutofillFields != null &&
+ !datasetWithFilledAutofillFields.filledAutofillFields.isEmpty()) {
+ listBuilder.add(datasetWithFilledAutofillFields);
+ }
+ }
+ return listBuilder.build();
+ }
+
+ private DatasetWithFilledAutofillFields buildCollectionForPartition(
+ AutofillDataset dataset, int partition) {
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields =
+ new DatasetWithFilledAutofillFields();
+ datasetWithFilledAutofillFields.autofillDataset = dataset;
+ for (FieldTypeWithHeuristics fieldTypeWithHeuristics : mFieldTypesWithHints) {
+ if (AutofillHints.matchesPartition(
+ fieldTypeWithHeuristics.getFieldType().getPartition(), partition)) {
+ FilledAutofillField fakeField =
+ AutofillHints.generateFakeField(fieldTypeWithHeuristics, mPackageName,
+ mSeed, dataset.getId());
+ datasetWithFilledAutofillFields.add(fakeField);
+ }
+ }
+ return datasetWithFilledAutofillFields;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/DatasetAdapter.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/DatasetAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f7e60368f401f9f6cdb4e482b8060e663f61c51
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/DatasetAdapter.java
@@ -0,0 +1,189 @@
+/*
+ * 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.example.android.autofill.service.data.adapter;
+
+import android.app.assist.AssistStructure;
+import android.content.IntentSender;
+import android.service.autofill.Dataset;
+import android.util.MutableBoolean;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
+
+import com.example.android.autofill.service.AutofillHints;
+import com.example.android.autofill.service.ClientParser;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+import static com.example.android.autofill.service.util.Util.indexOf;
+import static com.example.android.autofill.service.util.Util.logv;
+import static com.example.android.autofill.service.util.Util.logw;
+import static java.util.stream.Collectors.toMap;
+
+public class DatasetAdapter {
+ private final ClientParser mClientParser;
+
+ public DatasetAdapter(ClientParser clientParser) {
+ mClientParser = clientParser;
+ }
+
+ /**
+ * Wraps autofill data in a {@link Dataset} object which can then be sent back to the client.
+ */
+ public Dataset buildDataset(HashMap fieldTypesByAutofillHint,
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields,
+ RemoteViews remoteViews) {
+ return buildDataset(fieldTypesByAutofillHint, datasetWithFilledAutofillFields, remoteViews,
+ null);
+ }
+
+ public Dataset buildDatasetForFocusedNode(FilledAutofillField filledAutofillField,
+ FieldType fieldType, RemoteViews remoteViews) {
+ Dataset.Builder datasetBuilder = new Dataset.Builder(remoteViews);
+ boolean setAtLeastOneValue = bindDatasetToFocusedNode(filledAutofillField,
+ fieldType, datasetBuilder);
+ if (!setAtLeastOneValue) {
+ return null;
+ }
+ return datasetBuilder.build();
+ }
+
+ /**
+ * Wraps autofill data in a {@link Dataset} object with an IntentSender, which can then be
+ * sent back to the client.
+ */
+ public Dataset buildDataset(HashMap fieldTypesByAutofillHint,
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields,
+ RemoteViews remoteViews, IntentSender intentSender) {
+ Dataset.Builder datasetBuilder = new Dataset.Builder(remoteViews);
+ if (intentSender != null) {
+ datasetBuilder.setAuthentication(intentSender);
+ }
+ boolean setAtLeastOneValue = bindDataset(fieldTypesByAutofillHint,
+ datasetWithFilledAutofillFields, datasetBuilder);
+ if (!setAtLeastOneValue) {
+ return null;
+ }
+ return datasetBuilder.build();
+ }
+
+ /**
+ * Build an autofill {@link Dataset} using saved data and the client's AssistStructure.
+ */
+ private boolean bindDataset(HashMap fieldTypesByAutofillHint,
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields,
+ Dataset.Builder datasetBuilder) {
+ MutableBoolean setValueAtLeastOnce = new MutableBoolean(false);
+ Map filledAutofillFieldsByTypeName =
+ datasetWithFilledAutofillFields.filledAutofillFields.stream()
+ .collect(toMap(FilledAutofillField::getFieldTypeName, Function.identity()));
+ mClientParser.parse((node) ->
+ parseAutofillFields(node, fieldTypesByAutofillHint, filledAutofillFieldsByTypeName,
+ datasetBuilder, setValueAtLeastOnce)
+ );
+ return setValueAtLeastOnce.value;
+ }
+
+ private boolean bindDatasetToFocusedNode(FilledAutofillField field,
+ FieldType fieldType, Dataset.Builder builder) {
+ MutableBoolean setValueAtLeastOnce = new MutableBoolean(false);
+ mClientParser.parse((node) -> {
+ if (node.isFocused() && node.getAutofillId() != null) {
+ bindValueToNode(node, field, builder, setValueAtLeastOnce);
+ }
+ });
+ return setValueAtLeastOnce.value;
+ }
+
+ private void parseAutofillFields(AssistStructure.ViewNode viewNode,
+ HashMap fieldTypesByAutofillHint,
+ Map filledAutofillFieldsByTypeName,
+ Dataset.Builder builder, MutableBoolean setValueAtLeastOnce) {
+ String[] rawHints = viewNode.getAutofillHints();
+ if (rawHints == null || rawHints.length == 0) {
+ logv("No af hints at ViewNode - %s", viewNode.getIdEntry());
+ return;
+ }
+ String fieldTypeName = AutofillHints.getFieldTypeNameFromAutofillHints(
+ fieldTypesByAutofillHint, Arrays.asList(rawHints));
+ if (fieldTypeName == null) {
+ return;
+ }
+ FilledAutofillField field = filledAutofillFieldsByTypeName.get(fieldTypeName);
+ if (field == null) {
+ return;
+ }
+ bindValueToNode(viewNode, field, builder, setValueAtLeastOnce);
+ }
+
+ void bindValueToNode(AssistStructure.ViewNode viewNode,
+ FilledAutofillField field, Dataset.Builder builder,
+ MutableBoolean setValueAtLeastOnce) {
+ AutofillId autofillId = viewNode.getAutofillId();
+ if (autofillId == null) {
+ logw("Autofill ID null for %s", viewNode.toString());
+ return;
+ }
+ int autofillType = viewNode.getAutofillType();
+ switch (autofillType) {
+ case View.AUTOFILL_TYPE_LIST:
+ CharSequence[] options = viewNode.getAutofillOptions();
+ int listValue = -1;
+ if (options != null) {
+ listValue = indexOf(viewNode.getAutofillOptions(), field.getTextValue());
+ }
+ if (listValue != -1) {
+ builder.setValue(autofillId, AutofillValue.forList(listValue));
+ setValueAtLeastOnce.value = true;
+ }
+ break;
+ case View.AUTOFILL_TYPE_DATE:
+ Long dateValue = field.getDateValue();
+ if (dateValue != null) {
+ builder.setValue(autofillId, AutofillValue.forDate(dateValue));
+ setValueAtLeastOnce.value = true;
+ }
+ break;
+ case View.AUTOFILL_TYPE_TEXT:
+ String textValue = field.getTextValue();
+ if (textValue != null) {
+ builder.setValue(autofillId, AutofillValue.forText(textValue));
+ setValueAtLeastOnce.value = true;
+ }
+ break;
+ case View.AUTOFILL_TYPE_TOGGLE:
+ Boolean toggleValue = field.getToggleValue();
+ if (toggleValue != null) {
+ builder.setValue(autofillId, AutofillValue.forToggle(toggleValue));
+ setValueAtLeastOnce.value = true;
+ }
+ break;
+ case View.AUTOFILL_TYPE_NONE:
+ default:
+ logw("Invalid autofill type - %d", autofillType);
+ break;
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/ResponseAdapter.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/ResponseAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..0300fed641defa554282ccf80cf2222438a7c275
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/ResponseAdapter.java
@@ -0,0 +1,136 @@
+/*
+ * 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.example.android.autofill.service.data.adapter;
+
+import android.content.Context;
+import android.content.IntentSender;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.SaveInfo;
+import android.view.autofill.AutofillId;
+import android.widget.RemoteViews;
+
+import com.example.android.autofill.service.AuthActivity;
+import com.example.android.autofill.service.RemoteViewsHelper;
+import com.example.android.autofill.service.data.ClientViewMetadata;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+
+import java.util.HashMap;
+import java.util.List;
+
+public class ResponseAdapter {
+ private final Context mContext;
+ private final DatasetAdapter mDatasetAdapter;
+ private final String mPackageName;
+ private final ClientViewMetadata mClientViewMetadata;
+
+ public ResponseAdapter(Context context, ClientViewMetadata clientViewMetadata,
+ String packageName, DatasetAdapter datasetAdapter) {
+ mContext = context;
+ mClientViewMetadata = clientViewMetadata;
+ mDatasetAdapter = datasetAdapter;
+ mPackageName = packageName;
+ }
+
+ public FillResponse buildResponseForFocusedNode(String datasetName, FilledAutofillField field,
+ FieldType fieldType) {
+ FillResponse.Builder responseBuilder = new FillResponse.Builder();
+ RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(
+ mPackageName, datasetName);
+ Dataset dataset = mDatasetAdapter.buildDatasetForFocusedNode(field, fieldType, remoteViews);
+ if (dataset != null) {
+ responseBuilder.addDataset(dataset);
+ return responseBuilder.build();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Wraps autofill data in a Response object (essentially a series of Datasets) which can then
+ * be sent back to the client View.
+ */
+ public FillResponse buildResponse(HashMap fieldTypesByAutofillHint,
+ List datasets, boolean datasetAuth) {
+ FillResponse.Builder responseBuilder = new FillResponse.Builder();
+ if (datasets != null) {
+ for (DatasetWithFilledAutofillFields datasetWithFilledAutofillFields : datasets) {
+ if (datasetWithFilledAutofillFields != null) {
+ Dataset dataset;
+ String datasetName = datasetWithFilledAutofillFields.autofillDataset
+ .getDatasetName();
+ if (datasetAuth) {
+ IntentSender intentSender = AuthActivity.getAuthIntentSenderForDataset(
+ mContext, datasetName);
+ RemoteViews remoteViews = RemoteViewsHelper.viewsWithAuth(
+ mPackageName, datasetName);
+ dataset = mDatasetAdapter.buildDataset(fieldTypesByAutofillHint,
+ datasetWithFilledAutofillFields, remoteViews, intentSender);
+ } else {
+ RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(
+ mPackageName, datasetName);
+ dataset = mDatasetAdapter.buildDataset(fieldTypesByAutofillHint,
+ datasetWithFilledAutofillFields, remoteViews);
+ }
+ if (dataset != null) {
+ responseBuilder.addDataset(dataset);
+ }
+ }
+ }
+ }
+ int saveType = mClientViewMetadata.getSaveType();
+ AutofillId[] autofillIds = mClientViewMetadata.getAutofillIds();
+ if (autofillIds != null && autofillIds.length > 0) {
+ SaveInfo saveInfo = new SaveInfo.Builder(saveType, autofillIds).build();
+ responseBuilder.setSaveInfo(saveInfo);
+ return responseBuilder.build();
+ } else {
+ return null;
+ }
+ }
+
+ public FillResponse buildResponse(IntentSender sender, RemoteViews remoteViews) {
+ FillResponse.Builder responseBuilder = new FillResponse.Builder();
+ int saveType = mClientViewMetadata.getSaveType();
+ AutofillId[] autofillIds = mClientViewMetadata.getAutofillIds();
+ if (autofillIds != null && autofillIds.length > 0) {
+ SaveInfo saveInfo = new SaveInfo.Builder(saveType, autofillIds).build();
+ responseBuilder.setSaveInfo(saveInfo);
+ responseBuilder.setAuthentication(autofillIds, sender, remoteViews);
+ return responseBuilder.build();
+ } else {
+ return null;
+ }
+ }
+
+ public FillResponse buildManualResponse(IntentSender sender, RemoteViews remoteViews) {
+ FillResponse.Builder responseBuilder = new FillResponse.Builder();
+ int saveType = mClientViewMetadata.getSaveType();
+ AutofillId[] focusedIds = mClientViewMetadata.getFocusedIds();
+ if (focusedIds != null && focusedIds.length > 0) {
+ SaveInfo saveInfo = new SaveInfo.Builder(saveType, focusedIds).build();
+ return responseBuilder.setSaveInfo(saveInfo)
+ .setAuthentication(focusedIds, sender, remoteViews)
+ .build();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/AutofillDataSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/AutofillDataSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3579dd8561bb9e01fcd2cf65480d550941b9fe9
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/AutofillDataSource.java
@@ -0,0 +1,75 @@
+/*
+ * 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.example.android.autofill.service.data.source;
+
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.model.ResourceIdHeuristic;
+
+import java.util.HashMap;
+import java.util.List;
+
+public interface AutofillDataSource {
+
+ /**
+ * Asynchronously gets saved list of {@link DatasetWithFilledAutofillFields} that contains some
+ * objects that can autofill fields with these {@code autofillHints}.
+ */
+ void getAutofillDatasets(List allAutofillHints,
+ DataCallback> datasetsCallback);
+
+ void getAllAutofillDatasets(
+ DataCallback> datasetsCallback);
+
+ /**
+ * Asynchronously gets a saved {@link DatasetWithFilledAutofillFields} for a specific
+ * {@code datasetName} that contains some objects that can autofill fields with these
+ * {@code autofillHints}.
+ */
+ void getAutofillDataset(List allAutofillHints,
+ String datasetName, DataCallback datasetsCallback);
+
+ /**
+ * Stores a collection of Autofill fields.
+ */
+ void saveAutofillDatasets(List
+ datasetsWithFilledAutofillFields);
+
+ void saveResourceIdHeuristic(ResourceIdHeuristic resourceIdHeuristic);
+
+ /**
+ * Gets all autofill field types.
+ */
+ void getFieldTypes(DataCallback> fieldTypesCallback);
+
+ /**
+ * Gets all autofill field types.
+ */
+ void getFieldType(String typeName, DataCallback fieldTypeCallback);
+
+ void getFieldTypeByAutofillHints(
+ DataCallback> fieldTypeMapCallback);
+
+ void getFilledAutofillField(String datasetId, String fieldTypeName, DataCallback fieldCallback);
+
+ /**
+ * Clears all data.
+ */
+ void clear();
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DalService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DalService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9cf695680227eb7da43c016546e08a6d2402fa4
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DalService.java
@@ -0,0 +1,31 @@
+/*
+ * 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.example.android.autofill.service.data.source;
+
+import com.example.android.autofill.service.model.DalCheck;
+
+import retrofit2.Call;
+import retrofit2.http.GET;
+import retrofit2.http.Query;
+
+public interface DalService {
+ @GET("/v1/assetlinks:check")
+ Call check(@Query("source.web.site") String webDomain,
+ @Query("relation") String permission,
+ @Query("target.android_app.package_name") String packageName,
+ @Query("target.android_app.certificate.sha256_fingerprint") String fingerprint);
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DefaultFieldTypesSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DefaultFieldTypesSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..22cbeadce371456d09de6e40dc91ee24fffb9d41
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DefaultFieldTypesSource.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.data.source;
+
+import com.example.android.autofill.service.model.DefaultFieldTypeWithHints;
+
+import java.util.List;
+
+public interface DefaultFieldTypesSource {
+ List getDefaultFieldTypes();
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/DigitalAssetLinksDataSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DigitalAssetLinksDataSource.java
similarity index 63%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/DigitalAssetLinksDataSource.java
rename to input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DigitalAssetLinksDataSource.java
index 04624cbbb9c79604ebfbc67686ee56624a0f8acc..00661a7f9d48af036bfe956a1b7bd2b1eb681e08 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/DigitalAssetLinksDataSource.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/DigitalAssetLinksDataSource.java
@@ -13,23 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.multidatasetservice.datasource;
+package com.example.android.autofill.service.data.source;
-import android.content.Context;
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.model.DalCheck;
+import com.example.android.autofill.service.model.DalInfo;
+
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement;
/**
- * Helper format
- * Digital Asset Links needs.
+ * Data source for
+ * Digital Asset Links .
*/
public interface DigitalAssetLinksDataSource {
/**
* Checks if the association between a web domain and a package is valid.
*/
- boolean isValid(Context context, String webDomain, String packageName);
+ void checkValid(DalCheckRequirement dalCheckRequirement, DalInfo dalInfo,
+ DataCallback dalCheckCallback);
/**
* Clears all cached data.
*/
- void clear(Context context);
+ void clear();
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/PackageVerificationDataSource.java
similarity index 86%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java
rename to input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/PackageVerificationDataSource.java
index 129001d0b4fc1be81a6e47d5fcb1828e1d84be9a..7e271e028b636c4c5a824a39d9df05b8a7ecfa41 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/PackageVerificationDataSource.java
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.multidatasetservice.datasource;
-
-import android.content.Context;
+package com.example.android.autofill.service.data.source;
public interface PackageVerificationDataSource {
@@ -29,10 +27,10 @@ public interface PackageVerificationDataSource {
* {@code false} if the signatures in the passed {@code Context} do not match what is
* currently in storage or if an {@code Exception} was thrown while generating the signatures.
*/
- boolean putPackageSignatures(Context context, String packageName);
+ boolean putPackageSignatures(String packageName);
/**
* Clears all signature data currently in storage.
*/
- void clear(Context context);
+ void clear();
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DefaultFieldTypesLocalJsonSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DefaultFieldTypesLocalJsonSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..d403f320bd66c9f1230586eba9e258bf8e8283b7
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DefaultFieldTypesLocalJsonSource.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.data.source.local;
+
+import android.content.res.Resources;
+
+import com.example.android.autofill.service.R;
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.model.DefaultFieldTypeWithHints;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.lang.reflect.Type;
+import java.util.List;
+
+import static com.example.android.autofill.service.util.Util.loge;
+
+public class DefaultFieldTypesLocalJsonSource implements DefaultFieldTypesSource {
+ private static DefaultFieldTypesLocalJsonSource sInstance;
+
+ private final Resources mResources;
+ private final Gson mGson;
+
+ private DefaultFieldTypesLocalJsonSource(Resources resources, Gson gson) {
+ mResources = resources;
+ mGson = gson;
+ }
+
+ public static DefaultFieldTypesLocalJsonSource getInstance(Resources resources, Gson gson) {
+ if (sInstance == null) {
+ sInstance = new DefaultFieldTypesLocalJsonSource(resources, gson);
+ }
+ return sInstance;
+ }
+
+ @Override
+ public List getDefaultFieldTypes() {
+ Type fieldTypeListType = TypeToken.getParameterized(List.class,
+ DefaultFieldTypeWithHints.class).getType();
+ InputStream is = mResources.openRawResource(R.raw.default_field_types);
+ List fieldTypes = null;
+ try(Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
+ fieldTypes = mGson.fromJson(reader, fieldTypeListType);
+ } catch (IOException e) {
+ loge(e, "Exception during deserialization of FieldTypes.");
+ }
+ return fieldTypes;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DigitalAssetLinksRepository.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DigitalAssetLinksRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..6303a1e728c352f0b332d65e0854c0118fe04dc2
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DigitalAssetLinksRepository.java
@@ -0,0 +1,159 @@
+/*
+ * 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.example.android.autofill.service.data.source.local;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.support.annotation.NonNull;
+
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.source.DalService;
+import com.example.android.autofill.service.data.source.DigitalAssetLinksDataSource;
+import com.example.android.autofill.service.model.DalCheck;
+import com.example.android.autofill.service.model.DalInfo;
+import com.example.android.autofill.service.util.SecurityHelper;
+import com.google.common.net.InternetDomainName;
+
+import java.util.HashMap;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement;
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement.AllUrls;
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement.Disabled;
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement.LoginOnly;
+import static com.example.android.autofill.service.util.Util.logd;
+
+
+/**
+ * Singleton repository that caches the result of Digital Asset Links checks.
+ */
+public class DigitalAssetLinksRepository implements DigitalAssetLinksDataSource {
+ private static final String DAL_BASE_URL = "https://digitalassetlinks.googleapis.com";
+ private static final String PERMISSION_GET_LOGIN_CREDS = "common.get_login_creds";
+ private static final String PERMISSION_HANDLE_ALL_URLS = "common.handle_all_urls";
+ private static DigitalAssetLinksRepository sInstance;
+
+ private final PackageManager mPackageManager;
+ private final DalService mDalService;
+ private final HashMap mCache;
+
+ private DigitalAssetLinksRepository(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ mCache = new HashMap<>();
+ mDalService = new Retrofit.Builder()
+ .baseUrl(DAL_BASE_URL)
+ .build()
+ .create(DalService.class);
+ }
+
+ public static DigitalAssetLinksRepository getInstance(PackageManager packageManager) {
+ if (sInstance == null) {
+ sInstance = new DigitalAssetLinksRepository(packageManager);
+ }
+ return sInstance;
+ }
+
+ public static String getCanonicalDomain(String domain) {
+ InternetDomainName idn = InternetDomainName.from(domain);
+ while (idn != null && !idn.isTopPrivateDomain()) {
+ idn = idn.parent();
+ }
+ return idn == null ? null : idn.toString();
+ }
+
+ @Override
+ public void clear() {
+ mCache.clear();
+ }
+
+ public void checkValid(DalCheckRequirement dalCheckRequirement, DalInfo dalInfo,
+ DataCallback dalCheckDataCallback) {
+ if (dalCheckRequirement.equals(Disabled)) {
+ DalCheck dalCheck = new DalCheck();
+ dalCheck.linked = true;
+ dalCheckDataCallback.onLoaded(dalCheck);
+ return;
+ }
+
+ DalCheck dalCheck = mCache.get(dalInfo);
+ if (dalCheck != null) {
+ dalCheckDataCallback.onLoaded(dalCheck);
+ return;
+ }
+ String packageName = dalInfo.getPackageName();
+ String webDomain = dalInfo.getWebDomain();
+
+ final String fingerprint;
+ try {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNATURES);
+ fingerprint = SecurityHelper.getFingerprint(packageInfo, packageName);
+ } catch (Exception e) {
+ dalCheckDataCallback.onDataNotAvailable("Error getting fingerprint for %s",
+ packageName);
+ return;
+ }
+ logd("validating domain %s for pkg %s and fingerprint %s.", webDomain,
+ packageName, fingerprint);
+ mDalService.check(webDomain, PERMISSION_GET_LOGIN_CREDS, packageName, fingerprint).enqueue(
+ new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call,
+ @NonNull Response response) {
+ DalCheck dalCheck = response.body();
+ if (dalCheck == null || !dalCheck.linked) {
+ // get_login_creds check failed, so try handle_all_urls check
+ if (dalCheckRequirement.equals(LoginOnly)) {
+ dalCheckDataCallback.onDataNotAvailable(
+ "DAL: Login creds check failed.");
+ } else if (dalCheckRequirement.equals(AllUrls)) {
+ mDalService.check(webDomain, PERMISSION_HANDLE_ALL_URLS,
+ packageName, fingerprint).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call,
+ @NonNull Response response) {
+ DalCheck dalCheck = response.body();
+ mCache.put(dalInfo, dalCheck);
+ dalCheckDataCallback.onLoaded(dalCheck);
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call,
+ @NonNull Throwable t) {
+ dalCheckDataCallback.onDataNotAvailable(t.getMessage());
+ }
+ });
+ }
+ } else {
+ // get_login_creds check succeeded, so we're finished.
+ mCache.put(dalInfo, dalCheck);
+ dalCheckDataCallback.onLoaded(dalCheck);
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ // get_login_creds check failed, so try handle_all_urls check.
+ mDalService.check(webDomain, PERMISSION_HANDLE_ALL_URLS, packageName,
+ fingerprint);
+ }
+ });
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/LocalAutofillDataSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/LocalAutofillDataSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..e58e18b8db4bdc9301d28eb0fcf3413256ace416
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/LocalAutofillDataSource.java
@@ -0,0 +1,257 @@
+/*
+ * 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.example.android.autofill.service.data.source.local;
+
+import android.content.SharedPreferences;
+import android.service.autofill.Dataset;
+
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.source.AutofillDataSource;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.model.AutofillDataset;
+import com.example.android.autofill.service.model.AutofillHint;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.model.ResourceIdHeuristic;
+import com.example.android.autofill.service.util.AppExecutors;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.example.android.autofill.service.util.Util.logw;
+
+public class LocalAutofillDataSource implements AutofillDataSource {
+ public static final String SHARED_PREF_KEY = "com.example.android.autofill"
+ + ".service.datasource.LocalAutofillDataSource";
+ private static final String DATASET_NUMBER_KEY = "datasetNumber";
+ private static final Object sLock = new Object();
+
+ private static LocalAutofillDataSource sInstance;
+
+ private final AutofillDao mAutofillDao;
+ private final SharedPreferences mSharedPreferences;
+ private final AppExecutors mAppExecutors;
+
+ private LocalAutofillDataSource(SharedPreferences sharedPreferences, AutofillDao autofillDao,
+ AppExecutors appExecutors) {
+ mSharedPreferences = sharedPreferences;
+ mAutofillDao = autofillDao;
+ mAppExecutors = appExecutors;
+ }
+
+ public static LocalAutofillDataSource getInstance(SharedPreferences sharedPreferences,
+ AutofillDao autofillDao, AppExecutors appExecutors) {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new LocalAutofillDataSource(sharedPreferences, autofillDao,
+ appExecutors);
+ }
+ return sInstance;
+ }
+ }
+
+ public static void clearInstance() {
+ synchronized (sLock) {
+ sInstance = null;
+ }
+ }
+
+ @Override
+ public void getAutofillDatasets(List allAutofillHints,
+ DataCallback> datasetsCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ final List typeNames = getFieldTypesForAutofillHints(allAutofillHints)
+ .stream()
+ .map(FieldTypeWithHeuristics::getFieldType)
+ .map(FieldType::getTypeName)
+ .collect(Collectors.toList());
+ List datasetsWithFilledAutofillFields =
+ mAutofillDao.getDatasets(typeNames);
+ mAppExecutors.mainThread().execute(() ->
+ datasetsCallback.onLoaded(datasetsWithFilledAutofillFields)
+ );
+ });
+ }
+
+ @Override
+ public void getAllAutofillDatasets(
+ DataCallback> datasetsCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ List datasetsWithFilledAutofillFields =
+ mAutofillDao.getAllDatasets();
+ mAppExecutors.mainThread().execute(() ->
+ datasetsCallback.onLoaded(datasetsWithFilledAutofillFields)
+ );
+ });
+ }
+
+ @Override
+ public void getAutofillDataset(List allAutofillHints, String datasetName,
+ DataCallback datasetsCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ // Room does not support TypeConverters for collections.
+ List autofillDatasetFields =
+ mAutofillDao.getDatasetsWithName(allAutofillHints, datasetName);
+ if (autofillDatasetFields != null && !autofillDatasetFields.isEmpty()) {
+ if (autofillDatasetFields.size() > 1) {
+ logw("More than 1 dataset with name %s", datasetName);
+ }
+ DatasetWithFilledAutofillFields dataset = autofillDatasetFields.get(0);
+
+ mAppExecutors.mainThread().execute(() ->
+ datasetsCallback.onLoaded(dataset)
+ );
+ } else {
+ mAppExecutors.mainThread().execute(() ->
+ datasetsCallback.onDataNotAvailable("No data found.")
+ );
+ }
+ });
+ }
+
+
+ @Override
+ public void saveAutofillDatasets(List
+ datasetsWithFilledAutofillFields) {
+ mAppExecutors.diskIO().execute(() -> {
+ for (DatasetWithFilledAutofillFields datasetWithFilledAutofillFields :
+ datasetsWithFilledAutofillFields) {
+ List filledAutofillFields =
+ datasetWithFilledAutofillFields.filledAutofillFields;
+ AutofillDataset autofillDataset = datasetWithFilledAutofillFields.autofillDataset;
+ mAutofillDao.insertAutofillDataset(autofillDataset);
+ mAutofillDao.insertFilledAutofillFields(filledAutofillFields);
+ }
+ });
+ incrementDatasetNumber();
+ }
+
+ @Override
+ public void saveResourceIdHeuristic(ResourceIdHeuristic resourceIdHeuristic) {
+ mAppExecutors.diskIO().execute(() -> {
+ mAutofillDao.insertResourceIdHeuristic(resourceIdHeuristic);
+ });
+ }
+
+ @Override
+ public void getFieldTypes(DataCallback> fieldTypesCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ List fieldTypeWithHints = mAutofillDao.getFieldTypesWithHints();
+ mAppExecutors.mainThread().execute(() -> {
+ if (fieldTypeWithHints != null) {
+ fieldTypesCallback.onLoaded(fieldTypeWithHints);
+ } else {
+ fieldTypesCallback.onDataNotAvailable("Field Types not found.");
+ }
+ });
+ });
+ }
+
+ @Override
+ public void getFieldTypeByAutofillHints(
+ DataCallback> fieldTypeMapCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ HashMap hintMap = getFieldTypeByAutofillHints();
+ mAppExecutors.mainThread().execute(() -> {
+ if (hintMap != null) {
+ fieldTypeMapCallback.onLoaded(hintMap);
+ } else {
+ fieldTypeMapCallback.onDataNotAvailable("FieldTypes not found");
+ }
+ });
+ });
+ }
+
+ @Override
+ public void getFilledAutofillField(String datasetId, String fieldTypeName, DataCallback fieldCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ FilledAutofillField filledAutofillField = mAutofillDao.getFilledAutofillField(datasetId, fieldTypeName);
+ mAppExecutors.mainThread().execute(() -> {
+ fieldCallback.onLoaded(filledAutofillField);
+ });
+ });
+ }
+
+ @Override
+ public void getFieldType(String fieldTypeName, DataCallback fieldTypeCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ FieldType fieldType = mAutofillDao.getFieldType(fieldTypeName);
+ mAppExecutors.mainThread().execute(() -> {
+ fieldTypeCallback.onLoaded(fieldType);
+ });
+ });
+ }
+
+ public void getAutofillDatasetWithId(String datasetId,
+ DataCallback callback) {
+ mAppExecutors.diskIO().execute(() -> {
+ DatasetWithFilledAutofillFields dataset =
+ mAutofillDao.getAutofillDatasetWithId(datasetId);
+ mAppExecutors.mainThread().execute(() -> {
+ callback.onLoaded(dataset);
+ });
+ });
+ }
+
+ private HashMap getFieldTypeByAutofillHints() {
+ HashMap hintMap = new HashMap<>();
+ List fieldTypeWithHints =
+ mAutofillDao.getFieldTypesWithHints();
+ if (fieldTypeWithHints != null) {
+ for (FieldTypeWithHeuristics fieldType : fieldTypeWithHints) {
+ for (AutofillHint hint : fieldType.autofillHints) {
+ hintMap.put(hint.mAutofillHint, fieldType);
+ }
+ }
+ return hintMap;
+ } else {
+ return null;
+ }
+ }
+
+ private List getFieldTypesForAutofillHints(List autofillHints) {
+ return mAutofillDao.getFieldTypesForAutofillHints(autofillHints);
+ }
+
+ @Override
+ public void clear() {
+ mAppExecutors.diskIO().execute(() -> {
+ mAutofillDao.clearAll();
+ mSharedPreferences.edit().putInt(DATASET_NUMBER_KEY, 0).apply();
+ });
+ }
+
+ /**
+ * For simplicity, {@link Dataset}s will be named in the form {@code dataset-X.P} where
+ * {@code X} means this was the Xth group of datasets saved, and {@code P} refers to the dataset
+ * partition number. This method returns the appropriate {@code X}.
+ */
+ public int getDatasetNumber() {
+ return mSharedPreferences.getInt(DATASET_NUMBER_KEY, 0);
+ }
+
+ /**
+ * Every time a dataset is saved, this should be called to increment the dataset number.
+ * (only important for this service's dataset naming scheme).
+ */
+ private void incrementDatasetNumber() {
+ mSharedPreferences.edit().putInt(DATASET_NUMBER_KEY, getDatasetNumber() + 1).apply();
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/SharedPrefsPackageVerificationRepository.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/SharedPrefsPackageVerificationRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d43c1fe09ef949657aacc3c7e5a0172fd715fbd
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/SharedPrefsPackageVerificationRepository.java
@@ -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.example.android.autofill.service.data.source.local;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+
+import com.example.android.autofill.service.data.source.PackageVerificationDataSource;
+import com.example.android.autofill.service.util.SecurityHelper;
+
+import static com.example.android.autofill.service.util.Util.logd;
+import static com.example.android.autofill.service.util.Util.logw;
+
+public class SharedPrefsPackageVerificationRepository implements PackageVerificationDataSource {
+
+ private static final String SHARED_PREF_KEY = "com.example.android.autofill.service"
+ + ".datasource.PackageVerificationDataSource";
+ private static PackageVerificationDataSource sInstance;
+
+ private final SharedPreferences mSharedPrefs;
+ private final Context mContext;
+
+ private SharedPrefsPackageVerificationRepository(Context context) {
+ mSharedPrefs = context.getApplicationContext()
+ .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ mContext = context.getApplicationContext();
+ }
+
+ public static PackageVerificationDataSource getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new SharedPrefsPackageVerificationRepository(
+ context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ @Override
+ public void clear() {
+ mSharedPrefs.edit().clear().apply();
+ }
+
+ @Override
+ public boolean putPackageSignatures(String packageName) {
+ String hash;
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ hash = SecurityHelper.getFingerprint(packageInfo, packageName);
+ logd("Hash for %s: %s", packageName, hash);
+ } catch (Exception e) {
+ logw(e, "Error getting hash for %s.", packageName);
+ return false;
+ }
+
+ if (!containsSignatureForPackage(packageName)) {
+ // Storage does not yet contain signature for this package name.
+ mSharedPrefs.edit().putString(packageName, hash).apply();
+ return true;
+ }
+ return containsMatchingSignatureForPackage(packageName, hash);
+ }
+
+ private boolean containsSignatureForPackage(String packageName) {
+ return mSharedPrefs.contains(packageName);
+ }
+
+ private boolean containsMatchingSignatureForPackage(String packageName,
+ String hash) {
+ return hash.equals(mSharedPrefs.getString(packageName, null));
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/dao/AutofillDao.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/dao/AutofillDao.java
new file mode 100644
index 0000000000000000000000000000000000000000..084532ce8f13621c2eddff096b1fa78d0d3dd0cb
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/dao/AutofillDao.java
@@ -0,0 +1,126 @@
+/*
+ * 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.example.android.autofill.service.data.source.local.dao;
+
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.OnConflictStrategy;
+import android.arch.persistence.room.Query;
+
+import com.example.android.autofill.service.model.AutofillDataset;
+import com.example.android.autofill.service.model.AutofillHint;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.model.ResourceIdHeuristic;
+
+import java.util.Collection;
+import java.util.List;
+
+@Dao
+public interface AutofillDao {
+ /**
+ * Fetches a list of datasets associated to autofill fields on the page.
+ *
+ * @param allAutofillHints Filtering parameter; represents all of the hints associated with
+ * all of the views on the page.
+ */
+ @Query("SELECT DISTINCT id, datasetName FROM FilledAutofillField, AutofillDataset" +
+ " WHERE AutofillDataset.id = FilledAutofillField.datasetId" +
+ " AND FilledAutofillField.fieldTypeName IN (:allAutofillHints)")
+ List getDatasets(List allAutofillHints);
+
+ @Query("SELECT DISTINCT id, datasetName FROM FilledAutofillField, AutofillDataset" +
+ " WHERE AutofillDataset.id = FilledAutofillField.datasetId")
+ List getAllDatasets();
+
+ /**
+ * Fetches a list of datasets associated to autofill fields. It should only return a dataset
+ * if that dataset has an autofill field associate with the view the user is focused on, and
+ * if that dataset's name matches the name passed in.
+ *
+ * @param fieldTypes Filtering parameter; represents all of the field types associated with
+ * all of the views on the page.
+ * @param datasetName Filtering parameter; only return datasets with this name.
+ */
+ @Query("SELECT DISTINCT id, datasetName FROM FilledAutofillField, AutofillDataset" +
+ " WHERE AutofillDataset.id = FilledAutofillField.datasetId" +
+ " AND AutofillDataset.datasetName = (:datasetName)" +
+ " AND FilledAutofillField.fieldTypeName IN (:fieldTypes)")
+ List getDatasetsWithName(
+ List fieldTypes, String datasetName);
+
+ @Query("SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, " +
+ "textTemplate, dateTemplate" +
+ " FROM FieldType, AutofillHint" +
+ " WHERE FieldType.typeName = AutofillHint.fieldTypeName" +
+ " UNION " +
+ "SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, " +
+ "textTemplate, dateTemplate" +
+ " FROM FieldType, ResourceIdHeuristic" +
+ " WHERE FieldType.typeName = ResourceIdHeuristic.fieldTypeName")
+ List getFieldTypesWithHints();
+
+ @Query("SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, " +
+ "textTemplate, dateTemplate" +
+ " FROM FieldType, AutofillHint" +
+ " WHERE FieldType.typeName = AutofillHint.fieldTypeName" +
+ " AND AutofillHint.autofillHint IN (:autofillHints)" +
+ " UNION " +
+ "SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, " +
+ "textTemplate, dateTemplate" +
+ " FROM FieldType, ResourceIdHeuristic" +
+ " WHERE FieldType.typeName = ResourceIdHeuristic.fieldTypeName")
+ List getFieldTypesForAutofillHints(List autofillHints);
+
+ @Query("SELECT DISTINCT id, datasetName FROM FilledAutofillField, AutofillDataset" +
+ " WHERE AutofillDataset.id = FilledAutofillField.datasetId" +
+ " AND AutofillDataset.id = (:datasetId)")
+ DatasetWithFilledAutofillFields getAutofillDatasetWithId(String datasetId);
+
+ @Query("SELECT * FROM FilledAutofillField" +
+ " WHERE FilledAutofillField.datasetId = (:datasetId)" +
+ " AND FilledAutofillField.fieldTypeName = (:fieldTypeName)")
+ FilledAutofillField getFilledAutofillField(String datasetId, String fieldTypeName);
+
+ @Query("SELECT * FROM FieldType" +
+ " WHERE FieldType.typeName = (:fieldTypeName)")
+ FieldType getFieldType(String fieldTypeName);
+
+ /**
+ * @param autofillFields Collection of autofill fields to be saved to the db.
+ */
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertFilledAutofillFields(Collection autofillFields);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertAutofillDataset(AutofillDataset datasets);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertAutofillHints(List autofillHints);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertResourceIdHeuristic(ResourceIdHeuristic resourceIdHeuristic);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertFieldTypes(List fieldTypes);
+
+
+ @Query("DELETE FROM AutofillDataset")
+ void clearAll();
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/AutofillDatabase.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/AutofillDatabase.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe007ced9186faf977caa3897cf6fc0570026dca
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/AutofillDatabase.java
@@ -0,0 +1,114 @@
+/*
+ * 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.example.android.autofill.service.data.source.local.db;
+
+import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.TypeConverters;
+import android.content.Context;
+import android.support.annotation.NonNull;
+
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.model.AutofillDataset;
+import com.example.android.autofill.service.model.AutofillHint;
+import com.example.android.autofill.service.model.DefaultFieldTypeWithHints;
+import com.example.android.autofill.service.model.FakeData;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.model.ResourceIdHeuristic;
+import com.example.android.autofill.service.util.AppExecutors;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.example.android.autofill.service.data.source.local.db.Converters.IntList;
+import static java.util.stream.Collectors.toList;
+
+@Database(entities = {
+ FilledAutofillField.class,
+ AutofillDataset.class,
+ FieldType.class,
+ AutofillHint.class,
+ ResourceIdHeuristic.class
+}, version = 1)
+@TypeConverters({Converters.class})
+public abstract class AutofillDatabase extends RoomDatabase {
+
+ private static final Object sLock = new Object();
+ private static AutofillDatabase sInstance;
+
+ public static AutofillDatabase getInstance(Context context,
+ DefaultFieldTypesSource defaultFieldTypesSource,
+ AppExecutors appExecutors) {
+ if (sInstance == null) {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = Room.databaseBuilder(context.getApplicationContext(),
+ AutofillDatabase.class, "AutofillSample.db")
+ .addCallback(new RoomDatabase.Callback() {
+ @Override
+ public void onCreate(@NonNull SupportSQLiteDatabase db) {
+ appExecutors.diskIO().execute(() -> {
+ List fieldTypes =
+ defaultFieldTypesSource.getDefaultFieldTypes();
+ AutofillDatabase autofillDatabase =
+ getInstance(context, defaultFieldTypesSource,
+ appExecutors);
+ autofillDatabase.saveDefaultFieldTypes(fieldTypes);
+ });
+ }
+
+ @Override
+ public void onOpen(@NonNull SupportSQLiteDatabase db) {
+ super.onOpen(db);
+ }
+ })
+ .build();
+ }
+ }
+ }
+ return sInstance;
+ }
+
+ private void saveDefaultFieldTypes(List defaultFieldTypes) {
+ List storedFieldTypes = new ArrayList<>();
+ List storedAutofillHints = new ArrayList<>();
+ for (DefaultFieldTypeWithHints defaultType : defaultFieldTypes) {
+ DefaultFieldTypeWithHints.DefaultFieldType defaultFieldType = defaultType.fieldType;
+ List autofillHints = defaultType.autofillHints;
+ IntList autofillTypes = new IntList(defaultFieldType.autofillTypes);
+ DefaultFieldTypeWithHints.DefaultFakeData defaultFakeData = defaultType.fieldType.fakeData;
+ FakeData fakeData = new FakeData(new Converters.StringList(
+ defaultFakeData.strictExampleSet), defaultFakeData.textTemplate,
+ defaultFakeData.dateTemplate);
+ FieldType storedFieldType = new FieldType(defaultFieldType.typeName, autofillTypes,
+ defaultFieldType.saveInfo, defaultFieldType.partition, fakeData);
+ storedFieldTypes.add(storedFieldType);
+ storedAutofillHints.addAll(autofillHints.stream()
+ .map((autofillHint) -> new AutofillHint(autofillHint,
+ storedFieldType.getTypeName())).collect(toList()));
+ }
+ AutofillDao autofillDao = autofillDao();
+ autofillDao.insertFieldTypes(storedFieldTypes);
+ autofillDao.insertAutofillHints(storedAutofillHints);
+ }
+
+ public abstract AutofillDao autofillDao();
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/Converters.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/Converters.java
new file mode 100644
index 0000000000000000000000000000000000000000..2829f8bd8ed4d49e553474bf8d8056140e9df591
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/Converters.java
@@ -0,0 +1,100 @@
+/*
+ * 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.example.android.autofill.service.data.source.local.db;
+
+import android.arch.persistence.room.TypeConverter;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * Type converter for Room database.
+ */
+public class Converters {
+
+ /**
+ * If database returns a {@link String} containing a comma delimited list of ints, this converts
+ * the {@link String} to an {@link IntList}.
+ */
+ @TypeConverter
+ public static IntList storedStringToIntList(String value) {
+ List strings = Arrays.asList(value.split("\\s*,\\s*"));
+ List ints = strings.stream().map(Integer::parseInt).collect(toList());
+ return new IntList(ints);
+ }
+
+ /**
+ * Converts the {@link IntList} back into a String containing a comma delimited list of
+ * ints.
+ */
+ @TypeConverter
+ public static String intListToStoredString(IntList list) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (Integer integer : list.ints) {
+ stringBuilder.append(integer).append(",");
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * If database returns a {@link String} containing a comma delimited list of Strings, this
+ * converts the {@link String} to a {@link StringList}.
+ */
+ @TypeConverter
+ public static StringList storedStringToStringList(String value) {
+ List strings = Arrays.asList(value.split("\\s*,\\s*"));
+ return new StringList(strings);
+ }
+
+
+ /**
+ * Converts the {@link StringList} back into a {@link String} containing a comma delimited
+ * list of {@link String}s.
+ */
+ @TypeConverter
+ public static String stringListToStoredString(StringList list) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (String string : list.strings) {
+ stringBuilder.append(string).append(",");
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Wrapper class for {@code List} so it can work with Room type converters.
+ */
+ public static class IntList {
+ public final List ints;
+
+ public IntList(List ints) {
+ this.ints = ints;
+ }
+ }
+
+ /**
+ * Wrapper class for {@code List} so it can work with Room type converters.
+ */
+ public static class StringList {
+ public final List strings;
+
+ public StringList(List ints) {
+ this.strings = ints;
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/AutofillDataset.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/AutofillDataset.java
new file mode 100644
index 0000000000000000000000000000000000000000..6b6596c83ff8a33e1a7ae4eaa92c21d9d2723253
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/AutofillDataset.java
@@ -0,0 +1,78 @@
+/*
+ * 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.example.android.autofill.service.model;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.support.annotation.NonNull;
+
+@Entity(primaryKeys = {"id"})
+public class AutofillDataset {
+ @NonNull
+ @ColumnInfo(name = "id")
+ private final String mId;
+
+ @NonNull
+ @ColumnInfo(name = "datasetName")
+ private final String mDatasetName;
+
+ @NonNull
+ @ColumnInfo(name = "packageName")
+ private final String mPackageName;
+
+ public AutofillDataset(@NonNull String id, @NonNull String datasetName,
+ @NonNull String packageName) {
+ mId = id;
+ mDatasetName = datasetName;
+ mPackageName = packageName;
+ }
+
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ @NonNull
+ public String getDatasetName() {
+ return mDatasetName;
+ }
+
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AutofillDataset that = (AutofillDataset) o;
+
+ if (!mId.equals(that.mId)) return false;
+ if (!mDatasetName.equals(that.mDatasetName)) return false;
+ return mPackageName.equals(that.mPackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mId.hashCode();
+ result = 31 * result + mDatasetName.hashCode();
+ result = 31 * result + mPackageName.hashCode();
+ return result;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/AutofillHint.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/AutofillHint.java
new file mode 100644
index 0000000000000000000000000000000000000000..be4aa7d9726a32af7118f4bde5fb356c242885d3
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/AutofillHint.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.model;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.ForeignKey;
+import android.support.annotation.NonNull;
+
+@Entity(primaryKeys = {"autofillHint"}, foreignKeys = @ForeignKey(
+ entity = FieldType.class, parentColumns = "typeName", childColumns = "fieldTypeName",
+ onDelete = ForeignKey.CASCADE))
+public class AutofillHint {
+
+ @NonNull
+ @ColumnInfo(name = "autofillHint")
+ public String mAutofillHint;
+
+ @NonNull
+ @ColumnInfo(name = "fieldTypeName")
+ public String mFieldTypeName;
+
+ public AutofillHint(@NonNull String autofillHint, @NonNull String fieldTypeName) {
+ this.mAutofillHint = autofillHint;
+ this.mFieldTypeName = fieldTypeName;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DalCheck.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DalCheck.java
new file mode 100644
index 0000000000000000000000000000000000000000..ede77ba9cc99a1cf40caf97c0ae6c63e78ed8cf1
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DalCheck.java
@@ -0,0 +1,23 @@
+/*
+ * 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.example.android.autofill.service.model;
+
+public class DalCheck {
+ public boolean linked;
+ public String maxAge;
+ public String debugString;
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DalInfo.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DalInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..44002ca5ebf7913905b2e02c65036b5864a1005c
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DalInfo.java
@@ -0,0 +1,67 @@
+/*
+ * 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.example.android.autofill.service.model;
+
+import static com.example.android.autofill.service.data.source.local.DigitalAssetLinksRepository.getCanonicalDomain;
+
+public class DalInfo {
+ private final String mWebDomain;
+ private final String mPackageName;
+
+ public DalInfo(String webDomain, String packageName) {
+ String canonicalDomain = getCanonicalDomain(webDomain);
+ final String fullDomain;
+ if (!webDomain.startsWith("http:") && !webDomain.startsWith("https:")) {
+ // Unfortunately AssistStructure.ViewNode does not tell what the domain is, so let's
+ // assume it's https
+ fullDomain = "https://" + canonicalDomain;
+ } else {
+ fullDomain = canonicalDomain;
+ }
+ mWebDomain = fullDomain;
+ mPackageName = packageName;
+ }
+
+ public String getWebDomain() {
+ return mWebDomain;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DalInfo dalInfo = (DalInfo) o;
+
+ if (mWebDomain != null ? !mWebDomain.equals(dalInfo.mWebDomain) :
+ dalInfo.mWebDomain != null)
+ return false;
+ return mPackageName != null ? mPackageName.equals(dalInfo.mPackageName) :
+ dalInfo.mPackageName == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mWebDomain != null ? mWebDomain.hashCode() : 0;
+ result = 31 * result + (mPackageName != null ? mPackageName.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DatasetWithFilledAutofillFields.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DatasetWithFilledAutofillFields.java
new file mode 100644
index 0000000000000000000000000000000000000000..06b529fec346a1c50ae2b274b87d34e9aaf6fa54
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DatasetWithFilledAutofillFields.java
@@ -0,0 +1,60 @@
+/*
+ * 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.example.android.autofill.service.model;
+
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Relation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DatasetWithFilledAutofillFields {
+ @Embedded
+ public AutofillDataset autofillDataset;
+
+ @Relation(parentColumn = "id", entityColumn = "datasetId", entity = FilledAutofillField.class)
+ public List filledAutofillFields;
+
+ public void add(FilledAutofillField filledAutofillField) {
+ if (filledAutofillFields == null) {
+ this.filledAutofillFields = new ArrayList<>();
+ }
+ this.filledAutofillFields.add(filledAutofillField);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DatasetWithFilledAutofillFields that = (DatasetWithFilledAutofillFields) o;
+
+ if (autofillDataset != null ? !autofillDataset.equals(that.autofillDataset) :
+ that.autofillDataset != null)
+ return false;
+ return filledAutofillFields != null ?
+ filledAutofillFields.equals(that.filledAutofillFields) :
+ that.filledAutofillFields == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = autofillDataset != null ? autofillDataset.hashCode() : 0;
+ result = 31 * result + (filledAutofillFields != null ? filledAutofillFields.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DefaultFieldTypeWithHints.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DefaultFieldTypeWithHints.java
new file mode 100644
index 0000000000000000000000000000000000000000..70ad382e2e13cd08ef3d63953721845c6ec03aa0
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/DefaultFieldTypeWithHints.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.model;
+
+import java.util.List;
+
+/**
+ * JSON model class, representing an autofillable field type. It is called "Default" because only
+ * default field types will be included in the packaged JSON. After the JSON is initially read and
+ * written to the DB, the field types can be dynamically added, modified, and removed.
+ *
+ * It contains all of the metadata about the field type. For example, if the field type is
+ * "country", this is the JSON object associated with it:
+
+ {
+ "autofillHints": [
+ "country"
+ ],
+ "fieldType": {
+ "autofillTypes": [
+ 1,
+ 3
+ ],
+ "fakeData": {
+ "strictExampleSet": [],
+ "textTemplate": "countryseed"
+ },
+ "partition": 1,
+ "saveInfo": 2,
+ "typeName": "country"
+ }
+ }
+
+ */
+public class DefaultFieldTypeWithHints {
+ public DefaultFieldType fieldType;
+ public List autofillHints;
+
+ public static class DefaultFieldType {
+ public String typeName;
+ public List autofillTypes;
+ public int saveInfo;
+ public int partition;
+ public DefaultFakeData fakeData;
+ }
+
+ public static class DefaultFakeData {
+ public List strictExampleSet;
+ public String textTemplate;
+ public String dateTemplate;
+
+ public DefaultFakeData(List strictExampleSet, String textTemplate,
+ String dateTemplate) {
+ this.strictExampleSet = strictExampleSet;
+ this.textTemplate = textTemplate;
+ this.dateTemplate = dateTemplate;
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FakeData.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FakeData.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d0837b2ca70a745bd64e0697489add60133ff17
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FakeData.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.model;
+
+import com.example.android.autofill.service.data.source.local.db.Converters;
+
+public class FakeData {
+ public Converters.StringList strictExampleSet;
+ public String textTemplate;
+ public String dateTemplate;
+
+ public FakeData(Converters.StringList strictExampleSet, String textTemplate, String dateTemplate) {
+ this.strictExampleSet = strictExampleSet;
+ this.textTemplate = textTemplate;
+ this.dateTemplate = dateTemplate;
+ }
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldType.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldType.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d285f349d742395165e96f95c50affa0b4d8cc9
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldType.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.model;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Entity;
+import android.support.annotation.NonNull;
+
+import static com.example.android.autofill.service.data.source.local.db.Converters.IntList;
+
+@Entity(primaryKeys = {"typeName"})
+public class FieldType {
+ @NonNull
+ @ColumnInfo(name = "typeName")
+ private final String mTypeName;
+
+ @NonNull
+ @ColumnInfo(name = "autofillTypes")
+ private final IntList mAutofillTypes;
+
+ @NonNull
+ @ColumnInfo(name = "saveInfo")
+ private final Integer mSaveInfo;
+
+ @NonNull
+ @ColumnInfo(name = "partition")
+ private final Integer mPartition;
+
+ @NonNull
+ @Embedded
+ private final FakeData mFakeData;
+
+ public FieldType(@NonNull String typeName, @NonNull IntList autofillTypes,
+ @NonNull Integer saveInfo, @NonNull Integer partition, @NonNull FakeData fakeData) {
+ mTypeName = typeName;
+ mAutofillTypes = autofillTypes;
+ mSaveInfo = saveInfo;
+ mPartition = partition;
+ mFakeData = fakeData;
+ }
+
+ @NonNull
+ public String getTypeName() {
+ return mTypeName;
+ }
+
+ @NonNull
+ public IntList getAutofillTypes() {
+ return mAutofillTypes;
+ }
+
+ @NonNull
+ public Integer getSaveInfo() {
+ return mSaveInfo;
+ }
+
+ @NonNull
+ public Integer getPartition() {
+ return mPartition;
+ }
+
+ @NonNull
+ public FakeData getFakeData() {
+ return mFakeData;
+ }
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHeuristics.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHeuristics.java
new file mode 100644
index 0000000000000000000000000000000000000000..97a646e5715151129cfdf26d661cad1392aee9eb
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHeuristics.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.model;
+
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Relation;
+
+import java.util.List;
+
+public class FieldTypeWithHeuristics {
+ @Embedded
+ public FieldType fieldType;
+
+ @Relation(parentColumn = "typeName", entityColumn = "fieldTypeName", entity = AutofillHint.class)
+ public List autofillHints;
+
+ @Relation(parentColumn = "typeName", entityColumn = "fieldTypeName", entity = ResourceIdHeuristic.class)
+ public List resourceIdHeuristics;
+
+ public FieldType getFieldType() {
+ return fieldType;
+ }
+
+ public List getAutofillHints() {
+ return autofillHints;
+ }
+
+ public List getResourceIdHeuristics() {
+ return resourceIdHeuristics;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FilledAutofillField.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FilledAutofillField.java
new file mode 100644
index 0000000000000000000000000000000000000000..85ad05ac73c1adcd89f580bb663c0cf362ff0899
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FilledAutofillField.java
@@ -0,0 +1,146 @@
+/*
+ * 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.example.android.autofill.service.model;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.ForeignKey;
+import android.arch.persistence.room.Ignore;
+import android.support.annotation.NonNull;
+
+import javax.annotation.Nullable;
+
+@Entity(primaryKeys = {"datasetId", "fieldTypeName"}, foreignKeys = {
+ @ForeignKey(entity = AutofillDataset.class, parentColumns = "id",
+ childColumns = "datasetId", onDelete = ForeignKey.CASCADE),
+ @ForeignKey(entity = FieldType.class, parentColumns = "typeName",
+ childColumns = "fieldTypeName", onDelete = ForeignKey.CASCADE)
+})
+public class FilledAutofillField {
+
+ @NonNull
+ @ColumnInfo(name = "datasetId")
+ private final String mDatasetId;
+
+ @Nullable
+ @ColumnInfo(name = "textValue")
+ private final String mTextValue;
+
+ @Nullable
+ @ColumnInfo(name = "dateValue")
+ private final Long mDateValue;
+
+ @Nullable
+ @ColumnInfo(name = "toggleValue")
+ private final Boolean mToggleValue;
+
+ @NonNull
+ @ColumnInfo(name = "fieldTypeName")
+ private final String mFieldTypeName;
+
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String fieldTypeName,
+ @Nullable String textValue, @Nullable Long dateValue,
+ @Nullable Boolean toggleValue) {
+ mDatasetId = datasetId;
+ mFieldTypeName = fieldTypeName;
+ mTextValue = textValue;
+ mDateValue = dateValue;
+ mToggleValue = toggleValue;
+ }
+
+ @Ignore
+ public FilledAutofillField(@NonNull String datasetId,
+ @NonNull String fieldTypeName, @Nullable String textValue, @Nullable Long dateValue) {
+ this(datasetId, fieldTypeName, textValue, dateValue, null);
+ }
+
+ @Ignore
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String fieldTypeName,
+ @Nullable String textValue) {
+ this(datasetId, fieldTypeName, textValue, null, null);
+ }
+
+ @Ignore
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String fieldTypeName,
+ @Nullable Long dateValue) {
+ this(datasetId, fieldTypeName, null, dateValue, null);
+ }
+
+ @Ignore
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String fieldTypeName,
+ @Nullable Boolean toggleValue) {
+ this(datasetId, fieldTypeName, null, null, toggleValue);
+ }
+
+ @Ignore
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String fieldTypeName) {
+ this(datasetId, fieldTypeName, null, null, null);
+ }
+
+ @NonNull
+ public String getDatasetId() {
+ return mDatasetId;
+ }
+
+ @Nullable
+ public String getTextValue() {
+ return mTextValue;
+ }
+
+ @Nullable
+ public Long getDateValue() {
+ return mDateValue;
+ }
+
+ @Nullable
+ public Boolean getToggleValue() {
+ return mToggleValue;
+ }
+
+ @NonNull
+ public String getFieldTypeName() {
+ return mFieldTypeName;
+ }
+
+ 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;
+ if (mToggleValue != null ? !mToggleValue.equals(that.mToggleValue) : that.mToggleValue != null)
+ return false;
+ return mFieldTypeName.equals(that.mFieldTypeName);
+ }
+
+ @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);
+ result = 31 * result + mFieldTypeName.hashCode();
+ return result;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/ResourceIdHeuristic.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/ResourceIdHeuristic.java
new file mode 100644
index 0000000000000000000000000000000000000000..81c9abef01e985206062c2a1269bc74ba94535d5
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/ResourceIdHeuristic.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.model;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.ForeignKey;
+import android.support.annotation.NonNull;
+
+@Entity(primaryKeys = {"resourceIdHeuristic", "packageName"}, foreignKeys = @ForeignKey(
+ entity = FieldType.class, parentColumns = "typeName", childColumns = "fieldTypeName",
+ onDelete = ForeignKey.CASCADE))
+public class ResourceIdHeuristic {
+
+ @NonNull
+ @ColumnInfo(name = "resourceIdHeuristic")
+ public String mResourceIdHeuristic;
+
+ @NonNull
+ @ColumnInfo(name = "packageName")
+ public String mPackageName;
+
+ @NonNull
+ @ColumnInfo(name = "fieldTypeName")
+ public String mFieldTypeName;
+
+ public ResourceIdHeuristic(@NonNull String resourceIdHeuristic, @NonNull String fieldTypeName,
+ @NonNull String packageName) {
+ mResourceIdHeuristic = resourceIdHeuristic;
+ mFieldTypeName = fieldTypeName;
+ mPackageName = packageName;
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/MyPreferences.java
similarity index 75%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java
rename to input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/MyPreferences.java
index 34d63ef3a7d48cd895674235e4aa65e9afc913b6..8770ef8318a0d3550470b89d59483e547fce5518 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/MyPreferences.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.example.android.autofillframework.multidatasetservice.settings;
+package com.example.android.autofill.service.settings;
import android.content.Context;
import android.content.SharedPreferences;
@@ -21,10 +21,14 @@ import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.support.annotation.NonNull;
+import com.example.android.autofill.service.util.Util;
+
public class MyPreferences {
private static final String RESPONSE_AUTH_KEY = "response_auth";
private static final String DATASET_AUTH_KEY = "dataset_auth";
private static final String MASTER_PASSWORD_KEY = "master_password";
+ private static final String LOGGING_LEVEL = "logging_level";
+ private static final String DAL_CHECK_REQUIRED = "dal_check_required";
private static MyPreferences sInstance;
private final SharedPreferences mPrefs;
@@ -85,4 +89,22 @@ public class MyPreferences {
public void clearCredentials() {
mPrefs.edit().remove(MASTER_PASSWORD_KEY).apply();
}
+
+ public Util.LogLevel getLoggingLevel() {
+ return Util.LogLevel.values()[mPrefs.getInt(LOGGING_LEVEL, Util.LogLevel.Off.ordinal())];
+ }
+
+ public void setLoggingLevel(Util.LogLevel level) {
+ mPrefs.edit().putInt(LOGGING_LEVEL, level.ordinal()).apply();
+ Util.setLoggingLevel(level);
+ }
+
+ public Util.DalCheckRequirement getDalCheckRequirement() {
+ return Util.DalCheckRequirement.values()[mPrefs.getInt(DAL_CHECK_REQUIRED,
+ Util.DalCheckRequirement.AllUrls.ordinal())];
+ }
+
+ public void setDalCheckRequired(Util.DalCheckRequirement level) {
+ mPrefs.edit().putInt(DAL_CHECK_REQUIRED, level.ordinal()).apply();
+ }
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/SettingsActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/SettingsActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fd87df0555ea5176de5e58ccee7596f52042d2b
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/SettingsActivity.java
@@ -0,0 +1,371 @@
+/*
+ * 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.example.android.autofill.service.settings;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.autofill.AutofillManager;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.NumberPicker;
+import android.widget.RadioGroup;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.example.android.autofill.service.R;
+import com.example.android.autofill.service.data.AutofillDataBuilder;
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.FakeAutofillDataBuilder;
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.data.source.PackageVerificationDataSource;
+import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource;
+import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
+import com.example.android.autofill.service.data.source.local.SharedPrefsPackageVerificationRepository;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.util.AppExecutors;
+import com.example.android.autofill.service.util.Util;
+import com.google.gson.GsonBuilder;
+
+import java.util.List;
+
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement.AllUrls;
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement.Disabled;
+import static com.example.android.autofill.service.util.Util.DalCheckRequirement.LoginOnly;
+import static com.example.android.autofill.service.util.Util.logd;
+import static com.example.android.autofill.service.util.Util.logw;
+
+public class SettingsActivity extends AppCompatActivity {
+ private static final String TAG = "SettingsActivity";
+ private static final int REQUEST_CODE_SET_DEFAULT = 1;
+ private AutofillManager mAutofillManager;
+ private LocalAutofillDataSource mLocalAutofillDataSource;
+ private PackageVerificationDataSource mPackageVerificationDataSource;
+ private MyPreferences mPreferences;
+ private String mPackageName;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.multidataset_service_settings_activity);
+ SharedPreferences localAfDataSourceSharedPrefs =
+ getSharedPreferences(LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ DefaultFieldTypesSource defaultFieldTypesSource =
+ DefaultFieldTypesLocalJsonSource.getInstance(getResources(),
+ new GsonBuilder().create());
+ AutofillDao autofillDao = AutofillDatabase.getInstance(
+ this, defaultFieldTypesSource, new AppExecutors()).autofillDao();
+ mPackageName = getPackageName();
+ mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(localAfDataSourceSharedPrefs,
+ autofillDao, new AppExecutors());
+ mAutofillManager = getSystemService(AutofillManager.class);
+ mPackageVerificationDataSource =
+ SharedPrefsPackageVerificationRepository.getInstance(this);
+ mPreferences = MyPreferences.getInstance(this);
+ setupSettingsSwitch(R.id.settings_auth_responses_container,
+ R.id.settings_auth_responses_label,
+ R.id.settings_auth_responses_switch,
+ mPreferences.isResponseAuth(),
+ (compoundButton, isResponseAuth) -> mPreferences.setResponseAuth(isResponseAuth));
+ setupSettingsSwitch(R.id.settings_auth_datasets_container,
+ R.id.settings_auth_datasets_label,
+ R.id.settings_auth_datasets_switch,
+ mPreferences.isDatasetAuth(),
+ (compoundButton, isDatasetAuth) -> mPreferences.setDatasetAuth(isDatasetAuth));
+ setupSettingsButton(R.id.settings_add_data_container,
+ R.id.settings_add_data_label,
+ R.id.settings_add_data_icon,
+ (view) -> buildAddDataDialog().show());
+ setupSettingsButton(R.id.settings_clear_data_container,
+ R.id.settings_clear_data_label,
+ R.id.settings_clear_data_icon,
+ (view) -> buildClearDataDialog().show());
+ setupSettingsButton(R.id.settings_auth_credentials_container,
+ R.id.settings_auth_credentials_label,
+ R.id.settings_auth_credentials_icon,
+ (view) -> {
+ if (mPreferences.getMasterPassword() != null) {
+ buildCurrentCredentialsDialog().show();
+ } else {
+ buildNewCredentialsDialog().show();
+ }
+ });
+ setupSettingsSwitch(R.id.settingsSetServiceContainer,
+ R.id.settingsSetServiceLabel,
+ R.id.settingsSetServiceSwitch,
+ mAutofillManager.hasEnabledAutofillServices(),
+ (compoundButton, serviceSet) -> setService(serviceSet));
+ RadioGroup loggingLevelContainer = findViewById(R.id.loggingLevelContainer);
+ Util.LogLevel loggingLevel = mPreferences.getLoggingLevel();
+ Util.setLoggingLevel(loggingLevel);
+ switch (loggingLevel) {
+ case Off:
+ loggingLevelContainer.check(R.id.loggingOff);
+ break;
+ case Debug:
+ loggingLevelContainer.check(R.id.loggingDebug);
+ break;
+ case Verbose:
+ loggingLevelContainer.check(R.id.loggingVerbose);
+ break;
+ }
+ loggingLevelContainer.setOnCheckedChangeListener((group, checkedId) -> {
+ switch (checkedId) {
+ case R.id.loggingOff:
+ mPreferences.setLoggingLevel(Util.LogLevel.Off);
+ break;
+ case R.id.loggingDebug:
+ mPreferences.setLoggingLevel(Util.LogLevel.Debug);
+ break;
+ case R.id.loggingVerbose:
+ mPreferences.setLoggingLevel(Util.LogLevel.Verbose);
+ break;
+ }
+ });
+ RadioGroup dalCheckRequirementContainer = findViewById(R.id.dalCheckRequirementContainer);
+ Util.DalCheckRequirement dalCheckRequirement = mPreferences.getDalCheckRequirement();
+ switch (dalCheckRequirement) {
+ case Disabled:
+ dalCheckRequirementContainer.check(R.id.dalDisabled);
+ break;
+ case LoginOnly:
+ dalCheckRequirementContainer.check(R.id.dalLoginOnly);
+ break;
+ case AllUrls:
+ dalCheckRequirementContainer.check(R.id.dalAllUrls);
+ break;
+ }
+ dalCheckRequirementContainer.setOnCheckedChangeListener((group, checkedId) -> {
+ switch (checkedId) {
+ case R.id.dalDisabled:
+ mPreferences.setDalCheckRequired(Disabled);
+ break;
+ case R.id.dalLoginOnly:
+ mPreferences.setDalCheckRequired(LoginOnly);
+ break;
+ case R.id.dalAllUrls:
+ mPreferences.setDalCheckRequired(AllUrls);
+ break;
+ }
+ });
+ }
+
+ private AlertDialog buildClearDataDialog() {
+ return new AlertDialog.Builder(SettingsActivity.this)
+ .setMessage(R.string.settings_clear_data_confirmation)
+ .setTitle(R.string.settings_clear_data_confirmation_title)
+ .setNegativeButton(R.string.settings_cancel, null)
+ .setPositiveButton(R.string.settings_ok, (dialog, which) -> {
+ mLocalAutofillDataSource.clear();
+ mPackageVerificationDataSource.clear();
+ mPreferences.clearCredentials();
+ dialog.dismiss();
+ })
+ .create();
+ }
+
+ private AlertDialog buildAddDataDialog() {
+ NumberPicker numberOfDatasetsPicker = LayoutInflater
+ .from(SettingsActivity.this)
+ .inflate(R.layout.multidataset_service_settings_add_data_dialog, null)
+ .findViewById(R.id.number_of_datasets_picker);
+ numberOfDatasetsPicker.setMinValue(0);
+ numberOfDatasetsPicker.setMaxValue(10);
+ numberOfDatasetsPicker.setWrapSelectorWheel(false);
+ return new AlertDialog.Builder(SettingsActivity.this)
+ .setTitle(R.string.settings_add_data_title)
+ .setNegativeButton(R.string.settings_cancel, null)
+ .setMessage(R.string.settings_select_number_of_datasets)
+ .setView(numberOfDatasetsPicker)
+ .setPositiveButton(R.string.settings_ok, (dialog, which) -> {
+ int numOfDatasets = numberOfDatasetsPicker.getValue();
+ mLocalAutofillDataSource.getFieldTypes(new DataCallback>() {
+ @Override
+ public void onLoaded(List fieldTypes) {
+ boolean saved = buildAndSaveMockedAutofillFieldCollections(
+ fieldTypes, numOfDatasets);
+ dialog.dismiss();
+ if (saved) {
+ Snackbar.make(findViewById(R.id.settings_layout),
+ getResources().getQuantityString(
+ R.plurals.settings_add_data_success,
+ numOfDatasets, numOfDatasets),
+ Snackbar.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ })
+ .create();
+ }
+
+ public boolean buildAndSaveMockedAutofillFieldCollections(List fieldTypes,
+ int numOfDatasets) {
+ if (numOfDatasets < 0 || numOfDatasets > 10) {
+ logw("Number of Datasets (%d) out of range.", numOfDatasets);
+ }
+ for (int i = 0; i < numOfDatasets; i++) {
+ int datasetNumber = mLocalAutofillDataSource.getDatasetNumber();
+ AutofillDataBuilder autofillDataBuilder =
+ new FakeAutofillDataBuilder(fieldTypes, mPackageName, datasetNumber);
+ List datasetsWithFilledAutofillFields =
+ autofillDataBuilder.buildDatasetsByPartition(datasetNumber);
+ // Save datasets to database.
+ mLocalAutofillDataSource.saveAutofillDatasets(datasetsWithFilledAutofillFields);
+ }
+ return true;
+ }
+
+ private AlertDialog.Builder prepareCredentialsDialog() {
+ return new AlertDialog.Builder(SettingsActivity.this)
+ .setTitle(R.string.settings_auth_change_credentials_title)
+ .setNegativeButton(R.string.settings_cancel, null);
+ }
+
+ private AlertDialog buildCurrentCredentialsDialog() {
+ final EditText currentPasswordField = LayoutInflater
+ .from(SettingsActivity.this)
+ .inflate(R.layout.multidataset_service_settings_authentication_dialog, null)
+ .findViewById(R.id.master_password_field);
+ return prepareCredentialsDialog()
+ .setMessage(R.string.settings_auth_enter_current_password)
+ .setView(currentPasswordField)
+ .setPositiveButton(R.string.settings_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String password = currentPasswordField.getText().toString();
+ if (mPreferences.getMasterPassword()
+ .equals(password)) {
+ buildNewCredentialsDialog().show();
+ dialog.dismiss();
+ }
+ }
+ })
+ .create();
+ }
+
+ private AlertDialog buildNewCredentialsDialog() {
+ final EditText newPasswordField = LayoutInflater
+ .from(SettingsActivity.this)
+ .inflate(R.layout.multidataset_service_settings_authentication_dialog, null)
+ .findViewById(R.id.master_password_field);
+ return prepareCredentialsDialog()
+ .setMessage(R.string.settings_auth_enter_new_password)
+ .setView(newPasswordField)
+ .setPositiveButton(R.string.settings_ok, (dialog, which) -> {
+ String password = newPasswordField.getText().toString();
+ mPreferences.setMasterPassword(password);
+ dialog.dismiss();
+ })
+ .create();
+ }
+
+ private void setupSettingsSwitch(int containerId, int labelId, int switchId, boolean checked,
+ CompoundButton.OnCheckedChangeListener checkedChangeListener) {
+ ViewGroup container = findViewById(containerId);
+ String switchLabel = ((TextView) container.findViewById(labelId)).getText().toString();
+ final Switch switchView = container.findViewById(switchId);
+ switchView.setContentDescription(switchLabel);
+ switchView.setChecked(checked);
+ container.setOnClickListener((view) -> switchView.performClick());
+ switchView.setOnCheckedChangeListener(checkedChangeListener);
+ }
+
+ private void setupSettingsButton(int containerId, int labelId, int imageViewId,
+ final View.OnClickListener onClickListener) {
+ ViewGroup container = findViewById(containerId);
+ TextView buttonLabel = container.findViewById(labelId);
+ String buttonLabelText = buttonLabel.getText().toString();
+ ImageView imageView = container.findViewById(imageViewId);
+ imageView.setContentDescription(buttonLabelText);
+ container.setOnClickListener(onClickListener);
+ }
+
+ private void setService(boolean enableService) {
+ if (enableService) {
+ startEnableService();
+ } else {
+ disableService();
+ }
+ }
+
+ private void disableService() {
+ if (mAutofillManager != null && mAutofillManager.hasEnabledAutofillServices()) {
+ mAutofillManager.disableAutofillServices();
+ Snackbar.make(findViewById(R.id.settings_layout),
+ R.string.settings_autofill_disabled_message, Snackbar.LENGTH_SHORT).show();
+ } else {
+ logd("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"));
+ logd(TAG, "enableService(): intent=%s", intent);
+ startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT);
+ } else {
+ logd("Sample service already enabled.");
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ logd(TAG, "onActivityResult(): req=%s", requestCode);
+ switch (requestCode) {
+ case REQUEST_CODE_SET_DEFAULT:
+ onDefaultServiceSet(resultCode);
+ break;
+ }
+ }
+
+ private void onDefaultServiceSet(int resultCode) {
+ logd(TAG, "resultCode=%d", resultCode);
+ switch (resultCode) {
+ case RESULT_OK:
+ logd("Autofill service set.");
+ Snackbar.make(findViewById(R.id.settings_layout),
+ R.string.settings_autofill_service_set, Snackbar.LENGTH_SHORT)
+ .show();
+ break;
+ case RESULT_CANCELED:
+ logd("Autofill service not selected.");
+ Snackbar.make(findViewById(R.id.settings_layout),
+ R.string.settings_autofill_service_not_set, Snackbar.LENGTH_SHORT)
+ .show();
+ break;
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java
new file mode 100644
index 0000000000000000000000000000000000000000..843440c9519b2d504dafd8b3f953a7cf7591c4ba
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.simple;
+
+import android.app.assist.AssistStructure.ViewNode;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import com.example.android.autofill.service.MyAutofillService;
+
+/**
+ * A basic service that uses some rudimentary heuristics to identify fields that are not explicitly
+ * marked with autofill hints.
+ *
+ * The goal of this class is to provide a simple autofill service implementation that is easy
+ * to understand and extend, but it should not be used as-is on real apps because
+ * it lacks fundamental security requirements such as data partitioning and package verification
+ * &mdashthese requirements are fullfilled by {@link MyAutofillService}. *
+ */
+public class BasicHeuristicsService extends BasicService {
+
+ private static final String TAG = "BasicHeuristicsService";
+
+ @Override
+ @Nullable
+ protected String getHint(@NonNull ViewNode node) {
+
+ // First try the explicit autofill hints...
+
+ String hint = super.getHint(node);
+ if (hint != null) return hint;
+
+ // Then try some rudimentary heuristics based on other node properties
+
+ String viewHint = node.getHint();
+ hint = inferHint(viewHint);
+ if (hint != null) {
+ Log.d(TAG, "Found hint using view hint(" + viewHint + "): " + hint);
+ return hint;
+ } else if (!TextUtils.isEmpty(viewHint)) {
+ Log.v(TAG, "No hint using view hint: " + viewHint);
+ }
+
+ String resourceId = node.getIdEntry();
+ hint = inferHint(resourceId);
+ if (hint != null) {
+ Log.d(TAG, "Found hint using resourceId(" + resourceId + "): " + hint);
+ return hint;
+ } else if (!TextUtils.isEmpty(resourceId)) {
+ Log.v(TAG, "No hint using resourceId: " + resourceId);
+ }
+
+ CharSequence text = node.getText();
+ CharSequence className = node.getClassName();
+ if (text != null && className != null && className.toString().contains("EditText")) {
+ hint = inferHint(text.toString());
+ if (hint != null) {
+ // NODE: text should not be logged, as it could contain PII
+ Log.d(TAG, "Found hint using text(" + text + "): " + hint);
+ return hint;
+ }
+ } else if (!TextUtils.isEmpty(text)) {
+ // NODE: text should not be logged, as it could contain PII
+ Log.v(TAG, "No hint using text: " + text + " and class " + className);
+ }
+ return null;
+ }
+
+ /**
+ * Uses heuristics to infer an autofill hint from a {@code string}.
+ *
+ * @return standard autofill hint, or {@code null} when it could not be inferred.
+ */
+ @Nullable
+ protected String inferHint(@Nullable String string) {
+ if (string == null) return null;
+
+ string = string.toLowerCase();
+ if (string.contains("password")) return View.AUTOFILL_HINT_PASSWORD;
+ if (string.contains("username")
+ || (string.contains("login") && string.contains("id")))
+ return View.AUTOFILL_HINT_USERNAME;
+ if (string.contains("email")) return View.AUTOFILL_HINT_EMAIL_ADDRESS;
+ if (string.contains("name")) return View.AUTOFILL_HINT_NAME;
+ if (string.contains("phone")) return View.AUTOFILL_HINT_PHONE;
+
+ return null;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java
new file mode 100644
index 0000000000000000000000000000000000000000..92d6436f18779959278659bbc8fefa8bcac2eb9e
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 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.example.android.autofill.service.simple;
+
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.os.CancellationSignal;
+import android.service.autofill.AutofillService;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillCallback;
+import android.service.autofill.FillContext;
+import android.service.autofill.FillRequest;
+import android.service.autofill.FillResponse;
+import android.service.autofill.SaveCallback;
+import android.service.autofill.SaveInfo;
+import android.service.autofill.SaveRequest;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.ArrayMap;
+import android.util.Log;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
+import android.widget.Toast;
+
+import com.example.android.autofill.service.MyAutofillService;
+import com.example.android.autofill.service.R;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * A very basic {@link AutofillService} implementation that only shows dynamic-generated datasets
+ * and don't persist the saved data.
+ *
+ *
The goal of this class is to provide a simple autofill service implementation that is easy
+ * to understand and extend, but it should not be used as-is on real apps because
+ * it lacks fundamental security requirements such as data partitioning and package verification
+ * &mdashthese requirements are fullfilled by {@link MyAutofillService}.
+ */
+public class BasicService extends AutofillService {
+
+ private static final String TAG = "BasicService";
+
+ /**
+ * Number of datasets sent on each request - we're simple, that value is hardcoded in our DNA!
+ */
+ private static final int NUMBER_DATASETS = 4;
+
+ @Override
+ public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
+ FillCallback callback) {
+ Log.d(TAG, "onFillRequest()");
+
+ // Find autofillable fields
+ AssistStructure structure = getLatestAssistStructure(request);
+ Map fields = getAutofillableFields(structure);
+ Log.d(TAG, "autofillable fields:" + fields);
+
+ if (fields.isEmpty()) {
+ toast("No autofill hints found");
+ callback.onSuccess(null);
+ return;
+ }
+
+ // Create the base response
+ FillResponse.Builder response = new FillResponse.Builder();
+
+ // 1.Add the dynamic datasets
+ String packageName = getApplicationContext().getPackageName();
+ for (int i = 1; i <= NUMBER_DATASETS; i++) {
+ Dataset.Builder dataset = new Dataset.Builder();
+ for (Entry field : fields.entrySet()) {
+ String hint = field.getKey();
+ AutofillId id = field.getValue();
+ String value = hint + i;
+ // We're simple - our dataset values are hardcoded as "hintN" (for example,
+ // "username1", "username2") and they're displayed as such, except if they're a
+ // password
+ String displayValue = hint.contains("password") ? "password for #" + i : value;
+ RemoteViews presentation = newDatasetPresentation(packageName, displayValue);
+ dataset.setValue(id, AutofillValue.forText(value), presentation);
+ }
+ response.addDataset(dataset.build());
+ }
+
+ // 2.Add save info
+ Collection ids = fields.values();
+ AutofillId[] requiredIds = new AutofillId[ids.size()];
+ ids.toArray(requiredIds);
+ response.setSaveInfo(
+ // We're simple, so we're generic
+ new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC, requiredIds).build());
+
+ // 3.Profit!
+ callback.onSuccess(response.build());
+ }
+
+ @Override
+ public void onSaveRequest(SaveRequest request, SaveCallback callback) {
+ Log.d(TAG, "onSaveRequest()");
+ toast("Save not supported");
+ callback.onSuccess();
+ }
+
+ /**
+ * Parses the {@link AssistStructure} representing the activity being autofilled, and returns a
+ * map of autofillable fields (represented by their autofill ids) mapped by the hint associate
+ * with them.
+ *
+ * An autofillable field is a {@link ViewNode} whose {@link #getHint(ViewNode)} metho
+ */
+ @NonNull
+ private Map getAutofillableFields(@NonNull AssistStructure structure) {
+ Map fields = new ArrayMap<>();
+ int nodes = structure.getWindowNodeCount();
+ for (int i = 0; i < nodes; i++) {
+ ViewNode node = structure.getWindowNodeAt(i).getRootViewNode();
+ addAutofillableFields(fields, node);
+ }
+ return fields;
+ }
+
+ /**
+ * Adds any autofillable view from the {@link ViewNode} and its descendants to the map.
+ */
+ private void addAutofillableFields(@NonNull Map fields,
+ @NonNull ViewNode node) {
+ int type = node.getAutofillType();
+ // We're simple, we just autofill text fields.
+ if (type == View.AUTOFILL_TYPE_TEXT) {
+ String hint = getHint(node);
+ if (hint != null) {
+ AutofillId id = node.getAutofillId();
+ if (!fields.containsKey(hint)) {
+ Log.v(TAG, "Setting hint " + hint + " on " + id);
+ fields.put(hint, id);
+ } else {
+ Log.v(TAG, "Ignoring hint " + hint + " on " + id
+ + " because it was already set");
+ }
+ }
+ }
+ int childrenSize = node.getChildCount();
+ for (int i = 0; i < childrenSize; i++) {
+ addAutofillableFields(fields, node.getChildAt(i));
+ }
+ }
+
+ /**
+ * Gets the autofill hint associated with the given node.
+ *
+ * By default it just return the first entry on the node's
+ * {@link ViewNode#getAutofillHints() autofillHints} (when available), but subclasses could
+ * extend it to use heuristics when the app developer didn't explicitly provide these hints.
+ *
+ */
+ @Nullable
+ protected String getHint(@NonNull ViewNode node) {
+ String[] hints = node.getAutofillHints();
+ if (hints == null) return null;
+
+ // We're simple, we only care about the first hint
+ String hint = hints[0].toLowerCase();
+ return hint;
+ }
+
+ /**
+ * Helper method to get the {@link AssistStructure} associated with the latest request
+ * in an autofill context.
+ */
+ @NonNull
+ private static AssistStructure getLatestAssistStructure(@NonNull FillRequest request) {
+ List fillContexts = request.getFillContexts();
+ return fillContexts.get(fillContexts.size() - 1).getStructure();
+ }
+
+ /**
+ * Helper method to create a dataset presentation with the given text.
+ */
+ @NonNull
+ private static RemoteViews newDatasetPresentation(@NonNull String packageName,
+ @NonNull CharSequence text) {
+ RemoteViews presentation =
+ new RemoteViews(packageName, R.layout.multidataset_service_list_item);
+ presentation.setTextViewText(R.id.text, text);
+ presentation.setImageViewResource(R.id.icon, R.mipmap.ic_launcher);
+ return presentation;
+ }
+
+ /**
+ * Displays a toast with the given message.
+ */
+ private void toast(@NonNull CharSequence message) {
+ Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/AppExecutors.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/AppExecutors.java
new file mode 100644
index 0000000000000000000000000000000000000000..9befaebfa5908c783bf9205e3d2bc3cfa9539666
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/AppExecutors.java
@@ -0,0 +1,91 @@
+/*
+ * 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.example.android.autofill.service.util;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/**
+ * Global executor pools for the whole application.
+ *
+ * Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind
+ * webservice requests).
+ */
+public class AppExecutors {
+
+ private static final int THREAD_COUNT = 3;
+
+ private final Executor diskIO;
+
+ private final Executor networkIO;
+
+ private final Executor mainThread;
+
+ @VisibleForTesting
+ AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
+ this.diskIO = diskIO;
+ this.networkIO = networkIO;
+ this.mainThread = mainThread;
+ }
+
+ public AppExecutors() {
+ this(new DiskIOThreadExecutor(), Executors.newFixedThreadPool(THREAD_COUNT),
+ new MainThreadExecutor());
+ }
+
+ public Executor diskIO() {
+ return diskIO;
+ }
+
+ public Executor networkIO() {
+ return networkIO;
+ }
+
+ public Executor mainThread() {
+ return mainThread;
+ }
+
+ private static class MainThreadExecutor implements Executor {
+ private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void execute(@NonNull Runnable command) {
+ mainThreadHandler.post(command);
+ }
+ }
+
+ /**
+ * Executor that runs a task on a new background thread.
+ */
+ private static class DiskIOThreadExecutor implements Executor {
+
+ private final Executor mDiskIO;
+
+ public DiskIOThreadExecutor() {
+ mDiskIO = Executors.newSingleThreadExecutor();
+ }
+
+ @Override
+ public void execute(@NonNull Runnable command) {
+ mDiskIO.execute(command);
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/SecurityHelper.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/SecurityHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..5311a5f9f1b318537fa8793ba2ba93c0393b8856
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/SecurityHelper.java
@@ -0,0 +1,78 @@
+/*
+ * 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.example.android.autofill.service.util;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+/**
+ * Helper class for security checks.
+ */
+public final class SecurityHelper {
+
+ private SecurityHelper() {
+ throw new UnsupportedOperationException("Provides static methods only.");
+ }
+
+ /**
+ * Gets the fingerprint of the signed certificate of a package.
+ */
+ public static String getFingerprint(PackageInfo packageInfo, String packageName) throws
+ PackageManager.NameNotFoundException, IOException, NoSuchAlgorithmException,
+ CertificateException {
+ Signature[] signatures = packageInfo.signatures;
+ if (signatures.length != 1) {
+ throw new SecurityException(packageName + " has " + signatures.length + " signatures");
+ }
+ byte[] cert = signatures[0].toByteArray();
+ try (InputStream input = new ByteArrayInputStream(cert)) {
+ CertificateFactory factory = CertificateFactory.getInstance("X509");
+ X509Certificate x509 = (X509Certificate) factory.generateCertificate(input);
+ MessageDigest md = MessageDigest.getInstance("SHA256");
+ byte[] publicKey = md.digest(x509.getEncoded());
+ return toHexFormat(publicKey);
+ }
+ }
+
+ private static String toHexFormat(byte[] bytes) {
+ StringBuilder builder = new StringBuilder(bytes.length * 2);
+ for (int i = 0; i < bytes.length; i++) {
+ String hex = Integer.toHexString(bytes[i]);
+ int length = hex.length();
+ if (length == 1) {
+ hex = "0" + hex;
+ }
+ if (length > 2) {
+ hex = hex.substring(length - 2, length);
+ }
+ builder.append(hex.toUpperCase());
+ if (i < (bytes.length - 1)) {
+ builder.append(':');
+ }
+ }
+ return builder.toString();
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/Util.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/Util.java
new file mode 100644
index 0000000000000000000000000000000000000000..a86deea5a02b4b16825104c4596ea2bb4f372813
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/util/Util.java
@@ -0,0 +1,312 @@
+/*
+ * 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.example.android.autofill.service.util;
+
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
+import android.os.Bundle;
+import android.service.autofill.FillContext;
+import android.service.autofill.SaveInfo;
+import android.support.annotation.NonNull;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure.HtmlInfo;
+import android.view.autofill.AutofillValue;
+
+import com.google.common.base.Joiner;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public final class Util {
+
+ public static final String EXTRA_DATASET_NAME = "dataset_name";
+ public static final String EXTRA_FOR_RESPONSE = "for_response";
+ public static final NodeFilter AUTOFILL_ID_FILTER = (node, id) ->
+ id.equals(node.getAutofillId());
+ private static final String TAG = "AutofillSample";
+ public static LogLevel sLoggingLevel = LogLevel.Off;
+
+ private static void bundleToString(StringBuilder builder, Bundle data) {
+ final Set keySet = data.keySet();
+ builder.append("[Bundle with ").append(keySet.size()).append(" keys:");
+ for (String key : keySet) {
+ builder.append(' ').append(key).append('=');
+ Object value = data.get(key);
+ if ((value instanceof Bundle)) {
+ bundleToString(builder, (Bundle) value);
+ } else {
+ builder.append((value instanceof Object[])
+ ? Arrays.toString((Object[]) value) : value);
+ }
+ }
+ builder.append(']');
+ }
+
+ public static String bundleToString(Bundle data) {
+ if (data == null) {
+ return "N/A";
+ }
+ final StringBuilder builder = new StringBuilder();
+ bundleToString(builder, data);
+ return builder.toString();
+ }
+
+ public static String getTypeAsString(int type) {
+ switch (type) {
+ case View.AUTOFILL_TYPE_TEXT:
+ return "TYPE_TEXT";
+ case View.AUTOFILL_TYPE_LIST:
+ return "TYPE_LIST";
+ case View.AUTOFILL_TYPE_NONE:
+ return "TYPE_NONE";
+ case View.AUTOFILL_TYPE_TOGGLE:
+ return "TYPE_TOGGLE";
+ case View.AUTOFILL_TYPE_DATE:
+ return "TYPE_DATE";
+ }
+ return "UNKNOWN_TYPE";
+ }
+
+ private static String getAutofillValueAndTypeAsString(AutofillValue value) {
+ if (value == null) return "null";
+
+ StringBuilder builder = new StringBuilder(value.toString()).append('(');
+ if (value.isText()) {
+ builder.append("isText");
+ } else if (value.isDate()) {
+ builder.append("isDate");
+ } else if (value.isToggle()) {
+ builder.append("isToggle");
+ } else if (value.isList()) {
+ builder.append("isList");
+ }
+ return builder.append(')').toString();
+ }
+
+ public static void dumpStructure(AssistStructure structure) {
+ if (logVerboseEnabled()) {
+ int nodeCount = structure.getWindowNodeCount();
+ logv("dumpStructure(): component=%s numberNodes=%d",
+ structure.getActivityComponent(), nodeCount);
+ for (int i = 0; i < nodeCount; i++) {
+ logv("node #%d", i);
+ WindowNode node = structure.getWindowNodeAt(i);
+ dumpNode(new StringBuilder(), " ", node.getRootViewNode(), 0);
+ }
+ }
+ }
+
+ private static void dumpNode(StringBuilder builder, String prefix, ViewNode node, int childNumber) {
+ builder.append(prefix)
+ .append("child #").append(childNumber).append("\n");
+
+ builder.append(prefix)
+ .append("autoFillId: ").append(node.getAutofillId())
+ .append("\tidEntry: ").append(node.getIdEntry())
+ .append("\tid: ").append(node.getId())
+ .append("\tclassName: ").append(node.getClassName())
+ .append('\n');
+
+ builder.append(prefix)
+ .append("focused: ").append(node.isFocused())
+ .append("\tvisibility").append(node.getVisibility())
+ .append("\tchecked: ").append(node.isChecked())
+ .append("\twebDomain: ").append(node.getWebDomain())
+ .append("\thint: ").append(node.getHint())
+ .append('\n');
+
+ HtmlInfo htmlInfo = node.getHtmlInfo();
+
+ if (htmlInfo != null) {
+ builder.append(prefix)
+ .append("HTML TAG: ").append(htmlInfo.getTag())
+ .append(" attrs: ").append(htmlInfo.getAttributes())
+ .append('\n');
+ }
+
+ String[] afHints = node.getAutofillHints();
+ CharSequence[] options = node.getAutofillOptions();
+ builder.append(prefix).append("afType: ").append(getTypeAsString(node.getAutofillType()))
+ .append("\tafValue:")
+ .append(getAutofillValueAndTypeAsString(node.getAutofillValue()))
+ .append("\tafOptions:").append(options == null ? "N/A" : Arrays.toString(options))
+ .append("\tafHints: ").append(afHints == null ? "N/A" : Arrays.toString(afHints))
+ .append("\tinputType:").append(node.getInputType())
+ .append('\n');
+
+ int numberChildren = node.getChildCount();
+ builder.append(prefix).append("# children: ").append(numberChildren)
+ .append("\ttext: ").append(node.getText())
+ .append('\n');
+
+ final String prefix2 = prefix + " ";
+ for (int i = 0; i < numberChildren; i++) {
+ dumpNode(builder, prefix2, node.getChildAt(i), i);
+ }
+ logv(builder.toString());
+ }
+
+ public static String getSaveTypeAsString(int type) {
+ List types = new ArrayList<>();
+ if ((type & SaveInfo.SAVE_DATA_TYPE_ADDRESS) != 0) {
+ types.add("ADDRESS");
+ }
+ if ((type & SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD) != 0) {
+ types.add("CREDIT_CARD");
+ }
+ if ((type & SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS) != 0) {
+ types.add("EMAIL_ADDRESS");
+ }
+ if ((type & SaveInfo.SAVE_DATA_TYPE_USERNAME) != 0) {
+ types.add("USERNAME");
+ }
+ if ((type & SaveInfo.SAVE_DATA_TYPE_PASSWORD) != 0) {
+ types.add("PASSWORD");
+ }
+ if (types.isEmpty()) {
+ return "UNKNOWN(" + type + ")";
+ }
+ return Joiner.on('|').join(types);
+ }
+
+ /**
+ * Gets a node if it matches the filter criteria for the given id.
+ */
+ public static ViewNode findNodeByFilter(@NonNull List contexts, @NonNull Object id,
+ @NonNull NodeFilter filter) {
+ for (FillContext context : contexts) {
+ ViewNode node = findNodeByFilter(context.getStructure(), id, filter);
+ if (node != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets a node if it matches the filter criteria for the given id.
+ */
+ public static ViewNode findNodeByFilter(@NonNull AssistStructure structure, @NonNull Object id,
+ @NonNull NodeFilter filter) {
+ logv("Parsing request for activity %s", structure.getActivityComponent());
+ final int nodes = structure.getWindowNodeCount();
+ for (int i = 0; i < nodes; i++) {
+ final WindowNode windowNode = structure.getWindowNodeAt(i);
+ final ViewNode rootNode = windowNode.getRootViewNode();
+ final ViewNode node = findNodeByFilter(rootNode, id, filter);
+ if (node != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets a node if it matches the filter criteria for the given id.
+ */
+ public static ViewNode findNodeByFilter(@NonNull ViewNode node, @NonNull Object id,
+ @NonNull NodeFilter filter) {
+ if (filter.matches(node, id)) {
+ return node;
+ }
+ final int childrenSize = node.getChildCount();
+ if (childrenSize > 0) {
+ for (int i = 0; i < childrenSize; i++) {
+ final ViewNode found = findNodeByFilter(node.getChildAt(i), id, filter);
+ if (found != null) {
+ return found;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static void logd(String message, Object... params) {
+ if (logDebugEnabled()) {
+ Log.d(TAG, String.format(message, params));
+ }
+ }
+
+ public static void logv(String message, Object... params) {
+ if (logVerboseEnabled()) {
+ Log.v(TAG, String.format(message, params));
+ }
+ }
+
+ public static boolean logDebugEnabled() {
+ return sLoggingLevel.ordinal() >= LogLevel.Debug.ordinal();
+ }
+
+ public static boolean logVerboseEnabled() {
+ return sLoggingLevel.ordinal() >= LogLevel.Verbose.ordinal();
+ }
+
+ public static void logw(String message, Object... params) {
+ Log.w(TAG, String.format(message, params));
+ }
+
+ public static void logw(Throwable throwable, String message, Object... params) {
+ Log.w(TAG, String.format(message, params), throwable);
+ }
+
+ public static void loge(String message, Object... params) {
+ Log.e(TAG, String.format(message, params));
+ }
+
+ public static void loge(Throwable throwable, String message, Object... params) {
+ Log.e(TAG, String.format(message, params), throwable);
+ }
+
+ public static void setLoggingLevel(LogLevel level) {
+ sLoggingLevel = level;
+ }
+
+ /**
+ * Helper method for getting the index of a CharSequence object in an array.
+ */
+ public static int indexOf(@NonNull CharSequence[] array, CharSequence charSequence) {
+ int index = -1;
+ if (charSequence == null) {
+ return index;
+ }
+ for (int i = 0; i < array.length; i++) {
+ if (charSequence.equals(array[i])) {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ public enum LogLevel {Off, Debug, Verbose}
+
+ public enum DalCheckRequirement {Disabled, LoginOnly, AllUrls}
+
+ /**
+ * Helper interface used to filter Assist nodes.
+ */
+ public interface NodeFilter {
+ /**
+ * Returns whether the node passes the filter for such given id.
+ */
+ boolean matches(ViewNode node, Object id);
+ }
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_add_black_24dp.xml b/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_add_black_24dp.xml
similarity index 78%
rename from input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_add_black_24dp.xml
rename to input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_add_black_24dp.xml
index e50269d8ae5c17638676c9fb8138bcef9a8f1c34..ed8512a4294f382b24a0049a4c668a2d533e719d 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_add_black_24dp.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_add_black_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_delete_forever_black_24dp.xml b/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_delete_forever_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5b5927d4ba6e1d5500e50d3bbe774cf9d9da5ccd
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_delete_forever_black_24dp.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_lock_black_24dp.xml b/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_lock_black_24dp.xml
similarity index 84%
rename from input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_lock_black_24dp.xml
rename to input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_lock_black_24dp.xml
index 6b2f014dfc37fc37aaeb685b76318ef92e33009e..6e18f1acae4f2af34a8e35f235ed91a6b94041d1 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_lock_black_24dp.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_lock_black_24dp.xml
@@ -14,11 +14,11 @@
* limitations under the License.
-->
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml b/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_person_black_24dp.xml
similarity index 56%
rename from input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml
rename to input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_person_black_24dp.xml
index 4d2afb01d50eb42f17352bd4217bd0e06e2d36c4..032db1299bd28b612630c3fac06e48c94f48a2d1 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/drawable-v24/ic_person_black_24dp.xml
@@ -13,8 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
-
+
+
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/drawable/list_divider.xml b/input/autofill/AutofillFramework/afservice/src/main/res/drawable/list_divider.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3d6d16249f332cfd706dc707ac884ce8a4535629
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/drawable/list_divider.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/layout/activity_field_picker.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/activity_field_picker.xml
new file mode 100644
index 0000000000000000000000000000000000000000..949dbfb9a9d4e83e5ab7e514d02ef675dbf21bfc
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/activity_field_picker.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_field.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_field.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b441dba89cc3ac3770af8da786b62faad908200e
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_field.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_suggestion.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_suggestion.xml
new file mode 100644
index 0000000000000000000000000000000000000000..030de0a12a48c3e8699ffee385198b0855605746
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_suggestion.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_auth_activity.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_auth_activity.xml
similarity index 93%
rename from input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_auth_activity.xml
rename to input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_auth_activity.xml
index 34b4424be8ba9902de13521ffac7641339d373ff..844ea0a60a2585d480018218820016c415e44cb0 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_auth_activity.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_auth_activity.xml
@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
+ tools:context="com.example.android.autofill.service.AuthActivity">
-
+
+
+
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_settings_activity.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_settings_activity.xml
similarity index 59%
rename from input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_settings_activity.xml
rename to input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_settings_activity.xml
index 1618bdceaed588c7aaf5ffb8b363caa843d101e0..ce77e27ac31f689b53660530f562eb4245174ab9 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_settings_activity.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_settings_activity.xml
@@ -1,5 +1,4 @@
-
-
-
+ android:paddingBottom="@dimen/spacing_large">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_settings_add_data_dialog.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_settings_add_data_dialog.xml
similarity index 87%
rename from input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_settings_add_data_dialog.xml
rename to input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_settings_add_data_dialog.xml
index 019a3674a19bd74c35d1aef10d5334fa0915e4e8..530451e994cc6b0033a1dee16c4160d6ecbbcce5 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/multidataset_service_settings_add_data_dialog.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_settings_add_data_dialog.xml
@@ -1,5 +1,4 @@
-
-
-
-
-
+
+ #3F51B5
+ #303F9F
+ #FF4081
+ #ffeeeeee
+
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/values/dimens.xml b/input/autofill/AutofillFramework/afservice/src/main/res/values/dimens.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8a21b708df5d43fb3b99e978d226dbed59361319
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/values/dimens.xml
@@ -0,0 +1,25 @@
+
+
+ 16dp
+ 16dp
+ 16dp
+ 8dp
+ 32dp
+ 48dp
+ 250sp
+ 2dp
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/afservice/src/main/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e7b1f8801fd5a440daff9beeb56e98c097af3d67
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/values/strings.xml
@@ -0,0 +1,117 @@
+
+
+ Invalid package signature
+ Web domain security exception.
+ DAL verification failure.
+ Tap to sign in.
+ Tap to manually select data.
+ Autofill Authentication
+ Manual (Auto)fill
+ Select Field
+ Autofill Settings
+ Cancel
+ Save
+ Authenticate Responses
+ Authenticate Datasets
+ Add fake Autofill data
+ Add Autofill Datasets
+ Select number of Datasets.
+
+ - Added %d Dataset.
+ - Added %d Datasets.
+
+ Clear all autofill data (including credentials)
+ Are you sure you want to delete all autofill
+ data from the sample service?
+
+ Confirmation
+ Authentication
+ Data
+ Logging
+ Off
+ Debug
+ Verbose
+
+ DAL Check Requirement
+ Disabled
+ Login Credentials Only
+ Handle All URLs
+
+ Enable/Disable
+ Set default Autofill service
+ Disable Autofill services
+ Autofill service has been disabled.
+ Sample service set to default.
+ Sample service not set to default.
+ Update credentials
+ Enter current password
+ Enter new password
+ Change credentials
+ Number of Datasets
+ Autofill Master Login
+ Password
+ Cancel
+ Login
+ OK
+ Dataset Name
+ Field Types
+ Fields for %1$s
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+ - 13
+ - 14
+ - 15
+ - 16
+ - 17
+ - 18
+ - 19
+ - 20
+ - 21
+ - 22
+ - 23
+ - 24
+ - 25
+ - 26
+ - 27
+
+
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/values/styles.xml b/input/autofill/AutofillFramework/afservice/src/main/res/values/styles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a7e7bf2f5322592e5212fe42e7f87acebc99c1bb
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/values/styles.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/xml/multidataset_service.xml b/input/autofill/AutofillFramework/afservice/src/main/res/xml/multidataset_service.xml
similarity index 77%
rename from input/autofill/AutofillFramework/Application/src/main/res/xml/multidataset_service.xml
rename to input/autofill/AutofillFramework/afservice/src/main/res/xml/multidataset_service.xml
index 0c1c14d079957912c9e3ef41078e9bd94915507f..de58167c19a64fc9277940323e33f8a5a0e32134 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/xml/multidataset_service.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/xml/multidataset_service.xml
@@ -1,5 +1,4 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/build.gradle b/input/autofill/AutofillFramework/build.gradle
index efb29d942a5c78ae562aa5a56fa8c12cd199a415..5e6d6a3fdef211cd96fee6e349722dfb8c02cba4 100644
--- a/input/autofill/AutofillFramework/build.gradle
+++ b/input/autofill/AutofillFramework/build.gradle
@@ -1,32 +1,29 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- repositories {
- jcenter()
- maven {
- url 'https://maven.google.com'
+ repositories {
+ jcenter()
+ google()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.0.1'
}
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:2.3.3'
- }
}
allprojects {
- repositories {
- jcenter()
- maven {
- url 'https://maven.google.com'
+ repositories {
+ jcenter()
+ google()
}
- }
}
// BEGIN_EXCLUDE
import com.example.android.samples.build.SampleGenPlugin
+
apply plugin: SampleGenPlugin
samplegen {
- pathToBuild "../../../../../build"
- pathToSamplesCommon "../../../common"
+ pathToBuild "../../../../../build"
+ pathToSamplesCommon "../../../common"
}
apply from: "../../../../../build/build.gradle"
// END_EXCLUDE
diff --git a/input/autofill/AutofillFramework/gradle/wrapper/gradle-wrapper.jar b/input/autofill/AutofillFramework/gradle/wrapper/gradle-wrapper.jar
index 8c0fb64a8698b08ecc4158d828ca593c4928e9dd..13372aef5e24af05341d49695ee84e5f9b594659 100644
Binary files a/input/autofill/AutofillFramework/gradle/wrapper/gradle-wrapper.jar and b/input/autofill/AutofillFramework/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/input/autofill/AutofillFramework/gradle/wrapper/gradle-wrapper.properties b/input/autofill/AutofillFramework/gradle/wrapper/gradle-wrapper.properties
index 63426264aa78f2eed4c1084715ed7b0fff985193..f176f799ec6edd5b38ca29d4b5ef7b03692dedee 100644
--- a/input/autofill/AutofillFramework/gradle/wrapper/gradle-wrapper.properties
+++ b/input/autofill/AutofillFramework/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Jul 28 14:29:44 PDT 2017
+#Fri Jan 12 11:03:13 PST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/input/autofill/AutofillFramework/gradlew b/input/autofill/AutofillFramework/gradlew
index 91a7e269e19dfc62e27137a0b57ef3e430cee4fd..9d82f78915133e1c35a6ea51252590fb38efac2f 100755
--- a/input/autofill/AutofillFramework/gradlew
+++ b/input/autofill/AutofillFramework/gradlew
@@ -42,11 +42,6 @@ case "`uname`" in
;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -61,9 +56,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
+cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
+cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -114,6 +109,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml
index 2470645c79f05c64139772324fc0722640af3ef9..88a0d72c2b9e252ab492a1612a6572f456c8e7ae 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml
@@ -75,7 +75,7 @@
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
@@ -88,4 +88,4 @@
-
\ No newline at end of file
+
diff --git a/input/autofill/AutofillFramework/kotlinApp/README.md b/input/autofill/AutofillFramework/kotlinApp/README.md
index 282a1ea29cd32e910bcb85081a12573ee806fb8a..d0ab0e013c070fe97612e1c10447cf8b0d26edae 100644
--- a/input/autofill/AutofillFramework/kotlinApp/README.md
+++ b/input/autofill/AutofillFramework/kotlinApp/README.md
@@ -1,11 +1,17 @@
+Warning: This sample is currently outdated and you should reference the Java version instead.
+============================================================================================
-Android AutofillFramework Sample
-===================================
+Android AutofillFramework Sample (Kotlin)
+=========================================
This sample demonstrates the use of the Autofill Framework. It includes implementations of client
Activities with views that should be autofilled, and a Service that can provide autofill data to
client Activities.
+Maintainer's Note
+------------------
+**IMPORTANT:** The Kotlin version of this sample is temporarily out of date. Until this is corrected, you should reference the Java version instead.
+
Introduction
------------
diff --git a/input/autofill/AutofillFramework/kotlinApp/build.gradle b/input/autofill/AutofillFramework/kotlinApp/build.gradle
index 6fc4282bdc6bc962fc56e9fb369b072b27601ff1..7eb0dddc3984d2c48e9348aff5c24c81cb1145ee 100644
--- a/input/autofill/AutofillFramework/kotlinApp/build.gradle
+++ b/input/autofill/AutofillFramework/kotlinApp/build.gradle
@@ -7,7 +7,7 @@ buildscript {
}
ext.kotlin_version = '1.1.4-3'
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.3'
+ classpath 'com.android.tools.build:gradle:3.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
diff --git a/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.properties b/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.properties
index cb80c871761b42fcac76ab1115f5e690610698b4..34facd6b0a34c2a55f9dd564975d6981d06191c8 100644
--- a/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.properties
+++ b/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
diff --git a/input/autofill/AutofillFramework/settings.gradle b/input/autofill/AutofillFramework/settings.gradle
index 9464a359356df07cafaec1bfd44fa30eb2623dde..2d0eb20e9286e093a7a3e14e47b359f9a8aa8521 100644
--- a/input/autofill/AutofillFramework/settings.gradle
+++ b/input/autofill/AutofillFramework/settings.gradle
@@ -1 +1,2 @@
+include ':afservice'
include 'Application'
diff --git a/input/autofill/AutofillFramework/template-params.xml b/input/autofill/AutofillFramework/template-params.xml
index b355587a1595d37c38f548a25ed473d0c574443e..37d6bd2a3a12b1ecdfe9a8fe4b0c3aa53219e2e1 100644
--- a/input/autofill/AutofillFramework/template-params.xml
+++ b/input/autofill/AutofillFramework/template-params.xml
@@ -19,7 +19,7 @@
AutofillFramework
Input
- com.example.android.autofillframework
+ com.example.android.autofill.app
26
diff --git a/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties b/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties
index 4da4a5808fee2512de0bcbe3cd6b2bb866234b02..86fc139ac8ce03bb1d041893a60d25222baf6c33 100644
--- a/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties
+++ b/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
\ No newline at end of file
diff --git a/input/keyboard/CommitContentSampleApp/build.gradle b/input/keyboard/CommitContentSampleApp/build.gradle
index 1e30651c914b0165e0493d3469ac5de4b68d912f..144d85ead8888406e1b9aac53fbe8cb3a058dcd7 100644
--- a/input/keyboard/CommitContentSampleApp/build.gradle
+++ b/input/keyboard/CommitContentSampleApp/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.3'
+ classpath 'com.android.tools.build:gradle:3.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/input/keyboard/CommitContentSampleApp/gradle/wrapper/gradle-wrapper.properties b/input/keyboard/CommitContentSampleApp/gradle/wrapper/gradle-wrapper.properties
index d9028ccbaa686f0d0f6027446c0e8ef4a593d01e..fc5d4423a020b52313231b38775bc74e5484afaa 100644
--- a/input/keyboard/CommitContentSampleApp/gradle/wrapper/gradle-wrapper.properties
+++ b/input/keyboard/CommitContentSampleApp/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
\ No newline at end of file
diff --git a/input/keyboard/CommitContentSampleIME/build.gradle b/input/keyboard/CommitContentSampleIME/build.gradle
index 1e30651c914b0165e0493d3469ac5de4b68d912f..144d85ead8888406e1b9aac53fbe8cb3a058dcd7 100644
--- a/input/keyboard/CommitContentSampleIME/build.gradle
+++ b/input/keyboard/CommitContentSampleIME/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.3'
+ classpath 'com.android.tools.build:gradle:3.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/input/keyboard/CommitContentSampleIME/gradle/wrapper/gradle-wrapper.properties b/input/keyboard/CommitContentSampleIME/gradle/wrapper/gradle-wrapper.properties
index d9028ccbaa686f0d0f6027446c0e8ef4a593d01e..fc5d4423a020b52313231b38775bc74e5484afaa 100644
--- a/input/keyboard/CommitContentSampleIME/gradle/wrapper/gradle-wrapper.properties
+++ b/input/keyboard/CommitContentSampleIME/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
\ No newline at end of file
diff --git a/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties b/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties
index 4da4a5808fee2512de0bcbe3cd6b2bb866234b02..86fc139ac8ce03bb1d041893a60d25222baf6c33 100644
--- a/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties
+++ b/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
\ No newline at end of file
diff --git a/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties b/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties
index 4da4a5808fee2512de0bcbe3cd6b2bb866234b02..86fc139ac8ce03bb1d041893a60d25222baf6c33 100644
--- a/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties
+++ b/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
\ No newline at end of file
diff --git a/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties b/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties
index 7ec3018ddaa2c4c531c4afcc8c7a38096bb96bb8..21e2affd93920e906f53f89605d16a0259a0859c 100644
--- a/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties
+++ b/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
\ No newline at end of file
diff --git a/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties b/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties
index 4da4a5808fee2512de0bcbe3cd6b2bb866234b02..86fc139ac8ce03bb1d041893a60d25222baf6c33 100644
--- a/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties
+++ b/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
\ No newline at end of file
diff --git a/media/Camera2Basic/kotlinApp/.google/packaging.yaml b/media/Camera2Basic/kotlinApp/.google/packaging.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..938248506d3c82db5b0ac0c56112bda2eb37e91f
--- /dev/null
+++ b/media/Camera2Basic/kotlinApp/.google/packaging.yaml
@@ -0,0 +1,23 @@
+
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+status: PUBLISHED
+technologies: [Android]
+categories: [Media, Camera, Camera2]
+languages: [Kotlin]
+solutions: [Mobile]
+github: android-Camera2Basic
+level: INTERMEDIATE
+icon: screenshots/icon-web.png
+apiRefs:
+ - android:android.hardware.camera2.CameraManager
+ - android:android.hardware.camera2.CameraDevice
+ - android:android.hardware.camera2.CameraCharacteristics
+ - android:android.hardware.camera2.CameraCaptureSession
+ - android:android.hardware.camera2.CaptureRequest
+ - android:android.hardware.camera2.CaptureResult
+ - android:android.view.TextureView
+license: apache2
diff --git a/system/RuntimePermissionsBasic/Application/.gitignore b/media/Camera2Basic/kotlinApp/Application/.gitignore
similarity index 100%
rename from system/RuntimePermissionsBasic/Application/.gitignore
rename to media/Camera2Basic/kotlinApp/Application/.gitignore
diff --git a/media/Camera2Basic/kotlinApp/Application/build.gradle b/media/Camera2Basic/kotlinApp/Application/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..c30a09afa3bf2dc6d8a7eb66dd90e30759a28ce0
--- /dev/null
+++ b/media/Camera2Basic/kotlinApp/Application/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: 'com.android.application'
+
+apply plugin: 'kotlin-android'
+
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 26
+ buildToolsVersion '26.0.2'
+ defaultConfig {
+ applicationId "com.example.android.camera2basic"
+ minSdkVersion 21
+ targetSdkVersion 26
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation "com.android.support:appcompat-v7:26.1.0"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+}
diff --git a/media/Camera2Basic/kotlinApp/Application/src/main/AndroidManifest.xml b/media/Camera2Basic/kotlinApp/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5b0b5b0418f2d9ca4af6eacd7c7385dff920d03f
--- /dev/null
+++ b/media/Camera2Basic/kotlinApp/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+