chromium/components/webapps/browser/android/java/src/org/chromium/components/webapps/pwa_restore_ui/PwaRestoreBottomSheetView.java

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.webapps.pwa_restore_ui;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

import androidx.core.content.res.ResourcesCompat;

import org.chromium.components.webapps.R;
import org.chromium.components.webapps.pwa_restore_ui.PwaRestoreProperties.ViewState;

import java.util.List;

/**
 * The view portion of the PWA Install bottom sheet.
 *
 * <p>The class is suppressing lint error for 'missing performClick override' because we don't need
 * the override. We're forwarding the event along (and the implementation of the override would be a
 * no-op for us).
 */
@SuppressLint("ClickableViewAccessibility")
public class PwaRestoreBottomSheetView {
    private static final int APP_ICON_SIZE_DP = 40;
    private static final int APP_ICON_CORNER_RADIUS_DP = 20;
    private static final int APP_ICON_TEXT_SIZE_DP = 24;

    // The current context.
    private final Context mContext;

    // The main view for the bottom sheet (preview and contents).
    private View mContentView;

    // The listener to notify when the Back button is clicked.
    private OnClickListener mBackButtonListener;

    // The listener to notify when an app checkbox is toggled in the app list.
    private OnClickListener mSelectionToggleButtonListener;

    public PwaRestoreBottomSheetView(Context context) {
        mContext = context;
    }

    public void initialize(int backArrowId) {
        mContentView =
                LayoutInflater.from(mContext)
                        .inflate(R.layout.pwa_restore_bottom_sheet_dialog, /* root= */ null);

        int backgroundId = R.drawable.pwa_restore_icon;
        mContentView.findViewById(R.id.icon).setBackgroundResource(backgroundId);
        mContentView.findViewById(R.id.icon).setTag(backgroundId);
        Drawable backArrow =
                backArrowId != 0
                        ? ResourcesCompat.getDrawable(
                                mContext.getResources(), backArrowId, mContext.getTheme())
                        : null;
        ImageView backArrowView = (ImageView) mContentView.findViewById(R.id.back);
        backArrowView.setImageDrawable(backArrow);
        backArrowView.setOnClickListener(this::onClickBack);
    }

    public View getContentView() {
        return mContentView;
    }

    public void setDisplayedView(@ViewState int viewState) {
        ViewGroup previewView = mContentView.findViewById(R.id.preview_container);
        ViewGroup contentView = mContentView.findViewById(R.id.content_container);
        previewView.setVisibility(viewState == ViewState.VIEW_PWA_LIST ? View.GONE : View.VISIBLE);
        contentView.setVisibility(viewState == ViewState.PREVIEW ? View.GONE : View.VISIBLE);
    }

    protected void setAppList(List<PwaRestoreProperties.AppInfo> appList, String appLabel) {
        LinearLayout scrollViewContent = getContentView().findViewById(R.id.scroll_view_content);
        scrollViewContent.removeAllViews();
        prepareAppList(appList, appLabel, scrollViewContent);
    }

    private void prepareAppList(
            List<PwaRestoreProperties.AppInfo> appList,
            String labelContent,
            LinearLayout scrollViewContent) {
        // Set the heading for the app list.
        View label =
                LayoutInflater.from(mContext).inflate(R.layout.pwa_restore_list_item_label, null);
        ((TextView) label.findViewById(R.id.label_text)).setText(labelContent);
        scrollViewContent.addView(label);

        int item = 0;
        for (PwaRestoreProperties.AppInfo app : appList) {
            View appView =
                    LayoutInflater.from(mContext).inflate(R.layout.pwa_restore_list_item_app, null);

            // Apply the right background to the item (first item has rounded corner on top, last
            // item has rounded corners on bottom, and all items in between have no rounded
            // corners).
            if (item == 0) {
                appView.setBackgroundResource(
                        appList.size() == 1
                                ? R.drawable.pwa_restore_app_item_background_single
                                : R.drawable.pwa_restore_app_item_background_top);
            } else {
                appView.setBackgroundResource(
                        (item == appList.size() - 1)
                                ? R.drawable.pwa_restore_app_item_background_bottom
                                : R.drawable.pwa_restore_app_item_background_middle);
            }
            item += 1;

            ((ImageView) appView.findViewById(R.id.app_icon)).setImageBitmap(app.getIcon());
            ((TextView) appView.findViewById(R.id.app_name)).setText(app.getName());
            CheckBox checkBox = (CheckBox) appView.findViewById(R.id.checkbox);
            checkBox.setTag(app.getId());
            checkBox.setChecked(app.isSelected());
            checkBox.setOnClickListener(this::onClick);

            // Any click on an app item, that is not handled by the view itself, should be treated
            // as an attempt to toggle the checkbox.
            appView.setOnClickListener(this::onClick);

            scrollViewContent.addView(appView);

            // Add a 4dp separator view as a separate item in the ScrollView so as to not affect the
            // height of the app item view (or mess up the rounded corners).
            View separator = new View(mContext);
            separator.setLayoutParams(
                    new LayoutParams(
                            ViewGroup.LayoutParams.MATCH_PARENT,
                            (int)
                                    TypedValue.applyDimension(
                                            TypedValue.COMPLEX_UNIT_DIP,
                                            4,
                                            mContext.getResources().getDisplayMetrics())));
            scrollViewContent.addView(separator);
        }
    }

    public void onClickBack(View view) {
        mBackButtonListener.onClick(view);
    }

    public void onClick(View view) {
        CheckBox checkBox = null;
        if (view instanceof CheckBox) {
            checkBox = (CheckBox) view;
        } else {
            // Clicks outside the checkbox, that are not handled by the corresponding view, are
            // forwarded to the checkbox.
            checkBox = (CheckBox) view.findViewById(R.id.checkbox);
            checkBox.toggle();
        }

        // Notify of the change.
        mSelectionToggleButtonListener.onClick(checkBox);
    }

    protected void setBackButtonListener(OnClickListener listener) {
        mBackButtonListener = listener;
    }

    protected void setSelectionToggleButtonListener(OnClickListener listener) {
        mSelectionToggleButtonListener = listener;
    }
}