Android: don't call delegates outside of the Activity

The delegate classes shouldn't be used outside of the Activity/Service
classes, since they're practically private implementation, so don't use
them anywhere outside Activity/Service.

Since Qt Android apps still mainly support having one QtActivity/
QtService, QtNative heavily uses those objects to do various operations.
For that reason, we still need to use the delegate there. The aim is
to change that in future patches and do the operations where they make
more sense for example directly under QtActivityBase/QtActivityDelegate
or Service counterpart.

The QtServiceDelegate is used no where and have no special
implementation, so it's removed here.

Task-number: QTBUG-118077
Change-Id: I5e106318169be19fec8163e8e500ee573af0e1bc
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Assam Boudjelthia 2023-09-17 23:13:04 +03:00
parent 19baa077d9
commit f5e2302851
15 changed files with 351 additions and 460 deletions

View File

@ -12,7 +12,6 @@ set(java_sources
src/org/qtproject/qt/android/QtActivityBase.java
src/org/qtproject/qt/android/QtServiceBase.java
src/org/qtproject/qt/android/QtActivityDelegate.java
src/org/qtproject/qt/android/QtServiceDelegate.java
src/org/qtproject/qt/android/QtInputDelegate.java
src/org/qtproject/qt/android/QtLoader.java
src/org/qtproject/qt/android/QtActivityLoader.java

View File

@ -173,7 +173,7 @@ public class QtActivityBase extends Activity
super.onDestroy();
if (m_delegate.isQuitApp()) {
QtNative.terminateQt();
QtNative.setActivity(null, null);
QtNative.setActivity(null);
QtNative.m_qtThread.exit();
System.exit(0);
}
@ -325,6 +325,11 @@ public class QtActivityBase extends Activity
QtNative.sendRequestPermissionsResult(requestCode, permissions, grantResults);
}
public void hideSplashScreen(final int duration)
{
m_delegate.hideSplashScreen(duration);
}
QtActivityDelegate getActivityDelegate()
{
return m_delegate;

View File

@ -6,7 +6,6 @@
package org.qtproject.qt.android;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
@ -27,7 +26,6 @@ import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsetsController;
import android.view.inputmethod.InputMethodManager;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.PopupMenu;
@ -56,34 +54,36 @@ public class QtActivityDelegate
private QtAccessibilityDelegate m_accessibilityDelegate = null;
private final QtDisplayManager m_displayManager = new QtDisplayManager();
private QtInputDelegate.KeyboardVisibilityListener m_keyboardVisibilityListener =
new QtInputDelegate.KeyboardVisibilityListener() {
@Override
public void onKeyboardVisibilityChange() {
m_displayManager.updateFullScreen(m_activity);
}
};
private final QtInputDelegate m_inputDelegate = new QtInputDelegate(m_keyboardVisibilityListener);
private QtInputDelegate m_inputDelegate = null;
QtActivityDelegate(Activity activity)
{
m_activity = activity;
QtNative.setActivity(m_activity, this);
QtNative.setActivity(m_activity);
setActionBarVisibility(false);
m_displayManager.registerDisplayListener(m_activity, m_layout);
QtInputDelegate.KeyboardVisibilityListener keyboardVisibilityListener =
new QtInputDelegate.KeyboardVisibilityListener() {
@Override
public void onKeyboardVisibilityChange() {
m_displayManager.updateFullScreen(m_activity);
}
};
m_inputDelegate = new QtInputDelegate(m_activity, keyboardVisibilityListener);
try {
m_inputDelegate.setSoftInputMode(m_activity.getPackageManager()
.getActivityInfo(m_activity.getComponentName(), 0).softInputMode);
PackageManager pm = m_activity.getPackageManager();
ActivityInfo activityInfo = pm.getActivityInfo(m_activity.getComponentName(), 0);
m_inputDelegate.setSoftInputMode(activityInfo.softInputMode);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
m_displayManager.registerDisplayListener(m_activity, m_layout);
}
QtDisplayManager displayManager()
{
QtDisplayManager displayManager() {
return m_displayManager;
}
@ -147,7 +147,7 @@ public class QtActivityDelegate
try {
// set new activity
m_activity = activity;
QtNative.setActivity(m_activity, this);
QtNative.setActivity(m_activity);
// update the new activity content view to old layout
ViewGroup layoutParent = (ViewGroup) m_layout.getParent();
@ -220,8 +220,6 @@ public class QtActivityDelegate
e.printStackTrace();
}
m_inputDelegate.setEditText(new QtEditText(m_activity));
m_inputDelegate.setInputMethodManager((InputMethodManager)m_activity.getSystemService(Context.INPUT_METHOD_SERVICE));
m_surfaces = new HashMap<Integer, QtSurface>();
m_nativeViews = new HashMap<Integer, View>();
m_activity.registerForContextMenu(m_layout);
@ -273,31 +271,40 @@ public class QtActivityDelegate
public void hideSplashScreen(final int duration)
{
if (m_splashScreen == null)
return;
if (duration <= 0) {
m_layout.removeView(m_splashScreen);
m_splashScreen = null;
return;
}
final Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(duration);
fadeOut.setAnimationListener(new Animation.AnimationListener() {
QtNative.runAction(new Runnable() {
@Override
public void onAnimationEnd(Animation animation) { hideSplashScreen(0); }
public void run() {
if (m_splashScreen == null)
return;
@Override
public void onAnimationRepeat(Animation animation) {}
if (duration <= 0) {
m_layout.removeView(m_splashScreen);
m_splashScreen = null;
return;
}
@Override
public void onAnimationStart(Animation animation) {}
final Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(duration);
fadeOut.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
hideSplashScreen(0);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
}
});
m_splashScreen.startAnimation(fadeOut);
}
});
m_splashScreen.startAnimation(fadeOut);
}
public void notifyLocationChange(int viewId)
@ -381,7 +388,22 @@ public class QtActivityDelegate
public void resetOptionsMenu()
{
m_activity.invalidateOptionsMenu();
QtNative.runAction(new Runnable() {
@Override
public void run() {
m_activity.invalidateOptionsMenu();
}
});
}
public void openOptionsMenu()
{
QtNative.runAction(new Runnable() {
@Override
public void run() {
m_activity.openOptionsMenu();
}
});
}
private boolean m_contextMenuVisible = false;
@ -395,31 +417,36 @@ public class QtActivityDelegate
public void openContextMenu(final int x, final int y, final int w, final int h)
{
m_layout.postDelayed(new Runnable() {
@Override
public void run() {
m_layout.setLayoutParams(m_inputDelegate.getQtEditText(), new QtLayout.LayoutParams(w, h, x, y), false);
PopupMenu popup = new PopupMenu(m_activity, m_inputDelegate.getQtEditText());
QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return m_activity.onContextItemSelected(menuItem);
}
});
popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu popupMenu) {
m_activity.onContextMenuClosed(popupMenu.getMenu());
}
});
popup.show();
}
}, 100);
@Override
public void run() {
m_layout.setLayoutParams(m_inputDelegate.getQtEditText(), new QtLayout.LayoutParams(w, h, x, y), false);
PopupMenu popup = new PopupMenu(m_activity, m_inputDelegate.getQtEditText());
QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return m_activity.onContextItemSelected(menuItem);
}
});
popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu popupMenu) {
m_activity.onContextMenuClosed(popupMenu.getMenu());
}
});
popup.show();
}
}, 100);
}
public void closeContextMenu()
{
m_activity.closeContextMenu();
QtNative.runAction(new Runnable() {
@Override
public void run() {
m_activity.closeContextMenu();
}
});
}
void setActionBarVisibility(boolean visible)
@ -433,96 +460,116 @@ public class QtActivityDelegate
}
public void insertNativeView(int id, View view, int x, int y, int w, int h) {
if (m_dummyView != null) {
m_layout.removeView(m_dummyView);
m_dummyView = null;
}
QtNative.runAction(new Runnable() {
@Override
public void run() {
if (m_dummyView != null) {
m_layout.removeView(m_dummyView);
m_dummyView = null;
}
if (m_nativeViews.containsKey(id))
m_layout.removeView(m_nativeViews.remove(id));
if (m_nativeViews.containsKey(id))
m_layout.removeView(m_nativeViews.remove(id));
if (w < 0 || h < 0) {
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
} else {
view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
}
if (w < 0 || h < 0) {
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
} else {
view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
}
view.setId(id);
m_layout.addView(view);
m_nativeViews.put(id, view);
view.setId(id);
m_layout.addView(view);
m_nativeViews.put(id, view);
}
});
}
public void createSurface(int id, boolean onTop, int x, int y, int w, int h, int imageDepth) {
if (m_surfaces.size() == 0) {
TypedValue attr = new TypedValue();
m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground, attr, true);
if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT && attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
m_activity.getWindow().setBackgroundDrawable(new ColorDrawable(attr.data));
} else {
m_activity.getWindow().setBackgroundDrawable(m_activity.getResources().getDrawable(attr.resourceId, m_activity.getTheme()));
QtNative.runAction(new Runnable() {
@Override
public void run() {
if (m_surfaces.size() == 0) {
TypedValue attr = new TypedValue();
m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground, attr, true);
if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT && attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
m_activity.getWindow().setBackgroundDrawable(new ColorDrawable(attr.data));
} else {
m_activity.getWindow().setBackgroundDrawable(m_activity.getResources().getDrawable(attr.resourceId, m_activity.getTheme()));
}
if (m_dummyView != null) {
m_layout.removeView(m_dummyView);
m_dummyView = null;
}
}
if (m_surfaces.containsKey(id))
m_layout.removeView(m_surfaces.remove(id));
QtSurface surface = new QtSurface(m_activity, id, onTop, imageDepth);
if (w < 0 || h < 0) {
surface.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
} else {
surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
}
// Native views are always inserted in the end of the stack (i.e., on top).
// All other views are stacked based on the order they are created.
final int surfaceCount = getSurfaceCount();
m_layout.addView(surface, surfaceCount);
m_surfaces.put(id, surface);
if (!m_splashScreenSticky)
hideSplashScreen();
}
if (m_dummyView != null) {
m_layout.removeView(m_dummyView);
m_dummyView = null;
}
}
if (m_surfaces.containsKey(id))
m_layout.removeView(m_surfaces.remove(id));
QtSurface surface = new QtSurface(m_activity, id, onTop, imageDepth);
if (w < 0 || h < 0) {
surface.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
} else {
surface.setLayoutParams( new QtLayout.LayoutParams(w, h, x, y));
}
// Native views are always inserted in the end of the stack (i.e., on top).
// All other views are stacked based on the order they are created.
final int surfaceCount = getSurfaceCount();
m_layout.addView(surface, surfaceCount);
m_surfaces.put(id, surface);
if (!m_splashScreenSticky)
hideSplashScreen();
});
}
public void setSurfaceGeometry(int id, int x, int y, int w, int h) {
if (m_surfaces.containsKey(id)) {
QtSurface surface = m_surfaces.get(id);
surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
} else if (m_nativeViews.containsKey(id)) {
View view = m_nativeViews.get(id);
view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
} else {
Log.e(QtNative.QtTAG, "Surface " + id +" not found!");
return;
}
QtNative.runAction(new Runnable() {
@Override
public void run() {
if (m_surfaces.containsKey(id)) {
QtSurface surface = m_surfaces.get(id);
surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
} else if (m_nativeViews.containsKey(id)) {
View view = m_nativeViews.get(id);
view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
} else {
Log.e(QtNative.QtTAG, "Surface " + id + " not found!");
return;
}
}
});
}
public void destroySurface(int id) {
View view = null;
QtNative.runAction(new Runnable() {
@Override
public void run() {
View view = null;
if (m_surfaces.containsKey(id)) {
view = m_surfaces.remove(id);
} else if (m_nativeViews.containsKey(id)) {
view = m_nativeViews.remove(id);
} else {
Log.e(QtNative.QtTAG, "Surface " + id +" not found!");
}
if (m_surfaces.containsKey(id)) {
view = m_surfaces.remove(id);
} else if (m_nativeViews.containsKey(id)) {
view = m_nativeViews.remove(id);
} else {
Log.e(QtNative.QtTAG, "Surface " + id + " not found!");
}
if (view == null)
return;
if (view == null)
return;
// Keep last frame in stack until it is replaced to get correct
// shutdown transition
if (m_surfaces.size() == 0 && m_nativeViews.size() == 0) {
m_dummyView = view;
} else {
m_layout.removeView(view);
}
// Keep last frame in stack until it is replaced to get correct
// shutdown transition
if (m_surfaces.size() == 0 && m_nativeViews.size() == 0) {
m_dummyView = view;
} else {
m_layout.removeView(view);
}
}
});
}
public int getSurfaceCount()
@ -532,31 +579,41 @@ public class QtActivityDelegate
public void bringChildToFront(int id)
{
View view = m_surfaces.get(id);
if (view != null) {
final int surfaceCount = getSurfaceCount();
if (surfaceCount > 0)
m_layout.moveChild(view, surfaceCount - 1);
return;
}
QtNative.runAction(new Runnable() {
@Override
public void run() {
View view = m_surfaces.get(id);
if (view != null) {
final int surfaceCount = getSurfaceCount();
if (surfaceCount > 0)
m_layout.moveChild(view, surfaceCount - 1);
return;
}
view = m_nativeViews.get(id);
if (view != null)
m_layout.moveChild(view, -1);
view = m_nativeViews.get(id);
if (view != null)
m_layout.moveChild(view, -1);
}
});
}
public void bringChildToBack(int id)
{
View view = m_surfaces.get(id);
if (view != null) {
m_layout.moveChild(view, 0);
return;
}
QtNative.runAction(new Runnable() {
@Override
public void run() {
View view = m_surfaces.get(id);
if (view != null) {
m_layout.moveChild(view, 0);
return;
}
view = m_nativeViews.get(id);
if (view != null) {
final int index = getSurfaceCount();
m_layout.moveChild(view, index);
}
view = m_nativeViews.get(id);
if (view != null) {
final int index = getSurfaceCount();
m_layout.moveChild(view, index);
}
}
});
}
}

View File

@ -10,6 +10,8 @@ import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import org.qtproject.qt.android.QtInputConnection.QtInputConnectionListener;
public class QtEditText extends View
{
int m_initialCapsMode = 0;
@ -17,6 +19,13 @@ public class QtEditText extends View
int m_inputType = InputType.TYPE_CLASS_TEXT;
boolean m_optionsChanged = false;
private QtInputConnectionListener m_qtInputConnectionListener;
public void setQtInputConnectionListener(QtInputConnectionListener listener)
{
m_qtInputConnectionListener = listener;
}
public void setImeOptions(int m_imeOptions)
{
if (m_imeOptions == this.m_imeOptions)
@ -56,7 +65,7 @@ public class QtEditText extends View
outAttrs.imeOptions = m_imeOptions;
outAttrs.initialCapsMode = m_initialCapsMode;
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
return new QtInputConnection(this);
return new QtInputConnection(this, m_qtInputConnectionListener);
}
// // DEBUG CODE

View File

@ -51,31 +51,6 @@ class QtNativeInputConnection
static native boolean updateCursorPosition();
}
class HideKeyboardRunnable implements Runnable {
private long m_hideTimeStamp = System.nanoTime();
@Override
public void run() {
// Check that the keyboard is really no longer there.
Activity activity = QtNative.activity();
Rect r = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
int screenHeight = 0;
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenHeight = metrics.heightPixels;
} else {
final WindowMetrics maximumWindowMetrics = activity.getWindowManager().getMaximumWindowMetrics();
screenHeight = maximumWindowMetrics.getBounds().height();
}
final int kbHeight = screenHeight - r.bottom;
if (kbHeight < 100)
QtNative.activityDelegate().getInputDelegate().setKeyboardVisibility(false, m_hideTimeStamp);
}
}
public class QtInputConnection extends BaseInputConnection
{
private static final int ID_SELECT_ALL = android.R.id.selectAll;
@ -86,21 +61,52 @@ public class QtInputConnection extends BaseInputConnection
private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod;
private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary;
private final QtInputConnectionListener m_qtInputConnectionListener;
class HideKeyboardRunnable implements Runnable {
@Override
public void run() {
// Check that the keyboard is really no longer there.
Activity activity = QtNative.activity();
Rect r = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
int screenHeight = 0;
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenHeight = metrics.heightPixels;
} else {
final WindowMetrics maximumWindowMetrics = activity.getWindowManager().getMaximumWindowMetrics();
screenHeight = maximumWindowMetrics.getBounds().height();
}
final int kbHeight = screenHeight - r.bottom;
if (kbHeight < 100)
m_qtInputConnectionListener.onHideKeyboardRunnableDone(false, System.nanoTime());
}
}
public interface QtInputConnectionListener {
void onSetClosing(boolean closing);
void onHideKeyboardRunnableDone(boolean visibility, long hideTimeStamp);
void onSendKeyEventDefaultCase();
}
private QtEditText m_view = null;
private void setClosing(boolean closing)
{
if (closing) {
if (closing)
m_view.postDelayed(new HideKeyboardRunnable(), 100);
} else {
QtNative.activityDelegate().getInputDelegate().setKeyboardVisibility(true, System.nanoTime());
}
else
m_qtInputConnectionListener.onSetClosing(false);
}
public QtInputConnection(QtEditText targetView)
public QtInputConnection(QtEditText targetView, QtInputConnectionListener listener)
{
super(targetView, true);
m_view = targetView;
m_qtInputConnectionListener = listener;
}
@Override
@ -256,7 +262,7 @@ public class QtInputConnection extends BaseInputConnection
break;
default:
QtNative.activityDelegate().getInputDelegate().hideSoftwareKeyboard();
m_qtInputConnectionListener.onSendKeyEventDefaultCase();
break;
}
}

View File

@ -4,6 +4,7 @@
package org.qtproject.qt.android;
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@ -17,6 +18,8 @@ import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import org.qtproject.qt.android.QtInputConnection.QtInputConnectionListener;
public class QtInputDelegate {
// keyboard methods
@ -96,7 +99,7 @@ public class QtInputDelegate {
private int m_lastChar = 0;
private boolean m_backKeyPressedSent = false;
// Note: because of the circular call to updateFullScreen() from QtActivityDelegate, we need
// Note: because of the circular call to updateFullScreen() from the delegate, we need
// a listener to be able to do that call from the delegate, because that's where that
// logic lives
public interface KeyboardVisibilityListener {
@ -105,9 +108,29 @@ public class QtInputDelegate {
private final KeyboardVisibilityListener m_keyboardVisibilityListener;
QtInputDelegate(KeyboardVisibilityListener listener)
QtInputDelegate(Activity activity, KeyboardVisibilityListener listener)
{
this.m_keyboardVisibilityListener = listener;
m_editText = new QtEditText(activity);
m_imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
QtInputConnectionListener inputConnectionListener = new QtInputConnectionListener() {
@Override
public void onSetClosing(boolean closing) {
if (!closing)
setKeyboardVisibility(true, System.nanoTime());
}
@Override
public void onHideKeyboardRunnableDone(boolean visibility, long hideTimeStamp) {
setKeyboardVisibility(visibility, hideTimeStamp);
}
@Override
public void onSendKeyEventDefaultCase() {
hideSoftwareKeyboard();
}
};
m_editText.setQtInputConnectionListener(inputConnectionListener);
}
public boolean isKeyboardVisible()
@ -131,16 +154,6 @@ public class QtInputDelegate {
return m_editText;
}
void setEditText(QtEditText editText)
{
m_editText = editText;
}
void setInputMethodManager(InputMethodManager inputMethodManager)
{
m_imm = inputMethodManager;
}
void setEditPopupMenu(EditPopupMenu editPopupMenu)
{
m_editPopupMenu = editPopupMenu;

View File

@ -54,11 +54,10 @@ import android.graphics.Rect;
public class QtNative
{
// TODO get rid of the delegation from QtNative, call directly the Activity in c++
private static Activity m_activity = null;
private static boolean m_activityPaused = false;
private static Service m_service = null;
private static QtActivityDelegate m_activityDelegate = null;
private static QtServiceDelegate m_serviceDelegate = null;
public static Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
public static final String QtTAG = "Qt JAVA"; // string used for Log.x
@ -81,8 +80,8 @@ public class QtNative
public static boolean isStarted()
{
boolean hasActivity = m_activity != null && m_activityDelegate != null;
boolean hasService = m_service != null && m_serviceDelegate != null;
boolean hasActivity = m_activity != null;
boolean hasService = m_service != null;
return m_started && (hasActivity || hasService);
}
@ -111,21 +110,6 @@ public class QtNative
}
}
public static QtActivityDelegate activityDelegate()
{
synchronized (m_mainActivityMutex) {
return m_activityDelegate;
}
}
public static QtServiceDelegate serviceDelegate()
{
synchronized (m_mainActivityMutex) {
return m_serviceDelegate;
}
}
public static String[] getStringArray(String joinedString)
{
return joinedString.split(",");
@ -204,19 +188,17 @@ public class QtNative
return m_qtThread;
}
public static void setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate)
public static void setActivity(Activity qtMainActivity)
{
synchronized (m_mainActivityMutex) {
m_activity = qtMainActivity;
m_activityDelegate = qtActivityDelegate;
}
}
public static void setService(Service qtMainService, QtServiceDelegate qtServiceDelegate)
public static void setService(Service qtMainService)
{
synchronized (m_mainActivityMutex) {
m_service = qtMainService;
m_serviceDelegate = qtServiceDelegate;
}
}
@ -349,72 +331,6 @@ public class QtNative
return perm;
}
// TODO get rid of the delegation from QtNative, call directly the Activity in c++
private static void setSystemUiVisibility(final int systemUiVisibility)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null) {
m_activityDelegate.setSystemUiVisibility(systemUiVisibility);
}
updateWindow();
}
});
}
public static void notifyQtAndroidPluginRunning(final boolean running)
{
if (m_activityDelegate != null)
m_activityDelegate.notifyQtAndroidPluginRunning(running);
}
private static void openContextMenu(final int x, final int y, final int w, final int h)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.openContextMenu(x, y, w, h);
}
});
}
private static void closeContextMenu()
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.closeContextMenu();
}
});
}
private static void resetOptionsMenu()
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.resetOptionsMenu();
}
});
}
private static void openOptionsMenu()
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activity != null)
m_activity.openOptionsMenu();
}
});
}
private static byte[][] getSSLCertificates()
{
ArrayList<byte[]> certificateList = new ArrayList<byte[]>();
@ -442,83 +358,6 @@ public class QtNative
return certificateArray;
}
private static void createSurface(final int id, final boolean onTop, final int x, final int y, final int w, final int h, final int imageDepth)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.createSurface(id, onTop, x, y, w, h, imageDepth);
}
});
}
private static void insertNativeView(final int id, final View view, final int x, final int y, final int w, final int h)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.insertNativeView(id, view, x, y, w, h);
}
});
}
private static void setSurfaceGeometry(final int id, final int x, final int y, final int w, final int h)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.setSurfaceGeometry(id, x, y, w, h);
}
});
}
private static void bringChildToFront(final int id)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.bringChildToFront(id);
}
});
}
private static void bringChildToBack(final int id)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.bringChildToBack(id);
}
});
}
private static void destroySurface(final int id)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.destroySurface(id);
}
});
}
private static void hideSplashScreen(final int duration)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.hideSplashScreen(duration);
}
});
}
private static String[] listAssetContent(android.content.res.AssetManager asset, String path) {
String [] list;
ArrayList<String> res = new ArrayList<String>();

View File

@ -9,15 +9,11 @@ import android.os.IBinder;
import android.util.Log;
public class QtServiceBase extends Service {
private QtServiceDelegate m_delegate;
@Override
public void onCreate()
{
super.onCreate();
m_delegate = new QtServiceDelegate(this);
// the application has already started, do not reload everything again
if (QtNative.isStarted()) {
Log.w(QtNative.QtTAG,
@ -38,7 +34,7 @@ public class QtServiceBase extends Service {
super.onDestroy();
QtNative.quitQtCoreApplication();
QtNative.terminateQt();
QtNative.setService(null, null);
QtNative.setService(null);
QtNative.m_qtThread.exit();
System.exit(0);
}
@ -49,9 +45,4 @@ public class QtServiceBase extends Service {
return QtNative.onBind(intent);
}
}
QtServiceDelegate getServiceDelegate()
{
return m_delegate;
}
}

View File

@ -1,20 +0,0 @@
// Copyright (C) 2016 BogDan Vatra <bogdan@kde.org>
// Copyright (C) 2023 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;
import android.app.Service;
public class QtServiceDelegate
{
private Service m_service = null;
QtServiceDelegate(Service service)
{
m_service = service;
// Set native context
QtNative.setService(m_service, this);
}
}

View File

@ -94,8 +94,7 @@ int QNativeInterface::QAndroidApplication::sdkVersion()
*/
void QNativeInterface::QAndroidApplication::hideSplashScreen(int duration)
{
QJniObject::callStaticMethod<void>("org/qtproject/qt/android/QtNative",
"hideSplashScreen", "(I)V", duration);
QtAndroidPrivate::activity().callMethod<void>("hideSplashScreen", duration);
}
/*!

View File

@ -45,9 +45,6 @@ static jmethodID m_loadClassMethodID = nullptr;
static AAssetManager *m_assetManager = nullptr;
static jobject m_assets = nullptr;
static jobject m_resourcesObj = nullptr;
static jmethodID m_createSurfaceMethodID = nullptr;
static jmethodID m_setSurfaceGeometryMethodID = nullptr;
static jmethodID m_destroySurfaceMethodID = nullptr;
static QtJniTypes::QtActivityDelegate m_activityDelegate = nullptr;
static QtJniTypes::QtInputDelegate m_inputDelegate = nullptr;
@ -165,7 +162,7 @@ namespace QtAndroid
// TODO move calls from here to where they logically belong
void setSystemUiVisibility(SystemUiVisibility uiVisibility)
{
QJniObject::callStaticMethod<void>(m_applicationClass, "setSystemUiVisibility", "(I)V", jint(uiVisibility));
qtActivityDelegate().callMethod<void>("setSystemUiVisibility", jint(uiVisibility));
}
// FIXME: avoid direct access to QtActivityDelegate
@ -218,7 +215,7 @@ namespace QtAndroid
void notifyQtAndroidPluginRunning(bool running)
{
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyQtAndroidPluginRunning","(Z)V", running);
qtActivityDelegate().callMethod<void>("notifyQtAndroidPluginRunning", running);
}
jobject createBitmap(QImage img, JNIEnv *env)
@ -338,12 +335,8 @@ namespace QtAndroid
w = std::max(geometry.width(), 1);
h = std::max(geometry.height(), 1);
}
env->CallStaticVoidMethod(m_applicationClass,
m_createSurfaceMethodID,
surfaceId,
jboolean(onTop),
x, y, w, h,
imageDepth);
qtActivityDelegate().callMethod<void>("createSurface", surfaceId, jboolean(onTop),
x, y, w, h, imageDepth);
return surfaceId;
}
@ -358,9 +351,7 @@ namespace QtAndroid
if (!geometry.isNull())
geometry.getRect(&x, &y, &w, &h);
QJniObject::callStaticMethod<void>(m_applicationClass,
"insertNativeView",
"(ILandroid/view/View;IIII)V",
qtActivityDelegate().callMethod<void>("insertNativeView",
surfaceId,
view,
x,
@ -395,10 +386,7 @@ namespace QtAndroid
w = geometry.width();
h = geometry.height();
}
env->CallStaticVoidMethod(m_applicationClass,
m_setSurfaceGeometryMethodID,
surfaceId,
x, y, w, h);
qtActivityDelegate().callMethod<void>("setSurfaceGeometry", surfaceId, x, y, w, h);
}
@ -414,11 +402,7 @@ namespace QtAndroid
m_surfaces.erase(it);
}
QJniEnvironment env;
if (env.jniEnv())
env->CallStaticVoidMethod(m_applicationClass,
m_destroySurfaceMethodID,
surfaceId);
qtActivityDelegate().callMethod<void>("destroySurface", surfaceId);
}
void bringChildToFront(int surfaceId)
@ -426,10 +410,7 @@ namespace QtAndroid
if (surfaceId == -1)
return;
QJniObject::callStaticMethod<void>(m_applicationClass,
"bringChildToFront",
"(I)V",
surfaceId);
qtActivityDelegate().callMethod<void>("bringChildToFront", surfaceId);
}
void bringChildToBack(int surfaceId)
@ -437,10 +418,7 @@ namespace QtAndroid
if (surfaceId == -1)
return;
QJniObject::callStaticMethod<void>(m_applicationClass,
"bringChildToBack",
"(I)V",
surfaceId);
qtActivityDelegate().callMethod<void>("bringChildToBack", surfaceId);
}
bool blockEventLoopsWhenSuspended()
@ -893,10 +871,6 @@ static bool registerNatives(QJniEnvironment &env)
return JNI_FALSE;
}
GET_AND_CHECK_STATIC_METHOD(m_createSurfaceMethodID, m_applicationClass, "createSurface", "(IZIIIII)V");
GET_AND_CHECK_STATIC_METHOD(m_setSurfaceGeometryMethodID, m_applicationClass, "setSurfaceGeometry", "(IIIII)V");
GET_AND_CHECK_STATIC_METHOD(m_destroySurfaceMethodID, m_applicationClass, "destroySurface", "(I)V");
jmethodID methodID;
GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;");
jobject contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);

View File

@ -31,8 +31,6 @@ namespace QtAndroidMenu
static QWindow *activeTopLevelWindow = nullptr;
Q_CONSTINIT static QRecursiveMutex menuBarMutex;
static jmethodID openContextMenuMethodID = 0;
static jmethodID clearMenuMethodID = 0;
static jmethodID addMenuItemMethodID = 0;
static int menuNoneValue = 0;
@ -46,29 +44,31 @@ namespace QtAndroidMenu
void resetMenuBar()
{
QJniObject::callStaticMethod<void>(applicationClass(), "resetOptionsMenu");
qtActivityDelegate().callMethod<void>("resetOptionsMenu");
}
void openOptionsMenu()
{
QJniObject::callStaticMethod<void>(applicationClass(), "openOptionsMenu");
qtActivityDelegate().callMethod<void>("openOptionsMenu");
}
void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect, JNIEnv *env)
void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect)
{
QMutexLocker lock(&visibleMenuMutex);
if (visibleMenu)
pendingContextMenus.append(visibleMenu);
visibleMenu = menu;
menu->aboutToShow();
env->CallStaticVoidMethod(applicationClass(), openContextMenuMethodID, anchorRect.x(), anchorRect.y(), anchorRect.width(), anchorRect.height());
qtActivityDelegate().callMethod<void>("openContextMenu",
anchorRect.x(), anchorRect.y(),
anchorRect.width(), anchorRect.height());
}
void hideContextMenu(QAndroidPlatformMenu *menu)
{
QMutexLocker lock(&visibleMenuMutex);
if (visibleMenu == menu) {
QJniObject::callStaticMethod<void>(applicationClass(), "closeContextMenu");
qtActivityDelegate().callMethod<void>("closeContextMenu");
pendingContextMenus.clear();
} else {
pendingContextMenus.removeOne(menu);
@ -211,8 +211,10 @@ namespace QtAndroidMenu
return order;
}
static jboolean onPrepareOptionsMenu(JNIEnv *env, jobject /*thiz*/, jobject menu)
static jboolean onPrepareOptionsMenu(JNIEnv *env, jobject thiz, jobject menu)
{
Q_UNUSED(thiz)
env->CallVoidMethod(menu, clearMenuMethodID);
QMutexLocker lock(&menuBarMutex);
if (!visibleMenuBar)
@ -249,8 +251,11 @@ namespace QtAndroidMenu
return order ? JNI_TRUE : JNI_FALSE;
}
static jboolean onOptionsItemSelected(JNIEnv *env, jobject /*thiz*/, jint menuId, jboolean checked)
static jboolean onOptionsItemSelected(JNIEnv *env, jobject thiz, jint menuId, jboolean checked)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
QMutexLocker lock(&menuBarMutex);
if (!visibleMenuBar)
return JNI_FALSE;
@ -260,7 +265,7 @@ namespace QtAndroidMenu
QAndroidPlatformMenuItem *item = static_cast<QAndroidPlatformMenuItem *>(menus.front()->menuItemForId(menuId));
if (item) {
if (item->menu()) {
showContextMenu(item->menu(), QRect(), env);
showContextMenu(item->menu(), QRect());
} else {
if (item->isCheckable())
item->setChecked(checked);
@ -270,18 +275,23 @@ namespace QtAndroidMenu
} else {
QAndroidPlatformMenu *menu = static_cast<QAndroidPlatformMenu *>(visibleMenuBar->menuForId(menuId));
if (menu)
showContextMenu(menu, QRect(), env);
showContextMenu(menu, QRect());
}
return JNI_TRUE;
}
static void onOptionsMenuClosed(JNIEnv */*env*/, jobject /*thiz*/, jobject /*menu*/)
static void onOptionsMenuClosed(JNIEnv *env, jobject thiz, jobject menu)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
Q_UNUSED(menu)
}
static void onCreateContextMenu(JNIEnv *env, jobject /*thiz*/, jobject menu)
static void onCreateContextMenu(JNIEnv *env, jobject thiz, jobject menu)
{
Q_UNUSED(thiz)
env->CallVoidMethod(menu, clearMenuMethodID);
QMutexLocker lock(&visibleMenuMutex);
if (!visibleMenu)
@ -295,8 +305,9 @@ namespace QtAndroidMenu
addAllMenuItemsToMenu(env, menu, visibleMenu);
}
static void fillContextMenu(JNIEnv *env, jobject /*thiz*/, jobject menu)
static void fillContextMenu(JNIEnv *env, jobject thiz, jobject menu)
{
Q_UNUSED(thiz)
env->CallVoidMethod(menu, clearMenuMethodID);
QMutexLocker lock(&visibleMenuMutex);
if (!visibleMenu)
@ -305,13 +316,16 @@ namespace QtAndroidMenu
addAllMenuItemsToMenu(env, menu, visibleMenu);
}
static jboolean onContextItemSelected(JNIEnv *env, jobject /*thiz*/, jint menuId, jboolean checked)
static jboolean onContextItemSelected(JNIEnv *env, jobject thiz, jint menuId, jboolean checked)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
QMutexLocker lock(&visibleMenuMutex);
QAndroidPlatformMenuItem * item = static_cast<QAndroidPlatformMenuItem *>(visibleMenu->menuItemForId(menuId));
if (item) {
if (item->menu()) {
showContextMenu(item->menu(), QRect(), env);
showContextMenu(item->menu(), QRect());
} else {
if (item->isCheckable())
item->setChecked(checked);
@ -328,8 +342,12 @@ namespace QtAndroidMenu
return JNI_TRUE;
}
static void onContextMenuClosed(JNIEnv *env, jobject /*thiz*/, jobject /*menu*/)
static void onContextMenuClosed(JNIEnv *env, jobject thiz, jobject menu)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
Q_UNUSED(menu)
QMutexLocker lock(&visibleMenuMutex);
if (!visibleMenu)
return;
@ -337,7 +355,7 @@ namespace QtAndroidMenu
visibleMenu->aboutToHide();
visibleMenu = 0;
if (!pendingContextMenus.empty())
showContextMenu(pendingContextMenus.takeLast(), QRect(), env);
showContextMenu(pendingContextMenus.takeLast(), QRect());
}
static JNINativeMethod methods[] = {
@ -387,8 +405,6 @@ namespace QtAndroidMenu
return false;
}
GET_AND_CHECK_STATIC_METHOD(openContextMenuMethodID, appClass, "openContextMenu", "(IIII)V");
jclass clazz;
FIND_AND_CHECK_CLASS("android/view/Menu");
GET_AND_CHECK_METHOD(clearMenuMethodID, clazz, "clear", "()V");

View File

@ -21,7 +21,7 @@ namespace QtAndroidMenu
{
// Menu support
void openOptionsMenu();
void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect, JNIEnv *env);
void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect);
void hideContextMenu(QAndroidPlatformMenu *menu);
void syncMenu(QAndroidPlatformMenu *menu);
void androidPlatformMenuDestroyed(QAndroidPlatformMenu *menu);

View File

@ -119,7 +119,7 @@ void QAndroidPlatformMenu::showPopup(const QWindow *parentWindow, const QRect &t
Q_UNUSED(parentWindow);
Q_UNUSED(item);
setVisible(true);
QtAndroidMenu::showContextMenu(this, targetRect, QJniEnvironment().jniEnv());
QtAndroidMenu::showContextMenu(this, targetRect);
}
QPlatformMenuItem *QAndroidPlatformMenu::menuItemForTag(quintptr tag) const

View File

@ -199,11 +199,14 @@ void tst_Android::testRunOnAndroidMainThread()
}
}
Q_DECLARE_JNI_CLASS(QtActivityDelegate, "org/qtproject/qt/android/QtActivityDelegate")
void setSystemUiVisibility(int visibility)
{
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([visibility] {
QJniObject::callStaticMethod<void>("org/qtproject/qt/android/QtNative",
"setSystemUiVisibility", "(I)V", visibility);
auto context = QNativeInterface::QAndroidApplication::context();
auto activityDelegate = context.callMethod<QtJniTypes::QtActivityDelegate>("getActivityDelegate");
activityDelegate.callMethod<void>("setSystemUiVisibility", jint(visibility));
}).waitForFinished();
}