Home | History | Annotate | Download | only in android
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/android/shortcut_helper.h"
      6 
      7 #include <jni.h>
      8 
      9 #include "base/android/jni_android.h"
     10 #include "base/android/jni_string.h"
     11 #include "base/basictypes.h"
     12 #include "base/location.h"
     13 #include "base/strings/string16.h"
     14 #include "base/threading/worker_pool.h"
     15 #include "chrome/browser/android/tab_android.h"
     16 #include "chrome/browser/favicon/favicon_service.h"
     17 #include "chrome/browser/favicon/favicon_service_factory.h"
     18 #include "chrome/common/cancelable_task_tracker.h"
     19 #include "chrome/common/render_messages.h"
     20 #include "content/public/browser/user_metrics.h"
     21 #include "content/public/browser/web_contents.h"
     22 #include "content/public/browser/web_contents_observer.h"
     23 #include "content/public/common/frame_navigate_params.h"
     24 #include "jni/ShortcutHelper_jni.h"
     25 #include "ui/gfx/android/java_bitmap.h"
     26 #include "ui/gfx/codec/png_codec.h"
     27 #include "ui/gfx/color_analysis.h"
     28 #include "ui/gfx/favicon_size.h"
     29 #include "url/gurl.h"
     30 
     31 ShortcutBuilder::ShortcutBuilder(content::WebContents* web_contents,
     32                                  const base::string16& title,
     33                                  int launcher_large_icon_size)
     34     : launcher_large_icon_size_(launcher_large_icon_size),
     35       shortcut_type_(BOOKMARK) {
     36   Observe(web_contents);
     37   url_ = web_contents->GetURL();
     38   if (title.length() > 0)
     39     title_ = title;
     40   else
     41     title_ = web_contents->GetTitle();
     42 
     43   // Send a message to the renderer to retrieve information about the page.
     44   Send(new ChromeViewMsg_RetrieveWebappInformation(routing_id(), url_));
     45 }
     46 
     47 void ShortcutBuilder::OnDidRetrieveWebappInformation(
     48     bool success,
     49     bool is_mobile_webapp_capable,
     50     bool is_apple_mobile_webapp_capable,
     51     const GURL& expected_url) {
     52   Profile* profile =
     53       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
     54   Observe(NULL);
     55 
     56   if (!success) {
     57     LOG(ERROR) << "Failed to parse webpage.";
     58     Destroy();
     59     return;
     60   } else if (expected_url != url_) {
     61     LOG(ERROR) << "Unexpected URL returned.";
     62     Destroy();
     63     return;
     64   }
     65 
     66   if (is_apple_mobile_webapp_capable && !is_mobile_webapp_capable) {
     67     shortcut_type_ = APP_SHORTCUT_APPLE;
     68   } else if (is_apple_mobile_webapp_capable || is_mobile_webapp_capable) {
     69     shortcut_type_ = APP_SHORTCUT;
     70   } else {
     71     shortcut_type_ = BOOKMARK;
     72   }
     73 
     74   // Grab the best, largest icon we can find to represent this bookmark.
     75   // TODO(dfalcantara): Try combining with the new BookmarksHandler once its
     76   //                    rewrite is further along.
     77   std::vector<int> icon_types;
     78   icon_types.push_back(chrome::FAVICON);
     79   icon_types.push_back(chrome::TOUCH_PRECOMPOSED_ICON | chrome::TOUCH_ICON);
     80   FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
     81       profile, Profile::EXPLICIT_ACCESS);
     82 
     83   // Using favicon if its size is not smaller than platform required size,
     84   // otherwise using the largest icon among all avaliable icons.
     85   int threshold_to_get_any_largest_icon = launcher_large_icon_size_ - 1;
     86   favicon_service->GetLargestRawFaviconForURL(profile, url_, icon_types,
     87       threshold_to_get_any_largest_icon,
     88       base::Bind(&ShortcutBuilder::FinishAddingShortcut,
     89                  base::Unretained(this)),
     90       &cancelable_task_tracker_);
     91 }
     92 
     93 void ShortcutBuilder::FinishAddingShortcut(
     94     const chrome::FaviconBitmapResult& bitmap_result) {
     95   base::WorkerPool::PostTask(
     96       FROM_HERE,
     97       base::Bind(&ShortcutHelper::AddShortcutInBackground,
     98                  url_,
     99                  title_,
    100                  shortcut_type_,
    101                  bitmap_result),
    102       true);
    103   Destroy();
    104 }
    105 
    106 bool ShortcutBuilder::OnMessageReceived(const IPC::Message& message) {
    107   bool handled = true;
    108   IPC_BEGIN_MESSAGE_MAP(ShortcutBuilder, message)
    109     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidRetrieveWebappInformation,
    110                         OnDidRetrieveWebappInformation)
    111     IPC_MESSAGE_UNHANDLED(handled = false)
    112   IPC_END_MESSAGE_MAP()
    113   return handled;
    114 }
    115 
    116 void ShortcutBuilder::WebContentsDestroyed(content::WebContents* web_contents) {
    117   Destroy();
    118 }
    119 
    120 void ShortcutBuilder::Destroy() {
    121   if (cancelable_task_tracker_.HasTrackedTasks()) {
    122     cancelable_task_tracker_.TryCancelAll();
    123   }
    124   delete this;
    125 }
    126 
    127 void ShortcutHelper::AddShortcut(content::WebContents* web_contents,
    128                                  const base::string16& title,
    129                                  int launcher_large_icon_size) {
    130   // The ShortcutBuilder deletes itself when it's done.
    131   new ShortcutBuilder(web_contents, title, launcher_large_icon_size);
    132 }
    133 
    134 bool ShortcutHelper::RegisterShortcutHelper(JNIEnv* env) {
    135   return RegisterNativesImpl(env);
    136 }
    137 
    138 void ShortcutHelper::AddShortcutInBackground(
    139     const GURL& url,
    140     const base::string16& title,
    141     ShortcutBuilder::ShortcutType shortcut_type,
    142     const chrome::FaviconBitmapResult& bitmap_result) {
    143   DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
    144 
    145   // Grab the average color from the bitmap.
    146   SkColor color = SK_ColorWHITE;
    147   SkBitmap favicon_bitmap;
    148   if (bitmap_result.is_valid()) {
    149     color_utils::GridSampler sampler;
    150     color = color_utils::CalculateKMeanColorOfPNG(bitmap_result.bitmap_data,
    151                                                   100,
    152                                                   665,
    153                                                   &sampler);
    154     gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
    155                           bitmap_result.bitmap_data->size(),
    156                           &favicon_bitmap);
    157   }
    158 
    159   int r_value = SkColorGetR(color);
    160   int g_value = SkColorGetG(color);
    161   int b_value = SkColorGetB(color);
    162 
    163   // Send the data to the Java side to create the shortcut.
    164   JNIEnv* env = base::android::AttachCurrentThread();
    165   ScopedJavaLocalRef<jstring> java_url =
    166       base::android::ConvertUTF8ToJavaString(env, url.spec());
    167   ScopedJavaLocalRef<jstring> java_title =
    168       base::android::ConvertUTF16ToJavaString(env, title);
    169   ScopedJavaLocalRef<jobject> java_bitmap;
    170   if (favicon_bitmap.getSize())
    171     java_bitmap = gfx::ConvertToJavaBitmap(&favicon_bitmap);
    172 
    173   Java_ShortcutHelper_addShortcut(env,
    174                                   base::android::GetApplicationContext(),
    175                                   java_url.obj(),
    176                                   java_title.obj(),
    177                                   java_bitmap.obj(),
    178                                   r_value,
    179                                   g_value,
    180                                   b_value,
    181                                   shortcut_type != ShortcutBuilder::BOOKMARK);
    182 
    183   // Record what type of shortcut was added by the user.
    184   switch (shortcut_type) {
    185     case ShortcutBuilder::APP_SHORTCUT:
    186       content::RecordAction(
    187           content::UserMetricsAction("webapps.AddShortcut.AppShortcut"));
    188       break;
    189     case ShortcutBuilder::APP_SHORTCUT_APPLE:
    190       content::RecordAction(
    191           content::UserMetricsAction("webapps.AddShortcut.AppShortcutApple"));
    192       break;
    193     case ShortcutBuilder::BOOKMARK:
    194       content::RecordAction(
    195           content::UserMetricsAction("webapps.AddShortcut.Bookmark"));
    196       break;
    197     default:
    198       NOTREACHED();
    199   }
    200 }
    201 
    202 // Adds a shortcut to the current URL to the Android home screen, firing
    203 // background tasks to pull all the data required.
    204 // Note that we don't actually care about the tab here -- we just want
    205 // its otherwise inaccessible WebContents.
    206 static void AddShortcut(JNIEnv* env,
    207                         jclass clazz,
    208                         jlong tab_android_ptr,
    209                         jstring title,
    210                         jint launcher_large_icon_size) {
    211   TabAndroid* tab = reinterpret_cast<TabAndroid*>(tab_android_ptr);
    212   ShortcutHelper::AddShortcut(
    213       tab->web_contents(),
    214       base::android::ConvertJavaStringToUTF16(env, title),
    215       launcher_large_icon_size);
    216 }
    217