blob: c2ce43c9f5a69b185e53d8a344edf69acd272665 [file] [log] [blame]
/*
* 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.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.annotation.StringRes;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.recyclerview.extensions.ListAdapter;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.android.autofill.app.R;
import com.example.android.autofill.app.WelcomeActivity;
import java.util.Arrays;
import java.util.List;
/**
* This is mostly a normal Activity containing a RecyclerView. The only difference is, the rows in
* the RecyclerView have autofillable fields. Therefore, when we bind data to a recycled view, we
* need to also set the {@link AutofillId} on the view.
*/
@RequiresApi(28)
public class RecyclerViewActivity extends AppCompatActivity {
private AutofillManager mAfm;
private List<FieldMetadata> mFields;
private FieldAdapter mFieldAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycler_view_activity);
RecyclerView recyclerView = findViewById(R.id.recyclerView);
mAfm = getSystemService(AutofillManager.class);
// Init RecyclerView
LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
recyclerView.getContext(), layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
mFields = initList();
mFieldAdapter = new FieldAdapter(mFields);
recyclerView.setAdapter(mFieldAdapter);
recyclerView.setLayoutManager(layoutManager);
// Init submit and clear buttons.
findViewById(R.id.submitButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
submit();
}
});
findViewById(R.id.clearButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AutofillManager afm = getSystemService(AutofillManager.class);
if (afm != null) {
afm.cancel();
}
resetFields();
}
});
}
private void resetFields() {
for (FieldMetadata fieldMetadata : mFields) {
fieldMetadata.setEnteredText("");
}
mFieldAdapter.notifyDataSetChanged();
}
private void submit() {
Intent intent = WelcomeActivity.getStartActivityIntent(RecyclerViewActivity.this);
startActivity(intent);
finish();
}
public List<FieldMetadata> initList() {
return Arrays.asList(
new FieldMetadata(
mAfm.getNextAutofillId(),
View.AUTOFILL_HINT_NAME,
R.string.recycler_view_label_name,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_TEXT
),
new FieldMetadata(
mAfm.getNextAutofillId(),
"bday-month",
R.string.recycler_view_label_birthday_month,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_NUMBER
),
new FieldMetadata(
mAfm.getNextAutofillId(),
View.AUTOFILL_HINT_EMAIL_ADDRESS,
R.string.recycler_view_label_email,
R.drawable.ic_person_black_24dp,
InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
),
new FieldMetadata(
mAfm.getNextAutofillId(),
View.AUTOFILL_HINT_PHONE,
R.string.recycler_view_label_phone,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_PHONE
),
new FieldMetadata(
mAfm.getNextAutofillId(),
"tel_extension",
R.string.recycler_view_label_tel_extension,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_PHONE
),
new FieldMetadata(
mAfm.getNextAutofillId(),
View.AUTOFILL_HINT_CREDIT_CARD_NUMBER,
R.string.recycler_view_label_cc_number,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_NUMBER
),
new FieldMetadata(
mAfm.getNextAutofillId(),
View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE,
R.string.recycler_view_label_cc_sc,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_NUMBER
),
new FieldMetadata(
mAfm.getNextAutofillId(),
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,
R.string.recycler_view_label_cc_exp_month,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_NUMBER
),
new FieldMetadata(
mAfm.getNextAutofillId(),
View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,
R.string.recycler_view_label_cc_exp_year,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_NUMBER
),
new FieldMetadata(
mAfm.getNextAutofillId(),
"address-line1",
R.string.recycler_view_label_address_line_1,
R.drawable.ic_person_black_24dp,
InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
),
new FieldMetadata(
mAfm.getNextAutofillId(),
"address-line2",
R.string.recycler_view_label_address_line_2,
R.drawable.ic_person_black_24dp,
InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
),
new FieldMetadata(
mAfm.getNextAutofillId(),
"address-line3",
R.string.recycler_view_label_address_line_3,
R.drawable.ic_person_black_24dp,
InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
),
new FieldMetadata(
mAfm.getNextAutofillId(),
View.AUTOFILL_HINT_POSTAL_CODE,
R.string.recycler_view_label_postal_code,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_NUMBER
),
new FieldMetadata(
mAfm.getNextAutofillId(),
"bday-year",
R.string.recycler_view_label_birthday_year,
R.drawable.ic_person_black_24dp,
InputType.TYPE_CLASS_NUMBER
)
);
}
static class FieldAdapter extends ListAdapter<FieldMetadata, FieldViewHolder> {
private List<FieldMetadata> mFields;
public FieldAdapter(List<FieldMetadata> fields) {
super(new FieldDiff());
mFields = fields;
submitList(mFields);
}
@NonNull
@Override
public FieldViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new FieldViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.user_data_field, parent, false),
new FieldWatcher(mFields));
}
@Override
public void onBindViewHolder(@NonNull FieldViewHolder holder, int position) {
holder.bind(getItem(position));
}
}
static class FieldViewHolder extends RecyclerView.ViewHolder {
ImageView mIcon;
TextView mLabel;
EditText mField;
FieldWatcher mWatcher;
public FieldViewHolder(@NonNull View itemView, FieldWatcher textWatcher) {
super(itemView);
mIcon = itemView.findViewById(R.id.icon);
mLabel = itemView.findViewById(R.id.label);
mField = itemView.findViewById(R.id.field);
mWatcher = textWatcher;
mField.addTextChangedListener(mWatcher);
}
void bind(FieldMetadata fieldMetadata) {
mWatcher.updatePosition(getAdapterPosition());
Drawable drawable = mIcon.getResources().getDrawable(fieldMetadata.getIconRes());
mIcon.setImageDrawable(drawable);
mLabel.setText(fieldMetadata.getLabelRes());
mField.setAutofillHints(fieldMetadata.getAutofillHint());
mField.setInputType(fieldMetadata.getInputType());
mField.setText(fieldMetadata.getEnteredText());
// IMPORTANT: setAutofillId of recycled View.
mField.setAutofillId(fieldMetadata.getAutofillId());
}
}
/**
* TextWatcher implementation to ensure EditTexts get recycled properly.
*/
static class FieldWatcher implements TextWatcher {
private int mPosition;
private List<FieldMetadata> mFields;
public FieldWatcher(List<FieldMetadata> fields) {
mFields = fields;
}
public void updatePosition(int position) {
this.mPosition = position;
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
// NO-OP
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
mFields.get(mPosition).setEnteredText(charSequence);
}
@Override
public void afterTextChanged(Editable editable) {
// NO-OP
}
}
/**
* Model class that holds all of the data needed for a row in the {@link RecyclerView}.
*/
static class FieldMetadata {
AutofillId mAutofillId;
String mAutofillHint;
@StringRes int mLabelRes;
@DrawableRes int mIconRes;
int mInputType;
CharSequence mEnteredText = "";
FieldMetadata(AutofillId autofillId, String autofillHint, @StringRes int labelRes,
@DrawableRes int iconRes, int inputType) {
mAutofillId = autofillId;
mAutofillHint = autofillHint;
mLabelRes = labelRes;
mIconRes = iconRes;
mInputType = inputType;
}
public AutofillId getAutofillId() {
return mAutofillId;
}
public String getAutofillHint() {
return mAutofillHint;
}
public int getLabelRes() {
return mLabelRes;
}
public int getIconRes() {
return mIconRes;
}
public int getInputType() {
return mInputType;
}
public void setEnteredText(CharSequence enteredText) {
mEnteredText = enteredText;
}
public CharSequence getEnteredText() {
return mEnteredText;
}
}
static class FieldDiff extends DiffUtil.ItemCallback<FieldMetadata> {
@Override
public boolean areItemsTheSame(@NonNull FieldMetadata oldItem,
@NonNull FieldMetadata newItem) {
return oldItem == newItem;
}
@Override
public boolean areContentsTheSame(@NonNull FieldMetadata oldItem,
@NonNull FieldMetadata newItem) {
return oldItem.equals(newItem);
}
}
}