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 <touch/touch.h>
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 LOGI("destroyed Android Sal Instance");
197 bool AndroidSalInstance::AnyInput( sal_uInt16 nType
)
199 if( (nType
& VCL_INPUT_TIMER
) != 0 )
200 return CheckTimeout( false );
202 // Unfortunately there is no way to check for a specific type of
203 // input being queued. That information is too hidden, sigh.
204 return SvpSalInstance::s_pDefaultInstance
->PostedEventsInQueue();
207 class AndroidSalSystem
: public SvpSalSystem
{
209 AndroidSalSystem() : SvpSalSystem() {}
210 virtual ~AndroidSalSystem() {}
211 virtual int ShowNativeDialog( const OUString
& rTitle
,
212 const OUString
& rMessage
,
213 const std::list
< OUString
>& rButtons
,
217 SalSystem
*AndroidSalInstance::CreateSalSystem()
219 return new AndroidSalSystem();
222 class AndroidSalFrame
: public SvpSalFrame
225 AndroidSalFrame( AndroidSalInstance
*pInstance
,
227 sal_uLong nSalFrameStyle
,
228 SystemParentData
*pSysParent
)
229 : SvpSalFrame( pInstance
, pParent
, nSalFrameStyle
,
230 true, basebmp::Format::THIRTYTWO_BIT_TC_MASK_RGBA
,
233 enableDamageTracker();
234 if (pParent
== NULL
&& viewWidth
> 1 && viewHeight
> 1)
235 SetPosSize(0, 0, viewWidth
, viewHeight
, SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
);
238 virtual void GetWorkArea( Rectangle
& rRect
)
240 AndroidSalInstance::getInstance()->GetWorkArea( rRect
);
243 virtual void damaged( const basegfx::B2IBox
& rDamageRect
)
245 if (rDamageRect
.getWidth() <= 0 ||
246 rDamageRect
.getHeight() <= 0)
250 AndroidSalInstance::getInstance()->damaged( this );
253 virtual void UpdateSettings( AllSettings
&rSettings
)
255 // Clobber the UI fonts
257 psp::FastPrintFontInfo aInfo
;
258 aInfo
.m_aFamilyName
= OUString( "Roboto" );
259 aInfo
.m_eItalic
= ITALIC_NORMAL
;
260 aInfo
.m_eWeight
= WEIGHT_NORMAL
;
261 aInfo
.m_eWidth
= WIDTH_NORMAL
;
262 psp::PrintFontManager::get().matchFont( aInfo
, rSettings
.GetUILocale() );
265 // FIXME: is 14 point enough ?
266 Font
aFont( OUString( "Roboto" ), Size( 0, 14 ) );
268 StyleSettings aStyleSet
= rSettings
.GetStyleSettings();
269 aStyleSet
.SetAppFont( aFont
);
270 aStyleSet
.SetHelpFont( aFont
);
271 aStyleSet
.SetMenuFont( aFont
);
272 aStyleSet
.SetToolFont( aFont
);
273 aStyleSet
.SetLabelFont( aFont
);
274 aStyleSet
.SetInfoFont( aFont
);
275 aStyleSet
.SetRadioCheckFont( aFont
);
276 aStyleSet
.SetPushButtonFont( aFont
);
277 aStyleSet
.SetFieldFont( aFont
);
278 aStyleSet
.SetIconFont( aFont
);
279 aStyleSet
.SetGroupFont( aFont
);
281 rSettings
.SetStyleSettings( aStyleSet
);
285 SalFrame
*AndroidSalInstance::CreateChildFrame( SystemParentData
* pParent
, sal_uLong nStyle
)
287 return new AndroidSalFrame( this, NULL
, nStyle
, pParent
);
290 SalFrame
*AndroidSalInstance::CreateFrame( SalFrame
* pParent
, sal_uLong nStyle
)
292 return new AndroidSalFrame( this, pParent
, nStyle
, NULL
);
296 // All the interesting stuff is slaved from the AndroidSalInstance
297 void InitSalData() {}
298 void DeInitSalData() {}
299 void InitSalMain() {}
301 void SalAbort( const OUString
& rErrorText
, bool bDumpCore
)
303 OUString
aError( rErrorText
);
304 if( aError
.isEmpty() )
305 aError
= OUString::createFromAscii("Unknown application error");
306 LOGI("%s", OUStringToOString(rErrorText
, osl_getThreadTextEncoding()).getStr() );
308 LOGI("SalAbort: '%s'",
309 OUStringToOString(aError
, RTL_TEXTENCODING_ASCII_US
).getStr());
316 const OUString
& SalGetDesktopEnvironment()
318 static OUString
aEnv( "android" );
333 // This is our main entry point:
334 SalInstance
*CreateSalInstance()
336 LOGI("Android: CreateSalInstance!");
337 AndroidSalInstance
* pInstance
= new AndroidSalInstance( new SalYieldMutex() );
338 new AndroidSalData( pInstance
);
339 pInstance
->AcquireYieldMutex(1);
343 void DestroySalInstance( SalInstance
*pInst
)
345 pInst
->ReleaseYieldMutex();
349 #include <vcl/msgbox.hxx>
351 int AndroidSalSystem::ShowNativeDialog( const OUString
& rTitle
,
352 const OUString
& rMessage
,
353 const std::list
< OUString
>& rButtons
,
356 (void)rButtons
; (void)nDefButton
;
357 LOGI("LibreOffice native dialog '%s': '%s'",
358 OUStringToOString(rTitle
, RTL_TEXTENCODING_ASCII_US
).getStr(),
359 OUStringToOString(rMessage
, RTL_TEXTENCODING_ASCII_US
).getStr());
360 LOGI("Dialog '%s': '%s'",
361 OUStringToOString(rTitle
, RTL_TEXTENCODING_ASCII_US
).getStr(),
362 OUStringToOString(rMessage
, RTL_TEXTENCODING_ASCII_US
).getStr());
364 if (AndroidSalInstance::getInstance() != NULL
)
366 // Does Android have a native dialog ? if not,. we have to do this ...
368 // Of course it has. android.app.AlertDialog seems like a good
369 // choice, it even has one, two or three buttons. Naturally,
370 // it intended to be used from Java, so some verbose JNI
371 // horror would be needed to use it directly here. Probably we
372 // want some easier to use magic wrapper, hmm.
374 ErrorBox
aVclErrBox( NULL
, WB_OK
, rTitle
);
375 aVclErrBox
.SetText( rMessage
);
376 aVclErrBox
.Execute();
379 LOGE("VCL not initialized");
383 // public static native void renderVCL(Bitmap bitmap);
384 extern "C" SAL_JNI_EXPORT
void JNICALL
385 Java_org_libreoffice_android_AppSupport_renderVCL(JNIEnv
*env
,
389 AndroidBitmapInfo info
;
393 if ((ret
= AndroidBitmap_getInfo(env
, bitmap
, &info
)) < 0) {
394 LOGI("AndroidBitmap_getInfo() failed ! error=%d", ret
);
398 if ((ret
= AndroidBitmap_lockPixels(env
, bitmap
, &pixels
)) < 0) {
399 LOGI("AndroidBitmap_lockPixels() failed ! error=%d", ret
);
403 typedef struct ANativeWindow_Buffer {
404 // The number of pixels that are show horizontally.
407 // The number of pixels that are shown vertically.
410 // The number of *pixels* that a line in the buffer takes in
411 // memory. This may be >= width.
414 // The format of the buffer. One of WINDOW_FORMAT_*
421 uint32_t reserved[6];
422 } ANativeWindow_Buffer;
425 ANativeWindow_Buffer dummyOut
; // look like a window for now ...
426 dummyOut
.width
= info
.width
;
427 dummyOut
.height
= info
.height
;
428 dummyOut
.stride
= info
.stride
/ 4; // sigh !
429 dummyOut
.format
= info
.format
;
430 dummyOut
.bits
= pixels
;
431 AndroidSalInstance::getInstance()->RedrawWindows (&dummyOut
);
433 AndroidBitmap_unlockPixels(env
, bitmap
);
436 // public static native void registerForDamageCallback(Class destinationClass);
437 extern "C" SAL_JNI_EXPORT
void JNICALL
438 Java_org_libreoffice_android_AppSupport_registerForDamageCallback(JNIEnv
* env
,
440 jclass destinationClass
)
442 appClass
= (jclass
) env
->NewGlobalRef(destinationClass
);
445 // public static native void setViewSize(int width, int height);
446 extern "C" SAL_JNI_EXPORT
void JNICALL
447 Java_org_libreoffice_android_AppSupport_setViewSize(JNIEnv
* /* env */,
457 // public static native void key(char c);
458 extern "C" SAL_JNI_EXPORT
void JNICALL
459 Java_org_libreoffice_android_AppSupport_key(JNIEnv
* /* env */,
463 SalFrame
*pFocus
= AndroidSalInstance::getInstance()->getFocusFrame();
465 KeyEvent
aEvent(c
, c
, 0);
466 Application::PostKeyEvent(VCLEVENT_WINDOW_KEYINPUT
, pFocus
->GetWindow(), &aEvent
);
467 Application::PostKeyEvent(VCLEVENT_WINDOW_KEYUP
, pFocus
->GetWindow(), &aEvent
);
470 LOGW("No focused frame to emit event on");
473 // public static native void touch(int action, int x, int y);
474 extern "C" SAL_JNI_EXPORT
void JNICALL
475 Java_org_libreoffice_android_AppSupport_touch(JNIEnv
* /* env */,
481 SalFrame
*pFocus
= AndroidSalInstance::getInstance()->getFocusFrame();
487 case AMOTION_EVENT_ACTION_DOWN
:
488 aEvent
= MouseEvent(Point(x
, y
), 1, MOUSE_SIMPLECLICK
, MOUSE_LEFT
);
489 nEvent
= VCLEVENT_WINDOW_MOUSEBUTTONDOWN
;
491 case AMOTION_EVENT_ACTION_UP
:
492 aEvent
= MouseEvent(Point(x
, y
), 1, MOUSE_SIMPLECLICK
, MOUSE_LEFT
);
493 nEvent
= VCLEVENT_WINDOW_MOUSEBUTTONUP
;
495 case AMOTION_EVENT_ACTION_MOVE
:
496 aEvent
= MouseEvent(Point(x
, y
), 1, MOUSE_SIMPLEMOVE
, MOUSE_LEFT
);
497 nEvent
= VCLEVENT_WINDOW_MOUSEMOVE
;
500 LOGE("AppSupport.touch: Invalid action %d", action
);
503 Application::PostMouseEvent(nEvent
, pFocus
->GetWindow(), &aEvent
);
506 LOGW("No focused frame to emit event on");
509 // public static native void zoom(float scale, int x, int y);
510 extern "C" SAL_JNI_EXPORT
void JNICALL
511 Java_org_libreoffice_android_AppSupport_zoom(JNIEnv
* /* env */,
517 SalFrame
*pFocus
= AndroidSalInstance::getInstance()->getFocusFrame();
519 SAL_INFO( "vcl.androidinst", "zoom: " << scale
<< "@(" << x
<< "," << y
<< ")" );
520 ZoomEvent
aEvent( Point( x
, y
), scale
);
521 Application::PostZoomEvent(VCLEVENT_WINDOW_ZOOM
, pFocus
->GetWindow(), &aEvent
);
524 LOGW("No focused frame to emit event on");
527 // public static native void scroll(int x, int y);
528 extern "C" SAL_JNI_EXPORT
void JNICALL
529 Java_org_libreoffice_android_AppSupport_scroll(JNIEnv
* /* env */,
534 SalFrame
*pFocus
= AndroidSalInstance::getInstance()->getFocusFrame();
536 SAL_INFO( "vcl.androidinst", "scroll: " << "(" << x
<< "," << y
<< ")" );
537 ScrollEvent
aEvent( x
, y
);
538 Application::PostScrollEvent(VCLEVENT_WINDOW_SCROLL
, pFocus
->GetWindow(), &aEvent
);
541 LOGW("No focused frame to emit event on");
554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */