Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 87af2d85 authored by Felipe Leme's avatar Felipe Leme
Browse files

Adds autofill save ui

Test: Manual verification of save ui
Test: CtsAutoFillServiceTestCases passes

BUG: 31001899
Change-Id: Ibb154fc6108f20e6a1b889549bd264e8b80f2f0c
parent bd00fef4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -923,7 +923,7 @@ final class AutoFillManagerServiceImpl {

                // TODO(b/33197203): temporarily hack: show the save notification after autofilled,
                // since save is not automatically detected yet.
                mUi.showSaveUI(mUserId, mId);
                mUi.showSaveNotification(mUserId, mId); removeSelf = false;

            } catch (RemoteException e) {
                Slog.w(TAG, "Error auto-filling activity: " + e);
+60 −9
Original line number Diff line number Diff line
@@ -29,12 +29,16 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Rect;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.Bundle;
import android.util.Slog;
import android.view.autofill.AutoFillId;
import android.view.autofill.Dataset;
import android.view.autofill.FillResponse;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;

import com.android.internal.annotations.GuardedBy;
@@ -53,8 +57,16 @@ final class AutoFillUI {

    private final Context mContext;

    private final WindowManager mWm;

    /**
     * Custom snackbar UI used for saving autofill or other informational messages.
     */
    private View mSnackbar;

    AutoFillUI(Context context, AutoFillManagerService service, Object lock) {
        mContext = context;
        mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        mService = service;
        mLock = lock;

@@ -130,7 +142,20 @@ final class AutoFillUI {
     * Shows the UI asking the user to save for auto-fill.
     */
    void showSaveUI(int userId, int sessionId) {
        showSaveNotification(userId, sessionId);
        showSnackbar(new SavePrompt(mContext, new SavePrompt.OnSaveListener() {
            @Override
            public void onSaveClick() {
                hideSnackbar();
                synchronized (mLock) {
                    final AutoFillManagerServiceImpl service = getServiceLocked(userId);
                    service.requestSaveLocked(sessionId);
                }
            }
            @Override
            public void onCancelClick() {
                hideSnackbar();
            }
        }));
    }

    /**
@@ -158,10 +183,8 @@ final class AutoFillUI {
    }

    private void onSaveRequested(int userId, int sessionId) {
        synchronized (mLock) {
            final AutoFillManagerServiceImpl service = getServiceLocked(userId);
            service.requestSaveLocked(sessionId);
        }
        // TODO(b/33197203): displays the snack bar, until save notification is refactored
        showSaveUI(userId, sessionId);
    }

    private void onDatasetPicked(int userId, Dataset dataset, int sessionId) {
@@ -191,6 +214,34 @@ final class AutoFillUI {
        }
    }

    //similar to a snackbar, but can be a bit custom since it is more than just text. This will
    //allow two buttons for saving or not saving the autofill for instance as well.
    private void showSnackbar(View snackBar) {
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.FILL_PARENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, // TODO(b/33197203) use TYPE_AUTO_FILL
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN,
            PixelFormat.TRANSLUCENT);

        params.gravity = Gravity.BOTTOM | Gravity.LEFT;

        UiThread.getHandler().runWithScissors(() -> {
            mSnackbar = snackBar;
            mWm.addView(mSnackbar, params);
        }, 0);
    }

    private void hideSnackbar() {
        UiThread.getHandler().runWithScissors(() -> {
            if (mSnackbar != null) {
                mWm.removeView(mSnackbar);
                mSnackbar = null;
            }
        }, 0);
    }

    /////////////////////////////////////////////////////////////////////////////////
    // TODO(b/33197203): temporary code using a notification to request auto-fill. //
    // Will be removed once UX decide the right way to present it to the user.     //
@@ -215,9 +266,9 @@ final class AutoFillUI {
    private static final String TYPE_SAVE = "save";
    private static final String TYPE_AUTH_RESPONSE = "auth_response";

    @GuardedBy("mLock")
    @GuardedBy("mServiceLock")
    private BroadcastReceiver mNotificationReceiver;
    @GuardedBy("mLock")
    @GuardedBy("mServiceLock")
    private final AutoFillManagerService mService;
    private final Object mLock;

@@ -403,8 +454,8 @@ final class AutoFillUI {
        final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext,
                ++sResultCode, saveIntent, PendingIntent.FLAG_ONE_SHOT);

        final String title = "AutoFill Save";
        final String subTitle = "Tap notification to ask provider to save fields.";
        final String title = "AutoFill Save Emulation";
        final String subTitle = "Tap notification to launch the save snackbar.";

        final Notification notification = newNotificationBuilder()
                .setAutoCancel(true)
+92 −0
Original line number Diff line number Diff line
/*
 * 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.android.server.autofill;

import android.content.Context;
import android.graphics.Color;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
import android.view.View;

/**
 * Autofill Save Prompt
 */
final class SavePrompt extends RelativeLayout {
    public interface OnSaveListener {
        void onSaveClick();

        void onCancelClick();
    }

    private final TextView mTextView;
    private final TextView mNoButton;
    private final TextView mYesButton;
    private final OnSaveListener mListener;

    SavePrompt(Context context, OnSaveListener listener) {
        super(context);
        mListener = listener;
        setBackgroundColor(Color.YELLOW);

        // TODO(b/33197203): move layout to XML
        mTextView = new TextView(context);
        final LayoutParams textParams = new LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        textParams.setMargins(50, 25, 50, 0);
        mTextView.setLayoutParams(textParams);
        // TODO(b/33197203): use R.string once final wording is done
        mTextView.setText("Save for autofill?");
        mTextView.setId(View.generateViewId());

        mNoButton = new TextView(context);
        // TODO(b/33197203): use R.string once final wording is done
        mNoButton.setText("No thanks");
        mNoButton.setBackgroundColor(Color.TRANSPARENT);
        mNoButton.setAllCaps(true);
        mNoButton.setOnClickListener((v) -> {
            mListener.onCancelClick();
        });

        mYesButton = new TextView(context);
        // TODO(b/33197203): use R.string once final wording is done
        mYesButton.setText("Save");
        mYesButton.setBackgroundColor(Color.TRANSPARENT);
        mYesButton.setId(View.generateViewId());
        mYesButton.setAllCaps(true);
        mYesButton.setOnClickListener((v) -> {
            mListener.onSaveClick();
        });

        addView(mTextView);
        addView(mNoButton);
        addView(mYesButton);

        final LayoutParams yesLayoutParams = new LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        yesLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        yesLayoutParams.addRule(RelativeLayout.BELOW, mTextView.getId());
        yesLayoutParams.setMargins(25, 25, 50, 25);
        mYesButton.setLayoutParams(yesLayoutParams);
        final LayoutParams noLayoutParams = new LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        noLayoutParams.addRule(RelativeLayout.LEFT_OF, mYesButton.getId());
        noLayoutParams.addRule(RelativeLayout.BELOW, mTextView.getId());
        noLayoutParams.setMargins(50, 25, 25, 25);
        mNoButton.setLayoutParams(noLayoutParams);
    }
}