Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OS X Handoff Feature #5352

Merged
merged 22 commits into from
May 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a66565f
Merge remote-tracking branch 'upstream/master'
CharlieHess Apr 2, 2016
e9514bf
Merge branch 'master' of https://github.com/atom/atom-shell
CharlieHess Apr 29, 2016
9260657
Generalize this mate converter for reuse.
CharlieHess Apr 30, 2016
6df4bb1
Implement app.setUserActivity(type, userInfo).
CharlieHess Apr 30, 2016
c20acb0
Implement a "continue-activity" event on app for resuming from hand-off.
CharlieHess Apr 30, 2016
3a9a1d3
Add the AppDelegate override for restoring from hand-off, and fire th…
CharlieHess Apr 30, 2016
88805ec
Make the Linter happy.
CharlieHess Apr 30, 2016
1959402
This is preventDefault by convention.
CharlieHess Apr 30, 2016
cea1b49
Add some documentation in app.md.
CharlieHess Apr 30, 2016
0549350
Value first, key second.
CharlieHess Apr 30, 2016
21ae288
:memo: about the plist changes.
CharlieHess Apr 30, 2016
dbe3674
Merge branch 'master' of https://github.com/atom/atom-shell
CharlieHess May 2, 2016
b53480e
Merge remote-tracking branch 'origin/master' into hands-on-hand-off
CharlieHess May 2, 2016
2295f3a
Add some shady methods to get V8 objects or arrays from NSDictionary …
CharlieHess May 2, 2016
90cc109
Use a DictionaryValue everywhere instead of a string map.
CharlieHess May 2, 2016
f84a973
Revert "Use a DictionaryValue everywhere instead of a string map."
CharlieHess May 3, 2016
03d25ce
Revert "Add some shady methods to get V8 objects or arrays from NSDic…
CharlieHess May 3, 2016
a5a2e20
:memo: on using only strings.
CharlieHess May 3, 2016
42768bc
Save the activity on the application instance to ensure we hold a ref…
CharlieHess May 3, 2016
12764a6
Add an accessor for the current activity type and write the simplest …
CharlieHess May 3, 2016
7b207aa
Don't run this spec on platforms where the method is unavailable.
CharlieHess May 4, 2016
b2fb95f
Use scoped_nsobject to ensure our intermediate objects get cleaned up.
CharlieHess May 4, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions atom/browser/api/atom_api_app.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/native_mate_converters/string_map_converter.h"
#include "atom/common/node_includes.h"
#include "atom/common/options_switches.h"
#include "base/command_line.h"
Expand Down Expand Up @@ -249,6 +250,12 @@ void App::OnFinishLaunching() {
Emit("ready");
}

void App::OnContinueUserActivity(bool* prevent_default,
const std::string& type,
const std::map<std::string, std::string>& user_info) {
*prevent_default = Emit("continue-activity", type, user_info);
}

void App::OnLogin(LoginHandler* login_handler) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
Expand Down Expand Up @@ -460,6 +467,10 @@ void App::BuildPrototype(
#if defined(OS_MACOSX)
.SetMethod("hide", base::Bind(&Browser::Hide, browser))
.SetMethod("show", base::Bind(&Browser::Show, browser))
.SetMethod("setUserActivity",
base::Bind(&Browser::SetUserActivity, browser))
.SetMethod("getCurrentActivityType",
base::Bind(&Browser::GetCurrentActivityType, browser))
#endif
#if defined(OS_WIN)
.SetMethod("setUserTasks",
Expand Down
4 changes: 4 additions & 0 deletions atom/browser/api/atom_api_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define ATOM_BROWSER_API_ATOM_API_APP_H_

#include <string>
#include <map>

#include "atom/browser/api/event_emitter.h"
#include "atom/browser/atom_browser_client.h"
Expand Down Expand Up @@ -71,6 +72,9 @@ class App : public AtomBrowserClient::Delegate,
void OnWillFinishLaunching() override;
void OnFinishLaunching() override;
void OnLogin(LoginHandler* login_handler) override;
void OnContinueUserActivity(bool* prevent_default,
const std::string& type,
const std::map<std::string, std::string>& user_info) override;

// content::ContentBrowserClient:
void AllowCertificateError(
Expand Down
13 changes: 13 additions & 0 deletions atom/browser/browser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ void Browser::Activate(bool has_visible_windows) {
OnActivate(has_visible_windows));
}

#if defined(OS_MACOSX)
bool Browser::ContinueUserActivity(const std::string& type,
const std::map<std::string,
std::string>& user_info) {
bool prevent_default = false;
FOR_EACH_OBSERVER(BrowserObserver,
observers_,
OnContinueUserActivity(&prevent_default, type, user_info));

return prevent_default;
}
#endif

void Browser::WillFinishLaunching() {
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnWillFinishLaunching());
}
Expand Down
12 changes: 12 additions & 0 deletions atom/browser/browser.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <string>
#include <vector>
#include <map>

#include "base/macros.h"
#include "base/compiler_specific.h"
Expand Down Expand Up @@ -92,6 +93,17 @@ class Browser : public WindowListObserver {
// Show the application.
void Show();

// Creates an activity and sets it as the one currently in use.
void SetUserActivity(const std::string& type,
const std::map<std::string, std::string>& user_info);

// Returns the type name of the current user activity.
std::string GetCurrentActivityType();

// Resumes an activity via hand-off.
bool ContinueUserActivity(const std::string& type,
const std::map<std::string, std::string>& user_info);

// Bounce the dock icon.
enum BounceType {
BOUNCE_CRITICAL = 0,
Expand Down
28 changes: 26 additions & 2 deletions atom/browser/browser_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@
return false;

NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()];

CFStringRef bundle =
LSCopyDefaultHandlerForURLScheme(base::mac::NSToCFCast(protocol_ns));
NSString* bundleId = static_cast<NSString*>(
base::mac::CFTypeRefToNSObjectAutorelease(bundle));
if (!bundleId)
return false;

// Ensure the comparison is case-insensitive
// Ensure the comparison is case-insensitive
// as LS does not persist the case of the bundle id.
NSComparisonResult result =
[bundleId caseInsensitiveCompare:identifier];
Expand All @@ -87,6 +87,30 @@
void Browser::SetAppUserModelID(const base::string16& name) {
}

void Browser::SetUserActivity(const std::string& type, const std::map<std::string, std::string>& user_info) {
NSString* type_ns = [NSString stringWithUTF8String:type.c_str()];
NSUserActivity* user_activity = [[NSUserActivity alloc] initWithActivityType:type_ns];

base::scoped_nsobject<NSMutableDictionary> user_info_args([[NSMutableDictionary alloc] init]);
for (auto const &pair : user_info) {
NSString* value_ns = [NSString stringWithUTF8String:pair.second.c_str()];
NSString* key_ns = [NSString stringWithUTF8String:pair.first.c_str()];

[user_info_args.get() setObject:value_ns
forKey:key_ns];
}

user_activity.userInfo = user_info_args.get();
[user_activity becomeCurrent];

[[AtomApplication sharedApplication] setCurrentActivity:user_activity];
}

std::string Browser::GetCurrentActivityType() {
NSUserActivity* user_activity = [[AtomApplication sharedApplication] getCurrentActivity];
return base::SysNSStringToUTF8(user_activity.activityType);
}

std::string Browser::GetExecutableFileVersion() const {
return brightray::GetApplicationVersion();
}
Expand Down
6 changes: 6 additions & 0 deletions atom/browser/browser_observer.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define ATOM_BROWSER_BROWSER_OBSERVER_H_

#include <string>
#include <map>

namespace atom {

Expand Down Expand Up @@ -45,6 +46,11 @@ class BrowserObserver {
// The browser requests HTTP login.
virtual void OnLogin(LoginHandler* login_handler) {}

// The browser wants to resume a user activity via handoff. (OS X only)
virtual void OnContinueUserActivity(bool* prevent_default,
const std::string& type,
const std::map<std::string, std::string>& user_info) {}

protected:
virtual ~BrowserObserver() {}
};
Expand Down
6 changes: 6 additions & 0 deletions atom/browser/mac/atom_application.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
// found in the LICENSE file.

#import "base/mac/scoped_sending_event.h"
#import "base/mac/scoped_nsobject.h"

@interface AtomApplication : NSApplication<CrAppProtocol,
CrAppControlProtocol> {
@private
BOOL handlingSendEvent_;
base::scoped_nsobject<NSUserActivity> currentActivity_;
}

+ (AtomApplication*)sharedApplication;
Expand All @@ -18,4 +20,8 @@
// CrAppControlProtocol:
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent;

- (NSUserActivity*)getCurrentActivity;

- (void)setCurrentActivity:(NSUserActivity*)userActivity;

@end
8 changes: 8 additions & 0 deletions atom/browser/mac/atom_application.mm
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ - (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
handlingSendEvent_ = handlingSendEvent;
}

- (void)setCurrentActivity:(NSUserActivity*)userActivity {
currentActivity_ = base::scoped_nsobject<NSUserActivity>(userActivity);
}

- (NSUserActivity*)getCurrentActivity {
return currentActivity_.get();
}

- (void)awakeFromNib {
[[NSAppleEventManager sharedAppleEventManager]
setEventHandler:self
Expand Down
19 changes: 19 additions & 0 deletions atom/browser/mac/atom_application_delegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,23 @@ - (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
return flag;
}

- (BOOL)application:(NSApplication *)sender
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType));

std::map<std::string, std::string> user_info;
base::scoped_nsobject<NSArray> keys([userActivity.userInfo allKeys]);

for (NSString* key in keys.get()) {
NSString* value = [userActivity.userInfo objectForKey:key];
std::string key_str(base::SysNSStringToUTF8(key));
std::string value_str(base::SysNSStringToUTF8(value));
user_info[key_str] = value_str;
}

atom::Browser* browser = atom::Browser::Get();
return browser->ContinueUserActivity(activity_type, user_info) ? YES : NO;
}

@end
19 changes: 1 addition & 18 deletions atom/common/api/atom_api_crash_reporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string>

#include "atom/common/crash_reporter/crash_reporter.h"
#include "atom/common/native_mate_converters/string_map_converter.h"
#include "base/bind.h"
#include "native_mate/dictionary.h"

Expand All @@ -15,24 +16,6 @@ using crash_reporter::CrashReporter;

namespace mate {

template<>
struct Converter<std::map<std::string, std::string> > {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
std::map<std::string, std::string>* out) {
if (!val->IsObject())
return false;

v8::Local<v8::Object> dict = val->ToObject();
v8::Local<v8::Array> keys = dict->GetOwnPropertyNames();
for (uint32_t i = 0; i < keys->Length(); ++i) {
v8::Local<v8::Value> key = keys->Get(i);
(*out)[V8ToString(key)] = V8ToString(dict->Get(key));
}
return true;
}
};

template<>
struct Converter<CrashReporter::UploadReportResult> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
Expand Down
36 changes: 36 additions & 0 deletions atom/common/native_mate_converters/string_map_converter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/common/native_mate_converters/string_map_converter.h"

namespace mate {

bool Converter<std::map<std::string, std::string>>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
std::map<std::string, std::string>* out) {
if (!val->IsObject())
return false;

v8::Local<v8::Object> dict = val->ToObject();
v8::Local<v8::Array> keys = dict->GetOwnPropertyNames();
for (uint32_t i = 0; i < keys->Length(); ++i) {
v8::Local<v8::Value> key = keys->Get(i);
(*out)[V8ToString(key)] = V8ToString(dict->Get(key));
}
return true;
}

v8::Local<v8::Value> Converter<std::map<std::string, std::string>>::ToV8(
v8::Isolate* isolate,
const std::map<std::string, std::string>& in) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));

for (auto const &pair : in) {
dict.Set(pair.first, pair.second);
}

return dict.GetHandle();
}

} // namespace mate
28 changes: 28 additions & 0 deletions atom/common/native_mate_converters/string_map_converter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING_MAP_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING_MAP_CONVERTER_H_

#include <map>
#include <string>

#include "native_mate/converter.h"
#include "native_mate/dictionary.h"

namespace mate {

template<>
struct Converter<std::map<std::string, std::string>> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
std::map<std::string, std::string>* out);

static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const std::map<std::string, std::string>& in);
};

} // namespace mate

#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_STRING_MAP_CONVERTER_H_
44 changes: 38 additions & 6 deletions docs/api/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,26 @@ Returns:
* `event` Event
* `hasVisibleWindows` Boolean

Emitted when the application is activated, which usually happens when clicks on
the applications's dock icon.
Emitted when the application is activated, which usually happens when the user clicks on
the application's dock icon.

### Event: 'continue-activity' _OS X_

Returns:

* `event` Event
* `type` String - A string identifying the event. Maps to [`NSUserActivity.activityType`](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType).
* `userInfo` Object - Contains app-specific state stored by the activity on
another device. Currently only string data is supported.

Emitted during [handoff](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html) when an activity from a different device wants to be
resumed. You should call `event.preventDefault()` if you want to handle this
event.

A user activity can be continued only in an app that has the same developer
Team ID as the activity's source app and that supports the activity's type.
Supported activity types are specified in the app's Info.plist under the
`NSUserActivityTypes` key.

### Event: 'browser-window-blur'

Expand Down Expand Up @@ -386,12 +404,12 @@ default protocol handler.

### `app.isDefaultProtocolClient(protocol)` _OS X_ _Windows_

* `protocol` String - The name of your protocol, without `://`.
* `protocol` String - The name of your protocol, without `://`.

This method checks if the current executable is the default handler for a protocol
(aka URI scheme). If so, it will return true. Otherwise, it will return false.
(aka URI scheme). If so, it will return true. Otherwise, it will return false.

**Note:** On OS X, you can use this method to check if the app has been registered as the default protocol handler for a protocol. You can also verify this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the OS X machine.
**Note:** On OS X, you can use this method to check if the app has been registered as the default protocol handler for a protocol. You can also verify this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the OS X machine.
Please refer to [Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details.

The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally.
Expand Down Expand Up @@ -482,6 +500,20 @@ app.on('ready', function() {
});
```

### `app.setUserActivity(type, userInfo)` _OS X_

* `type` String - Uniquely identifies the activity. It's recommended to use a
reverse-DNS string.
* `userInfo` Object - App-specific state to store for use by another device.
Currently only string data is supported.

Creates an `NSUserActivity` and sets it as the current activity. The activity
is eligible for [handoff](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html) to another device afterward.

### `app.getCurrentActivityType()` _OS X_

Returns the type of the currently running activity.

### `app.setAppUserModelId(id)` _Windows_

* `id` String
Expand Down Expand Up @@ -568,5 +600,5 @@ Sets the `image` associated with this dock icon.
[tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks
[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx
[CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115
[LSCopyDefaultHandlerForURLScheme]:
[LSCopyDefaultHandlerForURLScheme]:
https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme
Loading