QAndroidOffscreenView.h 15 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
  Offscreen Android Views library for Qt

  Author:
  Sergey A. Galin <sergey.galin@gmail.com>

  Distrbuted under The BSD License

  Copyright (c) 2014, DoubleGIS, LLC.
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice,
16
	this list of conditions and the following disclaimer.
17
  * Redistributions in binary form must reproduce the above copyright notice,
18 19
	this list of conditions and the following disclaimer in the documentation
	and/or other materials provided with the distribution.
20
  * Neither the name of the DoubleGIS, LLC nor the names of its contributors
21 22
	may be used to endorse or promote products derived from this software
	without specific prior written permission.
23 24 25 26 27 28 29 30 31 32 33 34 35 36

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  THE POSSIBILITY OF SUCH DAMAGE.
*/

37
#pragma once
Sergey Galin's avatar
Sergey Galin committed
38 39 40 41 42
#include <QtGui/QColor>
#include <QtCore/QSize>
#include <QtCore/QRect>
#include <QtCore/QScopedPointer>
#include <QtCore/QMutex>
43
#include <QJniHelpers/QJniHelpers.h>
44
#include "QAndroidJniImagePair.h"
45
#include "QOpenGLTextureHolder.h"
46
#include "QApplicationActivityObserver.h"
47

48
/*!
Sergey Galin's avatar
Sergey Galin committed
49
 * A general wrapper for Android offscreen views.f
50 51 52
 * It can be used to create a QWidget / QGLWidget / QGraphicsWidget / QML component
 * which displays Android view.
 */
53 54 55 56 57
class QAndroidOffscreenView: public QObject
{
	Q_OBJECT
	Q_PROPERTY(QSize size READ size WRITE resize)
	Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor)
58
	Q_PROPERTY(bool visible READ visible WRITE setVisible)
59
	Q_PROPERTY(bool enabled READ enabled WRITE setEnabled)
60
protected:
61
	/*!
62 63 64 65
	 * \param classname - name of Java class of the View wrapper.
	 * \param objectname - set for QObject and also passed to Java side, to identify the object in logs and etc.
	 * \param defsize - initial size of the view.
	 * \param parent - passed to QObject constructor.
66 67 68
	 * \note The function invokes createView() through Qt::QueuedConnection so descenant constructor still
	 *       has time to register View-specific native functions and do some other initialization before
	 *       the construction of the View will start.
69
	 */
70
	QAndroidOffscreenView(const QString & classname, const QString & objectname, const QSize & defsize, QObject * parent = 0);
71

72 73
protected slots:
	void createView();
74 75

public:
76 77
	virtual ~QAndroidOffscreenView();

78
	static const QString & getDefaultJavaClassPath();
79
	static void preloadJavaClasses();
80 81 82 83 84

	//
	// Functions to check for available configuration
	//

85
	static bool openGlTextureSupportedOnJavaSide();
86
	static bool nonAttachingModeSupported();
87

88 89 90
	const QString & viewObjectName() const { return view_object_name_; }
	const QString & viewClassName() const { return view_class_name_; }

91
	/*!
92 93
	 * Initialize OpenGL rendering surface. This function should be called within active
	 * proper GL context as it will want to create an OpenGL texture.
94 95 96 97
	 * When View is initialized using this function it may be rendered on screen only using
	 * paintGL(). Call to getBitmapBuffer() may return 0.
	 * If GL mode is not supported on Android side this function will call initializeBitmap()
	 * instead of creating GL texture surface; paintGL() can still be used to draw the View.
98
	 * It is safe to call this function multiple times. All subsequent calls are ignored.
99 100 101
	 */
	virtual void initializeGL();

102
	/*!
103 104 105 106 107
	 * Initialize non-OpenGL (Bitmap-based) rendering surface. The View can be rendered on screen
	 * either using paintGL() or by getting QImage via getBitmapBuffer() and drawing it using
	 * some other method, e.g. QPainter::drawImage().
	 * This function should be used when Qt is working in unaccelerated UI mode.
	 * It is safe to call this function multiple times. All subsequent calls are ignored.
108 109 110
	 */
	virtual void initializeBitmap();

111 112 113
	/*!
	 * Returns true if initializeGL() has been called.
	 */
114
	virtual bool isIntialized() const { return tex_.isAllocated() || bitmap_a_.isAllocated(); }
115 116 117 118 119 120 121 122

	/*!
	 * Delete associated Android View and its rendering infrastructure. The texture continues
	 * to exist and can be painted, but updates, resizes and so on will not work anymore.
	 */
	virtual void deleteAndroidView();

	/*!
123 124 125 126 127
	 * Draw GL texture or Bitmap (depending on the rendering surface on the Java side) using OpenGL.
	 * targetRect is the output rectangle in OpenGL terms. If View image is not ready, the rectangle
	 * is filled with fillColor.
	 * Note: to draw the View when Qt is working in unaccelerated mode, intialize it
	 * via initializeBitmap() and use QImage returned by getBitmapBuffer().
128
	 */
129
	virtual void paintGL(int l, int b, int w, int h, bool reverse_y);
130

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
	/*!
	 * Makes sure that the GL texture holder contains actual image, if possible.
	 * This function should only be called if \ref getGLTextureHolder() is used to access
	 * the texure directly. It is not needed to call it when using paintGL().
	 * Note: in Bitmap+GL mode this function may cause re-allocation of GL texture.
	 */
	bool updateGLTextureInHolder();

	/*!
	 * Access \ref QOpenGLTextureHolder for reading texture id or other properties
	 * for direct painting without using \ref paintGL().
	 * \ref updateGLTextureInHolder() should be called to make sure that the texture
	 * contains actual image.
	 */
	const QOpenGLTextureHolder & getGLTextureHolder() const { return tex_; }

147 148 149
	/*!
	 * Used in Bitmap mode. Instead of paintGL(), get current bitmap buffer
	 * using getBitmapBuffer() and paint it by yourself.
150
	 * The image buffer is guaranteed to be unmodified until the next call to getBitmapBuffer().
Sergey Galin's avatar
Sergey Galin committed
151
	 * \note This function is not guaranteed to return expected image when
152 153
	 *  initialized through initializeGL(). For example, returned image may be padded to
	 *  have power-of-two size.
Sergey Galin's avatar
Sergey Galin committed
154 155 156
	 * \param out_texture_updated: the bool value is set to true/false to indicate
	 *  that the image has been actually changed since the last call to getBitmapBuffer().
	 *  This can be used, for example, to avoid reloading the bitmap into GL texture.
157
	 */
158
	const QImage * getBitmapBuffer(bool * out_texture_updated = 0);
159

160
	//! Check if Android View already exists.
161 162
	bool isCreated() const;

Sergey Galin's avatar
Sergey Galin committed
163
	//! Check if Android View has already painted something.
164 165 166
	virtual bool hasValidImage() const;

	QSize size() const { return size_; }
167
	virtual void resize(const QSize & newsize);
168 169 170
	QColor fillColor() const { return fill_color_; }
	virtual void setFillColor(const QColor & color);

171 172
	/*!
	 * Does the view thinks it's visible? When view is invisible, the texture may
173
	 * contain an empty or outdated image. Also it stops all animations and etc.
174 175 176 177 178
	 */
	bool visible() const { return is_visible_; }

	/*!
	 * Set visibility flag. It informs the view if it's visible or not.
179
	 * By default, view is invisible.
180 181 182 183 184 185 186 187 188 189 190 191 192 193
	 * When view is invisible, it may save resources by not doing any timer-based work,
	 * for example. For simple widgets, it is not necessary to set them invisible
	 * because it is enough to hide the control displaying the texture. For Wev Views
	 * with active contents (e.g. JavaScript with timers, GIF images) it is important
	 * to stop them when content updates are not visible to users to save CPU power
	 * and battery.
	 * Warning: unlike for widgets, visibility also should be explicitly set to false
	 * when application goes to background. Cases when offscreen view should be marked
	 * hidden are different depending on use-cases, graphical systems and QPA plugins
	 * so proper reacting to application activation/deactivation cannot be handled
	 * on this level.
	 */
	void setVisible(bool visible);

194 195 196 197
	bool enabled() const { return is_enabled_; }

	void setEnabled(bool enabled);

198 199 200 201 202
	/*!
	 * Control attaching View to the main activity View.
	 * It is typically called one time after constructing QAndroidOffscreenView.
	 * Unattached views will not receive keyboard events when focused and
	 * can't use SIP and popup menus.
203
	 * Non-attached mode is not supported on API < 11 (\see nonAttachingModeSupported()).
204 205 206
	 */
	void setAttachingMode(bool attaching);

207
	void reattachView();
208

209
	/* !
Sergey Galin's avatar
Sergey Galin committed
210 211 212 213
	 * Returns true if View is currently focused. Please note that this View takes and
	 * and releases focus in another thread so this function may e.g. return false if
	 * called immediately after setFocused(true).
	 */
214
	//bool isFocused() const;
Sergey Galin's avatar
Sergey Galin committed
215 216 217 218 219 220 221

	/*!
	 * Set or remove focusing from the View. This should be called to keep Qt focus
	 * in sync with Android focus.
	 */
	void setFocused(bool focused = true);

222 223 224 225 226 227 228 229 230
	/*!
	 * Inform Android about real screen coordinates of the View.
	 * This is important only if the View works with SIP (e.g.: EditText, interactive WebView)
	 * because input method may want to display selection markers and need to know the
	 * real position of the view. For non-interactive views calling this function is
	 * not necessary.
	 */
	void setPosition(int left, int top);

Sergey Galin's avatar
Sergey Galin committed
231 232 233
	//! Make sure software keyboard is hidden for this control.
	void hideKeyboard();

234
	//! Make sure software keyboard is shown for this control.
235 236
	void showKeyboard();

Sergey Galin's avatar
Sergey Galin committed
237 238 239 240 241 242 243 244 245 246 247 248
	/*!
	 * Set/cleaer the flag to hide software keyboard when focus is lost.
	 * By default, the mode is enabled and keyboard slides away when focus goes from the View.
	 * If it is disabled, SIP won't hide when user changes focus to any pure Qt control
	 * because main Qt window always accepts text input.
	 * Disable the mode if you want user to be able to switch between various form
	 * fields without hiding and re-opening keyboard. In this case, you have to call
	 * hideKeyboard() from your C++/Qt code to make sure the keyboard is hidden when focus
	 * goes to something which doesn't need it.
	 */
	void setHideKeyboardOnFocusLoss(bool hide);

249 250 251 252 253 254
	/*!
	 * Set/cleaer the flag to show software keyboard on focused.
	 * By default, the mode is disabled.
	 */
	void setShowKeyboardOnFocusIn(bool show);

255 256 257 258 259 260 261 262
	//
	// Handling of user input events
	//
	static const int
		ANDROID_MOTIONEVENT_ACTION_DOWN = 0,
		ANDROID_MOTIONEVENT_ACTION_UP = 1,
		ANDROID_MOTIONEVENT_ACTION_MOVE = 2;

263 264 265 266 267
	/*!
	 * Single-touch / mouse support.
	 * Typically, this function is called from Qt mouse event handlers.
	 * \param android_action can be ANDROID_MOTIONEVENT_ACTION_DOWN, ANDROID_MOTIONEVENT_ACTION_UP,
	 *  ANDROID_MOTIONEVENT_ACTION_MOVE.
268 269 270 271 272 273
	 * \param timestamp_uptime_mills - this should be set either as a System.uptimeMillis() of the
	 *  time when the event has been initially generated or 0. Unfortunately, stock Qt currently
	 *  doesn't pass the right timestamp in mouse events. So we can pass 0 here and the Java side
	 *  will enable a workaround of getting the current timestamp. It will cause errors in determination
	 *  of the movement speed because the initial Andoid UI event queue and the Qt UI queue run at
	 *  uneven speed. So movement animations will not work good (e.g. WebView scrolling).
274
	 */
275
	void mouse(int android_action, int x, int y, long long timestamp_uptime_millis = 0);
276

277 278 279 280 281 282
	//! Return the scrolled left position of this view.
	int getScrollX();

	//! Return the scrolled top position of this view.
	int getScrollY();

283 284 285
	void setScrollX(int x);
	void setScrollY(int y);

286 287 288 289 290 291
	//! Return the width measurement information for this view as computed by the most recent call to measure(int, int).
	int getMeasuredWidth();

	//! Return the height measurement information for this view as computed by the most recent call to measure(int, int).
	int getMeasuredHeight();

292
	//! \todo Add multi-touch support!
293

294 295
	void setSoftInputModeResize();
	void setSoftInputModeAdjustPan();
296
	void setSoftInputModeAdjustNothing(); // API 11+
297

298 299 300
	//! Test function for lib developers, don't use it
	void testFunction();

301 302 303 304 305 306 307 308 309
public slots:
	/*!
	 * Free Android view and OpenGL resources. The object will be unusable after that.
	 */
	virtual void deinitialize();

	//! Tell Android View to repaint.
	virtual void invalidate();

310 311 312
	//! Catch application activity status changes from the app activity observer.
	virtual void applicationActivityStatusChanged();

313
	//! Update Android View visibility from visibility of the Qt View and activity of Qt application.
314 315
	virtual void updateAndroidViewVisibility();

316 317 318 319 320 321 322 323 324
	/*!
	 * Request currently visible rectangle of the view.
	 * The result will be sent via visibleRectReceived() signal.
	 * This function returns size of rectangle not covered by software keyboard
	 * and may be used to scroll contents to have a text edit located in
	 * the visible part of the screen.
	 */
	void requestVisibleRect();

325

326 327 328 329 330 331
signals:
	/*!
	 * Emitted when texture has finished updating on Java side and the new image
	 * can be displayed in our Qt app UI (via paintGL()).
	 */
	void updated();
332 333 334 335

	/*!
	 * Emitted when view has been actually created.
	 */
336
	void viewCreated();
337

338 339 340 341 342
	/*!
	 * Emitted when visible screen rect requested via requestVisibleRect() is receieved.
	 */
	void visibleRectReceived(int width, int height);

343 344
private slots:
	void javaUpdate();
345
	void javaViewCreated();
346
	void javaVisibleRectReceived(int left, int top, int right, int bottom);
347

348 349 350
private:
	const QImage * getPreviousBitmapBuffer(bool convert_from_android_format);

351
protected:
352
	const QImage * getBitmapBuffer(bool * out_texture_updated, bool convert_from_android_format);
353
	bool updateGlTexture();
354
	bool updateBitmapToGlTexture();
355
	QJniObject * offscreenView() { return offscreen_view_.data(); }
356
	const QJniObject * offscreenView() const { return offscreen_view_.data(); }
357
	QJniObject * getView();
358

359
private:
360 361
	QString view_class_name_;
	QString view_object_name_;
362

363
	//! Keeps OpenGL texture when painting goes on in GL.
364
	QOpenGLTextureHolder tex_;
365

366
	//! Intermediate buffer used in Bitmap mode to convert Android's BGR to RGB.
367
	QImage android_to_qt_buffer_;
368
	int last_qt_buffer_;
369 370

	//! Double buffer for Bitmap mode.
371
	QAndroidJniImagePair bitmap_a_, bitmap_b_;
372

373 374 375
	//! Used to lock bitmap_a_/bitmap_b_ access.
	QMutex bitmaps_mutex_;

376
	QScopedPointer<QJniObject> offscreen_view_;
377 378
	QSize size_;
	QColor fill_color_;
379 380
	volatile bool need_update_texture_;
	volatile bool view_painted_;
381
	bool texture_received_;
382
	bool view_creation_requested_;
383
	bool is_visible_;
384
	bool is_enabled_;
385
	volatile mutable bool view_created_; //!< Cache for isCreated()
386
	int last_texture_width_, last_texture_height_;
387 388 389
private:
	Q_DISABLE_COPY(QAndroidOffscreenView)
	friend void JNICALL Java_OffscreenView_nativeUpdate(JNIEnv * env, jobject jo, jlong param);
390
	friend void JNICALL Java_OffscreenView_nativeViewCreated(JNIEnv *, jobject, jlong param);
391
	friend void JNICALL Java_OffscreenView_onVisibleRect(JNIEnv *, jobject, jlong param, int left, int top, int right, int bottom);
392 393
};

394 395 396
int QColorToAndroidColor(const QColor & color);