From b63793cbb68a6b546753efb77b2f05158012214f Mon Sep 17 00:00:00 2001 From: He Xinchao Date: Thu, 14 Aug 2014 09:31:53 +0800 Subject: [PATCH 1/5] [Tizen][Runtime] Disable device api in remote web frames. According to Tizen core spec, only local frames can access to device api, the remote pages can not do this. This patch will enable this feature on Tizen platform. BUG=XWALK-2153 --- .../xwalk_extension_renderer_controller.cc | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/extensions/renderer/xwalk_extension_renderer_controller.cc b/extensions/renderer/xwalk_extension_renderer_controller.cc index d3cc06292c..64f1071b01 100644 --- a/extensions/renderer/xwalk_extension_renderer_controller.cc +++ b/extensions/renderer/xwalk_extension_renderer_controller.cc @@ -24,6 +24,10 @@ #include "xwalk/extensions/renderer/xwalk_module_system.h" #include "xwalk/extensions/renderer/xwalk_v8tools_module.h" +#if defined(OS_TIZEN) +#include "xwalk/application/common/constants.h" +#endif + namespace xwalk { namespace extensions { @@ -70,6 +74,24 @@ void CreateExtensionModules(XWalkExtensionClient* client, } } +#if defined(OS_TIZEN) +void CreateExtensionModulesWithoutDeviceAPI(XWalkExtensionClient* client, + XWalkModuleSystem* module_system) { + const XWalkExtensionClient::ExtensionAPIMap& extensions = + client->extension_apis(); + XWalkExtensionClient::ExtensionAPIMap::const_iterator it = extensions.begin(); + for (; it != extensions.end(); ++it) { + XWalkExtensionClient::ExtensionCodePoints* codepoint = it->second; + if (codepoint->api.empty() || it->first.find("tizen") == 0) + continue; + scoped_ptr module( + new XWalkExtensionModule(client, module_system, + it->first, codepoint->api)); + module_system->RegisterExtensionModule(module.Pass(), + codepoint->entry_points); + } +} +#endif } // namespace void XWalkExtensionRendererController::DidCreateScriptContext( @@ -90,8 +112,19 @@ void XWalkExtensionRendererController::DidCreateScriptContext( module_system); if (external_extensions_client_) { +#if defined(OS_TIZEN) + // On Tizen platform, only local pages can access to device APIs. + GURL url = static_cast(frame->document().url()); + if (!url.SchemeIs(xwalk::application::kApplicationScheme) && + !url.SchemeIsFile()) + CreateExtensionModulesWithoutDeviceAPI(external_extensions_client_.get(), + module_system); + else + CreateExtensionModules(external_extensions_client_.get(), module_system); +#else CreateExtensionModules(external_extensions_client_.get(), module_system); +#endif } module_system->Initialize(); From 94d0859a0920b991d4294e8ab8969e61bc734888 Mon Sep 17 00:00:00 2001 From: He Xinchao Date: Fri, 4 Jul 2014 17:08:25 +0800 Subject: [PATCH 2/5] [Tizen][Runtime] Enable pause/resume web application JS engine. The Tizen core spec requires that the web runtime should have the ability to pause and resume the javascript engine when receiving the corresponding system events. BUG=XWALK-1497,XWALK-1498 --- application/browser/application_tizen.cc | 35 ++++++++++++- application/browser/application_tizen.h | 4 ++ .../linux/running_application_object.cc | 52 +++++++++++++++++++ .../linux/running_application_object.h | 7 ++- .../tools/linux/xwalk_launcher_main.cc | 2 +- .../tools/linux/xwalk_launcher_tizen.cc | 36 +++++++++++-- .../tools/linux/xwalk_launcher_tizen.h | 3 +- runtime/common/xwalk_common_messages.h | 3 ++ .../xwalk_render_process_observer_generic.cc | 17 +++++- .../xwalk_render_process_observer_generic.h | 2 + 10 files changed, 152 insertions(+), 9 deletions(-) diff --git a/application/browser/application_tizen.cc b/application/browser/application_tizen.cc index 129587eed6..09f0476536 100644 --- a/application/browser/application_tizen.cc +++ b/application/browser/application_tizen.cc @@ -72,7 +72,8 @@ ApplicationTizen::ApplicationTizen( scoped_refptr data, RuntimeContext* runtime_context, Application::Observer* observer) - : Application(data, runtime_context, observer) { + : Application(data, runtime_context, observer), + is_suspended_(false) { #if defined(USE_OZONE) ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); #endif @@ -111,6 +112,38 @@ base::FilePath ApplicationTizen::GetSplashScreenPath() { return base::FilePath(); } +void ApplicationTizen::Suspend() { + if (is_suspended_) + return; + + DCHECK(render_process_host_); + render_process_host_->Send(new ViewMsg_SuspendJSEngine(true)); + + DCHECK(!runtimes_.empty()); + std::set::iterator it = runtimes_.begin(); + for (; it != runtimes_.end(); ++it) { + if ((*it)->web_contents()) + (*it)->web_contents()->WasHidden(); + } + is_suspended_ = true; +} + +void ApplicationTizen::Resume() { + if (!is_suspended_) + return; + + DCHECK(render_process_host_); + render_process_host_->Send(new ViewMsg_SuspendJSEngine(false)); + + DCHECK(!runtimes_.empty()); + std::set::iterator it = runtimes_.begin(); + for (; it != runtimes_.end(); ++it) { + if ((*it)->web_contents()) + (*it)->web_contents()->WasShown(); + } + is_suspended_ = false; +} + #if defined(USE_OZONE) void ApplicationTizen::WillProcessEvent(const ui::PlatformEvent& event) {} diff --git a/application/browser/application_tizen.h b/application/browser/application_tizen.h index a8dedad54c..5cace02b66 100644 --- a/application/browser/application_tizen.h +++ b/application/browser/application_tizen.h @@ -23,6 +23,8 @@ class ApplicationTizen : // NOLINT public: virtual ~ApplicationTizen(); void Hide(); + void Suspend(); + void Resume(); private: // We enforce ApplicationService ownership. @@ -38,6 +40,8 @@ class ApplicationTizen : // NOLINT virtual void WillProcessEvent(const ui::PlatformEvent& event) OVERRIDE; virtual void DidProcessEvent(const ui::PlatformEvent& event) OVERRIDE; #endif + + bool is_suspended_; }; inline ApplicationTizen* ToApplicationTizen(Application* app) { diff --git a/application/browser/linux/running_application_object.cc b/application/browser/linux/running_application_object.cc index b5f2b10ad1..3b014b7e87 100644 --- a/application/browser/linux/running_application_object.cc +++ b/application/browser/linux/running_application_object.cc @@ -76,6 +76,20 @@ RunningApplicationObject::RunningApplicationObject( base::Unretained(this)), base::Bind(&RunningApplicationObject::OnExported, base::Unretained(this))); + + dbus_object()->ExportMethod( + kRunningApplicationDBusInterface, "Suspend", + base::Bind(&RunningApplicationObject::OnSuspend, + base::Unretained(this)), + base::Bind(&RunningApplicationObject::OnExported, + base::Unretained(this))); + + dbus_object()->ExportMethod( + kRunningApplicationDBusInterface, "Resume", + base::Bind(&RunningApplicationObject::OnResume, + base::Unretained(this)), + base::Bind(&RunningApplicationObject::OnExported, + base::Unretained(this))); #endif } @@ -148,6 +162,44 @@ void RunningApplicationObject::OnHide( dbus::Response::FromMethodCall(method_call); response_sender.Run(response.Pass()); } + +void RunningApplicationObject::OnSuspend( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + if (method_call->GetSender() != launcher_name_) { + scoped_ptr error_response = + dbus::ErrorResponse::FromMethodCall(method_call, + kRunningApplicationDBusError, + "Not permitted"); + response_sender.Run(error_response.PassAs()); + return; + } + + ToApplicationTizen(application_)->Suspend(); + + scoped_ptr response = + dbus::Response::FromMethodCall(method_call); + response_sender.Run(response.Pass()); +} + +void RunningApplicationObject::OnResume( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + if (method_call->GetSender() != launcher_name_) { + scoped_ptr error_response = + dbus::ErrorResponse::FromMethodCall(method_call, + kRunningApplicationDBusError, + "Not permitted"); + response_sender.Run(error_response.PassAs()); + return; + } + + ToApplicationTizen(application_)->Resume(); + + scoped_ptr response = + dbus::Response::FromMethodCall(method_call); + response_sender.Run(response.Pass()); +} #endif void RunningApplicationObject::ListenForOwnerChange() { diff --git a/application/browser/linux/running_application_object.h b/application/browser/linux/running_application_object.h index 4749aae924..70db94f010 100644 --- a/application/browser/linux/running_application_object.h +++ b/application/browser/linux/running_application_object.h @@ -53,6 +53,12 @@ class RunningApplicationObject : public dbus::ManagedObject { #if defined(OS_TIZEN) void OnHide(dbus::MethodCall* method_call, dbus::ExportedObject::ResponseSender response_sender); + + void OnSuspend(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); + + void OnResume(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender); #endif void ListenForOwnerChange(); @@ -76,4 +82,3 @@ class RunningApplicationObject : public dbus::ManagedObject { } // namespace xwalk #endif // XWALK_APPLICATION_BROWSER_LINUX_RUNNING_APPLICATION_OBJECT_H_ - diff --git a/application/tools/linux/xwalk_launcher_main.cc b/application/tools/linux/xwalk_launcher_main.cc index 2d31fec4ca..66c77cfb62 100644 --- a/application/tools/linux/xwalk_launcher_main.cc +++ b/application/tools/linux/xwalk_launcher_main.cc @@ -224,7 +224,7 @@ static void launch_application(const char* appid_or_url, char name[128]; snprintf(name, sizeof(name), "xwalk-%s", appid_or_url); - if (xwalk_appcore_init(g_argc, g_argv, name)) { + if (xwalk_appcore_init(g_argc, g_argv, name, app_proxy)) { fprintf(stderr, "Failed to initialize appcore"); exit(1); } diff --git a/application/tools/linux/xwalk_launcher_tizen.cc b/application/tools/linux/xwalk_launcher_tizen.cc index f7a83c030f..27f8eff03d 100644 --- a/application/tools/linux/xwalk_launcher_tizen.cc +++ b/application/tools/linux/xwalk_launcher_tizen.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include +#include #include #include #include @@ -60,15 +61,42 @@ static const char* event2str(enum app_event event) { static void application_event_cb(enum app_event event, void* data, bundle* b) { fprintf(stderr, "event '%s'\n", event2str(event)); + GDBusProxy* app_proxy = reinterpret_cast(data); - if (event == AE_TERMINATE) { - exit(0); + if (!app_proxy) { + fprintf(stderr, "Invalid DBus proxy."); + return; + } + + switch (event) { + case AE_UNKNOWN: + case AE_CREATE: + break; + case AE_TERMINATE: + exit(0); + break; + case AE_PAUSE: + g_dbus_proxy_call( + app_proxy, "Suspend", NULL, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); + break; + case AE_RESUME: + g_dbus_proxy_call( + app_proxy, "Resume", NULL, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); + break; + case AE_RESET: + case AE_LOWMEM_POST: + case AE_MEM_FLUSH: + case AE_MAX: + break; } } -int xwalk_appcore_init(int argc, char** argv, const char* name) { +int xwalk_appcore_init( + int argc, char** argv, const char* name, GDBusProxy* app_proxy) { appcore_ops.cb_app = application_event_cb; - appcore_ops.data = NULL; + appcore_ops.data = app_proxy; return appcore_init(name, &appcore_ops, argc, argv); } diff --git a/application/tools/linux/xwalk_launcher_tizen.h b/application/tools/linux/xwalk_launcher_tizen.h index 9956aaa2b4..75237bdd42 100644 --- a/application/tools/linux/xwalk_launcher_tizen.h +++ b/application/tools/linux/xwalk_launcher_tizen.h @@ -5,7 +5,8 @@ #ifndef XWALK_APPLICATION_TOOLS_LINUX_XWALK_LAUNCHER_TIZEN_H_ #define XWALK_APPLICATION_TOOLS_LINUX_XWALK_LAUNCHER_TIZEN_H_ -int xwalk_appcore_init(int argc, char** argv, const char* name); +int xwalk_appcore_init(int argc, char** argv, + const char* name, GDBusProxy* app_proxy); int xwalk_change_cmdline(int argc, char** argv, const char* app_id); diff --git a/runtime/common/xwalk_common_messages.h b/runtime/common/xwalk_common_messages.h index 4be689450a..ca440b1f67 100644 --- a/runtime/common/xwalk_common_messages.h +++ b/runtime/common/xwalk_common_messages.h @@ -39,6 +39,9 @@ IPC_MESSAGE_CONTROL2(ViewMsg_EnableSecurityMode, // NOLINT xwalk::application::SecurityPolicy::SecurityMode /* security mode */) +IPC_MESSAGE_CONTROL1(ViewMsg_SuspendJSEngine, // NOLINT + bool /* is suspend */) + IPC_MESSAGE_ROUTED1(ViewMsg_HWKeyPressed, int /*keycode*/) // NOLINT // These are messages sent from the renderer to the browser process. diff --git a/runtime/renderer/xwalk_render_process_observer_generic.cc b/runtime/renderer/xwalk_render_process_observer_generic.cc index de6b5a6ec5..a59456d262 100644 --- a/runtime/renderer/xwalk_render_process_observer_generic.cc +++ b/runtime/renderer/xwalk_render_process_observer_generic.cc @@ -6,7 +6,8 @@ #include -#include "content/public/renderer/render_thread.h" +#include "content/renderer/render_thread_impl.h" +#include "content/renderer/renderer_webkitplatformsupport_impl.h" #include "ipc/ipc_message_macros.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "third_party/WebKit/public/platform/WebString.h" @@ -44,6 +45,7 @@ void AddAccessWhiteListEntry( XWalkRenderProcessObserver::XWalkRenderProcessObserver() : is_webkit_initialized_(false), + is_suspended_(false), security_mode_(application::SecurityPolicy::NoSecurity) { } @@ -56,6 +58,7 @@ bool XWalkRenderProcessObserver::OnControlMessageReceived( IPC_BEGIN_MESSAGE_MAP(XWalkRenderProcessObserver, message) IPC_MESSAGE_HANDLER(ViewMsg_SetAccessWhiteList, OnSetAccessWhiteList) IPC_MESSAGE_HANDLER(ViewMsg_EnableSecurityMode, OnEnableSecurityMode) + IPC_MESSAGE_HANDLER(ViewMsg_SuspendJSEngine, OnSuspendJSEngine) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -95,4 +98,16 @@ void XWalkRenderProcessObserver::OnEnableSecurityMode( security_mode_ = mode; } +void XWalkRenderProcessObserver::OnSuspendJSEngine(bool is_suspend) { + if (is_suspend == is_suspended_) + return; + content::RenderThreadImpl* thread = content::RenderThreadImpl::current(); + thread->EnsureWebKitInitialized(); + if (is_suspend) + thread->webkit_platform_support()->SuspendSharedTimer(); + else + thread->webkit_platform_support()->ResumeSharedTimer(); + is_suspended_ = is_suspend; +} + } // namespace xwalk diff --git a/runtime/renderer/xwalk_render_process_observer_generic.h b/runtime/renderer/xwalk_render_process_observer_generic.h index eb0efc26cd..ae4913d183 100644 --- a/runtime/renderer/xwalk_render_process_observer_generic.h +++ b/runtime/renderer/xwalk_render_process_observer_generic.h @@ -51,8 +51,10 @@ class XWalkRenderProcessObserver : public content::RenderProcessObserver { const GURL& source, const GURL& dest, bool allow_subdomains); void OnEnableSecurityMode( const GURL& url, application::SecurityPolicy::SecurityMode mode); + void OnSuspendJSEngine(bool is_pause); bool is_webkit_initialized_; + bool is_suspended_; application::SecurityPolicy::SecurityMode security_mode_; GURL app_url_; }; From c6d5bfee5060218bdd7c4c9239fcce84f9b2e6a1 Mon Sep 17 00:00:00 2001 From: Alexis Menard Date: Fri, 15 Aug 2014 14:42:16 -0300 Subject: [PATCH 3/5] Roll Chromium 37.0.2062.76. --- DEPS.xwalk | 8 ++++---- .../src/org/xwalk/core/internal/XWalkContent.java | 3 ++- .../browser/runtime_resource_dispatcher_host_delegate.cc | 3 +-- .../browser/runtime_resource_dispatcher_host_delegate.h | 3 +-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/DEPS.xwalk b/DEPS.xwalk index ecda947551..9f99e8ffbe 100644 --- a/DEPS.xwalk +++ b/DEPS.xwalk @@ -17,10 +17,10 @@ # Edit these when rolling DEPS.xwalk. # ----------------------------------- -chromium_crosswalk_rev = 'b93afe0192aa9888d1246e8933d31272afecb7f0' -blink_crosswalk_rev = 'b656b39cc2eb71a9f4b70f8439c2d0a1ca54d619' -v8_crosswalk_rev = '402ca0b81254dd9d7b376485d4582c6b6eac2185' -ozone_wayland_rev = '0a8caf9bc740d767464b2d1d16fec08ff2f91d1f' +chromium_crosswalk_rev = 'f506465773c4515957798e38250a3a7becb2bea6' +blink_crosswalk_rev = 'db2fa6ff036de0611105c872817b487f0a88dd94' +v8_crosswalk_rev = '11af0678615217b5dce34e7e7d6a3257a2918ca4' +ozone_wayland_rev = '3372a0e23d925d5402eb16abbbe58dd82b583a5a' crosswalk_git = 'https://github.com/crosswalk-project' ozone_wayland_git = 'https://github.com/01org' diff --git a/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkContent.java b/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkContent.java index 3710195300..563b47e8a7 100644 --- a/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkContent.java +++ b/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkContent.java @@ -95,12 +95,13 @@ public XWalkContent(Context context, AttributeSet attrs, XWalkViewInternal xwVie boolean animated = XWalkPreferencesInternal.getValue(XWalkPreferencesInternal.ANIMATABLE_XWALK_VIEW); CompositingSurfaceType surfaceType = animated ? CompositingSurfaceType.TEXTURE_VIEW : CompositingSurfaceType.SURFACE_VIEW; - mContentViewRenderView = new ContentViewRenderView(context, mWindow, surfaceType) { + mContentViewRenderView = new ContentViewRenderView(context, surfaceType) { protected void onReadyToRender() { // Anything depending on the underlying Surface readiness should // be placed here. } }; + mContentViewRenderView.onNativeLibraryLoaded(mWindow); mLaunchScreenManager = new XWalkLaunchScreenManager(context, mXWalkView); mContentViewRenderView.registerFirstRenderedFrameListener(mLaunchScreenManager); addView(mContentViewRenderView, diff --git a/runtime/browser/runtime_resource_dispatcher_host_delegate.cc b/runtime/browser/runtime_resource_dispatcher_host_delegate.cc index 24bf2b62f4..3f3416cd25 100644 --- a/runtime/browser/runtime_resource_dispatcher_host_delegate.cc +++ b/runtime/browser/runtime_resource_dispatcher_host_delegate.cc @@ -63,8 +63,7 @@ void RuntimeResourceDispatcherHostDelegate::DownloadStarting( bool RuntimeResourceDispatcherHostDelegate::HandleExternalProtocol( const GURL& url, int child_id, - int route_id, - bool initiated_by_user_gesture) { + int route_id) { return true; } diff --git a/runtime/browser/runtime_resource_dispatcher_host_delegate.h b/runtime/browser/runtime_resource_dispatcher_host_delegate.h index f5cb6d651a..f4cc236b92 100644 --- a/runtime/browser/runtime_resource_dispatcher_host_delegate.h +++ b/runtime/browser/runtime_resource_dispatcher_host_delegate.h @@ -37,8 +37,7 @@ class RuntimeResourceDispatcherHostDelegate virtual bool HandleExternalProtocol( const GURL& url, int child_id, - int route_id, - bool initiated_by_user_gesture) OVERRIDE; + int route_id) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(RuntimeResourceDispatcherHostDelegate); From 561689ca87142c61a527d9460e0869661c84d2fc Mon Sep 17 00:00:00 2001 From: Ningxin Hu Date: Tue, 19 Aug 2014 01:35:37 +0800 Subject: [PATCH 4/5] DEPS.xwalk: Roll v8-crosswalk (11af067 -> 8ddfc6f). 8ddfc6f Merge pull request #46 from huningxin/fix-context-load c5c44ed Temporarily disable Distributivity1 test in M37 to pass x64 trybot 5aa07fb Fix invalid native context reference in simd128-tag BUG=XWALK-2255 --- DEPS.xwalk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS.xwalk b/DEPS.xwalk index 9f99e8ffbe..1230fa54d2 100644 --- a/DEPS.xwalk +++ b/DEPS.xwalk @@ -19,7 +19,7 @@ chromium_crosswalk_rev = 'f506465773c4515957798e38250a3a7becb2bea6' blink_crosswalk_rev = 'db2fa6ff036de0611105c872817b487f0a88dd94' -v8_crosswalk_rev = '11af0678615217b5dce34e7e7d6a3257a2918ca4' +v8_crosswalk_rev = '8ddfc6f1a103ddbe20e38afeb12c3e0666c8a361' ozone_wayland_rev = '3372a0e23d925d5402eb16abbbe58dd82b583a5a' crosswalk_git = 'https://github.com/crosswalk-project' From eeeb8eeab955539f89c20b74252662ab54d337aa Mon Sep 17 00:00:00 2001 From: Lin Sun Date: Tue, 12 Aug 2014 13:35:27 +0800 Subject: [PATCH 5/5] [Android] Support the tag member in W3C WebNotification API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix that the “tag” member of Notification does not work when running “WebAPI/Notification”. Add the indication of number for repeated notification. BUG=XWALK-1968 --- .../internal/XWalkContentsClientBridge.java | 2 +- .../internal/XWalkNotificationService.java | 2 +- .../XWalkNotificationServiceImpl.java | 281 ++++++++++-------- 3 files changed, 167 insertions(+), 118 deletions(-) diff --git a/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkContentsClientBridge.java b/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkContentsClientBridge.java index 06d5060a79..d483963316 100644 --- a/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkContentsClientBridge.java +++ b/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkContentsClientBridge.java @@ -601,7 +601,7 @@ private void showNotification(String title, String message, String replaceId, // FIXME(wang16): use replaceId to replace exist notification. It happens when // a notification with same name and tag fires. mNotificationService.showNotification( - title, message, notificationId, delegate); + title, message, replaceId, notificationId, delegate); } @CalledByNative diff --git a/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkNotificationService.java b/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkNotificationService.java index 466cebd716..61efb00a09 100644 --- a/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkNotificationService.java +++ b/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkNotificationService.java @@ -10,7 +10,7 @@ interface XWalkNotificationService { public void setBridge(XWalkContentsClientBridge bridge); public void showNotification( - String title, String message, int notificationId, long delegate); + String title, String message, String replaceId, int notificationId, long delegate); public void updateNotificationIcon(int notificationId, Bitmap icon); public void cancelNotification(int notificationId, long delegate); public void shutdown(); diff --git a/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkNotificationServiceImpl.java b/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkNotificationServiceImpl.java index da99005b07..c85b851427 100644 --- a/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkNotificationServiceImpl.java +++ b/runtime/android/core_internal/src/org/xwalk/core/internal/XWalkNotificationServiceImpl.java @@ -35,13 +35,24 @@ public class XWalkNotificationServiceImpl implements XWalkNotificationService { private static final String XWALK_INTENT_EXTRA_KEY_DELEGATE = "xwalk.DELEGATE"; private static final String XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX = "notification_"; + private class WebNotification { + WebNotification() { + mMessageNum = 1; + } + + public Integer mNotificationId; + public String mReplaceId; + public Notification.Builder mBuilder; + public Integer mMessageNum; + } + private Context mContext; private XWalkContentsClientBridge mBridge; private XWalkViewInternal mView; private NotificationManager mNotificationManager; private BroadcastReceiver mNotificationCloseReceiver; - private IntentFilter mNotificationCloseIntentFilter; - private HashMap mExistNotificationIds; + private HashMap mExistNotificationIds; + private HashMap mExistReplaceIds; public XWalkNotificationServiceImpl(Context context, XWalkViewInternal view) { mContext = context; @@ -50,55 +61,25 @@ public XWalkNotificationServiceImpl(Context context, XWalkViewInternal view) { (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); // Cancel all exist notifications at startup time. To avoid receiving legacy pendingIntents. mNotificationManager.cancelAll(); - mExistNotificationIds = new HashMap(); - } - @Override - public void setBridge(XWalkContentsClientBridge bridge) { - mBridge = bridge; - } - - private static String getCategoryFromNotificationId(int id) { - return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id; - } - - private void notificationChanged() { - unregisterReceiver(); - if (mExistNotificationIds.isEmpty()) { - Log.i(TAG, "notifications are all cleared," + - "unregister broadcast receiver for close pending intent"); - } else { - registerReceiver(); - } - } - - private void registerReceiver() { mNotificationCloseReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { mView.onNewIntent(intent); } }; - mNotificationCloseIntentFilter = new IntentFilter( - mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX); - for(Integer id : mExistNotificationIds.keySet()) { - mNotificationCloseIntentFilter.addCategory(getCategoryFromNotificationId(id)); - } - try { - mView.getActivity().registerReceiver(mNotificationCloseReceiver, mNotificationCloseIntentFilter); - } catch (AndroidRuntimeException e) { - //FIXME(wang16): The exception will happen when there are multiple xwalkviews in one activity. - // Remove it after notification service supports multi-views. - mNotificationCloseReceiver = null; - Log.w(TAG, e.getLocalizedMessage()); - } + + mExistNotificationIds = new HashMap(); + mExistReplaceIds = new HashMap(); } - private void unregisterReceiver() { - if (mNotificationCloseReceiver != null) { - mView.getActivity().unregisterReceiver(mNotificationCloseReceiver); - mNotificationCloseReceiver = null; - } + private static String getCategoryFromNotificationId(int id) { + return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id; + } + + @Override + public void setBridge(XWalkContentsClientBridge bridge) { + mBridge = bridge; } @Override @@ -128,75 +109,94 @@ public boolean maybeHandleIntent(Intent intent) { @Override @SuppressWarnings("deprecation") public void updateNotificationIcon(int notificationId, Bitmap icon) { - Notification.Builder builder = mExistNotificationIds.get(notificationId); - if (builder != null) { - int originalWidth = icon.getWidth(); - int originalHeight = icon.getHeight(); - if (originalWidth == 0 || originalHeight == 0) - return; - int targetWidth = mContext.getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_width); - int targetHeight = mContext.getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_height); - if (originalWidth > targetWidth && originalHeight > targetHeight) { - if (originalWidth * targetHeight > originalHeight * targetWidth) { - targetHeight = originalHeight * targetWidth / originalWidth; - } else { - targetWidth = originalWidth * targetHeight / originalHeight; - } - } - builder.setLargeIcon( - Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true)); - Notification notification; - if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { - notification = builder.build(); + WebNotification webNotification = mExistNotificationIds.get(notificationId); + if (webNotification == null) { + return; + } + + int originalWidth = icon.getWidth(); + int originalHeight = icon.getHeight(); + if (originalWidth == 0 || originalHeight == 0) { + return; + } + + int targetWidth = mContext.getResources().getDimensionPixelSize( + android.R.dimen.notification_large_icon_width); + int targetHeight = mContext.getResources().getDimensionPixelSize( + android.R.dimen.notification_large_icon_height); + + if (originalWidth > targetWidth && originalHeight > targetHeight) { + if (originalWidth * targetHeight > originalHeight * targetWidth) { + targetHeight = originalHeight * targetWidth / originalWidth; } else { - notification = builder.getNotification(); + targetWidth = originalWidth * targetHeight / originalHeight; } - doShowNotification(notificationId, notification); - mExistNotificationIds.put(notificationId, builder); } + + Notification.Builder builder = webNotification.mBuilder; + builder.setLargeIcon(Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true)); + + doShowNotification(notificationId, + VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification()); } @Override @SuppressWarnings("deprecation") - public void showNotification(String title, String message, + public void showNotification(String title, String message, String replaceId, int notificationId, long delegate) { - Context activity = mView.getActivity(); - String category = getCategoryFromNotificationId(notificationId); - Intent clickIntent = new Intent(activity, activity.getClass()); - clickIntent.setAction(activity.getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX); - clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId); - clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, delegate); - clickIntent.setFlags( - Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_SINGLE_TOP); - clickIntent.addCategory(category); - PendingIntent pendingClickIntent = PendingIntent.getActivity(activity, - 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT); - Intent closeIntent = - new Intent(activity.getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX); - closeIntent.putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId); - closeIntent.putExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, delegate); - closeIntent.addCategory(category); - PendingIntent pendingCloseIntent = PendingIntent.getBroadcast(activity, - 0, closeIntent, PendingIntent.FLAG_UPDATE_CURRENT); - Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext()) - .setContentIntent(pendingClickIntent) - .setDeleteIntent(pendingCloseIntent); - int iconRes = mContext.getApplicationInfo().icon; - if (iconRes == 0) iconRes = android.R.drawable.sym_def_app_icon; - builder = builder.setContentText(message) - .setContentTitle(title) - .setSmallIcon(iconRes) - .setAutoCancel(true); - Notification notification; - if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { - notification = builder.build(); + Notification.Builder builder; + + if (!replaceId.isEmpty() && mExistReplaceIds.containsKey(replaceId)) { + WebNotification webNotification = mExistReplaceIds.get(replaceId); + notificationId = webNotification.mNotificationId; + builder = webNotification.mBuilder; + builder.setNumber(++webNotification.mMessageNum); } else { - notification = builder.getNotification(); + builder = new Notification.Builder(mContext.getApplicationContext()) + .setAutoCancel(true); + + WebNotification webNotification = new WebNotification(); + webNotification.mNotificationId = notificationId; + webNotification.mReplaceId = replaceId; + webNotification.mBuilder = builder; + + mExistNotificationIds.put(notificationId, webNotification); + if (!replaceId.isEmpty()) { + mExistReplaceIds.put(replaceId, webNotification); + } } - doShowNotification(notificationId, notification); - mExistNotificationIds.put(notificationId, builder); + + builder.setContentTitle(title); + builder.setContentText(message); + + int iconRes = mContext.getApplicationInfo().icon; + if (iconRes == 0) { + iconRes = android.R.drawable.sym_def_app_icon; + } + builder.setSmallIcon(iconRes); + + Context activity = mView.getActivity(); + String category = getCategoryFromNotificationId(notificationId); + + Intent clickIntent = new Intent(activity, activity.getClass()) + .setAction(activity.getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX) + .putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId) + .putExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, delegate) + .setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_SINGLE_TOP) + .addCategory(category); + + Intent closeIntent = new Intent(activity.getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX) + .putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId) + .putExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, delegate) + .addCategory(category); + + builder.setContentIntent(PendingIntent.getActivity( + activity, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + builder.setDeleteIntent(PendingIntent.getBroadcast( + activity, 0, closeIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + + doShowNotification(notificationId, + VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification()); notificationChanged(); onNotificationShown(notificationId, delegate); } @@ -207,34 +207,83 @@ public void cancelNotification(int notificationId, long delegate) { onNotificationClose(notificationId, false, delegate); } - public void doShowNotification(int id, Notification notification) { - mNotificationManager.notify(id, notification); + public void doShowNotification(int notificationId, Notification notification) { + mNotificationManager.notify(notificationId, notification); } public void onNotificationShown(int notificationId, long delegate) { - if (mExistNotificationIds.containsKey(notificationId) && mBridge != null) { + WebNotification webNotification = mExistNotificationIds.get(notificationId); + if (webNotification == null) { + return; + } + + if (mBridge != null) { mBridge.notificationDisplayed(delegate); } } public void onNotificationClick(int notificationId, long delegate) { - if (mExistNotificationIds.containsKey(notificationId)) { - mExistNotificationIds.remove(notificationId); - notificationChanged(); - if (mBridge != null) { - mBridge.notificationClicked(notificationId, delegate); - } + WebNotification webNotification = mExistNotificationIds.get(notificationId); + if (webNotification == null) { + return; + } + + mExistNotificationIds.remove(notificationId); + mExistReplaceIds.remove(webNotification.mReplaceId); + + notificationChanged(); + if (mBridge != null) { + mBridge.notificationClicked(notificationId, delegate); } } public void onNotificationClose( int notificationId, boolean byUser, long delegate) { - if (mExistNotificationIds.containsKey(notificationId)) { - mExistNotificationIds.remove(notificationId); - notificationChanged(); - if (mBridge != null) { - mBridge.notificationClosed(notificationId, byUser, delegate); - } + WebNotification webNotification = mExistNotificationIds.get(notificationId); + if (webNotification == null) { + return; + } + + mExistNotificationIds.remove(notificationId); + mExistReplaceIds.remove(webNotification.mReplaceId); + + notificationChanged(); + if (mBridge != null) { + mBridge.notificationClosed(notificationId, byUser, delegate); + } + } + + private void notificationChanged() { + if (mExistNotificationIds.isEmpty()) { + Log.i(TAG, "notifications are all cleared," + + "unregister broadcast receiver for close pending intent"); + } else { + registerReceiver(); + } + } + + private void registerReceiver() { + IntentFilter filter = new IntentFilter( + mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX); + + for(Integer id : mExistNotificationIds.keySet()) { + filter.addCategory(getCategoryFromNotificationId(id)); + } + + try { + mView.getActivity().registerReceiver(mNotificationCloseReceiver, filter); + } catch (AndroidRuntimeException e) { + //FIXME(wang16): The exception will happen when there are multiple xwalkviews in one activity. + // Remove it after notification service supports multi-views. + mNotificationCloseReceiver = null; + Log.w(TAG, e.getLocalizedMessage()); + } + } + + private void unregisterReceiver() { + if (mNotificationCloseReceiver != null) { + mView.getActivity().unregisterReceiver(mNotificationCloseReceiver); + mNotificationCloseReceiver = null; } } }