Commit 2b232cbf authored by Maggie Cai's avatar Maggie Cai Committed by Commit Bot

[IntentHandling] Launch share intent with specific activity name.

Currently, the specific activity name is not passed from ARC. This
causes an issue where if we try to share a file to an app that has
multiple activities that can handle the file, it will trigger the
disambiguity dialog. This CL pass the activity name with the activity
label from ARC to ensure launching the correct activity.

BUG=1092784
Test: Manually test with share jpeg file to facebook app.

Change-Id: I4290ddce72c80548d23171f020284c39c87ce688
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2360094Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarMelissa Zhang <melzhang@chromium.org>
Reviewed-by: default avatarDavid Jacobo <djacobo@chromium.org>
Commit-Queue: Maggie Cai <mxcai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799523}
parent 96e12743
......@@ -508,20 +508,20 @@ void AppServiceProxy::UninstallForTesting(const std::string& app_id,
#endif
std::vector<std::string> AppServiceProxy::GetAppIdsForUrl(const GURL& url) {
auto app_id_and_activities =
auto intent_launch_info =
GetAppsForIntent(apps_util::CreateIntentFromUrl(url));
std::vector<std::string> app_ids;
for (auto& app_id_and_activity : app_id_and_activities) {
app_ids.push_back(std::move(app_id_and_activity.app_id));
for (auto& entry : intent_launch_info) {
app_ids.push_back(std::move(entry.app_id));
}
return app_ids;
}
std::vector<AppIdAndActivityName> AppServiceProxy::GetAppsForIntent(
std::vector<IntentLaunchInfo> AppServiceProxy::GetAppsForIntent(
const apps::mojom::IntentPtr& intent) {
std::vector<AppIdAndActivityName> app_id_and_activities;
std::vector<IntentLaunchInfo> intent_launch_info;
if (app_service_.is_bound()) {
cache_.ForEachApp([&app_id_and_activities,
cache_.ForEachApp([&intent_launch_info,
&intent](const apps::AppUpdate& update) {
if (update.Readiness() == apps::mojom::Readiness::kUninstalledByUser) {
return;
......@@ -529,28 +529,30 @@ std::vector<AppIdAndActivityName> AppServiceProxy::GetAppsForIntent(
std::set<std::string> existing_activities;
for (const auto& filter : update.IntentFilters()) {
if (apps_util::IntentMatchesFilter(intent, filter)) {
AppIdAndActivityName app_id_and_activity;
app_id_and_activity.app_id = update.AppId();
std::string activity_name;
if (filter->activity_name && !filter->activity_name.value().empty()) {
activity_name = filter->activity_name.value();
IntentLaunchInfo entry;
entry.app_id = update.AppId();
std::string activity_label;
if (filter->activity_label &&
!filter->activity_label.value().empty()) {
activity_label = filter->activity_label.value();
} else {
activity_name = update.Name();
activity_label = update.Name();
}
if (base::Contains(existing_activities, activity_name)) {
if (base::Contains(existing_activities, activity_label)) {
continue;
}
existing_activities.insert(activity_name);
app_id_and_activity.activity_name = activity_name;
app_id_and_activities.push_back(app_id_and_activity);
existing_activities.insert(activity_label);
entry.activity_label = activity_label;
entry.activity_name = filter->activity_name.value_or("");
intent_launch_info.push_back(entry);
}
}
});
}
return app_id_and_activities;
return intent_launch_info;
}
std::vector<AppIdAndActivityName> AppServiceProxy::GetAppsForFiles(
std::vector<IntentLaunchInfo> AppServiceProxy::GetAppsForFiles(
const std::vector<GURL>& filesystem_urls,
const std::vector<std::string>& mime_types) {
return GetAppsForIntent(
......
......@@ -56,9 +56,10 @@ struct PauseData {
};
#endif
struct AppIdAndActivityName {
struct IntentLaunchInfo {
std::string app_id;
std::string activity_name;
std::string activity_label;
};
// Singleton (per Profile) proxy and cache of an App Service's apps.
......@@ -223,12 +224,12 @@ class AppServiceProxy : public KeyedService,
// Returns a list of apps (represented by their ids) and activities (if
// applied) which can handle |intent|.
std::vector<AppIdAndActivityName> GetAppsForIntent(
std::vector<IntentLaunchInfo> GetAppsForIntent(
const apps::mojom::IntentPtr& intent);
// Returns a list of apps (represented by their ids) and activities (if
// applied) which can handle |filesystem_urls| and |mime_types|.
std::vector<AppIdAndActivityName> GetAppsForFiles(
std::vector<IntentLaunchInfo> GetAppsForFiles(
const std::vector<GURL>& filesystem_urls,
const std::vector<std::string>& mime_types);
......
......@@ -323,6 +323,9 @@ apps::mojom::IntentFilterPtr ConvertArcIntentFilter(
if (!arc_intent_filter.activity_name().empty()) {
intent_filter->activity_name = arc_intent_filter.activity_name();
}
if (!arc_intent_filter.activity_label().empty()) {
intent_filter->activity_label = arc_intent_filter.activity_label();
}
}
return intent_filter;
......@@ -512,8 +515,6 @@ arc::mojom::OpenUrlsRequestPtr ConstructOpenUrlsRequest(
arc::mojom::OpenUrlsRequestPtr request = arc::mojom::OpenUrlsRequest::New();
request->action_type = GetArcActionType(intent->action.value());
request->activity_name = activity.Clone();
// Set activity name to empty to avoid launching the main activity.
request->activity_name->activity_name = "";
for (const auto& content_url : content_urls) {
arc::mojom::ContentUrlWithMimeTypePtr url_with_type =
arc::mojom::ContentUrlWithMimeType::New();
......@@ -756,7 +757,10 @@ void ArcApps::LaunchAppWithIntent(const std::string& app_id,
arc::mojom::ActivityNamePtr activity = arc::mojom::ActivityName::New();
activity->package_name = app_info->package_name;
activity->activity_name = app_info->activity;
if (intent->activity_name.has_value() &&
!intent->activity_name.value().empty()) {
activity->activity_name = intent->activity_name.value();
}
// At the moment, the only case we have mime_type field set is to share
// files, in this case, convert the file urls to content urls and use
// arc file system instance to launch the app with files.
......
......@@ -77,16 +77,16 @@ void FindAppServiceTasks(Profile* profile,
std::vector<std::string> mime_types;
for (auto& entry : entries)
mime_types.push_back(entry.mime_type);
std::vector<apps::AppIdAndActivityName> app_id_and_activities =
std::vector<apps::IntentLaunchInfo> intent_launch_info =
proxy->GetAppsForFiles(file_urls, mime_types);
std::string task_action_id =
entries.size() == 1 ? kActionIdSend : kActionIdSendMultiple;
using extensions::api::file_manager_private::Verb;
// TODO(crbug/1092784): Support file open with in the future.
for (auto& app_id_and_activity : app_id_and_activities) {
for (auto& launch_entry : intent_launch_info) {
apps::mojom::AppType app_type =
proxy->AppRegistryCache().GetAppType(app_id_and_activity.app_id);
proxy->AppRegistryCache().GetAppType(launch_entry.app_id);
// TODO(crbug/1092784): Only going to support ARC app and web app.
if (!(app_type == apps::mojom::AppType::kArc ||
app_type == apps::mojom::AppType::kWeb)) {
......@@ -95,11 +95,11 @@ void FindAppServiceTasks(Profile* profile,
constexpr int kIconSize = 32;
GURL icon_url =
apps::AppIconSource::GetIconURL(app_id_and_activity.app_id, kIconSize);
apps::AppIconSource::GetIconURL(launch_entry.app_id, kIconSize);
result_list->push_back(FullTaskDescriptor(
TaskDescriptor(app_id_and_activity.app_id, GetTaskType(app_type),
TaskDescriptor(launch_entry.app_id, GetTaskType(app_type),
task_action_id),
app_id_and_activity.activity_name, Verb::VERB_SHARE_WITH, icon_url,
launch_entry.activity_label, Verb::VERB_SHARE_WITH, icon_url,
/* is_default=*/false,
/* is_generic=*/true,
/* is_file_extension_match=*/false));
......
......@@ -32,9 +32,9 @@ const char kAppIdAny[] = "hijklmn";
const char kMimeTypeText[] = "text/plain";
const char kMimeTypeImage[] = "image/jpeg";
const char kMimeTypeAny[] = "*/*";
const char kActivityNameText[] = "some_text_activity";
const char kActivityNameImage[] = "some_image_activity";
const char kActivityNameAny[] = "some_any_file";
const char kActivityLabelText[] = "some_text_activity";
const char kActivityLabelImage[] = "some_image_activity";
const char kActivityLabelAny[] = "some_any_file";
} // namespace
namespace file_manager {
......@@ -58,7 +58,7 @@ class AppServiceFileTasksTest : public testing::Test {
void AddFakeAppWithIntentFilter(const std::string& app_id,
const std::string& mime_type,
const std::string& activity_name,
const std::string& activity_label,
bool is_send_multiple) {
std::vector<apps::mojom::AppPtr> apps;
auto app = apps::mojom::App::New();
......@@ -67,8 +67,8 @@ class AppServiceFileTasksTest : public testing::Test {
auto intent_filter =
is_send_multiple
? apps_util::CreateIntentFilterForSendMultiple(mime_type,
activity_name)
: apps_util::CreateIntentFilterForSend(mime_type, activity_name);
activity_label)
: apps_util::CreateIntentFilterForSend(mime_type, activity_label);
app->intent_filters.push_back(std::move(intent_filter));
apps.push_back(std::move(app));
app_service_proxy_->AppRegistryCache().OnApps(std::move(apps));
......@@ -76,11 +76,11 @@ class AppServiceFileTasksTest : public testing::Test {
}
void AddApps() {
AddFakeAppWithIntentFilter(kAppIdText, kMimeTypeText, kActivityNameText,
AddFakeAppWithIntentFilter(kAppIdText, kMimeTypeText, kActivityLabelText,
/*is_send_multiple=*/false);
AddFakeAppWithIntentFilter(kAppIdImage, kMimeTypeImage, kActivityNameImage,
AddFakeAppWithIntentFilter(kAppIdImage, kMimeTypeImage, kActivityLabelImage,
/*is_send_multiple=*/false);
AddFakeAppWithIntentFilter(kAppIdAny, kMimeTypeAny, kActivityNameAny,
AddFakeAppWithIntentFilter(kAppIdAny, kMimeTypeAny, kActivityLabelAny,
/*is_send_multiple=*/true);
}
......@@ -106,7 +106,7 @@ TEST_F(AppServiceFileTasksTest, FindAppServiceFileTasksText) {
FindAppServiceTasks(profile(), entries, file_urls, &tasks);
ASSERT_EQ(1U, tasks.size());
EXPECT_EQ(kAppIdText, tasks[0].task_descriptor().app_id);
EXPECT_EQ(kActivityNameText, tasks[0].task_title());
EXPECT_EQ(kActivityLabelText, tasks[0].task_title());
}
// Test that between an image app and text app, the image app can be
......@@ -124,7 +124,7 @@ TEST_F(AppServiceFileTasksTest, FindAppServiceFileTasksImage) {
FindAppServiceTasks(profile(), entries, file_urls, &tasks);
ASSERT_EQ(1U, tasks.size());
EXPECT_EQ(kAppIdImage, tasks[0].task_descriptor().app_id);
EXPECT_EQ(kActivityNameImage, tasks[0].task_title());
EXPECT_EQ(kActivityLabelImage, tasks[0].task_title());
}
// Test that between an image app, text app and an app that can handle every
......@@ -145,7 +145,7 @@ TEST_F(AppServiceFileTasksTest, FindAppServiceFileTasksMultiple) {
FindAppServiceTasks(profile(), entries, file_urls, &tasks);
ASSERT_EQ(1U, tasks.size());
EXPECT_EQ(kAppIdAny, tasks[0].task_descriptor().app_id);
EXPECT_EQ(kActivityNameAny, tasks[0].task_title());
EXPECT_EQ(kActivityLabelAny, tasks[0].task_title());
}
} // namespace file_tasks
......
......@@ -7,6 +7,7 @@
#include <utility>
#include "base/bind.h"
#include "base/optional.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
......@@ -113,24 +114,24 @@ SharesheetServiceDelegate* SharesheetService::GetDelegate(
bool SharesheetService::HasShareTargets(const apps::mojom::IntentPtr& intent) {
auto& actions = sharesheet_action_cache_->GetShareActions();
std::vector<apps::AppIdAndActivityName> app_id_and_activities =
std::vector<apps::IntentLaunchInfo> intent_launch_info =
app_service_proxy_->GetAppsForIntent(intent);
return !actions.empty() || !app_id_and_activities.empty();
return !actions.empty() || !intent_launch_info.empty();
}
void SharesheetService::LoadAppIcons(
std::vector<apps::AppIdAndActivityName> app_id_and_activities,
std::vector<apps::IntentLaunchInfo> intent_launch_info,
std::vector<TargetInfo> targets,
size_t index,
base::OnceCallback<void(std::vector<TargetInfo> targets)> callback) {
if (index >= app_id_and_activities.size()) {
if (index >= intent_launch_info.size()) {
std::move(callback).Run(std::move(targets));
return;
}
// Making a copy because we move |app_id_and_activities| out below.
auto app_id = app_id_and_activities[index].app_id;
// Making a copy because we move |intent_launch_info| out below.
auto app_id = intent_launch_info[index].app_id;
auto app_type = app_service_proxy_->AppRegistryCache().GetAppType(app_id);
auto icon_type =
(base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
......@@ -140,23 +141,23 @@ void SharesheetService::LoadAppIcons(
app_service_proxy_->LoadIcon(
app_type, app_id, icon_type, kIconSize, allow_placeholder_icon,
base::BindOnce(&SharesheetService::OnIconLoaded,
weak_factory_.GetWeakPtr(),
std::move(app_id_and_activities), std::move(targets),
index, std::move(callback)));
weak_factory_.GetWeakPtr(), std::move(intent_launch_info),
std::move(targets), index, std::move(callback)));
}
void SharesheetService::OnIconLoaded(
std::vector<apps::AppIdAndActivityName> app_id_and_activities,
std::vector<apps::IntentLaunchInfo> intent_launch_info,
std::vector<TargetInfo> targets,
size_t index,
base::OnceCallback<void(std::vector<TargetInfo> targets)> callback,
apps::mojom::IconValuePtr icon_value) {
const auto& app_id_and_activity = app_id_and_activities[index];
const auto& launch_entry = intent_launch_info[index];
targets.emplace_back(TargetType::kApp, icon_value->uncompressed,
base::UTF8ToUTF16(app_id_and_activity.app_id),
base::UTF8ToUTF16(app_id_and_activity.activity_name));
base::UTF8ToUTF16(launch_entry.app_id),
base::UTF8ToUTF16(launch_entry.activity_label),
launch_entry.activity_name);
LoadAppIcons(std::move(app_id_and_activities), std::move(targets), index + 1,
LoadAppIcons(std::move(intent_launch_info), std::move(targets), index + 1,
std::move(callback));
}
......@@ -176,13 +177,14 @@ void SharesheetService::ShowBubbleWithDelegate(
auto iter = actions.begin();
while (iter != actions.end()) {
targets.emplace_back(TargetType::kAction, (*iter)->GetActionIcon(),
(*iter)->GetActionName(), (*iter)->GetActionName());
(*iter)->GetActionName(), (*iter)->GetActionName(),
base::nullopt);
++iter;
}
std::vector<apps::AppIdAndActivityName> app_id_and_activities =
std::vector<apps::IntentLaunchInfo> intent_launch_info =
app_service_proxy_->GetAppsForIntent(intent);
LoadAppIcons(std::move(app_id_and_activities), std::move(targets), 0,
LoadAppIcons(std::move(intent_launch_info), std::move(targets), 0,
base::BindOnce(&SharesheetService::OnAppIconsLoaded,
weak_factory_.GetWeakPtr(), std::move(delegate),
std::move(intent)));
......
......@@ -19,7 +19,7 @@
class Profile;
namespace apps {
struct AppIdAndActivityName;
struct IntentLaunchInfo;
class AppServiceProxy;
}
......@@ -65,14 +65,12 @@ class SharesheetService : public KeyedService {
using SharesheetServiceIconLoaderCallback =
base::OnceCallback<void(std::vector<TargetInfo> targets)>;
void LoadAppIcons(
std::vector<apps::AppIdAndActivityName> app_id_and_activities,
void LoadAppIcons(std::vector<apps::IntentLaunchInfo> intent_launch_info,
std::vector<TargetInfo> targets,
size_t index,
SharesheetServiceIconLoaderCallback callback);
void OnIconLoaded(
std::vector<apps::AppIdAndActivityName> app_id_and_activities,
void OnIconLoaded(std::vector<apps::IntentLaunchInfo> intent_launch_info,
std::vector<TargetInfo> targets,
size_t index,
SharesheetServiceIconLoaderCallback callback,
......
......@@ -9,11 +9,15 @@ namespace sharesheet {
TargetInfo::TargetInfo(TargetType type,
const gfx::ImageSkia& icon,
const base::string16& launch_name,
const base::string16& display_name)
const base::string16& display_name,
const base::Optional<std::string>& activity_name)
: type(type),
icon(icon),
launch_name(launch_name),
display_name(display_name) {}
display_name(display_name),
activity_name(activity_name) {}
TargetInfo::~TargetInfo() = default;
TargetInfo::TargetInfo(TargetInfo&& other) = default;
......
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_SHARESHEET_SHARESHEET_TYPES_H_
#define CHROME_BROWSER_SHARESHEET_SHARESHEET_TYPES_H_
#include "base/optional.h"
#include "base/strings/string16.h"
#include "ui/gfx/image/image_skia.h"
......@@ -24,7 +25,10 @@ struct TargetInfo {
TargetInfo(TargetType type,
const gfx::ImageSkia& icon,
const base::string16& launch_name,
const base::string16& display_name);
const base::string16& display_name,
const base::Optional<std::string>& activity_name);
~TargetInfo();
// Allow move.
TargetInfo(TargetInfo&& other);
TargetInfo& operator=(TargetInfo&& other);
......@@ -47,6 +51,10 @@ struct TargetInfo {
// The string shown to the user to identify this target in the sharesheet
// bubble.
base::string16 display_name;
// The activity of the app for the target. This only applies when the app type
// is kArc.
base::Optional<std::string> activity_name;
};
} // namespace sharesheet
......
......@@ -234,6 +234,8 @@ void SharesheetBubbleView::ButtonPressed(views::Button* sender,
auto type = targets_[sender->tag()].type;
if (type == sharesheet::TargetType::kAction) {
active_target_ = targets_[sender->tag()].launch_name;
} else {
intent_->activity_name = targets_[sender->tag()].activity_name;
}
delegate_->OnTargetSelected(targets_[sender->tag()].launch_name, type,
std::move(intent_), share_action_view_);
......
......@@ -39,6 +39,7 @@ IntentFilter::IntentFilter(
IntentFilter::IntentFilter(
const std::string& package_name,
const std::string& activity_name,
const std::string& activity_label,
std::vector<std::string> actions,
std::vector<IntentFilter::AuthorityEntry> authorities,
std::vector<IntentFilter::PatternMatcher> paths,
......@@ -46,6 +47,7 @@ IntentFilter::IntentFilter(
std::vector<std::string> mime_types)
: package_name_(package_name),
activity_name_(activity_name),
activity_label_(activity_label),
actions_(std::move(actions)),
authorities_(std::move(authorities)),
schemes_(std::move(schemes)),
......
......@@ -77,6 +77,7 @@ class IntentFilter {
std::vector<std::string> mime_types);
IntentFilter(const std::string& package_name,
const std::string& activity_name,
const std::string& activity_label,
std::vector<std::string> actions,
std::vector<IntentFilter::AuthorityEntry> authorities,
std::vector<IntentFilter::PatternMatcher> paths,
......@@ -90,6 +91,7 @@ class IntentFilter {
const std::string& package_name() const { return package_name_; }
const std::string& activity_name() const { return activity_name_; }
const std::string& activity_label() const { return activity_label_; }
const std::vector<std::string>& actions() const { return actions_; }
const std::vector<AuthorityEntry>& authorities() const {
return authorities_;
......@@ -104,6 +106,7 @@ class IntentFilter {
std::string package_name_;
std::string activity_name_;
std::string activity_label_;
std::vector<std::string> actions_;
std::vector<AuthorityEntry> authorities_;
std::vector<PatternMatcher> paths_;
......
......@@ -43,9 +43,14 @@ bool StructTraits<arc::mojom::IntentFilterDataView, arc::IntentFilter>::Read(
if (!data.ReadActivityName(&activity_name))
return false;
*out = arc::IntentFilter(package_name, activity_name, std::move(actions),
std::move(authorities), std::move(paths),
std::move(schemes), std::move(mime_types));
std::string activity_label;
if (!data.ReadActivityLabel(&activity_label))
return false;
*out = arc::IntentFilter(package_name, activity_name, activity_label,
std::move(actions), std::move(authorities),
std::move(paths), std::move(schemes),
std::move(mime_types));
return true;
}
......
......@@ -54,6 +54,10 @@ struct StructTraits<arc::mojom::IntentFilterDataView, arc::IntentFilter> {
return r.activity_name();
}
static const std::string& activity_label(const arc::IntentFilter& r) {
return r.activity_label();
}
static bool Read(arc::mojom::IntentFilterDataView data,
arc::IntentFilter* out);
};
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Next MinVersion: 41
// Next MinVersion: 42
module arc.mojom;
......@@ -87,6 +87,9 @@ struct IntentFilter {
// Activity which registered the filter.
[MinVersion=39] string? activity_name;
// The label shown to the user for this activity.
[MinVersion=41] string? activity_label;
};
// Describes a package that can handle an intent.
......
......@@ -15,7 +15,7 @@ namespace {
apps::mojom::IntentFilterPtr CreateIntentFilterForShare(
const std::string& action,
const std::string& mime_type,
const std::string& activity_name) {
const std::string& activity_label) {
auto intent_filter = apps::mojom::IntentFilter::New();
apps_util::AddSingleValueCondition(
......@@ -26,7 +26,7 @@ apps::mojom::IntentFilterPtr CreateIntentFilterForShare(
apps::mojom::ConditionType::kMimeType, mime_type,
apps::mojom::PatternMatchType::kMimeType, intent_filter);
intent_filter->activity_name = activity_name;
intent_filter->activity_label = activity_label;
return intent_filter;
}
......@@ -72,15 +72,15 @@ apps::mojom::IntentFilterPtr CreateSchemeAndHostOnlyFilter(
apps::mojom::IntentFilterPtr CreateIntentFilterForSend(
const std::string& mime_type,
const std::string& activity_name) {
const std::string& activity_label) {
return CreateIntentFilterForShare(kIntentActionSend, mime_type,
activity_name);
activity_label);
}
apps::mojom::IntentFilterPtr CreateIntentFilterForSendMultiple(
const std::string& mime_type,
const std::string& activity_name) {
const std::string& activity_label) {
return CreateIntentFilterForShare(kIntentActionSendMultiple, mime_type,
activity_name);
activity_label);
}
} // namespace apps_util
......@@ -22,12 +22,12 @@ apps::mojom::IntentFilterPtr CreateSchemeAndHostOnlyFilter(
// Create intent filter for send action.
apps::mojom::IntentFilterPtr CreateIntentFilterForSend(
const std::string& mime_types,
const std::string& activity_name = "");
const std::string& activity_label = "");
// Create intent filter for send multiple action.
apps::mojom::IntentFilterPtr CreateIntentFilterForSendMultiple(
const std::string& mime_types,
const std::string& activity_name = "");
const std::string& activity_label = "");
} // namespace apps_util
......
......@@ -311,6 +311,9 @@ struct IntentFilter {
// Activity which registered this filter. We only fill this field for ARC
// share intent filters.
string? activity_name;
// The label shown to the user for this activity.
string? activity_label;
};
// Action and resource handling request. This includes the scheme and URL at
......@@ -320,6 +323,7 @@ struct Intent {
url.mojom.Url? url; // The URL of the intent. e.g. https://www.google.com/.
string? mime_type; // MIME type. e.g. text/plain, image/*.
array<url.mojom.Url>? file_urls; // The URLs of the files to share.
string? activity_name; // The activity for the app to launch.
};
// Represents a group of |app_ids| that is no longer preferred app of their
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment