| // Copyright 2021 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/share/default_ranking.h" |
| |
| #include <algorithm> |
| |
| #include "base/strings/string_util.h" |
| |
| namespace sharing { |
| |
| namespace { |
| |
| // All four of these rankings are sourced from a combination of public app usage |
| // data sets and Chrome-side metrics about common share targets; the exact |
| // details of that process are described in go/sh-plus. |
| // |
| // These are lists of activities rather than packages, so that multiple share |
| // targets from the same app can appear in different places in the ranking. This |
| // is a bit brittle; see https://crbug.com/1222156#c4 for why. Regrettably, the |
| // mapping from human-readable (app name, action) pairs to activities was done |
| // by hand; see go/sh-3p-activities for that. |
| // |
| // These lists are used to rank which share targets are shown for users with no |
| // share history. In all cases, only apps actually present on the device are |
| // shown; these lists control the relative ordering when many of these apps are |
| // all present at once. Once the user has any share history, ShareRanking will |
| // use that history rather than these default lists. |
| // |
| // You'll notice that this data is split up by "EnUs" and "World"; this is |
| // because the popularity of apps around the world in general is pretty similar, |
| // *except* for en-US users, who have pretty major differences. If other locales |
| // diverge more from the main world list we will need to add more cases here for |
| // them. |
| |
| struct ComponentName { |
| ComponentName(const std::string& package, const std::string& activity) |
| : package(package), activity(activity) {} |
| |
| std::string package; |
| std::string activity; |
| |
| // Note: the format of this string must match that returned by |
| // android.content.ComponentName#flattenToString |
| std::string Flatten() const { return package + "/" + activity; } |
| }; |
| |
| std::vector<ComponentName> DefaultEnUsImageRanking() { |
| return { |
| {"com.whatsapp", "com.whatsapp.ContactPicker"}, |
| {"com.google.android.apps.messaging", |
| "com.google.android.apps.messaging.ui.conversationlist." |
| "ShareIntentActivity"}, |
| {"com.google.android.gm", |
| "com.google.android.gm.ComposeActivityGmailExternal"}, |
| {"com.facebook.orca", |
| "com.facebook.messenger.intents.ShareIntentHandler"}, |
| {"com.facebook.katana", |
| "com.facebook.composer.shareintent." |
| "ImplicitShareIntentHandlerDefaultAlias"}, |
| {"com.google.android.apps.photos", |
| "com.google.android.apps.photos.uploadtoalbum.UploadContentActivity"}, |
| // TODO(crbug.com/40777590): Files |
| { |
| "com.snapchat.android", |
| "com.snap.mushroom.MainActivity", |
| }, |
| { |
| "org.telegram.messenger", |
| "org.telegram.ui.LaunchActivity", |
| }, |
| {"com.instagram.android", |
| "com.instagram.share.handleractivity.StoryShareHandlerActivity"}, |
| {"com.instagram.android", |
| "com.instagram.share.handleractivity.ShareHandlerActivity"}, |
| {"com.discord", "com.discord.app.AppActivity$AppAction"}, |
| {"com.instagram.android", |
| "com.instagram.direct.share.handler." |
| "DirectExternalMediaShareActivityPhoto"}, |
| { |
| "com.google.android.talk", |
| "com.google.android.apps.hangouts.phone.ShareIntentActivity", |
| }, |
| { |
| "com.google.android.keep", |
| "com.google.android.keep.activities.ShareReceiverActivity", |
| }, |
| { |
| "com.google.android.apps.docs.editors.docs", |
| "com.google.android.apps.docs.common.shareitem.UploadMenuActivity", |
| }, |
| { |
| "com.verizon.messaging.vzmsgs", |
| "com.verizon.mms.ui.LaunchConversationActivity", |
| }, |
| { |
| "org.thoughtcrime.securesms", |
| "org.thoughtcrime.securesms.sharing.ShareActivity", |
| }, |
| { |
| "com.twitter.android", |
| "com.twitter.composer.ComposerActivity", |
| }, |
| { |
| "com.pinterest", |
| "com.pinterest.activity.create.PinItActivity", |
| }, |
| { |
| "com.twitter.android", |
| "com.twitter.app.dm.DMActivity", |
| }, |
| { |
| "com.google.android.apps.dynamite", |
| "com.google.android.apps.dynamite.activity.main.MainActivity", |
| }, |
| { |
| "com.microsoft.office.outlook", |
| "com.microsoft.office.outlook.compose.ComposeLauncherActivity", |
| }, |
| { |
| "com.yahoo.mobile.client.android.mail", |
| "com.yahoo.mail.flux.ui.MailComposeActivity", |
| }, |
| { |
| "com.linkedin.android", |
| "com.linkedin.android.publishing.sharing.SharingDeepLinkActivity", |
| }, |
| // TODO(crbug.com/40777590): Samsung email |
| { |
| "com.reddit.frontpage", |
| "com.reddit.sharing.ShareActivity", |
| }, |
| { |
| "jp.naver.line.android", |
| "com.linecorp.line.share.common.view.FullPickerLaunchActivity", |
| }, |
| { |
| "com.tencent.mm", |
| "com.tencent.mm.ui.tools.ShareImgUI", |
| }, |
| { |
| "com.tencent.mm", |
| "com.tencent.mm.ui.tools.ShareToTimeLineUI", |
| }, |
| // TODO(crbug.com/40777590): Groupme |
| }; |
| } |
| |
| std::vector<ComponentName> DefaultEnUsTextRanking() { |
| return { |
| {"com.whatsapp", "com.whatsapp.ContactPicker"}, |
| {"com.google.android.apps.messaging", |
| "com.google.android.apps.messaging.ui.conversationlist." |
| "ShareIntentActivity"}, |
| {"com.google.android.gm", |
| "com.google.android.gm.ComposeActivityGmailExternal"}, |
| {"com.facebook.orca", |
| "com.facebook.messenger.intents.ShareIntentHandler"}, |
| {"com.facebook.katana", |
| "com.facebook.composer.shareintent." |
| "ImplicitShareIntentHandlerDefaultAlias"}, |
| { |
| "com.snapchat.android", |
| "com.snap.mushroom.MainActivity", |
| }, |
| { |
| "org.telegram.messenger", |
| "org.telegram.ui.LaunchActivity", |
| }, |
| {"com.discord", "com.discord.app.AppActivity$AppAction"}, |
| {"com.instagram.android", |
| "com.instagram.direct.share.handler." |
| "DirectExternalMediaShareActivityPhoto"}, |
| { |
| "com.google.android.talk", |
| "com.google.android.apps.hangouts.phone.ShareIntentActivity", |
| }, |
| { |
| "com.google.android.keep", |
| "com.google.android.keep.activities.ShareReceiverActivity", |
| }, |
| { |
| "com.verizon.messaging.vzmsgs", |
| "com.verizon.mms.ui.LaunchConversationActivity", |
| }, |
| { |
| "org.thoughtcrime.securesms", |
| "org.thoughtcrime.securesms.sharing.ShareActivity", |
| }, |
| { |
| "com.twitter.android", |
| "com.twitter.composer.ComposerActivity", |
| }, |
| { |
| "com.pinterest", |
| "com.pinterest.activity.create.PinItActivity", |
| }, |
| { |
| "com.twitter.android", |
| "com.twitter.app.dm.DMActivity", |
| }, |
| { |
| "com.ideashower.readitlater.pro", |
| "com.ideashower.readitlater.activity.AddActivity", |
| }, |
| { |
| "com.google.android.apps.dynamite", |
| "com.google.android.apps.dynamite.activity.main.MainActivity", |
| }, |
| { |
| "com.google.android.apps.docs.editors.docs", |
| "com.google.android.apps.docs.common.shareitem.UploadMenuActivity", |
| }, |
| { |
| "com.microsoft.office.outlook", |
| "com.microsoft.office.outlook.compose.ComposeLauncherActivity", |
| }, |
| { |
| "com.yahoo.mobile.client.android.mail", |
| "com.yahoo.mail.flux.ui.MailComposeActivity", |
| }, |
| { |
| "com.linkedin.android", |
| "com.linkedin.android.publishing.sharing.SharingDeepLinkActivity", |
| }, |
| // TODO(crbug.com/40777590): Samsung email |
| { |
| "com.reddit.frontpage", |
| "com.reddit.sharing.ShareActivity", |
| }, |
| { |
| "jp.naver.line.android", |
| "com.linecorp.line.share.common.view.FullPickerLaunchActivity", |
| }, |
| { |
| "com.tencent.mm", |
| "com.tencent.mm.ui.tools.ShareImgUI", |
| }, |
| { |
| "com.tencent.mm", |
| "com.tencent.mm.ui.tools.ShareToTimeLineUI", |
| }, |
| // TODO(crbug.com/40777590): Groupme |
| }; |
| } |
| |
| std::vector<ComponentName> DefaultWorldImageRanking() { |
| return { |
| {"com.whatsapp", "com.whatsapp.ContactPicker"}, |
| {"com.google.android.apps.messaging", |
| "com.google.android.apps.messaging.ui.conversationlist." |
| "ShareIntentActivity"}, |
| {"com.facebook.orca", |
| "com.facebook.messenger.intents.ShareIntentHandler"}, |
| {"com.google.android.gm", |
| "com.google.android.gm.ComposeActivityGmailExternal"}, |
| {"com.instagram.android", |
| "com.instagram.share.handleractivity.ShareHandlerActivity"}, |
| {"com.instagram.android", |
| "com.instagram.share.handleractivity.StoryShareHandlerActivity"}, |
| { |
| "com.tencent.mm", |
| "com.tencent.mm.ui.tools.ShareToTimeLineUI", |
| }, |
| { |
| "org.telegram.messenger", |
| "org.telegram.ui.LaunchActivity", |
| }, |
| {"com.facebook.katana", |
| "com.facebook.composer.shareintent." |
| "ImplicitShareIntentHandlerDefaultAlias"}, |
| { |
| "jp.naver.line.android", |
| "com.linecorp.line.share.common.view.FullPickerLaunchActivity", |
| }, |
| { |
| "com.google.android.talk", |
| "com.google.android.apps.hangouts.phone.ShareIntentActivity", |
| }, |
| {"com.google.android.apps.photos", |
| "com.google.android.apps.photos.uploadtoalbum.UploadContentActivity"}, |
| // TODO(crbug.com/40777590): Files |
| { |
| "com.google.android.apps.docs.editors.docs", |
| "com.google.android.apps.docs.common.shareitem.UploadMenuActivity", |
| }, |
| {"com.instagram.android", |
| "com.instagram.direct.share.handler." |
| "DirectExternalMediaShareActivityPhoto"}, |
| { |
| "com.google.android.keep", |
| "com.google.android.keep.activities.ShareReceiverActivity", |
| }, |
| { |
| "com.snapchat.android", |
| "com.snap.mushroom.MainActivity", |
| }, |
| {"com.discord", "com.discord.app.AppActivity$AppAction"}, |
| { |
| "com.twitter.android", |
| "com.twitter.composer.ComposerActivity", |
| }, |
| // TODO(crbug.com/40777253): Whatsapp Business |
| { |
| "com.pinterest", |
| "com.pinterest.activity.create.PinItActivity", |
| }, |
| { |
| "com.linkedin.android", |
| "com.linkedin.android.publishing.sharing.SharingDeepLinkActivity", |
| }, |
| { |
| "com.facebook.mlite", |
| "com.facebook.mlite.share.view.ShareActivity", |
| }, |
| { |
| "org.thoughtcrime.securesms", |
| "org.thoughtcrime.securesms.sharing.ShareActivity", |
| }, |
| { |
| "com.twitter.android", |
| "com.twitter.app.dm.DMActivity", |
| }, |
| { |
| "com.facebook.lite", |
| "com.facebook.lite.stories.activities.ShareToFbStoriesAlias", |
| }, |
| { |
| "com.microsoft.office.outlook", |
| "com.microsoft.office.outlook.compose.ComposeLauncherActivity", |
| }, |
| { |
| "com.yahoo.mobile.client.android.mail", |
| "com.yahoo.mail.flux.ui.MailComposeActivity", |
| }, |
| { |
| "com.viber.voip", |
| "com.viber.voip.WelcomeShareActivity", |
| }, |
| { |
| "com.imo.android.imoim", |
| "com.imo.android.imoim.globalshare.SharingActivity2", |
| }, |
| // TODO(crbug.com/40777590): Samsung email |
| { |
| "com.tencent.mm", |
| "com.tencent.mm.ui.tools.ShareImgUI", |
| }, |
| { |
| "com.verizon.messaging.vzmsgs", |
| "com.verizon.mms.ui.LaunchConversationActivity", |
| }, |
| { |
| "com.google.android.apps.dynamite", |
| "com.google.android.apps.dynamite.activity.main.MainActivity", |
| }, |
| }; |
| } |
| |
| std::vector<ComponentName> DefaultWorldTextRanking() { |
| return { |
| {"com.whatsapp", "com.whatsapp.ContactPicker"}, |
| {"com.google.android.apps.messaging", |
| "com.google.android.apps.messaging.ui.conversationlist." |
| "ShareIntentActivity"}, |
| {"com.facebook.orca", |
| "com.facebook.messenger.intents.ShareIntentHandler"}, |
| {"com.google.android.gm", |
| "com.google.android.gm.ComposeActivityGmailExternal"}, |
| { |
| "org.telegram.messenger", |
| "org.telegram.ui.LaunchActivity", |
| }, |
| {"com.facebook.katana", |
| "com.facebook.composer.shareintent." |
| "ImplicitShareIntentHandlerDefaultAlias"}, |
| { |
| "com.tencent.mm", |
| "com.tencent.mm.ui.tools.ShareToTimeLineUI", |
| }, |
| { |
| "com.google.android.talk", |
| "com.google.android.apps.hangouts.phone.ShareIntentActivity", |
| }, |
| // TODO(crbug.com/40777590): Instagram Chat |
| { |
| "jp.naver.line.android", |
| "com.linecorp.line.share.common.view.FullPickerLaunchActivity", |
| }, |
| { |
| "com.google.android.keep", |
| "com.google.android.keep.activities.ShareReceiverActivity", |
| }, |
| { |
| "com.snapchat.android", |
| "com.snap.mushroom.MainActivity", |
| }, |
| { |
| "com.twitter.android", |
| "com.twitter.composer.ComposerActivity", |
| }, |
| {"com.discord", "com.discord.app.AppActivity$AppAction"}, |
| // TODO(crbug.com/40777253): Whatsapp Business |
| { |
| "com.ideashower.readitlater.pro", |
| "com.ideashower.readitlater.activity.AddActivity", |
| }, |
| { |
| "com.google.android.apps.docs.editors.docs", |
| "com.google.android.apps.docs.common.shareitem.UploadMenuActivity", |
| }, |
| { |
| "com.pinterest", |
| "com.pinterest.activity.create.PinItActivity", |
| }, |
| { |
| "com.linkedin.android", |
| "com.linkedin.android.publishing.sharing.SharingDeepLinkActivity", |
| }, |
| { |
| "com.facebook.mlite", |
| "com.facebook.mlite.share.view.ShareActivity", |
| }, |
| { |
| "org.thoughtcrime.securesms", |
| "org.thoughtcrime.securesms.sharing.ShareActivity", |
| }, |
| { |
| "com.twitter.android", |
| "com.twitter.app.dm.DMActivity", |
| }, |
| { |
| "com.facebook.lite", |
| "com.facebook.lite.stories.activities.ShareToFbStoriesAlias", |
| }, |
| { |
| "com.microsoft.office.outlook", |
| "com.microsoft.office.outlook.compose.ComposeLauncherActivity", |
| }, |
| { |
| "com.yahoo.mobile.client.android.mail", |
| "com.yahoo.mail.flux.ui.MailComposeActivity", |
| }, |
| { |
| "com.viber.voip", |
| "com.viber.voip.WelcomeShareActivity", |
| }, |
| { |
| "com.imo.android.imoim", |
| "com.imo.android.imoim.globalshare.SharingActivity2", |
| }, |
| { |
| "com.reddit.frontpage", |
| "com.reddit.sharing.ShareActivity", |
| }, |
| // TODO(crbug.com/40777590): Samsung email |
| { |
| "com.tencent.mm", |
| "com.tencent.mm.ui.tools.ShareImgUI", |
| }, |
| { |
| "com.verizon.messaging.vzmsgs", |
| "com.verizon.mms.ui.LaunchConversationActivity", |
| }, |
| { |
| "com.google.android.apps.dynamite", |
| "com.google.android.apps.dynamite.activity.main.MainActivity", |
| }, |
| }; |
| } |
| |
| bool IsImageType(const std::string& type) { |
| // TODO(ellyjones): The type string here comes from the Android side, which is |
| // where the decision about image vs other is made - should we use a constant |
| // for this, or an enum of ints instead? |
| return type == "image"; |
| } |
| |
| bool IsEnUsLocale(const std::string& locale) { |
| // TODO(ellyjones): This seems very unprincipled. |
| return base::StartsWith(locale, "en_US"); |
| } |
| |
| std::vector<std::string> FlattenComponents( |
| const std::vector<ComponentName>& cs) { |
| std::vector<std::string> result; |
| std::ranges::transform(cs, std::back_inserter(result), |
| &ComponentName::Flatten); |
| return result; |
| } |
| |
| } // namespace |
| |
| std::vector<std::string> DefaultRankingForLocaleAndType( |
| const std::string& locale, |
| const std::string& type) { |
| if (IsEnUsLocale(locale)) { |
| if (IsImageType(type)) |
| return FlattenComponents(DefaultEnUsImageRanking()); |
| return FlattenComponents(DefaultEnUsTextRanking()); |
| } |
| if (IsImageType(type)) |
| return FlattenComponents(DefaultWorldImageRanking()); |
| return FlattenComponents(DefaultWorldTextRanking()); |
| } |
| |
| } // namespace sharing |