Android: Implement context menu backend interface

Implements interface for context menu in QtActivityDelegate and
QtEmbeddedDelegate.

Task-number: QTBUG-118874
Change-Id: I799ad1aca4beb789b87830b720abf0963ca09274
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
This commit is contained in:
Petri Virkkunen 2024-03-31 15:37:31 +03:00
parent 2322c71cc7
commit 9319576a59
5 changed files with 80 additions and 20 deletions

View File

@ -43,6 +43,7 @@ set(java_sources
src/org/qtproject/qt/android/BackendRegister.java src/org/qtproject/qt/android/BackendRegister.java
src/org/qtproject/qt/android/QtWindowInterface.java src/org/qtproject/qt/android/QtWindowInterface.java
src/org/qtproject/qt/android/QtAccessibilityInterface.java src/org/qtproject/qt/android/QtAccessibilityInterface.java
src/org/qtproject/qt/android/QtMenuInterface.java
) )
qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android

View File

@ -31,8 +31,8 @@ import android.widget.PopupMenu;
import java.util.HashMap; import java.util.HashMap;
class QtActivityDelegate class QtActivityDelegate extends QtActivityDelegateBase
extends QtActivityDelegateBase implements QtWindowInterface, QtAccessibilityInterface implements QtWindowInterface, QtAccessibilityInterface, QtMenuInterface
{ {
private static final String QtTAG = "QtActivityDelegate"; private static final String QtTAG = "QtActivityDelegate";
@ -61,6 +61,8 @@ class QtActivityDelegate
(QtWindowInterface)QtActivityDelegate.this); (QtWindowInterface)QtActivityDelegate.this);
BackendRegister.registerBackend(QtAccessibilityInterface.class, BackendRegister.registerBackend(QtAccessibilityInterface.class,
(QtAccessibilityInterface)QtActivityDelegate.this); (QtAccessibilityInterface)QtActivityDelegate.this);
BackendRegister.registerBackend(QtMenuInterface.class,
(QtMenuInterface)QtActivityDelegate.this);
} }
} }
@ -70,6 +72,7 @@ class QtActivityDelegate
m_backendsRegistered = false; m_backendsRegistered = false;
BackendRegister.unregisterBackend(QtWindowInterface.class); BackendRegister.unregisterBackend(QtWindowInterface.class);
BackendRegister.unregisterBackend(QtAccessibilityInterface.class); BackendRegister.unregisterBackend(QtAccessibilityInterface.class);
BackendRegister.unregisterBackend(QtMenuInterface.class);
} }
} }
@ -290,27 +293,25 @@ class QtActivityDelegate
}); });
} }
@UsedFromNativeCode // QtMenuInterface implementation begin
@Override
public void resetOptionsMenu() public void resetOptionsMenu()
{ {
QtNative.runAction(() -> m_activity.invalidateOptionsMenu()); QtNative.runAction(() -> m_activity.invalidateOptionsMenu());
} }
@UsedFromNativeCode @Override
public void openOptionsMenu() public void openOptionsMenu()
{ {
QtNative.runAction(() -> m_activity.openOptionsMenu()); QtNative.runAction(() -> m_activity.openOptionsMenu());
} }
private boolean m_contextMenuVisible = false; @Override
public void closeContextMenu()
public void onCreatePopupMenu(Menu menu)
{ {
QtNative.fillContextMenu(menu); QtNative.runAction(() -> m_activity.closeContextMenu());
m_contextMenuVisible = true;
} }
@UsedFromNativeCode
@Override @Override
public void openContextMenu(final int x, final int y, final int w, final int h) public void openContextMenu(final int x, final int y, final int w, final int h)
{ {
@ -330,11 +331,14 @@ class QtActivityDelegate
popup.show(); popup.show();
}, 100); }, 100);
} }
// QtMenuInterface implementation end
@UsedFromNativeCode private boolean m_contextMenuVisible = false;
public void closeContextMenu()
public void onCreatePopupMenu(Menu menu)
{ {
QtNative.runAction(() -> m_activity.closeContextMenu()); QtNative.fillContextMenu(menu);
m_contextMenuVisible = true;
} }
@Override @Override

View File

@ -15,15 +15,19 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.Menu;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.PopupMenu;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
class QtEmbeddedDelegate extends QtActivityDelegateBase class QtEmbeddedDelegate extends QtActivityDelegateBase
implements QtNative.AppStateDetailsListener, QtEmbeddedViewInterface, QtWindowInterface implements QtNative.AppStateDetailsListener, QtEmbeddedViewInterface, QtWindowInterface,
QtMenuInterface
{ {
private static final String QtTAG = "QtEmbeddedDelegate";
// TODO simplistic implementation with one QtView, expand to support multiple views QTBUG-117649 // TODO simplistic implementation with one QtView, expand to support multiple views QTBUG-117649
private QtView m_view; private QtView m_view;
private QtNative.ApplicationStateDetails m_stateDetails; private QtNative.ApplicationStateDetails m_stateDetails;
@ -93,9 +97,11 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase
if (details.isStarted && !m_backendsRegistered) { if (details.isStarted && !m_backendsRegistered) {
m_backendsRegistered = true; m_backendsRegistered = true;
BackendRegister.registerBackend(QtWindowInterface.class, (QtWindowInterface)this); BackendRegister.registerBackend(QtWindowInterface.class, (QtWindowInterface)this);
BackendRegister.registerBackend(QtMenuInterface.class, (QtMenuInterface)this);
} else if (!details.isStarted && m_backendsRegistered) { } else if (!details.isStarted && m_backendsRegistered) {
m_backendsRegistered = false; m_backendsRegistered = false;
BackendRegister.unregisterBackend(QtWindowInterface.class); BackendRegister.unregisterBackend(QtWindowInterface.class);
BackendRegister.unregisterBackend(QtMenuInterface.class);
} }
} }
} }
@ -175,4 +181,36 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase
m_windowLoaded = true; m_windowLoaded = true;
} }
} }
// QtMenuInterface implementation begin
@Override
public void resetOptionsMenu() { QtNative.runAction(() -> m_activity.invalidateOptionsMenu()); }
@Override
public void openOptionsMenu() { QtNative.runAction(() -> m_activity.openOptionsMenu()); }
@Override
public void closeContextMenu() { QtNative.runAction(() -> m_activity.closeContextMenu()); }
@Override
public void openContextMenu(final int x, final int y, final int w, final int h)
{
QtLayout layout = getQtLayout();
layout.postDelayed(() -> {
final QtEditText focusedEditText = m_inputDelegate.getCurrentQtEditText();
if (focusedEditText == null) {
Log.w(QtTAG, "No focused view when trying to open context menu");
return;
}
layout.setLayoutParams(focusedEditText, new QtLayout.LayoutParams(w, h, x, y), false);
PopupMenu popup = new PopupMenu(m_activity, focusedEditText);
QtNative.fillContextMenu(popup.getMenu());
popup.setOnMenuItemClickListener(menuItem ->
m_activity.onContextItemSelected(menuItem));
popup.setOnDismissListener(popupMenu ->
m_activity.onContextMenuClosed(popupMenu.getMenu()));
popup.show();
}, 100);
}
// QtMenuInterface implementation end
} }

View File

@ -0,0 +1,11 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android;
@UsedFromNativeCode
interface QtMenuInterface {
void resetOptionsMenu();
void openOptionsMenu();
void closeContextMenu();
void openContextMenu(final int x, final int y, final int w, final int h);
}

View File

@ -20,6 +20,8 @@ QT_BEGIN_NAMESPACE
using namespace QtAndroid; using namespace QtAndroid;
Q_DECLARE_JNI_CLASS(QtMenuInterface, "org/qtproject/qt/android/QtMenuInterface");
namespace QtAndroidMenu namespace QtAndroidMenu
{ {
static QList<QAndroidPlatformMenu *> pendingContextMenus; static QList<QAndroidPlatformMenu *> pendingContextMenus;
@ -44,12 +46,14 @@ namespace QtAndroidMenu
void resetMenuBar() void resetMenuBar()
{ {
qtActivityDelegate().callMethod<void>("resetOptionsMenu"); AndroidBackendRegister *reg = QtAndroid::backendRegister();
reg->callInterface<QtJniTypes::QtMenuInterface, void>("resetOptionsMenu");
} }
void openOptionsMenu() void openOptionsMenu()
{ {
qtActivityDelegate().callMethod<void>("openOptionsMenu"); AndroidBackendRegister *reg = QtAndroid::backendRegister();
reg->callInterface<QtJniTypes::QtMenuInterface, void>("openOptionsMenu");
} }
void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect) void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect)
@ -59,16 +63,18 @@ namespace QtAndroidMenu
pendingContextMenus.append(visibleMenu); pendingContextMenus.append(visibleMenu);
visibleMenu = menu; visibleMenu = menu;
menu->aboutToShow(); menu->aboutToShow();
qtActivityDelegate().callMethod<void>("openContextMenu", AndroidBackendRegister *reg = QtAndroid::backendRegister();
anchorRect.x(), anchorRect.y(), reg->callInterface<QtJniTypes::QtMenuInterface, void>("openContextMenu", anchorRect.x(),
anchorRect.width(), anchorRect.height()); anchorRect.y(), anchorRect.width(),
anchorRect.height());
} }
void hideContextMenu(QAndroidPlatformMenu *menu) void hideContextMenu(QAndroidPlatformMenu *menu)
{ {
QMutexLocker lock(&visibleMenuMutex); QMutexLocker lock(&visibleMenuMutex);
if (visibleMenu == menu) { if (visibleMenu == menu) {
qtActivityDelegate().callMethod<void>("closeContextMenu"); AndroidBackendRegister *reg = QtAndroid::backendRegister();
reg->callInterface<QtJniTypes::QtMenuInterface, void>("closeContextMenu");
pendingContextMenus.clear(); pendingContextMenus.clear();
} else { } else {
pendingContextMenus.removeOne(menu); pendingContextMenus.removeOne(menu);