chromium/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc

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

#include "ash/webui/eche_app_ui/eche_recent_app_click_handler.h"

#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/system/eche/eche_tray.h"
#include "ash/system/phonehub/phone_hub_ui_controller.h"
#include "ash/webui/eche_app_ui/apps_launch_info_provider.h"
#include "ash/webui/eche_app_ui/launch_app_helper.h"
#include "base/metrics/histogram_functions.h"
#include "chromeos/ash/components/phonehub/phone_hub_manager.h"

namespace ash {
namespace eche_app {

EcheRecentAppClickHandler::EcheRecentAppClickHandler(
    phonehub::PhoneHubManager* phone_hub_manager,
    FeatureStatusProvider* feature_status_provider,
    LaunchAppHelper* launch_app_helper,
    EcheStreamStatusChangeHandler* stream_status_change_handler,
    AppsLaunchInfoProvider* apps_launch_info_provider)
    : phone_hub_manager_(phone_hub_manager),
      feature_status_provider_(feature_status_provider),
      launch_app_helper_(launch_app_helper),
      stream_status_change_handler_(stream_status_change_handler),
      apps_launch_info_provider_(apps_launch_info_provider) {
  notification_handler_ =
      phone_hub_manager->GetNotificationInteractionHandler();
  recent_apps_handler_ = phone_hub_manager->GetRecentAppsInteractionHandler();
  feature_status_provider_->AddObserver(this);
  stream_status_change_handler_->AddObserver(this);

  if (notification_handler_ && recent_apps_handler_ &&
      IsClickable(feature_status_provider_->GetStatus())) {
    notification_handler_->AddNotificationClickHandler(this);
    recent_apps_handler_->AddRecentAppClickObserver(this);
    is_click_handler_set_ = true;
  }
}

EcheRecentAppClickHandler::~EcheRecentAppClickHandler() {
  feature_status_provider_->RemoveObserver(this);
  stream_status_change_handler_->RemoveObserver(this);
  if (notification_handler_)
    notification_handler_->RemoveNotificationClickHandler(this);
  if (recent_apps_handler_)
    recent_apps_handler_->RemoveRecentAppClickObserver(this);
}

void EcheRecentAppClickHandler::HandleNotificationClick(
    int64_t notification_id,
    const phonehub::Notification::AppMetadata& app_metadata) {
  DCHECK(phone_hub_manager_);
  // Add the notification’s `app_metadata` that the user clicks to recents list
  // if the stream is already started. If the stream hasn't started yet, we keep
  // this notification’s `app_metadata` until this notification is streaming
  // successfully.
  if (is_stream_started_) {
    const LaunchAppHelper::AppLaunchProhibitedReason prohibited_reason =
        launch_app_helper_->CheckAppLaunchProhibitedReason(
            feature_status_provider_->GetStatus());
    if (recent_apps_handler_ &&
        prohibited_reason ==
            LaunchAppHelper::AppLaunchProhibitedReason::kNotProhibited) {
      recent_apps_handler_->NotifyRecentAppAddedOrUpdated(app_metadata,
                                                          base::Time::Now());
    }
  } else {
    // Only cache the last `app_metadata` when we receive multiple notification
    // click events.
    to_stream_apps_.clear();
    to_stream_apps_.emplace_back(app_metadata);
  }
}

void EcheRecentAppClickHandler::OnRecentAppClicked(
    const phonehub::Notification::AppMetadata& app_metadata,
    mojom::AppStreamLaunchEntryPoint entrypoint) {
  const LaunchAppHelper::AppLaunchProhibitedReason prohibited_reason =
      launch_app_helper_->CheckAppLaunchProhibitedReason(
          feature_status_provider_->GetStatus());
  switch (prohibited_reason) {
    case LaunchAppHelper::AppLaunchProhibitedReason::kNotProhibited:
      base::UmaHistogramEnumeration("Eche.AppStream.LaunchAttempt", entrypoint);

      to_stream_apps_.emplace_back(app_metadata);
      apps_launch_info_provider_->SetAppLaunchInfo(entrypoint);
      launch_app_helper_->LaunchEcheApp(
          /*notification_id=*/std::nullopt, app_metadata.package_name,
          app_metadata.visible_app_name, app_metadata.user_id,
          app_metadata.color_icon,
          phone_hub_manager_->GetPhoneModel()->phone_name().value_or(
              std::u16string()),
          apps_launch_info_provider_);
      break;
    case LaunchAppHelper::AppLaunchProhibitedReason::kDisabledByScreenLock:
      launch_app_helper_->ShowNotification(
          /* title= */ app_metadata.visible_app_name,
          /* message= */ std::nullopt,
          std::make_unique<LaunchAppHelper::NotificationInfo>(
              LaunchAppHelper::NotificationInfo::Category::kNative,
              LaunchAppHelper::NotificationInfo::NotificationType::
                  kScreenLock));
      break;
  }
}

void EcheRecentAppClickHandler::OnFeatureStatusChanged() {
  if (!notification_handler_ || !recent_apps_handler_) {
    return;
  }
  bool clickable = IsClickable(feature_status_provider_->GetStatus());
  if (!is_click_handler_set_ && clickable) {
    notification_handler_->AddNotificationClickHandler(this);
    recent_apps_handler_->AddRecentAppClickObserver(this);
    is_click_handler_set_ = true;
  } else if (is_click_handler_set_ && !clickable) {
    notification_handler_->RemoveNotificationClickHandler(this);
    recent_apps_handler_->RemoveRecentAppClickObserver(this);
    is_click_handler_set_ = false;
  }
}

void EcheRecentAppClickHandler::OnStreamStatusChanged(
    mojom::StreamStatus status) {
  if (status == eche_app::mojom::StreamStatus::kStreamStatusStarted) {
    is_stream_started_ = true;
    const LaunchAppHelper::AppLaunchProhibitedReason prohibited_reason =
        launch_app_helper_->CheckAppLaunchProhibitedReason(
            feature_status_provider_->GetStatus());
    if (recent_apps_handler_ &&
        prohibited_reason ==
            LaunchAppHelper::AppLaunchProhibitedReason::kNotProhibited)
      for (const auto& app_metadata : to_stream_apps_) {
        recent_apps_handler_->NotifyRecentAppAddedOrUpdated(app_metadata,
                                                            base::Time::Now());
      }
  } else if (status == eche_app::mojom::StreamStatus::kStreamStatusStopped) {
    is_stream_started_ = false;
    to_stream_apps_.clear();
  }
}

bool EcheRecentAppClickHandler::IsClickable(FeatureStatus status) {
  return status == FeatureStatus::kDisconnected ||
         status == FeatureStatus::kConnecting ||
         status == FeatureStatus::kConnected;
}

}  // namespace eche_app
}  // namespace ash