Bug 679966, part 1: Add vibrator support for android. r=blassey,cjones

This commit is contained in:
Chris Jones 2011-09-30 00:00:48 -07:00
parent 5dec9d83e1
commit dc33fffa65
20 changed files with 586 additions and 23 deletions

View File

@ -64,6 +64,7 @@ EXPORTS = \
nsStubMutationObserver.h \
nsTextFragment.h \
mozAutoDocUpdate.h \
nsFrameMessageManager.h \
$(NULL)
EXPORTS_NAMESPACES = mozilla/dom

View File

@ -49,6 +49,7 @@ LIBXUL_LIBRARY = 1
EXPORTS = \
nsEventStateManager.h \
nsEventListenerManager.h \
nsDOMEventTargetHelper.h \
$(NULL)
CPPSRCS = \

View File

@ -233,6 +233,7 @@ ContentChild* ContentChild::sSingleton;
ContentChild::ContentChild()
#ifdef ANDROID
: mScreenSize(0, 0)
, mID(PRUint64(-1))
#endif
{
}
@ -803,5 +804,15 @@ ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID)
return true;
}
bool
ContentChild::RecvSetID(const PRUint64 &id)
{
if (mID != PRUint64(-1)) {
NS_WARNING("Setting content child's ID twice?");
}
mID = id;
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -168,6 +168,7 @@ public:
virtual bool RecvCycleCollect();
virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID);
virtual bool RecvSetID(const PRUint64 &id);
#ifdef ANDROID
gfxIntSize GetScreenSize() { return mScreenSize; }
@ -177,6 +178,8 @@ public:
// cache the value
nsString &GetIndexedDBPath();
PRUint64 GetID() { return mID; }
private:
NS_OVERRIDE
virtual void ActorDestroy(ActorDestroyReason why);
@ -198,6 +201,15 @@ private:
AppInfo mAppInfo;
/**
* An ID unique to the process containing our corresponding
* content parent.
*
* We expect our content parent to set this ID immediately after opening a
* channel to us.
*/
PRUint64 mID;
static ContentChild* sSingleton;
DISALLOW_EVIL_CONSTRUCTORS(ContentChild);

View File

@ -162,6 +162,9 @@ MemoryReportRequestParent::~MemoryReportRequestParent()
nsTArray<ContentParent*>* ContentParent::gContentParents;
// The first content child has ID 1, so the chrome process can have ID 0.
static PRUint64 gContentChildID = 1;
ContentParent*
ContentParent::GetNewOrUsed()
{
@ -424,6 +427,7 @@ ContentParent::ContentParent()
mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content);
mSubprocess->AsyncLaunch();
Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle());
unused << SendSetID(gContentChildID++);
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
nsChromeRegistryChrome* chromeRegistry =

View File

@ -147,6 +147,8 @@ child:
AppInfo(nsCString version, nsCString buildID);
SetID(PRUint64 id);
parent:
PAudio(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat);

View File

@ -303,6 +303,14 @@ GetTabChildFrom(nsIPresShell* aPresShell)
return GetTabChildFrom(docShell);
}
inline TabChild*
GetTabChildFrom(nsIDOMWindow* aWindow)
{
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
return GetTabChildFrom(docShell);
}
}
}

View File

@ -91,6 +91,7 @@ TabParent::TabParent()
, mIMECompositionEnding(false)
, mIMESeqno(0)
, mDPI(0)
, mActive(false)
{
}
@ -217,15 +218,23 @@ TabParent::UpdateDimensions(const nsRect& rect, const nsIntSize& size)
void
TabParent::Activate()
{
mActive = true;
unused << SendActivate();
}
void
TabParent::Deactivate()
{
mActive = false;
unused << SendDeactivate();
}
bool
TabParent::Active()
{
return mActive;
}
NS_IMETHODIMP
TabParent::Init(nsIDOMWindow *window)
{

View File

@ -131,6 +131,13 @@ public:
void UpdateDimensions(const nsRect& rect, const nsIntSize& size);
void Activate();
void Deactivate();
/**
* Is this object active? That is, was Activate() called more recently than
* Deactivate()?
*/
bool Active();
void SendMouseEvent(const nsAString& aType, float aX, float aY,
PRInt32 aButton, PRInt32 aClickCount,
PRInt32 aModifiers, bool aIgnoreRootScrollFrame);
@ -221,6 +228,7 @@ protected:
PRUint32 mIMESeqno;
float mDPI;
bool mActive;
private:
already_AddRefed<nsFrameLoader> GetFrameLoader() const;

View File

@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>

View File

@ -1043,6 +1043,22 @@ public class GeckoAppShell
HapticFeedbackConstants.VIRTUAL_KEY);
}
private static Vibrator vibrator() {
return (Vibrator) GeckoApp.surfaceView.getContext().getSystemService(Context.VIBRATOR_SERVICE);
}
public static void vibrate(long milliseconds) {
vibrator().vibrate(milliseconds);
}
public static void vibrate(long[] pattern, int repeat) {
vibrator().vibrate(pattern, repeat);
}
public static void cancelVibrate() {
vibrator().cancel();
}
public static void showInputMethodPicker() {
InputMethodManager imm = (InputMethodManager) GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showInputMethodPicker();

View File

@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: sw=2 ts=8 et ft=cpp : */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -38,10 +38,24 @@
* ***** END LICENSE BLOCK ***** */
#include "Hal.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/Util.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "mozilla/Observer.h"
#include "nsIDOMDocument.h"
#include "nsIDOMWindow.h"
#include "nsPIDOMWindow.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "nsIWebNavigation.h"
#include "nsITabChild.h"
#include "nsIDocShell.h"
using namespace mozilla::dom;
using namespace mozilla::services;
#define PROXY_IF_SANDBOXED(_call) \
do { \
@ -55,25 +69,215 @@
namespace mozilla {
namespace hal {
static void
PRLogModuleInfo *sHalLog = PR_LOG_DEFINE("hal");
namespace {
void
AssertMainThread()
{
MOZ_ASSERT(NS_IsMainThread());
}
static bool
bool
InSandbox()
{
return GeckoProcessType_Content == XRE_GetProcessType();
}
void
Vibrate(const nsTArray<uint32>& pattern)
bool
WindowIsActive(nsIDOMWindow *window)
{
AssertMainThread();
PROXY_IF_SANDBOXED(Vibrate(pattern));
NS_ENSURE_TRUE(window, false);
nsCOMPtr<nsIDOMDocument> doc;
window->GetDocument(getter_AddRefs(doc));
NS_ENSURE_TRUE(doc, false);
bool hidden = true;
doc->GetMozHidden(&hidden);
return !hidden;
}
nsAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
// This observer makes sure we delete gLastIDToVibrate, so we don't
// leak.
class ShutdownObserver : public nsIObserver
{
public:
ShutdownObserver() {}
virtual ~ShutdownObserver() {}
NS_DECL_ISUPPORTS
NS_IMETHOD Observe(nsISupports *subject, const char *aTopic,
const PRUnichar *aData)
{
MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
gLastIDToVibrate = nsnull;
return NS_OK;
}
};
NS_IMPL_ISUPPORTS1(ShutdownObserver, nsIObserver);
void InitLastIDToVibrate()
{
gLastIDToVibrate = new WindowIdentifier::IDArrayType();
nsCOMPtr<nsIObserverService> observerService = GetObserverService();
if (!observerService) {
NS_WARNING("Could not get observer service!");
return;
}
ShutdownObserver *obs = new ShutdownObserver();
observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
}
} // anonymous namespace
WindowIdentifier::WindowIdentifier()
: mWindow(nsnull)
, mIsEmpty(true)
{
}
WindowIdentifier::WindowIdentifier(nsIDOMWindow *window)
: mWindow(window)
, mIsEmpty(false)
{
mID.AppendElement(GetWindowID());
}
WindowIdentifier::WindowIdentifier(nsCOMPtr<nsIDOMWindow> &window)
: mWindow(window)
, mIsEmpty(false)
{
mID.AppendElement(GetWindowID());
}
WindowIdentifier::WindowIdentifier(const nsTArray<uint64> &id, nsIDOMWindow *window)
: mID(id)
, mWindow(window)
, mIsEmpty(false)
{
mID.AppendElement(GetWindowID());
}
WindowIdentifier::WindowIdentifier(const WindowIdentifier &other)
: mID(other.mID)
, mWindow(other.mWindow)
, mIsEmpty(other.mIsEmpty)
{
}
const InfallibleTArray<uint64>&
WindowIdentifier::AsArray() const
{
MOZ_ASSERT(!mIsEmpty);
return mID;
}
bool
WindowIdentifier::HasTraveledThroughIPC() const
{
MOZ_ASSERT(!mIsEmpty);
return mID.Length() >= 2;
}
void
WindowIdentifier::AppendProcessID()
{
MOZ_ASSERT(!mIsEmpty);
mID.AppendElement(ContentChild::GetSingleton()->GetID());
}
uint64
WindowIdentifier::GetWindowID() const
{
MOZ_ASSERT(!mIsEmpty);
nsCOMPtr<nsPIDOMWindow> pidomWindow = do_QueryInterface(mWindow);
if (!pidomWindow) {
return uint64(-1);
}
return pidomWindow->WindowID();
}
nsIDOMWindow*
WindowIdentifier::GetWindow() const
{
MOZ_ASSERT(!mIsEmpty);
return mWindow;
}
void
Vibrate(const nsTArray<uint32>& pattern, const WindowIdentifier &id)
{
AssertMainThread();
// Only active windows may start vibrations. If |id| hasn't gone
// through the IPC layer -- that is, if our caller is the outside
// world, not hal_proxy -- check whether the window is active. If
// |id| has gone through IPC, don't check the window's visibility;
// only the window corresponding to the bottommost process has its
// visibility state set correctly.
if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
HAL_LOG(("Vibrate: Window is inactive, dropping vibrate."));
return;
}
if (InSandbox()) {
hal_sandbox::Vibrate(pattern, id);
}
else {
if (!gLastIDToVibrate)
InitLastIDToVibrate();
*gLastIDToVibrate = id.AsArray();
HAL_LOG(("Vibrate: Forwarding to hal_impl."));
// hal_impl doesn't need |id|. Send it an empty id, which will
// assert if it's used.
hal_impl::Vibrate(pattern, WindowIdentifier());
}
}
void
CancelVibrate(const WindowIdentifier &id)
{
AssertMainThread();
// Although only active windows may start vibrations, a window may
// cancel its own vibration even if it's no longer active.
//
// After a window is marked as inactive, it sends a CancelVibrate
// request. We want this request to cancel a playing vibration
// started by that window, so we certainly don't want to reject the
// cancellation request because the window is now inactive.
//
// But it could be the case that, after this window became inactive,
// some other window came along and started a vibration. We don't
// want this window's cancellation request to cancel that window's
// actively-playing vibration!
//
// To solve this problem, we keep track of the id of the last window
// to start a vibration, and only accepts cancellation requests from
// the same window. All other cancellation requests are ignored.
if (InSandbox()) {
hal_sandbox::CancelVibrate(id);
}
else if (*gLastIDToVibrate == id.AsArray()) {
// Don't forward our ID to hal_impl. It doesn't need it, and we
// don't want it to be tempted to read it. The empty identifier
// will assert if it's used.
HAL_LOG(("CancelVibrate: Forwarding to hal_impl."));
hal_impl::CancelVibrate(WindowIdentifier());
}
}
class BatteryObserversManager
{
public:

143
hal/Hal.h
View File

@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: sw=2 ts=8 et ft=cpp : */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -43,9 +43,120 @@
#include "base/basictypes.h"
#include "mozilla/Types.h"
#include "nsTArray.h"
#include "nsCOMPtr.h"
#include "nsIDOMWindow.h"
#include "prlog.h"
#include "mozilla/dom/battery/Types.h"
#ifndef MOZ_HAL_NAMESPACE
namespace mozilla {
namespace dom {
class TabChild;
class PBrowserChild;
}
}
// Only include this hunk of code once, and include it before
// HalImpl.h and HalSandbox.h.
namespace mozilla {
namespace hal {
extern PRLogModuleInfo *sHalLog;
#define HAL_LOG(msg) PR_LOG(sHalLog, PR_LOG_DEBUG, msg)
/**
* This class serves two purposes.
*
* First, this class wraps a pointer to a window.
*
* Second, WindowIdentifier lets us uniquely identify a window across
* processes. A window exposes an ID which is unique only within its
* process. Thus to identify a window, we need to know the ID of the
* process which contains it. But the scope of a process's ID is its
* parent; that is, two processes with different parents might have
* the same ID.
*
* So to identify a window, we need its ID plus the IDs of all the
* processes in the path from the window's process to the root
* process. We throw in the IDs of the intermediate windows (a
* content window is contained in a window at each level of the
* process tree) for good measures.
*
* You can access this list of IDs by calling AsArray().
*/
class WindowIdentifier
{
public:
/**
* Create an empty WindowIdentifier. Calls to any of this object's
* public methods will assert -- an empty WindowIdentifier may be
* used only as a placeholder to code which promises not to touch
* the object.
*/
WindowIdentifier();
/**
* Copy constructor.
*/
WindowIdentifier(const WindowIdentifier& other);
/**
* Wrap the given window in a WindowIdentifier. These two
* constructors automatically grab the window's ID and append it to
* the array of IDs.
*
* Note that these constructors allow an implicit conversion to a
* WindowIdentifier.
*/
WindowIdentifier(nsIDOMWindow* window);
WindowIdentifier(nsCOMPtr<nsIDOMWindow> &window);
/**
* Create a new WindowIdentifier with the given id array and window.
* This automatically grabs the window's ID and appends it to the
* array.
*/
WindowIdentifier(const nsTArray<uint64>& id, nsIDOMWindow* window);
/**
* Get the list of window and process IDs we contain.
*/
typedef InfallibleTArray<uint64> IDArrayType;
const IDArrayType& AsArray() const;
/**
* Append the ID of the ContentChild singleton to our array of
* window/process IDs.
*/
void AppendProcessID();
/**
* Does this WindowIdentifier identify both a window and the process
* containing that window? If so, we say it has traveled through
* IPC.
*/
bool HasTraveledThroughIPC() const;
/**
* Get the window this object wraps.
*/
nsIDOMWindow* GetWindow() const;
private:
/**
* Get the ID of the window object we wrap.
*/
uint64 GetWindowID() const;
AutoInfallibleTArray<uint64, 3> mID;
nsCOMPtr<nsIDOMWindow> mWindow;
bool mIsEmpty;
};
} // namespace hal
} // namespace mozilla
// This goop plays some cpp tricks to ensure a uniform API across the
// API entry point, "sandbox" implementations (for content processes),
// and "impl" backends where the real work happens. After this runs
@ -78,8 +189,32 @@ namespace MOZ_HAL_NAMESPACE /*hal*/ {
* |pattern| is an "on" element, the next is "off", and so on.
*
* If |pattern| is empty, any in-progress vibration is canceled.
*
* Only an active window within an active tab may call Vibrate; calls
* from inactive windows and windows on inactive tabs do nothing.
*
* If you're calling hal::Vibrate from the outside world, pass an
* nsIDOMWindow* or an nsCOMPtr<nsIDOMWindow>& in place of the
* WindowIdentifier parameter. It'll be converted to a WindowIdentifier
* automatically.
*/
void Vibrate(const nsTArray<uint32>& pattern);
void Vibrate(const nsTArray<uint32>& pattern,
const hal::WindowIdentifier &id);
/**
* Cancel a vibration started by the content window identified by
* WindowIdentifier.
*
* If the window was the last window to start a vibration, the
* cancellation request will go through even if the window is not
* active.
*
* As with hal::Vibrate(), if you're calling hal::CancelVibrate from
* the outside world, pass an nsIDOMWindow* or an
* nsCOMPtr<nsIDOMWindow>&. This will automatically be converted to a
* WindowIdentifier object.
*/
void CancelVibrate(const hal::WindowIdentifier &id);
/**
* Inform the battery backend there is a new battery observer.
@ -122,8 +257,8 @@ void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
*/
void NotifyBatteryChange(const hal::BatteryInformation& aBatteryInfo);
}
}
} // namespace MOZ_HAL_NAMESPACE
} // namespace mozilla
#ifdef MOZ_DEFINED_HAL_NAMESPACE
# undef MOZ_DEFINED_HAL_NAMESPACE

View File

@ -38,12 +38,39 @@
#include "Hal.h"
#include "AndroidBridge.h"
using mozilla::hal::WindowIdentifier;
namespace mozilla {
namespace hal_impl {
void
Vibrate(const nsTArray<uint32>& pattern)
{}
Vibrate(const nsTArray<uint32> &pattern, const WindowIdentifier &)
{
// Ignore the WindowIdentifier parameter; it's here only because hal::Vibrate,
// hal_sandbox::Vibrate, and hal_impl::Vibrate all must have the same
// signature.
AndroidBridge* b = AndroidBridge::Bridge();
if (!b) {
return;
}
if (pattern.Length() == 0) {
b->CancelVibrate();
} else {
b->Vibrate(pattern);
}
}
void
CancelVibrate(const WindowIdentifier &)
{
// Ignore WindowIdentifier parameter.
AndroidBridge* b = AndroidBridge::Bridge();
if (b)
b->CancelVibrate();
}
void
EnableBatteryNotifications()

View File

@ -40,11 +40,17 @@
#include "Hal.h"
#include "mozilla/dom/battery/Constants.h"
using mozilla::hal::WindowIdentifier;
namespace mozilla {
namespace hal_impl {
void
Vibrate(const nsTArray<uint32>& pattern)
Vibrate(const nsTArray<uint32>& pattern, const hal::WindowIdentifier &)
{}
void
CancelVibrate(const hal::WindowIdentifier &)
{}
void

View File

@ -45,7 +45,11 @@ namespace mozilla {
namespace hal_impl {
void
Vibrate(const nsTArray<uint32>& pattern)
Vibrate(const nsTArray<uint32>& pattern, const hal::WindowIdentifier &)
{}
void
CancelVibrate(const hal::WindowIdentifier &)
{}
#ifndef MOZ_ENABLE_DBUS

View File

@ -38,6 +38,7 @@
* ***** END LICENSE BLOCK ***** */
include protocol PContent;
include protocol PBrowser;
namespace mozilla {
@ -57,7 +58,8 @@ child:
NotifyBatteryChange(BatteryInformation aBatteryInfo);
parent:
Vibrate(uint32[] pattern);
Vibrate(uint32[] pattern, uint64[] id, PBrowser browser);
CancelVibrate(uint64[] id, PBrowser browser);
EnableBatteryNotifications();
DisableBatteryNotifications();

View File

@ -41,6 +41,8 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/hal_sandbox/PHalChild.h"
#include "mozilla/hal_sandbox/PHalParent.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/battery/Types.h"
#include "mozilla/Observer.h"
#include "mozilla/unused.h"
@ -63,10 +65,25 @@ Hal()
}
void
Vibrate(const nsTArray<uint32>& pattern)
Vibrate(const nsTArray<uint32>& pattern, const WindowIdentifier &id)
{
HAL_LOG(("Vibrate: Sending to parent process."));
AutoInfallibleTArray<uint32, 8> p(pattern);
Hal()->SendVibrate(p);
WindowIdentifier newID(id);
newID.AppendProcessID();
Hal()->SendVibrate(p, newID.AsArray(), GetTabChildFrom(newID.GetWindow()));
}
void
CancelVibrate(const WindowIdentifier &id)
{
HAL_LOG(("CancelVibrate: Sending to parent process."));
WindowIdentifier newID(id);
newID.AppendProcessID();
Hal()->SendCancelVibrate(newID.AsArray(), GetTabChildFrom(newID.GetWindow()));
}
void
@ -91,11 +108,41 @@ class HalParent : public PHalParent
, public BatteryObserver {
public:
NS_OVERRIDE virtual bool
RecvVibrate(const InfallibleTArray<unsigned int>& pattern) {
RecvVibrate(const InfallibleTArray<unsigned int>& pattern,
const InfallibleTArray<uint64> &id,
PBrowserParent *browserParent)
{
// Check whether browserParent is active. We should have already
// checked that the corresponding window is active, but this check
// isn't redundant. A window may be inactive in an active
// browser. And a window is not notified synchronously when it's
// deactivated, so the window may think it's active when the tab
// is actually inactive.
TabParent *tabParent = static_cast<TabParent*>(browserParent);
if (!tabParent->Active()) {
HAL_LOG(("RecvVibrate: Tab is not active. Cancelling."));
return true;
}
// Forward to hal::, not hal_impl::, because we might be a
// subprocess of another sandboxed process. The hal:: entry point
// will do the right thing.
hal::Vibrate(pattern);
nsCOMPtr<nsIDOMWindow> window =
do_QueryInterface(tabParent->GetBrowserDOMWindow());
WindowIdentifier newID(id, window);
hal::Vibrate(pattern, newID);
return true;
}
NS_OVERRIDE virtual bool
RecvCancelVibrate(const InfallibleTArray<uint64> &id,
PBrowserParent *browserParent)
{
TabParent *tabParent = static_cast<TabParent*>(browserParent);
nsCOMPtr<nsIDOMWindow> window =
do_QueryInterface(tabParent->GetBrowserDOMWindow());
WindowIdentifier newID(id, window);
hal::CancelVibrate(newID);
return true;
}

View File

@ -141,6 +141,9 @@ AndroidBridge::Init(JNIEnv *jEnv,
jShowInputMethodPicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showInputMethodPicker", "()V");
jHideProgressDialog = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideProgressDialog", "()V");
jPerformHapticFeedback = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "performHapticFeedback", "(Z)V");
jVibrate1 = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "(J)V");
jVibrateA = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "([JI)V");
jCancelVibrate = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "cancelVibrate", "()V");
jSetKeepScreenOn = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setKeepScreenOn", "(Z)V");
jIsNetworkLinkUp = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isNetworkLinkUp", "()Z");
jIsNetworkLinkKnown = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isNetworkLinkKnown", "()Z");
@ -436,9 +439,9 @@ AndroidBridge::GetHandlersForMimeType(const char *aMimeType,
bool
AndroidBridge::GetHandlersForURL(const char *aURL,
nsIMutableArray* aHandlersArray,
nsIHandlerApp **aDefaultApp,
const nsAString& aAction)
nsIMutableArray* aHandlersArray,
nsIHandlerApp **aDefaultApp,
const nsAString& aAction)
{
ALOG_BRIDGE("AndroidBridge::GetHandlersForURL");
@ -676,6 +679,62 @@ AndroidBridge::PerformHapticFeedback(bool aIsLongPress)
jPerformHapticFeedback, aIsLongPress);
}
void
AndroidBridge::Vibrate(const nsTArray<PRUint32>& aPattern)
{
ALOG_BRIDGE("AndroidBridge::Vibrate");
PRUint32 len = aPattern.Length();
if (!len) {
ALOG_BRIDGE(" invalid 0-length array");
return;
}
// It's clear if this worth special-casing, but it creates less
// java junk, so dodges the GC.
if (len == 1) {
jlong d = aPattern[0];
if (d < 0) {
ALOG_BRIDGE(" invalid vibration duration < 0");
return;
}
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jVibrate1, d);
return;
}
// First element of the array vibrate() expects is how long to wait
// *before* vibrating. For us, this is always 0.
jlongArray array = mJNIEnv->NewLongArray(len + 1);
if (!array) {
ALOG_BRIDGE(" failed to allocate array");
return;
}
jlong* elts = mJNIEnv->GetLongArrayElements(array, nsnull);
elts[0] = 0;
for (PRUint32 i = 0; i < aPattern.Length(); ++i) {
jlong d = aPattern[i];
if (d < 0) {
ALOG_BRIDGE(" invalid vibration duration < 0");
mJNIEnv->ReleaseLongArrayElements(array, elts, JNI_ABORT);
return;
}
elts[i + 1] = d;
}
mJNIEnv->ReleaseLongArrayElements(array, elts, 0);
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jVibrateA,
array, -1/*don't repeat*/);
// GC owns |array| now?
}
void
AndroidBridge::CancelVibrate()
{
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jCancelVibrate);
}
bool
AndroidBridge::IsNetworkLinkUp()
{

View File

@ -199,6 +199,9 @@ public:
void PerformHapticFeedback(bool aIsLongPress);
void Vibrate(const nsTArray<PRUint32>& aPattern);
void CancelVibrate();
void SetFullScreen(bool aFullScreen);
void ShowInputMethodPicker();
@ -355,6 +358,9 @@ protected:
jmethodID jShowInputMethodPicker;
jmethodID jHideProgressDialog;
jmethodID jPerformHapticFeedback;
jmethodID jVibrate1;
jmethodID jVibrateA;
jmethodID jCancelVibrate;
jmethodID jSetKeepScreenOn;
jmethodID jIsNetworkLinkUp;
jmethodID jIsNetworkLinkKnown;