Home | History | Annotate | Download | only in service
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.example.android.autofill.service;
     17 
     18 import android.app.PendingIntent;
     19 import android.app.assist.AssistStructure;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentSender;
     23 import android.content.SharedPreferences;
     24 import android.os.Bundle;
     25 import android.service.autofill.Dataset;
     26 import android.service.autofill.FillResponse;
     27 import android.support.annotation.Nullable;
     28 import android.support.v7.app.AppCompatActivity;
     29 import android.text.Editable;
     30 import android.widget.EditText;
     31 import android.widget.RemoteViews;
     32 import android.widget.Toast;
     33 
     34 import com.example.android.autofill.service.data.ClientViewMetadata;
     35 import com.example.android.autofill.service.data.ClientViewMetadataBuilder;
     36 import com.example.android.autofill.service.data.DataCallback;
     37 import com.example.android.autofill.service.data.adapter.DatasetAdapter;
     38 import com.example.android.autofill.service.data.adapter.ResponseAdapter;
     39 import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
     40 import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource;
     41 import com.example.android.autofill.service.data.source.local.DigitalAssetLinksRepository;
     42 import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
     43 import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
     44 import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
     45 import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
     46 import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
     47 import com.example.android.autofill.service.settings.MyPreferences;
     48 import com.example.android.autofill.service.util.AppExecutors;
     49 import com.google.gson.GsonBuilder;
     50 
     51 import java.util.HashMap;
     52 import java.util.List;
     53 
     54 import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE;
     55 import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT;
     56 import static com.example.android.autofill.service.util.Util.EXTRA_DATASET_NAME;
     57 import static com.example.android.autofill.service.util.Util.EXTRA_FOR_RESPONSE;
     58 import static com.example.android.autofill.service.util.Util.logw;
     59 
     60 
     61 /**
     62  * This Activity controls the UI for logging in to the Autofill service.
     63  * It is launched when an Autofill Response or specific Dataset within the Response requires
     64  * authentication to access. It bundles the result in an Intent.
     65  */
     66 public class AuthActivity extends AppCompatActivity {
     67 
     68     // Unique id for dataset intents.
     69     private static int sDatasetPendingIntentId = 0;
     70 
     71     private LocalAutofillDataSource mLocalAutofillDataSource;
     72     private DigitalAssetLinksRepository mDalRepository;
     73     private EditText mMasterPassword;
     74     private DatasetAdapter mDatasetAdapter;
     75     private ResponseAdapter mResponseAdapter;
     76     private ClientViewMetadata mClientViewMetadata;
     77     private String mPackageName;
     78     private Intent mReplyIntent;
     79     private MyPreferences mPreferences;
     80 
     81     public static IntentSender getAuthIntentSenderForResponse(Context context) {
     82         final Intent intent = new Intent(context, AuthActivity.class);
     83         return PendingIntent.getActivity(context, 0, intent,
     84                 PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
     85     }
     86 
     87     public static IntentSender getAuthIntentSenderForDataset(Context originContext,
     88             String datasetName) {
     89         Intent intent = new Intent(originContext, AuthActivity.class);
     90         intent.putExtra(EXTRA_DATASET_NAME, datasetName);
     91         intent.putExtra(EXTRA_FOR_RESPONSE, false);
     92         return PendingIntent.getActivity(originContext, ++sDatasetPendingIntentId, intent,
     93                 PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
     94     }
     95 
     96     @Override
     97     protected void onCreate(@Nullable Bundle savedInstanceState) {
     98         super.onCreate(savedInstanceState);
     99         setContentView(R.layout.multidataset_service_auth_activity);
    100         SharedPreferences sharedPreferences =
    101                 getSharedPreferences(LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
    102         DefaultFieldTypesSource defaultFieldTypesSource =
    103                 DefaultFieldTypesLocalJsonSource.getInstance(getResources(),
    104                         new GsonBuilder().create());
    105         AutofillDao autofillDao = AutofillDatabase.getInstance(this,
    106                 defaultFieldTypesSource, new AppExecutors()).autofillDao();
    107         mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(sharedPreferences,
    108                 autofillDao, new AppExecutors());
    109         mDalRepository = DigitalAssetLinksRepository.getInstance(getPackageManager());
    110         mMasterPassword = findViewById(R.id.master_password);
    111         mPackageName = getPackageName();
    112         mPreferences = MyPreferences.getInstance(this);
    113         findViewById(R.id.login).setOnClickListener((view) -> login());
    114         findViewById(R.id.cancel).setOnClickListener((view) -> {
    115             onFailure();
    116             AuthActivity.this.finish();
    117         });
    118     }
    119 
    120     private void login() {
    121         Editable password = mMasterPassword.getText();
    122         String correctPassword = MyPreferences.getInstance(AuthActivity.this).getMasterPassword();
    123         if (password.toString().equals(correctPassword)) {
    124             onSuccess();
    125         } else {
    126             Toast.makeText(this, "Password incorrect", Toast.LENGTH_SHORT).show();
    127             onFailure();
    128         }
    129     }
    130 
    131     @Override
    132     public void finish() {
    133         if (mReplyIntent != null) {
    134             setResult(RESULT_OK, mReplyIntent);
    135         } else {
    136             setResult(RESULT_CANCELED);
    137         }
    138         super.finish();
    139     }
    140 
    141     private void onFailure() {
    142         logw("Failed auth.");
    143         mReplyIntent = null;
    144     }
    145 
    146     private void onSuccess() {
    147         Intent intent = getIntent();
    148         boolean forResponse = intent.getBooleanExtra(EXTRA_FOR_RESPONSE, true);
    149         AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
    150         ClientParser clientParser = new ClientParser(structure);
    151         mReplyIntent = new Intent();
    152         mLocalAutofillDataSource.getFieldTypeByAutofillHints(
    153                 new DataCallback<HashMap<String, FieldTypeWithHeuristics>>() {
    154             @Override
    155             public void onLoaded(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) {
    156                 ClientViewMetadataBuilder builder = new ClientViewMetadataBuilder(clientParser,
    157                         fieldTypesByAutofillHint);
    158                 mClientViewMetadata = builder.buildClientViewMetadata();
    159                 mDatasetAdapter = new DatasetAdapter(clientParser);
    160                 mResponseAdapter = new ResponseAdapter(AuthActivity.this,
    161                         mClientViewMetadata, mPackageName, mDatasetAdapter);
    162                 if (forResponse) {
    163                     fetchAllDatasetsAndSetIntent(fieldTypesByAutofillHint);
    164                 } else {
    165                     String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME);
    166                     fetchDatasetAndSetIntent(fieldTypesByAutofillHint, datasetName);
    167                 }
    168             }
    169 
    170             @Override
    171             public void onDataNotAvailable(String msg, Object... params) {
    172 
    173             }
    174         });
    175     }
    176 
    177     private void fetchDatasetAndSetIntent(
    178             HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint, String datasetName) {
    179         mLocalAutofillDataSource.getAutofillDataset(mClientViewMetadata.getAllHints(),
    180                 datasetName, new DataCallback<DatasetWithFilledAutofillFields>() {
    181                     @Override
    182                     public void onLoaded(DatasetWithFilledAutofillFields dataset) {
    183                         String datasetName = dataset.autofillDataset.getDatasetName();
    184                         RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(
    185                                 mPackageName, datasetName);
    186                         setDatasetIntent(mDatasetAdapter.buildDataset(fieldTypesByAutofillHint,
    187                                 dataset, remoteViews));
    188                         finish();
    189                     }
    190 
    191                     @Override
    192                     public void onDataNotAvailable(String msg, Object... params) {
    193                         logw(msg, params);
    194                         finish();
    195                     }
    196                 });
    197     }
    198 
    199     private void fetchAllDatasetsAndSetIntent(
    200             HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) {
    201         mLocalAutofillDataSource.getAutofillDatasets(mClientViewMetadata.getAllHints(),
    202                 new DataCallback<List<DatasetWithFilledAutofillFields>>() {
    203                     @Override
    204                     public void onLoaded(List<DatasetWithFilledAutofillFields> datasets) {
    205                         boolean datasetAuth = mPreferences.isDatasetAuth();
    206                         FillResponse fillResponse = mResponseAdapter.buildResponse(
    207                                 fieldTypesByAutofillHint, datasets, datasetAuth);
    208                         setResponseIntent(fillResponse);
    209                         finish();
    210                     }
    211 
    212                     @Override
    213                     public void onDataNotAvailable(String msg, Object... params) {
    214                         logw(msg, params);
    215                         finish();
    216                     }
    217                 });
    218     }
    219 
    220     private void setResponseIntent(FillResponse fillResponse) {
    221         mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);
    222     }
    223 
    224     private void setDatasetIntent(Dataset dataset) {
    225         mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
    226     }
    227 }
    228