From 3171385ef8b33116f7f564fd94e6806633cf80db Mon Sep 17 00:00:00 2001 From: Tim Sergeant Date: Thu, 14 Apr 2022 01:43:36 +0000 Subject: [PATCH] Intents: Add support for launching ARC apps with an 'edit' intent This allows sending an 'edit' Intent through the App Service. Currently, only ARC apps support this intent, which will be translated to an ACTION_EDIT on the Android side. Bug: 1314151 Change-Id: I375de94edc7f3fff53fe26c287f7da0b0c7734ae Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3574015 Reviewed-by: Dominick Ng Commit-Queue: Tim Sergeant Cr-Commit-Position: refs/heads/main@{#992352} --- ash/components/arc/mojom/intent_common.mojom | 1 + .../apps/app_service/publishers/arc_apps.cc | 2 + .../publishers/arc_apps_unittest.cc | 87 +++++++++++++++++-- .../app_service/public/cpp/intent_util.cc | 16 ++++ .../app_service/public/cpp/intent_util.h | 7 ++ 5 files changed, 108 insertions(+), 5 deletions(-) diff --git a/ash/components/arc/mojom/intent_common.mojom b/ash/components/arc/mojom/intent_common.mojom index aedd017e125a30..dfe9c0c6bd1ae4 100644 --- a/ash/components/arc/mojom/intent_common.mojom +++ b/ash/components/arc/mojom/intent_common.mojom @@ -11,6 +11,7 @@ enum ActionType { SEND, // Can handle only one URL. SEND_MULTIPLE, // Can handle multiple URLs. CREATE_NOTE, // Can handle only one URL. + EDIT, // Can handle only one URL. }; // Describes an activity. diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc index b79c5eb54e226a..aa1fd3b6caf511 100644 --- a/chrome/browser/apps/app_service/publishers/arc_apps.cc +++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc @@ -389,6 +389,8 @@ arc::mojom::ActionType GetArcActionType(const std::string& action) { return arc::mojom::ActionType::SEND; } else if (action == apps_util::kIntentActionSendMultiple) { return arc::mojom::ActionType::SEND_MULTIPLE; + } else if (action == apps_util::kIntentActionEdit) { + return arc::mojom::ActionType::EDIT; } else { return arc::mojom::ActionType::VIEW; } diff --git a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc index 0722d098599e9b..533dfce562565f 100644 --- a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc +++ b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc @@ -7,12 +7,17 @@ #include "ash/components/arc/mojom/intent_helper.mojom.h" #include "ash/components/arc/session/arc_bridge_service.h" #include "ash/components/arc/session/arc_service_manager.h" +#include "ash/components/arc/test/connection_holder_util.h" #include "ash/components/arc/test/fake_app_instance.h" +#include "ash/components/arc/test/fake_file_system_instance.h" +#include "base/strings/string_util.h" #include "base/test/scoped_feature_list.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/apps/app_service/app_service_test.h" #include "chrome/browser/apps/app_service/publishers/arc_apps_factory.h" +#include "chrome/browser/ash/arc/fileapi/arc_file_system_bridge.h" +#include "chrome/browser/ash/file_manager/path_util.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" #include "chrome/browser/ui/app_list/arc/arc_app_test.h" #include "chrome/browser/web_applications/test/fake_web_app_provider.h" @@ -26,6 +31,10 @@ #include "components/services/app_service/public/cpp/intent_util.h" #include "components/services/app_service/public/cpp/preferred_apps_list_handle.h" #include "content/public/test/browser_task_environment.h" +#include "storage/browser/file_system/external_mount_points.h" +#include "storage/browser/file_system/file_system_url.h" +#include "storage/common/file_system/file_system_mount_option.h" +#include "storage/common/file_system/file_system_types.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -84,6 +93,24 @@ apps::IntentFilters CreateIntentFilters( return filters; } +// Returns a FileSystemURL, encoded as a GURL, that points to a file in the +// Downloads directory. +GURL FileInDownloads(Profile* profile, base::FilePath file) { + url::Origin origin = file_manager::util::GetFilesAppOrigin(); + std::string mount_point_name = + file_manager::util::GetDownloadsMountPointName(profile); + storage::ExternalMountPoints* mount_points = + storage::ExternalMountPoints::GetSystemInstance(); + mount_points->RegisterFileSystem( + mount_point_name, storage::kFileSystemTypeLocal, + storage::FileSystemMountOption(), + file_manager::util::GetDownloadsFolderForProfile(profile)); + return mount_points + ->CreateExternalFileSystemURL(blink::StorageKey(origin), mount_point_name, + file) + .ToGURL(); +} + } // namespace class ArcAppsPublisherTest : public testing::Test { @@ -101,13 +128,24 @@ class ArcAppsPublisherTest : public testing::Test { arc_test_.set_start_app_service_publisher(false); arc_test_.SetUp(profile()); + auto* arc_bridge_service = + arc_test_.arc_service_manager()->arc_bridge_service(); + + // Initialize Host interfaces so that our fake Instances can connect via + // mojo. We want to use the real ArcIntentHelper KeyedService so that + // it's the same object that ArcApps uses. intent_helper_ = arc::ArcIntentHelperBridge::GetForBrowserContextForTesting(profile()); + arc_file_system_bridge_ = std::make_unique( + profile(), arc_bridge_service); + intent_helper_instance_ = std::make_unique(); - auto* arc_bridge_service = - arc_test_.arc_service_manager()->arc_bridge_service(); - arc_bridge_service->intent_helper()->SetInstance( - intent_helper_instance_.get()); + arc_bridge_service->intent_helper()->SetInstance(intent_helper_instance()); + arc::WaitForInstanceReady(arc_bridge_service->intent_helper()); + + file_system_instance_ = std::make_unique(); + arc_bridge_service->file_system()->SetInstance(file_system_instance()); + arc::WaitForInstanceReady(arc_bridge_service->file_system()); auto* const provider = web_app::FakeWebAppProvider::Get(&profile_); provider->SkipAwaitingExtensionSystem(); @@ -120,7 +158,10 @@ class ArcAppsPublisherTest : public testing::Test { task_environment_.RunUntilIdle(); } - void TearDown() override { arc_test_.TearDown(); } + void TearDown() override { + arc_test_.StopArcInstance(); + arc_test_.TearDown(); + } void VerifyIntentFilters(const std::string& app_id, const std::vector& authorities) { @@ -143,12 +184,20 @@ class ArcAppsPublisherTest : public testing::Test { TestingProfile* profile() { return &profile_; } + apps::AppServiceProxy* app_service_proxy() { + return apps::AppServiceProxyFactory::GetForProfile(profile()); + } + arc::ArcIntentHelperBridge* intent_helper() { return intent_helper_; } arc::FakeIntentHelperInstance* intent_helper_instance() { return intent_helper_instance_.get(); } + arc::FakeFileSystemInstance* file_system_instance() { + return file_system_instance_.get(); + } + ArcAppTest* arc_test() { return &arc_test_; } apps::PreferredAppsListHandle& preferred_apps() { @@ -164,6 +213,8 @@ class ArcAppsPublisherTest : public testing::Test { apps::AppServiceTest app_service_test_; arc::ArcIntentHelperBridge* intent_helper_; std::unique_ptr intent_helper_instance_; + std::unique_ptr file_system_instance_; + std::unique_ptr arc_file_system_bridge_; }; // Verifies that a call to set the supported links preference from ARC persists @@ -323,3 +374,29 @@ TEST_F(ArcAppsPublisherTest, ASSERT_EQ(app_id, preferred_apps().FindPreferredAppForUrl( GURL("https://www.example.com/foo"))); } + +TEST_F(ArcAppsPublisherTest, + LaunchAppWithIntent_EditIntent_SendsOpenUrlRequest) { + auto intent = apps_util::CreateEditIntentFromFile( + FileInDownloads(profile(), base::FilePath("test.txt")), "text/plain"); + + const auto& fake_apps = arc_test()->fake_apps(); + std::string package_name = fake_apps[0]->package_name; + std::string app_id = ArcAppListPrefs::GetAppId(fake_apps[0]->package_name, + fake_apps[0]->activity); + arc_test()->app_instance()->SendRefreshAppList(fake_apps); + + app_service_proxy()->LaunchAppWithIntent( + app_id, 0, std::move(intent), + apps::mojom::LaunchSource::kFromFileManager); + + FlushMojoCalls(); + + ASSERT_EQ(file_system_instance()->handledUrlRequests().size(), 1); + auto& url_request = file_system_instance()->handledUrlRequests()[0]; + ASSERT_EQ(url_request->action_type, arc::mojom::ActionType::EDIT); + ASSERT_EQ(url_request->urls.size(), 1); + ASSERT_EQ(url_request->urls[0]->mime_type, "text/plain"); + ASSERT_TRUE( + base::EndsWith(url_request->urls[0]->content_url.spec(), "test.txt")); +} diff --git a/components/services/app_service/public/cpp/intent_util.cc b/components/services/app_service/public/cpp/intent_util.cc index 0f7d5b3488ccf7..7cf26942140bda 100644 --- a/components/services/app_service/public/cpp/intent_util.cc +++ b/components/services/app_service/public/cpp/intent_util.cc @@ -91,6 +91,8 @@ const char kIntentActionView[] = "view"; const char kIntentActionSend[] = "send"; const char kIntentActionSendMultiple[] = "send_multiple"; const char kIntentActionCreateNote[] = "create_note"; +const char kIntentActionEdit[] = "edit"; + const char kUseBrowserForLink[] = "use_browser"; apps::mojom::IntentPtr CreateIntentFromUrl(const GURL& url) { @@ -177,6 +179,20 @@ apps::mojom::IntentPtr CreateShareIntentFromText( return intent; } +apps::mojom::IntentPtr CreateEditIntentFromFile(const GURL& filesystem_url, + const std::string& mime_type) { + auto intent = apps::mojom::Intent::New(); + intent->action = kIntentActionEdit; + intent->files = std::vector{}; + intent->mime_type = mime_type; + + auto file = apps::mojom::IntentFile::New(); + file->url = filesystem_url; + file->mime_type = mime_type; + intent->files->push_back(std::move(file)); + return intent; +} + apps::mojom::IntentPtr CreateIntentForActivity(const std::string& activity, const std::string& start_type, const std::string& category) { diff --git a/components/services/app_service/public/cpp/intent_util.h b/components/services/app_service/public/cpp/intent_util.h index 9f7b855d6615b0..92adcba5a9dd5a 100644 --- a/components/services/app_service/public/cpp/intent_util.h +++ b/components/services/app_service/public/cpp/intent_util.h @@ -27,6 +27,8 @@ extern const char kIntentActionView[]; extern const char kIntentActionSend[]; extern const char kIntentActionSendMultiple[]; extern const char kIntentActionCreateNote[]; +// A request to edit a file in an app. Must include an attached file. +extern const char kIntentActionEdit[]; // App ID value which can be used as a Preferred App to denote that the browser // will open the link, and that we should not prompt the user about it. @@ -74,6 +76,11 @@ apps::mojom::IntentPtr CreateShareIntentFromText( const std::string& share_text, const std::string& share_title); +// Create an edit intent struct for the file with a given filesystem:// URL and +// mime type. +apps::mojom::IntentPtr CreateEditIntentFromFile(const GURL& filesystem_url, + const std::string& mime_type); + // Create an intent struct from activity and start type. apps::mojom::IntentPtr CreateIntentForActivity(const std::string& activity, const std::string& start_type,