diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java index e9252d6f30c4..8e799f99d407 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java @@ -32,6 +32,7 @@ import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.util.BitmapUtils; import org.mozilla.gecko.util.HardwareCodecCapabilityUtils; import org.mozilla.gecko.util.HardwareUtils; +import org.mozilla.gecko.util.InputDeviceUtils; import org.mozilla.gecko.util.IOUtils; import org.mozilla.gecko.util.ProxySelector; import org.mozilla.gecko.util.StrictModeContext; @@ -1879,14 +1880,6 @@ public class GeckoAppShell return result; } - private static boolean isPointerTypeDevice(InputDevice inputDevice) { - int sources = inputDevice.getSources(); - return (sources & (InputDevice.SOURCE_CLASS_JOYSTICK | - InputDevice.SOURCE_CLASS_POINTER | - InputDevice.SOURCE_CLASS_POSITION | - InputDevice.SOURCE_CLASS_TRACKBALL)) != 0; - } - @WrapForJNI(calledFrom = "gecko") // For any-pointer and any-hover media queries features. private static int getAllPointerCapabilities() { @@ -1895,7 +1888,7 @@ public class GeckoAppShell for (int deviceId : InputDevice.getDeviceIds()) { InputDevice inputDevice = InputDevice.getDevice(deviceId); if (inputDevice == null || - !isPointerTypeDevice(inputDevice)) { + !InputDeviceUtils.isPointerTypeDevice(inputDevice)) { continue; } @@ -1913,7 +1906,7 @@ public class GeckoAppShell for (int deviceId : InputDevice.getDeviceIds()) { InputDevice inputDevice = InputDevice.getDevice(deviceId); if (inputDevice == null || - !isPointerTypeDevice(inputDevice)) { + !InputDeviceUtils.isPointerTypeDevice(inputDevice)) { continue; } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputDeviceListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputDeviceListener.java new file mode 100644 index 000000000000..24c8e33c6181 --- /dev/null +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputDeviceListener.java @@ -0,0 +1,88 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko; + +import android.content.Context; +import android.hardware.input.InputManager; +import android.util.Log; +import android.view.InputDevice; +import org.mozilla.gecko.annotation.WrapForJNI; +import org.mozilla.gecko.util.InputDeviceUtils; +import org.mozilla.gecko.util.ThreadUtils; + +public class GeckoInputDeviceListener + implements InputManager.InputDeviceListener { + private static final String LOGTAG = "GeckoInputDeviceListener"; + + private static final GeckoInputDeviceListener listenerInstance = new GeckoInputDeviceListener(); + + private boolean initialized; + private InputManager mInputManager; + + public static GeckoInputDeviceListener getInstance() { + return listenerInstance; + } + + private GeckoInputDeviceListener() { + } + + public synchronized void initialize(final Context context) { + if (initialized) { + Log.w(LOGTAG, "Already initialized!"); + return; + } + mInputManager = (InputManager) + context.getSystemService(Context.INPUT_SERVICE); + mInputManager.registerInputDeviceListener(listenerInstance, ThreadUtils.getUiHandler()); + initialized = true; + } + + public synchronized void shutdown() { + if (!initialized) { + Log.w(LOGTAG, "Already shut down!"); + return; + } + + if (mInputManager != null) { + Log.e(LOGTAG, "mInputManager should be valid!"); + return; + } + + mInputManager.unregisterInputDeviceListener(listenerInstance); + initialized = false; + mInputManager = null; + } + + @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") + private static native void onDeviceChanged(); + + private void notifyDeviceChanged(int deviceId) { + InputDevice device = InputDevice.getDevice(deviceId); + if (device == null || + !InputDeviceUtils.isPointerTypeDevice(device)) { + return; + } + onDeviceChanged(); + } + + @Override + public void onInputDeviceAdded(int deviceId) { + notifyDeviceChanged(deviceId); + } + + @Override + public void onInputDeviceRemoved(int deviceId) { + // Call onDeviceChanged directly without checking device source types + // since we can no longer get a valid `InputDevice` in the case of + // device removal. + onDeviceChanged(); + } + + @Override + public void onInputDeviceChanged(int deviceId) { + notifyDeviceChanged(deviceId); + } +} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/InputDeviceUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/InputDeviceUtils.java new file mode 100644 index 000000000000..f8dba5d4aaa0 --- /dev/null +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/InputDeviceUtils.java @@ -0,0 +1,18 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.util; + +import android.view.InputDevice; + +public class InputDeviceUtils { + public static boolean isPointerTypeDevice(InputDevice inputDevice) { + int sources = inputDevice.getSources(); + return (sources & (InputDevice.SOURCE_CLASS_JOYSTICK | + InputDevice.SOURCE_CLASS_POINTER | + InputDevice.SOURCE_CLASS_POSITION | + InputDevice.SOURCE_CLASS_TRACKBALL)) != 0; + } +} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java index 8358efdecc0f..590d51121a25 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java @@ -23,6 +23,7 @@ import android.util.Log; import org.mozilla.gecko.EventDispatcher; import org.mozilla.gecko.GeckoAppShell; +import org.mozilla.gecko.GeckoInputDeviceListener; import org.mozilla.gecko.GeckoScreenOrientation; import org.mozilla.gecko.GeckoThread; import org.mozilla.gecko.PrefsHelper; @@ -223,6 +224,8 @@ public final class GeckoRuntime implements Parcelable { // Initialize the system ClipboardManager by accessing it on the main thread. GeckoAppShell.getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE); + + GeckoInputDeviceListener.getInstance().initialize(context); return true; } @@ -278,6 +281,7 @@ public final class GeckoRuntime implements Parcelable { Log.d(LOGTAG, "shutdown"); } + GeckoInputDeviceListener.getInstance().shutdown(); GeckoThread.forceQuit(); } diff --git a/widget/android/GeckoInputDeviceListener.h b/widget/android/GeckoInputDeviceListener.h new file mode 100644 index 000000000000..bad5967af9cf --- /dev/null +++ b/widget/android/GeckoInputDeviceListener.h @@ -0,0 +1,61 @@ +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GeckoInputDeviceListener_h +#define GeckoInputDeviceListener_h + +#include "GeneratedJNINatives.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +#include "nsIWindowMediator.h" +#include "nsPIDOMWindow.h" +#include "mozilla/Assertions.h" + +namespace mozilla { + +class GeckoInputDeviceListener final + : public java::GeckoInputDeviceListener::Natives +{ + GeckoInputDeviceListener() = delete; + +public: + static void + OnDeviceChanged() + { + MOZ_ASSERT(NS_IsMainThread()); + + // Iterate all toplevel windows + nsCOMPtr windowMediator = + do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); + NS_ENSURE_TRUE_VOID(windowMediator); + + nsCOMPtr windowEnumerator; + windowMediator->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator)); + NS_ENSURE_TRUE_VOID(windowEnumerator); + + bool more; + while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) { + nsCOMPtr elements; + if (NS_FAILED(windowEnumerator->GetNext(getter_AddRefs(elements)))) { + break; + } + + if (nsCOMPtr window = do_QueryInterface(elements)) { + if (window->Closed()) { + continue; + } + if (nsIDocument* doc = window->GetExtantDoc()) { + if (nsIPresShell* presShell = doc->GetShell()) { + presShell->ThemeChanged(); + } + } + } + } + } +}; + +} // namespace mozilla + +#endif // GeckoInputDeviceListener_h diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 47ab42624a1a..3caa1dd88de0 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -64,6 +64,7 @@ #include "GeckoNetworkManager.h" #include "GeckoProcessManager.h" #include "GeckoScreenOrientation.h" +#include "GeckoInputDeviceListener.h" #include "GeckoVRManager.h" #include "PrefsHelper.h" #include "fennec/MemoryMonitor.h" @@ -427,6 +428,7 @@ nsAppShell::nsAppShell() GeckoAppShellSupport::Init(); GeckoThreadSupport::Init(); mozilla::GeckoBatteryManager::Init(); + mozilla::GeckoInputDeviceListener::Init(); mozilla::GeckoNetworkManager::Init(); mozilla::GeckoProcessManager::Init(); mozilla::GeckoScreenOrientation::Init();