chromium/chrome/browser/ash/app_list/search/app_discovery_metrics_manager.cc

// 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.

#include "chrome/browser/ash/app_list/search/app_discovery_metrics_manager.h"

#include <utility>

#include "ash/public/cpp/app_list/app_list_metrics.h"
#include "chrome/browser/ash/app_list/search/common/types_util.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chromeos/ash/components/string_matching/fuzzy_tokenized_string_match.h"
#include "chromeos/ash/components/string_matching/tokenized_string.h"
#include "components/metrics/structured/structured_events.h"
#include "components/metrics/structured/structured_metrics_client.h"
#include "components/sync/base/data_type.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_service_utils.h"

namespace app_list {

namespace {

namespace cros_events = metrics::structured::events::v2::cr_os_events;

bool IsResultAppInstallRelated(ash::SearchResultType result_type) {
  return result_type == ash::SearchResultType::PLAY_STORE_UNINSTALLED_APP ||
         result_type == ash::SearchResultType::GAME_SEARCH ||
         result_type == ash::SearchResultType::PLAY_STORE_INSTANT_APP;
}

}  // namespace

AppDiscoveryMetricsManager::AppDiscoveryMetricsManager(Profile* profile)
    : profile_(profile) {}
AppDiscoveryMetricsManager::~AppDiscoveryMetricsManager() = default;

void AppDiscoveryMetricsManager::OnOpenResult(ChromeSearchResult* result,
                                              const std::u16string& query) {
  // If the result is not app-install related, then ignore result as it does not
  // pertain to app-discovery.
  if (!IsResultAppInstallRelated(result->metrics_type())) {
    return;
  }

  auto app_name = ash::string_matching::TokenizedString(result->title());
  auto tokenized_query = ash::string_matching::TokenizedString(query);

  double string_match_score =
      ash::string_matching::FuzzyTokenizedStringMatch::WeightedRatio(
          app_name, tokenized_query);

  // If app-sync is disabled, then do not send app_id.
  std::string app_id = IsAppSyncEnabled() ? result->id() : "";
  std::u16string app_title = IsAppSyncEnabled() ? result->title() : u"";

  metrics::structured::StructuredMetricsClient::Record(
      std::move(cros_events::AppDiscovery_AppLauncherResultOpened()
                    .SetAppId(app_id)
                    .SetAppName(std::string(app_title.begin(), app_title.end()))
                    .SetFuzzyStringMatch(string_match_score)
                    .SetResultCategory(result->metrics_type())));
}

void AppDiscoveryMetricsManager::OnLauncherOpen() {
  metrics::structured::StructuredMetricsClient::Record(
      std::move(cros_events::AppDiscovery_LauncherOpen()));
}

bool AppDiscoveryMetricsManager::IsAppSyncEnabled() {
  switch (syncer::GetUploadToGoogleState(
      SyncServiceFactory::GetForProfile(profile_), syncer::DataType::APPS)) {
    case syncer::UploadState::NOT_ACTIVE:
      return false;
    case syncer::UploadState::INITIALIZING:
      // Note that INITIALIZING is considered good enough, because syncing apps
      // is known to be enabled, and transient errors don't really matter here.
    case syncer::UploadState::ACTIVE:
      return true;
  }
}

}  // namespace app_list