chromium/components/webapps/browser/android/webapps_icon_utils.cc

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

#include "components/webapps/browser/android/webapps_icon_utils.h"

#include "base/android/build_info.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/color_analysis.h"
#include "url/android/gurl_android.h"
#include "url/gurl.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/webapps/browser/android/webapps_jni_headers/WebappsIconUtils_jni.h"

using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;

namespace webapps {

namespace {

int g_ideal_homescreen_icon_size = -1;
int g_minimum_homescreen_icon_size = -1;
int g_ideal_splash_image_size = -1;
int g_minimum_splash_image_size = -1;
int g_ideal_monochrome_icon_size = -1;
int g_ideal_adaptive_launcher_icon_size = -1;
int g_ideal_shortcut_icon_size = -1;

// Retrieves and caches the ideal and minimum sizes of the Home screen icon,
// the splash screen image, and the shortcut icons.
void GetIconSizes() {
  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jintArray> java_size_array =
      Java_WebappsIconUtils_getIconSizes(env);
  std::vector<int> sizes;
  base::android::JavaIntArrayToIntVector(env, java_size_array, &sizes);

  // Check that the size returned is what is expected.
  DCHECK_EQ(7u, sizes.size());

  // This ordering must be kept up to date with the Java WebappsIconUtils.
  g_ideal_homescreen_icon_size = sizes[0];
  g_minimum_homescreen_icon_size = sizes[1];
  g_ideal_splash_image_size = sizes[2];
  g_minimum_splash_image_size = sizes[3];
  g_ideal_monochrome_icon_size = sizes[4];
  g_ideal_adaptive_launcher_icon_size = sizes[5];
  g_ideal_shortcut_icon_size = sizes[6];

  // Try to ensure that the data returned is sensible.
  DCHECK_LE(g_minimum_homescreen_icon_size, g_ideal_homescreen_icon_size);
  DCHECK_LE(g_minimum_splash_image_size, g_ideal_splash_image_size);
}

}  // anonymous namespace

int WebappsIconUtils::GetIdealHomescreenIconSizeInPx() {
  if (g_ideal_homescreen_icon_size == -1)
    GetIconSizes();
  return g_ideal_homescreen_icon_size;
}

int WebappsIconUtils::GetMinimumHomescreenIconSizeInPx() {
  if (g_minimum_homescreen_icon_size == -1)
    GetIconSizes();
  return g_minimum_homescreen_icon_size;
}

int WebappsIconUtils::GetIdealSplashImageSizeInPx() {
  if (g_ideal_splash_image_size == -1)
    GetIconSizes();
  return g_ideal_splash_image_size;
}

int WebappsIconUtils::GetMinimumSplashImageSizeInPx() {
  if (g_minimum_splash_image_size == -1)
    GetIconSizes();
  return g_minimum_splash_image_size;
}

int WebappsIconUtils::GetIdealAdaptiveLauncherIconSizeInPx() {
  if (g_ideal_adaptive_launcher_icon_size == -1)
    GetIconSizes();
  return g_ideal_adaptive_launcher_icon_size;
}

int WebappsIconUtils::GetIdealShortcutIconSizeInPx() {
  if (g_ideal_shortcut_icon_size == -1)
    GetIconSizes();
  return g_ideal_shortcut_icon_size;
}

int WebappsIconUtils::GetIdealIconSizeForIconType(
    webapk::Image::Usage usage,
    webapk::Image::Purpose purpose) {
  switch (usage) {
    case webapk::Image::PRIMARY_ICON:
      if (purpose == webapk::Image::MASKABLE) {
        return GetIdealAdaptiveLauncherIconSizeInPx();
      } else {
        return GetIdealHomescreenIconSizeInPx();
      }
    case webapk::Image::SPLASH_ICON:
      return GetIdealSplashImageSizeInPx();
    case webapk::Image::SHORTCUT_ICON:
      return GetIdealShortcutIconSizeInPx();
    default:
      return 0;
  }
}

bool WebappsIconUtils::DoesAndroidSupportMaskableIcons() {
  return base::android::BuildInfo::GetInstance()->sdk_int() >=
         base::android::SDK_VERSION_OREO;
}

void WebappsIconUtils::FinalizeLauncherIconInBackground(
    const SkBitmap& bitmap,
    const GURL& url,
    scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner,
    base::OnceCallback<void(const SkBitmap&, bool)> callback) {
  base::AssertLongCPUWorkAllowed();

  JNIEnv* env = base::android::AttachCurrentThread();

  if (!bitmap.isNull() && Java_WebappsIconUtils_isIconLargeEnoughForLauncher(
                              env, bitmap.width(), bitmap.height())) {
    ui_thread_task_runner->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), bitmap, false));
    return;
  }

  ScopedJavaLocalRef<jobject> java_url =
      url::GURLAndroid::FromNativeGURL(env, url);
  SkColor mean_color = SkColorSetRGB(0x91, 0x91, 0x91);

  if (!bitmap.isNull()) {
    mean_color = color_utils::CalculateKMeanColorOfBitmap(bitmap);
  }

  ScopedJavaLocalRef<jobject> result =
      Java_WebappsIconUtils_generateHomeScreenIcon(
          env, java_url, SkColorGetR(mean_color), SkColorGetG(mean_color),
          SkColorGetB(mean_color));

  SkBitmap result_bitmap =
      result.obj() ? gfx::CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(result))
                   : SkBitmap();
  ui_thread_task_runner->PostTask(
      FROM_HERE, base::BindOnce(std::move(callback), result_bitmap, true));
}

SkBitmap WebappsIconUtils::GenerateHomeScreenIconInBackground(
    const GURL& page_url) {
  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jobject> java_url =
      url::GURLAndroid::FromNativeGURL(env, page_url);
  ScopedJavaLocalRef<jobject> result =
      Java_WebappsIconUtils_generateHomeScreenIcon(env, java_url, /*red=*/0x91,
                                                   /*green=*/0x91,
                                                   /*blue=*/0x91);
  SkBitmap result_bitmap =
      result.obj() ? gfx::CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(result))
                   : SkBitmap();
  return result_bitmap;
}

SkBitmap WebappsIconUtils::GenerateAdaptiveIconBitmap(const SkBitmap& bitmap) {
  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jobject> result;

  if (!bitmap.isNull()) {
    ScopedJavaLocalRef<jobject> java_bitmap = gfx::ConvertToJavaBitmap(bitmap);
    result = Java_WebappsIconUtils_generateAdaptiveIconBitmap(env, java_bitmap);
  }

  return result.obj()
             ? gfx::CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(result))
             : SkBitmap();
}

int WebappsIconUtils::GetIdealIconCornerRadiusPxForPromptUI() {
  return Java_WebappsIconUtils_getIdealIconCornerRadiusPxForPromptUI(
      base::android::AttachCurrentThread());
}

void WebappsIconUtils::SetIdealShortcutSizeForTesting(int size) {
  g_ideal_shortcut_icon_size = size;
}

void WebappsIconUtils::SetIconSizesForTesting(std::vector<int> sizes) {
  // This ordering must be kept up to date with the |GetIconSizes()| above.
  g_ideal_homescreen_icon_size = sizes[0];
  g_minimum_homescreen_icon_size = sizes[1];
  g_ideal_splash_image_size = sizes[2];
  g_minimum_splash_image_size = sizes[3];
  g_ideal_monochrome_icon_size = sizes[4];
  g_ideal_adaptive_launcher_icon_size = sizes[5];
  g_ideal_shortcut_icon_size = sizes[6];
}

}  // namespace webapps