blob: b236728872ac2e5d273b7d78b073666d14c9dcfa [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/android/shortcut_helper.h"
#include <jni.h>
#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/task/cancelable_task_tracker.h"
#include "base/threading/worker_pool.h"
#include "chrome/browser/android/tab_android.h"
#include "chrome/browser/favicon/favicon_service.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/common/render_messages.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/frame_navigate_params.h"
#include "jni/ShortcutHelper_jni.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 "url/gurl.h"
ShortcutBuilder::ShortcutBuilder(content::WebContents* web_contents,
const base::string16& title,
int launcher_large_icon_size)
: launcher_large_icon_size_(launcher_large_icon_size),
shortcut_type_(BOOKMARK) {
Observe(web_contents);
url_ = web_contents->GetURL();
if (title.length() > 0)
title_ = title;
else
title_ = web_contents->GetTitle();
// Send a message to the renderer to retrieve information about the page.
Send(new ChromeViewMsg_RetrieveWebappInformation(routing_id(), url_));
}
void ShortcutBuilder::OnDidRetrieveWebappInformation(
bool success,
bool is_mobile_webapp_capable,
bool is_apple_mobile_webapp_capable,
const GURL& expected_url) {
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
Observe(NULL);
if (!success) {
LOG(ERROR) << "Failed to parse webpage.";
Destroy();
return;
} else if (expected_url != url_) {
LOG(ERROR) << "Unexpected URL returned.";
Destroy();
return;
}
if (is_apple_mobile_webapp_capable && !is_mobile_webapp_capable) {
shortcut_type_ = APP_SHORTCUT_APPLE;
} else if (is_apple_mobile_webapp_capable || is_mobile_webapp_capable) {
shortcut_type_ = APP_SHORTCUT;
} else {
shortcut_type_ = BOOKMARK;
}
// Grab the best, largest icon we can find to represent this bookmark.
// TODO(dfalcantara): Try combining with the new BookmarksHandler once its
// rewrite is further along.
std::vector<int> icon_types;
icon_types.push_back(favicon_base::FAVICON);
icon_types.push_back(favicon_base::TOUCH_PRECOMPOSED_ICON |
favicon_base::TOUCH_ICON);
FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
profile, Profile::EXPLICIT_ACCESS);
// 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;
favicon_service->GetLargestRawFaviconForPageURL(profile, url_, icon_types,
threshold_to_get_any_largest_icon,
base::Bind(&ShortcutBuilder::FinishAddingShortcut,
base::Unretained(this)),
&cancelable_task_tracker_);
}
void ShortcutBuilder::FinishAddingShortcut(
const favicon_base::FaviconRawBitmapResult& bitmap_result) {
base::WorkerPool::PostTask(
FROM_HERE,
base::Bind(&ShortcutHelper::AddShortcutInBackground,
url_,
title_,
shortcut_type_,
bitmap_result),
true);
Destroy();
}
bool ShortcutBuilder::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ShortcutBuilder, message)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidRetrieveWebappInformation,
OnDidRetrieveWebappInformation)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ShortcutBuilder::WebContentsDestroyed() {
Destroy();
}
void ShortcutBuilder::Destroy() {
if (cancelable_task_tracker_.HasTrackedTasks()) {
cancelable_task_tracker_.TryCancelAll();
}
delete this;
}
void ShortcutHelper::AddShortcut(content::WebContents* web_contents,
const base::string16& title,
int launcher_large_icon_size) {
// The ShortcutBuilder deletes itself when it's done.
new ShortcutBuilder(web_contents, title, launcher_large_icon_size);
}
bool ShortcutHelper::RegisterShortcutHelper(JNIEnv* env) {
return RegisterNativesImpl(env);
}
void ShortcutHelper::AddShortcutInBackground(
const GURL& url,
const base::string16& title,
ShortcutBuilder::ShortcutType shortcut_type,
const favicon_base::FaviconRawBitmapResult& bitmap_result) {
DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
// Grab the average color from the bitmap.
SkColor color = SK_ColorWHITE;
SkBitmap favicon_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);
}
int r_value = SkColorGetR(color);
int g_value = SkColorGetG(color);
int b_value = SkColorGetB(color);
// Send the data to the Java side to create the shortcut.
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> java_url =
base::android::ConvertUTF8ToJavaString(env, url.spec());
ScopedJavaLocalRef<jstring> java_title =
base::android::ConvertUTF16ToJavaString(env, title);
ScopedJavaLocalRef<jobject> java_bitmap;
if (favicon_bitmap.getSize())
java_bitmap = gfx::ConvertToJavaBitmap(&favicon_bitmap);
Java_ShortcutHelper_addShortcut(env,
base::android::GetApplicationContext(),
java_url.obj(),
java_title.obj(),
java_bitmap.obj(),
r_value,
g_value,
b_value,
shortcut_type != ShortcutBuilder::BOOKMARK);
// Record what type of shortcut was added by the user.
switch (shortcut_type) {
case ShortcutBuilder::APP_SHORTCUT:
content::RecordAction(
base::UserMetricsAction("webapps.AddShortcut.AppShortcut"));
break;
case ShortcutBuilder::APP_SHORTCUT_APPLE:
content::RecordAction(
base::UserMetricsAction("webapps.AddShortcut.AppShortcutApple"));
break;
case ShortcutBuilder::BOOKMARK:
content::RecordAction(
base::UserMetricsAction("webapps.AddShortcut.Bookmark"));
break;
default:
NOTREACHED();
}
}
// Adds a shortcut to the current URL to the Android home screen, firing
// background tasks to pull all the data required.
// Note that we don't actually care about the tab here -- we just want
// its otherwise inaccessible WebContents.
static void AddShortcut(JNIEnv* env,
jclass clazz,
jlong tab_android_ptr,
jstring title,
jint launcher_large_icon_size) {
TabAndroid* tab = reinterpret_cast<TabAndroid*>(tab_android_ptr);
ShortcutHelper::AddShortcut(
tab->web_contents(),
base::android::ConvertJavaStringToUTF16(env, title),
launcher_large_icon_size);
}