chromium/chrome/browser/ui/views/arc_data_removal_dialog_view.cc

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

#include "base/memory/raw_ptr.h"
#include "chrome/browser/ash/app_list/app_service/app_service_app_icon_loader.h"
#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
#include "chrome/browser/ash/app_list/arc/arc_data_removal_dialog.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/arc/session/arc_session_manager_observer.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"

namespace arc {

namespace {

constexpr int kArcAppIconSize = 48;

// This dialog is shown when ARC++ comes into the state when normal
// functionality could not be possible without resetting whole container by data
// removal. It provides an option for the user to remove data and restart the
// ARC++ or keep current data. Following is known case:
//  * Child user failed to transit from/to regular state.
class DataRemovalConfirmationDialog : public views::DialogDelegateView,
                                      public AppIconLoaderDelegate,
                                      public ArcSessionManagerObserver {
  METADATA_HEADER(DataRemovalConfirmationDialog, views::DialogDelegateView)

 public:
  DataRemovalConfirmationDialog(
      Profile* profile,
      DataRemovalConfirmationCallback confirm_data_removal);
  DataRemovalConfirmationDialog(const DataRemovalConfirmationDialog&) = delete;
  DataRemovalConfirmationDialog& operator=(
      const DataRemovalConfirmationDialog&) = delete;
  ~DataRemovalConfirmationDialog() override;

  // AppIconLoaderDelegate:
  void OnAppImageUpdated(
      const std::string& app_id,
      const gfx::ImageSkia& image,
      bool is_placeholder_icon,
      const std::optional<gfx::ImageSkia>& badge_image) override;

  // ArcSessionManagerObserver:
  void OnArcPlayStoreEnabledChanged(bool enabled) override;

 private:
  // UI hierarchy owned.
  raw_ptr<views::ImageView> icon_view_ = nullptr;

  std::unique_ptr<AppServiceAppIconLoader> icon_loader_;

  const raw_ptr<Profile> profile_;

  DataRemovalConfirmationCallback confirm_callback_;
};

DataRemovalConfirmationDialog* g_current_data_removal_confirmation = nullptr;

DataRemovalConfirmationDialog::DataRemovalConfirmationDialog(
    Profile* profile,
    DataRemovalConfirmationCallback confirm_callback)
    : profile_(profile), confirm_callback_(std::move(confirm_callback)) {
  SetTitle(l10n_util::GetStringUTF16(IDS_ARC_DATA_REMOVAL_CONFIRMATION_TITLE));
  SetButtonLabel(
      ui::mojom::DialogButton::kOk,
      l10n_util::GetStringUTF16(IDS_ARC_DATA_REMOVAL_CONFIRMATION_OK_BUTTON));

  auto run_callback = [](DataRemovalConfirmationDialog* dialog, bool accept) {
    std::move(dialog->confirm_callback_).Run(accept);
  };
  SetAcceptCallback(base::BindOnce(run_callback, base::Unretained(this), true));
  SetCancelCallback(
      base::BindOnce(run_callback, base::Unretained(this), false));

  SetModalType(ui::mojom::ModalType::kWindow);
  set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
      views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));

  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();

  std::unique_ptr<views::BoxLayout> layout = std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kHorizontal,
      provider->GetDialogInsetsForContentType(views::DialogContentType::kText,
                                              views::DialogContentType::kText),
      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL));
  layout->set_cross_axis_alignment(
      views::BoxLayout::CrossAxisAlignment::kStart);
  SetLayoutManager(std::move(layout));

  auto icon_view = std::make_unique<views::ImageView>();
  icon_view->SetPreferredSize(gfx::Size(kArcAppIconSize, kArcAppIconSize));
  icon_view_ = AddChildView(std::move(icon_view));

  // UI hierarchy owned.
  auto label = std::make_unique<views::Label>(
      l10n_util::GetStringUTF16(IDS_ARC_DATA_REMOVAL_CONFIRMATION_HEADING),
      views::style::CONTEXT_DIALOG_BODY_TEXT);
  label->SetMultiLine(true);
  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  AddChildView(std::move(label));

  icon_loader_ = std::make_unique<AppServiceAppIconLoader>(
      profile_, kArcAppIconSize, this);
  icon_loader_->FetchImage(kPlayStoreAppId);

  ArcSessionManager::Get()->AddObserver(this);

  constrained_window::CreateBrowserModalDialogViews(this, nullptr)->Show();
}

DataRemovalConfirmationDialog::~DataRemovalConfirmationDialog() {
  ArcSessionManager::Get()->RemoveObserver(this);

  DCHECK_EQ(g_current_data_removal_confirmation, this);
  g_current_data_removal_confirmation = nullptr;
}

void DataRemovalConfirmationDialog::OnAppImageUpdated(
    const std::string& app_id,
    const gfx::ImageSkia& image,
    bool is_placeholder_icon,
    const std::optional<gfx::ImageSkia>& badge_image) {
  DCHECK(!image.isNull());
  DCHECK_EQ(image.width(), kArcAppIconSize);
  DCHECK_EQ(image.height(), kArcAppIconSize);
  icon_view_->SetImageSize(image.size());
  icon_view_->SetImage(image);
}

void DataRemovalConfirmationDialog::OnArcPlayStoreEnabledChanged(bool enabled) {
  // Close dialog on ARC++ OptOut. In this case data is automatically removed
  // and current dialog is no longer needed.
  if (enabled) {
    return;
  }
  CancelDialog();
}

BEGIN_METADATA(DataRemovalConfirmationDialog)
END_METADATA

}  // namespace

void ShowDataRemovalConfirmationDialog(
    Profile* profile,
    DataRemovalConfirmationCallback callback) {
  if (!g_current_data_removal_confirmation) {
    g_current_data_removal_confirmation =
        new DataRemovalConfirmationDialog(profile, std::move(callback));
  }
}

bool IsDataRemovalConfirmationDialogOpenForTesting() {
  return g_current_data_removal_confirmation != nullptr;
}

void CloseDataRemovalConfirmationDialogForTesting(bool confirm) {
  DCHECK(g_current_data_removal_confirmation);
  if (confirm) {
    g_current_data_removal_confirmation->AcceptDialog();
  } else {
    g_current_data_removal_confirmation->CancelDialog();
  }
}

}  // namespace arc