1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 #include <android/log.h>
13 #include <android/looper.h>
14 #include <android/bitmap.h>
16 #include <android/androidinst.hxx>
17 #include <headless/svpdummies.hxx>
18 #include <generic/gendata.hxx>
19 #include <osl/detail/android-bootstrap.h>
20 #include <rtl/strbuf.hxx>
21 #include <basebmp/scanlineformats.hxx>
22 #include <vcl/settings.hxx>
24 #define LOGTAG "LibreOffice/androidinst"
25 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOGTAG, __VA_ARGS__))
26 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOGTAG, __VA_ARGS__))
27 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOGTAG, __VA_ARGS__))
29 static jclass appClass
= 0;
32 static int viewWidth
= 1, viewHeight
= 1;
34 class AndroidSalData
: public SalGenericData
37 AndroidSalData( SalInstance
*pInstance
) : SalGenericData( SAL_DATA_ANDROID
, pInstance
) {}
38 virtual void ErrorTrapPush() {}
39 virtual bool ErrorTrapPop( bool ) { return false; }
42 static void BlitFrameRegionToWindow(ANativeWindow_Buffer
*pOutBuffer
,
43 const basebmp::BitmapDeviceSharedPtr
& aDev
,
44 const ARect
&rSrcRect
,
45 int nDestX
, int nDestY
)
47 basebmp::RawMemorySharedArray aSrcData
= aDev
->getBuffer();
48 basegfx::B2IVector aDevSize
= aDev
->getSize();
49 sal_Int32 nStride
= aDev
->getScanlineStride();
50 unsigned char *pSrc
= aSrcData
.get();
52 // FIXME: do some cropping goodness on aSrcRect to ensure no overflows etc.
53 ARect aSrcRect
= rSrcRect
;
55 for (unsigned int y
= 0; y
< (unsigned int)(aSrcRect
.bottom
- aSrcRect
.top
); y
++)
57 unsigned char *sp
= ( pSrc
+ nStride
* (aSrcRect
.top
+ y
) +
58 aSrcRect
.left
* 4 /* src pixel size */ );
60 switch (pOutBuffer
->format
) {
61 case WINDOW_FORMAT_RGBA_8888
:
62 case WINDOW_FORMAT_RGBX_8888
:
64 unsigned char *dp
= ( (unsigned char *)pOutBuffer
->bits
+
65 pOutBuffer
->stride
* 4 * (y
+ nDestY
) +
66 nDestX
* 4 /* dest pixel size */ );
67 for (unsigned int x
= 0; x
< (unsigned int)(aSrcRect
.right
- aSrcRect
.left
); x
++)
69 dp
[x
*4 + 0] = sp
[x
*4 + 0]; // R
70 dp
[x
*4 + 1] = sp
[x
*4 + 1]; // G
71 dp
[x
*4 + 2] = sp
[x
*4 + 2]; // B
72 dp
[x
*4 + 3] = 255; // A
76 case WINDOW_FORMAT_RGB_565
:
78 unsigned char *dp
= ( (unsigned char *)pOutBuffer
->bits
+
79 pOutBuffer
->stride
* 2 * (y
+ nDestY
) +
80 nDestX
* 2 /* dest pixel size */ );
81 for (unsigned int x
= 0; x
< (unsigned int)(aSrcRect
.right
- aSrcRect
.left
); x
++)
83 unsigned char b
= sp
[x
*3 + 0]; // B
84 unsigned char g
= sp
[x
*3 + 1]; // G
85 unsigned char r
= sp
[x
*3 + 2]; // R
86 dp
[x
*2 + 0] = (r
& 0xf8) | (g
>> 5);
87 dp
[x
*2 + 1] = ((g
& 0x1c) << 5) | ((b
& 0xf8) >> 3);
92 LOGI("unknown pixel format %d !", pOutBuffer
->format
);
98 void AndroidSalInstance::BlitFrameToWindow(ANativeWindow_Buffer
*pOutBuffer
,
99 const basebmp::BitmapDeviceSharedPtr
& aDev
)
101 basegfx::B2IVector aDevSize
= aDev
->getSize();
102 ARect aWhole
= { 0, 0, aDevSize
.getX(), aDevSize
.getY() };
103 BlitFrameRegionToWindow(pOutBuffer
, aDev
, aWhole
, 0, 0);
106 void AndroidSalInstance::RedrawWindows(ANativeWindow_Buffer
*pBuffer
)
108 if (pBuffer
->bits
!= NULL
)
111 std::list
< SalFrame
* >::const_iterator it
;
112 for ( it
= getFrames().begin(); it
!= getFrames().end(); i
++, ++it
)
114 SvpSalFrame
*pFrame
= static_cast<SvpSalFrame
*>(*it
);
116 if (pFrame
->IsVisible())
118 BlitFrameToWindow (pBuffer
, pFrame
->getDevice());
123 LOGI("no buffer for locked window");
126 void AndroidSalInstance::damaged(AndroidSalFrame */
* frame */
)
128 static bool beenHere
= false;
129 static jmethodID nCallbackDamaged
= 0;
131 // Check if we are running in an app that has registered for damage callbacks
132 // static public void callbackDamaged();
133 // Call the Java layer to post an invalidate if necessary
135 if (appClass
!= 0 && !beenHere
) {
136 nCallbackDamaged
= m_pJNIEnv
->GetStaticMethodID(appClass
, "callbackDamaged", "()V");
137 if (nCallbackDamaged
== 0)
138 LOGE("Could not find the callbackDamaged method");
142 if (appClass
!= 0 && nCallbackDamaged
!= 0)
143 m_pJNIEnv
->CallStaticVoidMethod(appClass
, nCallbackDamaged
);
146 void AndroidSalInstance::GetWorkArea( Rectangle
& rRect
)
148 rRect
= Rectangle( Point( 0, 0 ),
149 Size( viewWidth
, viewHeight
) );
153 * Try too hard to get a frame, in the absence of anything better to do
155 SalFrame
*AndroidSalInstance::getFocusFrame() const
157 SalFrame
*pFocus
= SvpSalFrame::GetFocusFrame();
159 LOGI("no focus frame, re-focusing first visible frame");
160 const std::list
< SalFrame
* >& rFrames( getFrames() );
161 for( std::list
< SalFrame
* >::const_iterator it
= rFrames
.begin(); it
!= rFrames
.end(); ++it
)
163 SvpSalFrame
*pFrame
= const_cast<SvpSalFrame
*>(static_cast<const SvpSalFrame
*>(*it
));
164 if( pFrame
->IsVisible() )
175 AndroidSalInstance
*AndroidSalInstance::getInstance()
177 if (!ImplGetSVData())
179 AndroidSalData
*pData
= static_cast<AndroidSalData
*>(ImplGetSVData()->mpSalData
);
182 return static_cast<AndroidSalInstance
*>(pData
->m_pInstance
);
185 AndroidSalInstance::AndroidSalInstance( SalYieldMutex
*pMutex
)
186 : SvpSalInstance( pMutex
)
188 int res
= (lo_get_javavm())->AttachCurrentThread(&m_pJNIEnv
, NULL
);
189 LOGI("AttachCurrentThread res=%d env=%p", res
, m_pJNIEnv
);
192 AndroidSalInstance::~AndroidSalInstance()
194 int res
= (lo_get_javavm())->DetachCurrentThread();
195 LOGI("DetachCurrentThread res=%d", res
);
196 LOGI("destroyed Android Sal Instance");
199 bool AndroidSalInstance::AnyInput( VclInputFlags nType
)
201 if( nType
& VclInputFlags::TIMER
)
202 return CheckTimeout( false );
204 // Unfortunately there is no way to check for a specific type of
205 // input being queued. That information is too hidden, sigh.
206 return SvpSalInstance::s_pDefaultInstance
->PostedEventsInQueue();
209 class AndroidSalSystem
: public SvpSalSystem
{
211 AndroidSalSystem() : SvpSalSystem() {}
212 virtual ~AndroidSalSystem() {}
213 virtual int ShowNativeDialog( const OUString
& rTitle
,
214 const OUString
& rMessage
,
215 const std::list
< OUString
>& rButtons
,
219 SalSystem
*AndroidSalInstance::CreateSalSystem()
221 return new AndroidSalSystem();
224 class AndroidSalFrame
: public SvpSalFrame
227 AndroidSalFrame( AndroidSalInstance
*pInstance
,
229 sal_uLong nSalFrameStyle
,
230 SystemParentData
*pSysParent
)
231 : SvpSalFrame( pInstance
, pParent
, nSalFrameStyle
,
232 true, basebmp::FORMAT_THIRTYTWO_BIT_TC_MASK_RGBA
,
235 enableDamageTracker();
236 if (pParent
== NULL
&& viewWidth
> 1 && viewHeight
> 1)
237 SetPosSize(0, 0, viewWidth
, viewHeight
, SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
);
240 virtual void GetWorkArea( Rectangle
& rRect
)
242 AndroidSalInstance::getInstance()->GetWorkArea( rRect
);
245 virtual void damaged( const basegfx::B2IBox
& rDamageRect
)
247 if (rDamageRect
.getWidth() <= 0 ||
248 rDamageRect
.getHeight() <= 0)
252 AndroidSalInstance::getInstance()->damaged( this );
255 virtual void UpdateSettings( AllSettings
&rSettings
)
257 // Clobber the UI fonts
259 psp::FastPrintFontInfo aInfo
;
260 aInfo
.m_aFamilyName
= "Roboto";
261 aInfo
.m_eItalic
= ITALIC_NORMAL
;
262 aInfo
.m_eWeight
= WEIGHT_NORMAL
;
263 aInfo
.m_eWidth
= WIDTH_NORMAL
;
264 psp::PrintFontManager::get().matchFont( aInfo
, rSettings
.GetUILocale() );
267 // FIXME: is 14 point enough ?
268 vcl::Font
aFont( OUString( "Roboto" ), Size( 0, 14 ) );
270 StyleSettings aStyleSet
= rSettings
.GetStyleSettings();
271 aStyleSet
.SetAppFont( aFont
);
272 aStyleSet
.SetHelpFont( aFont
);
273 aStyleSet
.SetMenuFont( aFont
);
274 aStyleSet
.SetToolFont( aFont
);
275 aStyleSet
.SetLabelFont( aFont
);
276 aStyleSet
.SetInfoFont( aFont
);
277 aStyleSet
.SetRadioCheckFont( aFont
);
278 aStyleSet
.SetPushButtonFont( aFont
);
279 aStyleSet
.SetFieldFont( aFont
);
280 aStyleSet
.SetIconFont( aFont
);
281 aStyleSet
.SetTabFont( aFont
);
282 aStyleSet
.SetGroupFont( aFont
);
284 rSettings
.SetStyleSettings( aStyleSet
);
288 SalFrame
*AndroidSalInstance::CreateChildFrame( SystemParentData
* pParent
, sal_uLong nStyle
)
290 return new AndroidSalFrame( this, NULL
, nStyle
, pParent
);
293 SalFrame
*AndroidSalInstance::CreateFrame( SalFrame
* pParent
, sal_uLong nStyle
)
295 return new AndroidSalFrame( this, pParent
, nStyle
, NULL
);
298 // All the interesting stuff is slaved from the AndroidSalInstance
299 void InitSalData() {}
300 void DeInitSalData() {}
301 void InitSalMain() {}
303 void SalAbort( const OUString
& rErrorText
, bool bDumpCore
)
305 OUString
aError( rErrorText
);
306 if( aError
.isEmpty() )
307 aError
= "Unknown application error";
308 LOGI("%s", OUStringToOString(rErrorText
, osl_getThreadTextEncoding()).getStr() );
310 LOGI("SalAbort: '%s'",
311 OUStringToOString(aError
, RTL_TEXTENCODING_ASCII_US
).getStr());
318 const OUString
& SalGetDesktopEnvironment()
320 static OUString
aEnv( "android" );
335 // This is our main entry point:
336 SalInstance
*CreateSalInstance()
338 LOGI("Android: CreateSalInstance!");
339 AndroidSalInstance
* pInstance
= new AndroidSalInstance( new SalYieldMutex() );
340 new AndroidSalData( pInstance
);
341 pInstance
->AcquireYieldMutex(1);
345 void DestroySalInstance( SalInstance
*pInst
)
347 pInst
->ReleaseYieldMutex();
351 #include <vcl/layout.hxx>
353 int AndroidSalSystem::ShowNativeDialog( const OUString
& rTitle
,
354 const OUString
& rMessage
,
355 const std::list
< OUString
>& rButtons
,
358 (void)rButtons
; (void)nDefButton
;
359 LOGI("LibreOffice native dialog '%s': '%s'",
360 OUStringToOString(rTitle
, RTL_TEXTENCODING_ASCII_US
).getStr(),
361 OUStringToOString(rMessage
, RTL_TEXTENCODING_ASCII_US
).getStr());
362 LOGI("Dialog '%s': '%s'",
363 OUStringToOString(rTitle
, RTL_TEXTENCODING_ASCII_US
).getStr(),
364 OUStringToOString(rMessage
, RTL_TEXTENCODING_ASCII_US
).getStr());
366 if (AndroidSalInstance::getInstance() != NULL
)
368 // Does Android have a native dialog ? if not,. we have to do this ...
370 // Of course it has. android.app.AlertDialog seems like a good
371 // choice, it even has one, two or three buttons. Naturally,
372 // it intended to be used from Java, so some verbose JNI
373 // horror would be needed to use it directly here. Probably we
374 // want some easier to use magic wrapper, hmm.
376 MessageDialog
aVclErrBox(NULL
, rMessage
);
377 aVclErrBox
.SetText(rTitle
);
378 aVclErrBox
.Execute();
381 LOGE("VCL not initialized");
385 // public static native void renderVCL(Bitmap bitmap);
386 extern "C" SAL_JNI_EXPORT
void JNICALL
387 Java_org_libreoffice_android_AppSupport_renderVCL(JNIEnv
*env
,
391 AndroidBitmapInfo info
;
395 if ((ret
= AndroidBitmap_getInfo(env
, bitmap
, &info
)) < 0) {
396 LOGI("AndroidBitmap_getInfo() failed ! error=%d", ret
);
400 if ((ret
= AndroidBitmap_lockPixels(env
, bitmap
, &pixels
)) < 0) {
401 LOGI("AndroidBitmap_lockPixels() failed ! error=%d", ret
);
405 typedef struct ANativeWindow_Buffer {
406 // The number of pixels that are show horizontally.
409 // The number of pixels that are shown vertically.
412 // The number of *pixels* that a line in the buffer takes in
413 // memory. This may be >= width.
416 // The format of the buffer. One of WINDOW_FORMAT_*
423 uint32_t reserved[6];
424 } ANativeWindow_Buffer;
427 ANativeWindow_Buffer dummyOut
; // look like a window for now ...
428 dummyOut
.width
= info
.width
;
429 dummyOut
.height
= info
.height
;
430 dummyOut
.stride
= info
.stride
/ 4; // sigh !
431 dummyOut
.format
= info
.format
;
432 dummyOut
.bits
= pixels
;
433 AndroidSalInstance::getInstance()->RedrawWindows (&dummyOut
);
435 AndroidBitmap_unlockPixels(env
, bitmap
);
438 // public static native void registerForDamageCallback(Class destinationClass);
439 extern "C" SAL_JNI_EXPORT
void JNICALL
440 Java_org_libreoffice_android_AppSupport_registerForDamageCallback(JNIEnv
* env
,
442 jclass destinationClass
)
444 appClass
= (jclass
) env
->NewGlobalRef(destinationClass
);
447 // public static native void setViewSize(int width, int height);
448 extern "C" SAL_JNI_EXPORT
void JNICALL
449 Java_org_libreoffice_android_AppSupport_setViewSize(JNIEnv
* /* env */,
459 // public static native void key(char c);
460 extern "C" SAL_JNI_EXPORT
void JNICALL
461 Java_org_libreoffice_android_AppSupport_key(JNIEnv
* /* env */,
465 SalFrame
*pFocus
= AndroidSalInstance::getInstance()->getFocusFrame();
467 KeyEvent
aEvent(c
, c
, 0);
468 Application::PostKeyEvent(VCLEVENT_WINDOW_KEYINPUT
, pFocus
->GetWindow(), &aEvent
);
469 Application::PostKeyEvent(VCLEVENT_WINDOW_KEYUP
, pFocus
->GetWindow(), &aEvent
);
472 LOGW("No focused frame to emit event on");
475 // public static native void touch(int action, int x, int y);
476 extern "C" SAL_JNI_EXPORT
void JNICALL
477 Java_org_libreoffice_android_AppSupport_touch(JNIEnv
* /* env */,
483 SalFrame
*pFocus
= AndroidSalInstance::getInstance()->getFocusFrame();
489 case AMOTION_EVENT_ACTION_DOWN
:
490 aEvent
= MouseEvent(Point(x
, y
), 1, MouseEventModifiers::SIMPLECLICK
, MOUSE_LEFT
);
491 nEvent
= VCLEVENT_WINDOW_MOUSEBUTTONDOWN
;
493 case AMOTION_EVENT_ACTION_UP
:
494 aEvent
= MouseEvent(Point(x
, y
), 1, MouseEventModifiers::SIMPLECLICK
, MOUSE_LEFT
);
495 nEvent
= VCLEVENT_WINDOW_MOUSEBUTTONUP
;
497 case AMOTION_EVENT_ACTION_MOVE
:
498 aEvent
= MouseEvent(Point(x
, y
), 1, MouseEventModifiers::SIMPLEMOVE
, MOUSE_LEFT
);
499 nEvent
= VCLEVENT_WINDOW_MOUSEMOVE
;
502 LOGE("AppSupport.touch: Invalid action %d", action
);
505 Application::PostMouseEvent(nEvent
, pFocus
->GetWindow(), &aEvent
);
508 LOGW("No focused frame to emit event on");
511 // public static native void zoom(float scale, int x, int y);
512 extern "C" SAL_JNI_EXPORT
void JNICALL
513 Java_org_libreoffice_android_AppSupport_zoom(JNIEnv
* /* env */,
519 SalFrame
*pFocus
= AndroidSalInstance::getInstance()->getFocusFrame();
521 SAL_INFO( "vcl.androidinst", "zoom: " << scale
<< "@(" << x
<< "," << y
<< ")" );
522 ZoomEvent
aEvent( Point( x
, y
), scale
);
523 Application::PostZoomEvent(VCLEVENT_WINDOW_ZOOM
, pFocus
->GetWindow(), &aEvent
);
526 LOGW("No focused frame to emit event on");
529 // public static native void scroll(int x, int y);
530 extern "C" SAL_JNI_EXPORT
void JNICALL
531 Java_org_libreoffice_android_AppSupport_scroll(JNIEnv
* /* env */,
536 SalFrame
*pFocus
= AndroidSalInstance::getInstance()->getFocusFrame();
538 SAL_INFO( "vcl.androidinst", "scroll: " << "(" << x
<< "," << y
<< ")" );
539 ScrollEvent
aEvent( x
, y
);
540 Application::PostScrollEvent(VCLEVENT_WINDOW_SCROLL
, pFocus
->GetWindow(), &aEvent
);
543 LOGW("No focused frame to emit event on");
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */