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