| Index: chrome/browser/android/shortcut_helper.cc
|
| diff --git a/chrome/browser/android/shortcut_helper.cc b/chrome/browser/android/shortcut_helper.cc
|
| index a485d40ec62cb6b12304e5e7130fc5b5da01f1d8..339e26ffd0582c4c49f7267e176150cbfc66650a 100644
|
| --- a/chrome/browser/android/shortcut_helper.cc
|
| +++ b/chrome/browser/android/shortcut_helper.cc
|
| @@ -5,12 +5,14 @@
|
| #include "chrome/browser/android/shortcut_helper.h"
|
|
|
| #include <jni.h>
|
| +#include <limits>
|
|
|
| #include "base/android/jni_android.h"
|
| #include "base/android/jni_string.h"
|
| #include "base/basictypes.h"
|
| #include "base/location.h"
|
| #include "base/strings/string16.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| #include "base/task/cancelable_task_tracker.h"
|
| #include "base/threading/worker_pool.h"
|
| #include "chrome/browser/android/tab_android.h"
|
| @@ -25,12 +27,20 @@
|
| #include "content/public/common/frame_navigate_params.h"
|
| #include "content/public/common/manifest.h"
|
| #include "jni/ShortcutHelper_jni.h"
|
| +#include "net/base/mime_util.h"
|
| #include "ui/gfx/android/java_bitmap.h"
|
| #include "ui/gfx/codec/png_codec.h"
|
| #include "ui/gfx/color_analysis.h"
|
| #include "ui/gfx/favicon_size.h"
|
| +#include "ui/gfx/screen.h"
|
| #include "url/gurl.h"
|
|
|
| +using content::Manifest;
|
| +
|
| +// Android's preferred icon size in DP is 48, as defined in
|
| +// http://developer.android.com/design/style/iconography.html
|
| +const int ShortcutHelper::kPreferredIconSizeInDp = 48;
|
| +
|
| jlong Initialize(JNIEnv* env, jobject obj, jlong tab_android_ptr) {
|
| TabAndroid* tab = reinterpret_cast<TabAndroid*>(tab_android_ptr);
|
|
|
| @@ -49,6 +59,11 @@ ShortcutHelper::ShortcutHelper(JNIEnv* env,
|
| url_(web_contents->GetURL()),
|
| display_(content::Manifest::DISPLAY_MODE_BROWSER),
|
| orientation_(blink::WebScreenOrientationLockDefault),
|
| + add_shortcut_requested_(false),
|
| + manifest_icon_status_(MANIFEST_ICON_STATUS_NONE),
|
| + preferred_icon_size_in_px_(kPreferredIconSizeInDp *
|
| + gfx::Screen::GetScreenFor(web_contents->GetNativeView())->
|
| + GetPrimaryDisplay().device_scale_factor()),
|
| weak_ptr_factory_(this) {
|
| }
|
|
|
| @@ -97,6 +112,124 @@ void ShortcutHelper::OnDidGetWebApplicationInfo(
|
| weak_ptr_factory_.GetWeakPtr()));
|
| }
|
|
|
| +bool ShortcutHelper::IconSizesContainsPreferredSize(
|
| + const std::vector<gfx::Size>& sizes) const {
|
| + for (size_t i = 0; i < sizes.size(); ++i) {
|
| + if (sizes[i].height() != sizes[i].width())
|
| + continue;
|
| + if (sizes[i].width() == preferred_icon_size_in_px_)
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool ShortcutHelper::IconSizesContainsAny(
|
| + const std::vector<gfx::Size>& sizes) const {
|
| + for (size_t i = 0; i < sizes.size(); ++i) {
|
| + if (sizes[i].IsEmpty())
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +GURL ShortcutHelper::FindBestMatchingIcon(
|
| + const std::vector<Manifest::Icon>& icons, float density) const {
|
| + GURL url;
|
| + int best_delta = std::numeric_limits<int>::min();
|
| +
|
| + for (size_t i = 0; i < icons.size(); ++i) {
|
| + if (icons[i].density != density)
|
| + continue;
|
| +
|
| + const std::vector<gfx::Size>& sizes = icons[i].sizes;
|
| + for (size_t j = 0; j < sizes.size(); ++j) {
|
| + if (sizes[j].height() != sizes[j].width())
|
| + continue;
|
| + int delta = sizes[j].width() - preferred_icon_size_in_px_;
|
| + if (delta == 0)
|
| + return icons[i].src;
|
| + if (best_delta > 0 && delta < 0)
|
| + continue;
|
| + if ((best_delta > 0 && delta < best_delta) ||
|
| + (best_delta < 0 && delta > best_delta)) {
|
| + url = icons[i].src;
|
| + best_delta = delta;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return url;
|
| +}
|
| +
|
| +// static
|
| +std::vector<Manifest::Icon> ShortcutHelper::FilterIconsByType(
|
| + const std::vector<Manifest::Icon>& icons) {
|
| + std::vector<Manifest::Icon> result;
|
| +
|
| + for (size_t i = 0; i < icons.size(); ++i) {
|
| + if (icons[i].type.is_null() ||
|
| + net::IsSupportedImageMimeType(
|
| + base::UTF16ToUTF8(icons[i].type.string()))) {
|
| + result.push_back(icons[i]);
|
| + }
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +GURL ShortcutHelper::FindBestMatchingIcon(
|
| + const std::vector<Manifest::Icon>& unfiltered_icons) const {
|
| + const float device_scale_factor =
|
| + gfx::Screen::GetScreenFor(web_contents()->GetNativeView())->
|
| + GetPrimaryDisplay().device_scale_factor();
|
| +
|
| + GURL url;
|
| + std::vector<Manifest::Icon> icons = FilterIconsByType(unfiltered_icons);
|
| +
|
| + // The first pass is to find the ideal icon. That icon is of the right size
|
| + // with the default density or the device's density.
|
| + for (size_t i = 0; i < icons.size(); ++i) {
|
| + if (icons[i].density == device_scale_factor &&
|
| + IconSizesContainsPreferredSize(icons[i].sizes)) {
|
| + return icons[i].src;
|
| + }
|
| +
|
| + // If there is an icon with the right size but not the right density, keep
|
| + // it on the side and only use it if nothing better is found.
|
| + if (icons[i].density == Manifest::Icon::kDefaultDensity &&
|
| + IconSizesContainsPreferredSize(icons[i].sizes)) {
|
| + url = icons[i].src;
|
| + }
|
| + }
|
| +
|
| + // The second pass is to find an icon with 'any'. The current device scale
|
| + // factor is preferred. Otherwise, the default scale factor is used.
|
| + for (size_t i = 0; i < icons.size(); ++i) {
|
| + if (icons[i].density == device_scale_factor &&
|
| + IconSizesContainsAny(icons[i].sizes)) {
|
| + return icons[i].src;
|
| + }
|
| +
|
| + // If there is an icon with 'any' but not the right density, keep it on the
|
| + // side and only use it if nothing better is found.
|
| + if (icons[i].density == Manifest::Icon::kDefaultDensity &&
|
| + IconSizesContainsAny(icons[i].sizes)) {
|
| + url = icons[i].src;
|
| + }
|
| + }
|
| +
|
| + // The last pass will try to find the best suitable icon for the device's
|
| + // scale factor. If none, another pass will be run using kDefaultDensity.
|
| + if (!url.is_valid())
|
| + url = FindBestMatchingIcon(icons, device_scale_factor);
|
| + if (!url.is_valid())
|
| + url = FindBestMatchingIcon(icons, Manifest::Icon::kDefaultDensity);
|
| +
|
| + return url;
|
| +}
|
| +
|
| void ShortcutHelper::OnDidGetManifest(const content::Manifest& manifest) {
|
| // Set the title based on the manifest value, if any.
|
| if (!manifest.short_name.is_null())
|
| @@ -128,6 +261,16 @@ void ShortcutHelper::OnDidGetManifest(const content::Manifest& manifest) {
|
| orientation_ = manifest.orientation;
|
| }
|
|
|
| + GURL icon_src = FindBestMatchingIcon(manifest.icons);
|
| + if (icon_src.is_valid()) {
|
| + web_contents()->DownloadImage(icon_src,
|
| + false,
|
| + preferred_icon_size_in_px_,
|
| + base::Bind(&ShortcutHelper::OnDidDownloadIcon,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| + manifest_icon_status_ = MANIFEST_ICON_STATUS_FETCHING;
|
| + }
|
| +
|
| // The ShortcutHelper is now able to notify its Java counterpart that it is
|
| // initialized. OnInitialized method is not conceptually part of getting the
|
| // manifest data but it happens that the initialization is finalized when
|
| @@ -140,6 +283,40 @@ void ShortcutHelper::OnDidGetManifest(const content::Manifest& manifest) {
|
| Java_ShortcutHelper_onInitialized(env, j_obj.obj(), j_title.obj());
|
| }
|
|
|
| +void ShortcutHelper::OnDidDownloadIcon(int id,
|
| + int http_status_code,
|
| + const GURL& url,
|
| + const std::vector<SkBitmap>& bitmaps,
|
| + const std::vector<gfx::Size>& sizes) {
|
| + // If getting the candidate manifest icon failed, the ShortcutHelper should
|
| + // fallback to the favicon.
|
| + // If the user already requested to add the shortcut, it will do so but use
|
| + // the favicon instead.
|
| + // Otherwise, it sets the state as if there was no manifest icon pending.
|
| + if (bitmaps.empty()) {
|
| + if (add_shortcut_requested_)
|
| + AddShortcutUsingFavicon();
|
| + else
|
| + manifest_icon_status_ = MANIFEST_ICON_STATUS_NONE;
|
| + return;
|
| + }
|
| +
|
| + // There might be multiple bitmaps returned. The one to pick is bigger or
|
| + // equal to the preferred size. |bitmaps| is ordered from bigger to smaller.
|
| + int preferred_bitmap_index = 0;
|
| + for (size_t i = 0; i < bitmaps.size(); ++i) {
|
| + if (bitmaps[i].height() < preferred_icon_size_in_px_)
|
| + break;
|
| + preferred_bitmap_index = i;
|
| + }
|
| +
|
| + manifest_icon_ = bitmaps[preferred_bitmap_index];
|
| + manifest_icon_status_ = MANIFEST_ICON_STATUS_DONE;
|
| +
|
| + if (add_shortcut_requested_)
|
| + AddShortcutUsingManifestIcon();
|
| +}
|
| +
|
| void ShortcutHelper::TearDown(JNIEnv*, jobject) {
|
| Destroy();
|
| }
|
| @@ -153,10 +330,43 @@ void ShortcutHelper::AddShortcut(
|
| jobject obj,
|
| jstring jtitle,
|
| jint launcher_large_icon_size) {
|
| + add_shortcut_requested_ = true;
|
| +
|
| base::string16 title = base::android::ConvertJavaStringToUTF16(env, jtitle);
|
| if (!title.empty())
|
| title_ = title;
|
|
|
| + switch (manifest_icon_status_) {
|
| + case MANIFEST_ICON_STATUS_NONE:
|
| + AddShortcutUsingFavicon();
|
| + break;
|
| + case MANIFEST_ICON_STATUS_FETCHING:
|
| + // ::OnDidDownloadIcon() will call AddShortcutUsingManifestIcon().
|
| + break;
|
| + case MANIFEST_ICON_STATUS_DONE:
|
| + AddShortcutUsingManifestIcon();
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void ShortcutHelper::AddShortcutUsingManifestIcon() {
|
| + // Stop observing so we don't get destroyed while doing the last steps.
|
| + Observe(NULL);
|
| +
|
| + base::WorkerPool::PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ShortcutHelper::AddShortcutInBackgroundWithSkBitmap,
|
| + url_,
|
| + title_,
|
| + display_,
|
| + manifest_icon_,
|
| + orientation_),
|
| + true);
|
| +
|
| + Destroy();
|
| +}
|
| +
|
| +void ShortcutHelper::AddShortcutUsingFavicon() {
|
| Profile* profile =
|
| Profile::FromBrowserContext(web_contents()->GetBrowserContext());
|
|
|
| @@ -172,28 +382,26 @@ void ShortcutHelper::AddShortcut(
|
|
|
| // Using favicon if its size is not smaller than platform required size,
|
| // otherwise using the largest icon among all avaliable icons.
|
| - int threshold_to_get_any_largest_icon = launcher_large_icon_size_ - 1;
|
| + int threshold_to_get_any_largest_icon = preferred_icon_size_in_px_ - 1;
|
| favicon_service->GetLargestRawFaviconForPageURL(url_, icon_types,
|
| threshold_to_get_any_largest_icon,
|
| - base::Bind(&ShortcutHelper::FinishAddingShortcut,
|
| + base::Bind(&ShortcutHelper::OnDidGetFavicon,
|
| base::Unretained(this)),
|
| &cancelable_task_tracker_);
|
| }
|
|
|
| -void ShortcutHelper::FinishAddingShortcut(
|
| +void ShortcutHelper::OnDidGetFavicon(
|
| const favicon_base::FaviconRawBitmapResult& bitmap_result) {
|
| - icon_ = bitmap_result;
|
| -
|
| // Stop observing so we don't get destroyed while doing the last steps.
|
| Observe(NULL);
|
|
|
| base::WorkerPool::PostTask(
|
| FROM_HERE,
|
| - base::Bind(&ShortcutHelper::AddShortcutInBackground,
|
| + base::Bind(&ShortcutHelper::AddShortcutInBackgroundWithRawBitmap,
|
| url_,
|
| title_,
|
| display_,
|
| - icon_,
|
| + bitmap_result,
|
| orientation_),
|
| true);
|
|
|
| @@ -220,7 +428,7 @@ bool ShortcutHelper::RegisterShortcutHelper(JNIEnv* env) {
|
| return RegisterNativesImpl(env);
|
| }
|
|
|
| -void ShortcutHelper::AddShortcutInBackground(
|
| +void ShortcutHelper::AddShortcutInBackgroundWithRawBitmap(
|
| const GURL& url,
|
| const base::string16& title,
|
| content::Manifest::DisplayMode display,
|
| @@ -228,16 +436,26 @@ void ShortcutHelper::AddShortcutInBackground(
|
| blink::WebScreenOrientationLockType orientation) {
|
| DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
|
|
|
| - // Grab the average color from the bitmap.
|
| - SkColor color = SK_ColorWHITE;
|
| - SkBitmap favicon_bitmap;
|
| + SkBitmap icon_bitmap;
|
| if (bitmap_result.is_valid()) {
|
| - if (gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
|
| - bitmap_result.bitmap_data->size(),
|
| - &favicon_bitmap))
|
| - color = color_utils::CalculateKMeanColorOfBitmap(favicon_bitmap);
|
| + gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
|
| + bitmap_result.bitmap_data->size(),
|
| + &icon_bitmap);
|
| }
|
|
|
| + AddShortcutInBackgroundWithSkBitmap(
|
| + url, title, display, icon_bitmap, orientation);
|
| +}
|
| +
|
| +void ShortcutHelper::AddShortcutInBackgroundWithSkBitmap(
|
| + const GURL& url,
|
| + const base::string16& title,
|
| + content::Manifest::DisplayMode display,
|
| + const SkBitmap& icon_bitmap,
|
| + blink::WebScreenOrientationLockType orientation) {
|
| + DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
|
| +
|
| + SkColor color = color_utils::CalculateKMeanColorOfBitmap(icon_bitmap);
|
| int r_value = SkColorGetR(color);
|
| int g_value = SkColorGetG(color);
|
| int b_value = SkColorGetB(color);
|
| @@ -249,8 +467,8 @@ void ShortcutHelper::AddShortcutInBackground(
|
| ScopedJavaLocalRef<jstring> java_title =
|
| base::android::ConvertUTF16ToJavaString(env, title);
|
| ScopedJavaLocalRef<jobject> java_bitmap;
|
| - if (favicon_bitmap.getSize())
|
| - java_bitmap = gfx::ConvertToJavaBitmap(&favicon_bitmap);
|
| + if (icon_bitmap.getSize())
|
| + java_bitmap = gfx::ConvertToJavaBitmap(&icon_bitmap);
|
|
|
| Java_ShortcutHelper_addShortcut(
|
| env,
|
|
|