Skip to content

Commit

Permalink
Intents: Add support for launching ARC apps with an 'edit' intent
Browse files Browse the repository at this point in the history
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 <dominickn@chromium.org>
Commit-Queue: Tim Sergeant <tsergeant@chromium.org>
Cr-Commit-Position: refs/heads/main@{#992352}
  • Loading branch information
tgsergeant authored and Chromium LUCI CQ committed Apr 14, 2022
1 parent 879d9d3 commit 3171385
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 5 deletions.
1 change: 1 addition & 0 deletions ash/components/arc/mojom/intent_common.mojom
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions chrome/browser/apps/app_service/publishers/arc_apps.cc
Expand Up @@ -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;
}
Expand Down
87 changes: 82 additions & 5 deletions chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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<arc::ArcFileSystemBridge>(
profile(), arc_bridge_service);

intent_helper_instance_ = std::make_unique<arc::FakeIntentHelperInstance>();
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::FakeFileSystemInstance>();
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();
Expand All @@ -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<std::string>& authorities) {
Expand All @@ -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() {
Expand All @@ -164,6 +213,8 @@ class ArcAppsPublisherTest : public testing::Test {
apps::AppServiceTest app_service_test_;
arc::ArcIntentHelperBridge* intent_helper_;
std::unique_ptr<arc::FakeIntentHelperInstance> intent_helper_instance_;
std::unique_ptr<arc::FakeFileSystemInstance> file_system_instance_;
std::unique_ptr<arc::ArcFileSystemBridge> arc_file_system_bridge_;
};

// Verifies that a call to set the supported links preference from ARC persists
Expand Down Expand Up @@ -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"));
}
16 changes: 16 additions & 0 deletions components/services/app_service/public/cpp/intent_util.cc
Expand Up @@ -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) {
Expand Down Expand Up @@ -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<apps::mojom::IntentFilePtr>{};
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) {
Expand Down
7 changes: 7 additions & 0 deletions components/services/app_service/public/cpp/intent_util.h
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 3171385

Please sign in to comment.