SaveUi.java revision 0962262f42e3636270689100e1bb90b8f3cf6d77
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 17package com.android.server.autofill.ui; 18 19import static com.android.server.autofill.ui.Helper.DEBUG; 20 21import android.annotation.NonNull; 22import android.app.Dialog; 23import android.content.Context; 24import android.content.IntentSender; 25import android.os.Handler; 26import android.service.autofill.SaveInfo; 27import android.text.format.DateUtils; 28import android.util.Slog; 29import android.view.Gravity; 30import android.view.Window; 31import android.view.WindowManager; 32import android.widget.TextView; 33import android.view.LayoutInflater; 34import android.view.View; 35 36import com.android.internal.R; 37import com.android.server.UiThread; 38 39/** 40 * Autofill Save Prompt 41 */ 42final class SaveUi { 43 44 private static final String TAG = "SaveUi"; 45 46 public interface OnSaveListener { 47 void onSave(); 48 void onCancel(IntentSender listener); 49 void onDestroy(); 50 } 51 52 private class OneTimeListener implements OnSaveListener { 53 54 private final OnSaveListener mRealListener; 55 private boolean mDone; 56 57 OneTimeListener(OnSaveListener realListener) { 58 mRealListener = realListener; 59 } 60 61 @Override 62 public void onSave() { 63 if (DEBUG) Slog.d(TAG, "onSave(): " + mDone); 64 if (mDone) { 65 return; 66 } 67 mDone = true; 68 mRealListener.onSave(); 69 } 70 71 @Override 72 public void onCancel(IntentSender listener) { 73 if (DEBUG) Slog.d(TAG, "onCancel(): " + mDone); 74 if (mDone) { 75 return; 76 } 77 mDone = true; 78 mRealListener.onCancel(listener); 79 } 80 81 @Override 82 public void onDestroy() { 83 if (DEBUG) Slog.d(TAG, "onDestroy(): " + mDone); 84 if (mDone) { 85 return; 86 } 87 mDone = true; 88 mRealListener.onDestroy(); 89 } 90 } 91 92 private final Handler mHandler = UiThread.getHandler(); 93 94 private final @NonNull Dialog mDialog; 95 96 private final @NonNull OneTimeListener mListener; 97 98 private boolean mDestroyed; 99 100 SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info, 101 @NonNull OnSaveListener listener, int lifeTimeMs) { 102 mListener = new OneTimeListener(listener); 103 104 final LayoutInflater inflater = LayoutInflater.from(context); 105 final View view = inflater.inflate(R.layout.autofill_save, null); 106 107 final TextView titleView = (TextView) view.findViewById(R.id.autofill_save_title); 108 final String type; 109 110 switch(info.getType()) { 111 case SaveInfo.SAVE_DATA_TYPE_PASSWORD: 112 type = context.getString(R.string.autofill_save_type_password); 113 break; 114 case SaveInfo.SAVE_DATA_TYPE_ADDRESS: 115 type = context.getString(R.string.autofill_save_type_address); 116 break; 117 case SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD: 118 type = context.getString(R.string.autofill_save_type_credit_card); 119 break; 120 case SaveInfo.SAVE_DATA_TYPE_USERNAME: 121 type = context.getString(R.string.autofill_save_type_username); 122 break; 123 case SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS: 124 type = context.getString(R.string.autofill_save_type_email_address); 125 break; 126 default: 127 type = null; 128 } 129 130 final String title = (type == null) 131 ? context.getString(R.string.autofill_save_title, providerLabel) 132 : context.getString(R.string.autofill_save_title_with_type, type, providerLabel); 133 134 titleView.setText(title); 135 final CharSequence subTitle = info.getDescription(); 136 if (subTitle != null) { 137 final TextView subTitleView = (TextView) view.findViewById(R.id.autofill_save_subtitle); 138 subTitleView.setText(subTitle); 139 subTitleView.setVisibility(View.VISIBLE); 140 } 141 142 final TextView noButton = view.findViewById(R.id.autofill_save_no); 143 if (info.getNegativeActionTitle() != null) { 144 noButton.setText(info.getNegativeActionTitle()); 145 noButton.setOnClickListener((v) -> mListener.onCancel( 146 info.getNegativeActionListener())); 147 } else { 148 noButton.setOnClickListener((v) -> mListener.onCancel(null)); 149 } 150 151 final View yesButton = view.findViewById(R.id.autofill_save_yes); 152 yesButton.setOnClickListener((v) -> mListener.onSave()); 153 154 final View closeButton = view.findViewById(R.id.autofill_save_close); 155 closeButton.setOnClickListener((v) -> mListener.onCancel(null)); 156 157 mDialog = new Dialog(context, R.style.Theme_Material_Panel); 158 mDialog.setContentView(view); 159 160 final Window window = mDialog.getWindow(); 161 window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); 162 window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 163 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 164 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 165 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); 166 window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); 167 window.setGravity(Gravity.BOTTOM | Gravity.CENTER); 168 window.setCloseOnTouchOutside(true); 169 window.getAttributes().width = WindowManager.LayoutParams.MATCH_PARENT; 170 171 mDialog.show(); 172 173 mHandler.postDelayed(() -> { 174 if (!mListener.mDone) { 175 mListener.onCancel(null); 176 Slog.d(TAG, "Save snackbar timed out after " + lifeTimeMs + "ms"); 177 } 178 }, lifeTimeMs); 179 } 180 181 void destroy() { 182 throwIfDestroyed(); 183 mListener.onDestroy(); 184 mHandler.removeCallbacksAndMessages(mListener); 185 mDialog.dismiss(); 186 mDestroyed = true; 187 } 188 189 private void throwIfDestroyed() { 190 if (mDestroyed) { 191 throw new IllegalStateException("cannot interact with a destroyed instance"); 192 } 193 } 194} 195