SaveUi.java revision 7b7711680244108923f0dbb4ca6e35db7e659e6a
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 android.annotation.NonNull;
20import android.app.Dialog;
21import android.content.Context;
22import android.content.IntentSender;
23import android.os.Handler;
24import android.service.autofill.SaveInfo;
25import android.text.format.DateUtils;
26import android.view.Gravity;
27import android.view.Window;
28import android.view.WindowManager;
29import android.widget.TextView;
30import android.view.LayoutInflater;
31import android.view.View;
32
33import com.android.internal.R;
34import com.android.server.UiThread;
35
36/**
37 * Autofill Save Prompt
38 */
39final class SaveUi {
40    public interface OnSaveListener {
41        void onSave();
42        void onCancel(IntentSender listener);
43        void onDestroy();
44    }
45
46    private final Handler mHandler = UiThread.getHandler();
47
48    private final @NonNull Dialog mDialog;
49
50    private final @NonNull OnSaveListener mListener;
51
52    private boolean mDestroyed;
53
54    SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
55            @NonNull OnSaveListener listener, int lifeTimeMs) {
56        mListener = listener;
57
58        final LayoutInflater inflater = LayoutInflater.from(context);
59        final View view = inflater.inflate(R.layout.autofill_save, null);
60
61        final TextView titleView = (TextView) view.findViewById(R.id.autofill_save_title);
62        final String type;
63
64        switch(info.getType()) {
65            case SaveInfo.SAVE_DATA_TYPE_PASSWORD:
66                type = context.getString(R.string.autofill_save_type_password);
67                break;
68            case SaveInfo.SAVE_DATA_TYPE_ADDRESS:
69                type = context.getString(R.string.autofill_save_type_address);
70                break;
71            case SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD:
72                type = context.getString(R.string.autofill_save_type_credit_card);
73                break;
74            default:
75                type = null;
76        }
77
78        final String title = (type == null)
79                ? context.getString(R.string.autofill_save_title, providerLabel)
80                : context.getString(R.string.autofill_save_title_with_type, type, providerLabel);
81
82        titleView.setText(title);
83        final CharSequence subTitle = info.getDescription();
84        if (subTitle != null) {
85            final TextView subTitleView = (TextView) view.findViewById(R.id.autofill_save_subtitle);
86            subTitleView.setText(subTitle);
87            subTitleView.setVisibility(View.VISIBLE);
88        }
89
90        final TextView noButton = view.findViewById(R.id.autofill_save_no);
91        if (info.getNegativeActionTitle() != null) {
92            noButton.setText(info.getNegativeActionTitle());
93            noButton.setOnClickListener((v) -> mListener.onCancel(
94                    info.getNegativeActionListener()));
95        } else {
96            noButton.setOnClickListener((v) -> mListener.onCancel(null));
97        }
98
99        final View yesButton = view.findViewById(R.id.autofill_save_yes);
100        yesButton.setOnClickListener((v) -> mListener.onSave());
101
102        final View closeButton = view.findViewById(R.id.autofill_save_close);
103        closeButton.setOnClickListener((v) -> mListener.onCancel(null));
104
105        mDialog = new Dialog(context, R.style.Theme_Material_Panel);
106        mDialog.setContentView(view);
107
108        final Window window = mDialog.getWindow();
109        window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
110        window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
111                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
112                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
113                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
114        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
115        window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
116        window.setCloseOnTouchOutside(true);
117        window.getAttributes().width = WindowManager.LayoutParams.MATCH_PARENT;
118
119        mDialog.show();
120
121        mHandler.postDelayed(() -> mListener.onCancel(null), lifeTimeMs);
122    }
123
124    void destroy() {
125        throwIfDestroyed();
126        mListener.onDestroy();
127        mHandler.removeCallbacksAndMessages(mListener);
128        mDialog.dismiss();
129        mDestroyed = true;
130    }
131
132    private void throwIfDestroyed() {
133        if (mDestroyed) {
134            throw new IllegalStateException("cannot interact with a destroyed instance");
135        }
136    }
137}
138