How to wrap List<int[]> with C++Builder 11? - android

I'm struggling to access the data returned by camera_parameters->getSupportedPreviewFpsRange() with C++Builder 11.
The getSupportedPreviewFpsRange() function is described here: https://developer.android.com/reference/android/hardware/Camera.Parameters#getSupportedPreviewFpsRange()
I don't have any issue with wrapping data which is not an array returned by other functions, but I could not find a way to wrap the List<int[]> data type.
The Delphi way to handle this seems to be:
camera_parameters: JCamera_Parameters;
pointer: Pointer;
jobject: JObject;
list_fps_ranges: JList;
currentFpsRange, suitableFPSRange: TJavaArray<Integer>;
...
list_fps_ranges := camera_parameters.getSupportedPreviewFpsRange();
jobject := list_fps_ranges.get(I);
pointer := (jobject as ILocalObject).GetObjectID;
fpsRange := TJavaArray<Integer> (WrapJNIArray(pointer, TypeInfo(TJavaArray<Integer>)));
Unfortunately, I could not find a way to properly translate:
fpsRange := TJavaArray<Integer> (WrapJNIArray(pointer, TypeInfo(TJavaArray<Integer>)));
into C++.
I tried with:
TJavaObjectArray__1<_di_JInteger>
without any luck, as there is no WrapJNIArray() function.
How could it be done?

Unfortunately, I could not find a way to properly translate:
fpsRange := TJavaArray<Integer> (WrapJNIArray(pointer, TypeInfo(TJavaArray<Integer>)));
into C++.
Try something like this:
_di_JCamera_Parameters camera_parameters;
TJavaArray__1<int> *currentFpsRange;
TJavaArray__1<int> *suitableFPSRange;
...
_di_JList list_fps_ranges = camera_parameters->getSupportedPreviewFpsRange();
_di_JObject jobject = list_fps_ranges->get(I);
void *objID = ((_di_ILocalObject)jobject)->GetObjectID();
// alternatively:
//
// #include <Androidapi.Helpers.hpp>
// void *objID = TAndroidHelper::JObjectToID(jobject);
TJavaArray__1<int> *fpsRange = (TJavaArray__1<int>*) WrapJNIArray(objID, __typeinfo(TJavaArray__1<int>));
I tried with:
TJavaObjectArray__1<_di_JInteger>
without any luck, as there is no WrapJNIArray() function.
First, TJavaArray<Integer> translates to TJavaArray__1<int>, not to TJavaObjectArray__1<_di_JInteger>.
Second, WrapJNIArray() is a helper function declared in <Androidapi.JNIBridge.hpp>.

The List<int[] list_fps_ranges example you posted works fine but I'm still confused about how to use WrapJNIArray properly in the case of a simple List<Integer>
I tried to replicate your solution with the getZoomRatios() function which is also part of Camera.Parameters
The getZoomRatios() function returns a List<Integer>
int n_zoomratios;
void *pointer;
_di_JList list_zoomratios;
list_zoomratios= jcamera_parameters->getZoomRatios(); // return List<Integer>
_di_JObject jobject= list_zoomratios; //fishy...
pointer= TAndroidHelper::JObjectToID(jobject);
TJavaArray__1<int> *zoomratios = (TJavaArray__1<int>*) WrapJNIArray(pointer, __typeinfo(TJavaArray__1<int>));
Unfortunately I can not figure which parameters of list_zoomratio should be used by jobject.
In the fps ranges example, the list_preview_fps->get() function returns a JObject which is attributed to jobject but in the case of the list_zoomratios there is no obvious JObject parameter I could use. I tried many different ways but without any success.
What is the proper way to wrap the integer array?
By the way, is there a book/document which describes these wrapping mechanisms? I could not find any information online on this subject.
Thank you for your help

Problem solved this way:
int n_zoomratios;
int i;
void *pointer;
_di_JList list_zoomratios;
_di_JInteger zoomratio;
list_zoomratios= jcamera_parameters->getZoomRatios(); // List<Integer>
n_zoomratios=list_zoomratios->size();
for(i=0;i<n_zoomratios;i++)
{
_di_JObject jobject=list_zoomratios->get(i);
pointer= TAndroidHelper::JObjectToID(jobject);
zoomratio = TJInteger::Wrap(pointer);
sprintf(txt,"%d %d",i,zoomratio->intValue());
log(txt);
}
However I can not figure why I can not convert a full array with WrapJNIArray and I have to convert each element one by one instead.

Related

How to use native string in Retrofit header?

I'm a newbie in the NDK world. I have a key placed in the C/C++ layer and I want to use it when I do a GET with Retrofit.
Here is my code
I have 2 .mk files but they are not relevant to my question and I have a keys.c file where I store the key:
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_hello_world_PlacesListActivity_getNativeKey(JNIEnv *env, jobject instance) {
return (*env)-> NewStringUTF(env, "my_key");
}
I'm able to read the key with this code in my PlacesListActivity:
static {
System.loadLibrary("keys");
}
public static native String getNativeKey();
and now I want to use the native String in the auth header of a GET request in a interface:
public interface TestService {
#Headers({
"client_id:" + PlacesListActivity.getNativeKey()
})
#GET("/some_end_point/")
}
The issue I'm getting is this:
Any ideas on how to fix this? Thank you in advance.
Have you tried implementing the interface and calling the C++ function from there. Interfaces are usually used to declare the function but not actually implement the function.
Ended up modifying the way I pass the auth header
#GET("/some_end_point/")
Observable<PlaceLocations> getPlaces(#Header("client_id") String authKey,#Path("name") String name);

How to convert const char* to KString in Kotlin/Native?

In a C++ file, I want to convert a const char* to KString, so that I can then pass the KString to a Kotlin file using Kotlin/Native.
I believe the answer lies in the function
OBJ_GETTER(utf8ToUtf16, const char* rawString, size_t rawStringLength)
that I found in KString.cpp. But even though I discovered the used define statements in Memory.h, I have not yet managed to properly call the function utf8ToUtf16 from my own C++ file to get a KString. Any help is appreciated.
It depends on how you want to interact with Kotlin code. If you produce dynamic library with -produce dynamic, then string are converted automatically, see for example https://github.com/JetBrains/kotlin-native/blob/adf8614889e8cf5038a79960aa9651ca7d45e409/samples/python_extension/src/main/c/kotlin_bridge.c#L72.
So no additional magic is required at all. Same with Objective-C strings and -produce framework. And for other cases, there shall be no need to pass strings C -> Kotlin (callbacks produced with staticCFunction also do autoconversion).
I ended up taking the pieces to write my own function:
KString getKString(const char* rawString) {
size_t rawStringLength = strlen(rawString);
ObjHeader** OBJ_RESULT;
uint32_t charCount = utf8::unchecked::distance(rawString, rawString + rawStringLength);
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, charCount, OBJ_RESULT)->array();
KChar* rawResult = CharArrayAddressOfElementAt(result, 0);
auto convertResult =
utf8::unchecked::utf8to16(rawString, rawString + rawStringLength, rawResult);
ObjHeader* obj = result->obj();
UpdateReturnRef(OBJ_RESULT, obj);
return (const ArrayHeader*)obj;
}
In my test code (C++), I use it like this:
...
RuntimeState* state = InitRuntime();
KString inMessage;
{
ObjHolder args;
inMessage = getKString("Hello from C++");
}
...
DeinitRuntime(state);
and include Memory.h, Natives.h, Runtime.h, KString.h, utf8.h, stdlib.h, and string. You may be able to get rid of some of these.
As a side remark, you may realize how AllocArrayInstance is used in the function. It would be nice, if one simply could do the same thing for getting a KString, something like:
ObjHeader** OBJ_RESULT;
KString kstr = utf8ToUtf16(rawString, rawStringLength, OBJ_RESULT);
This did not work from my function, since utf8ToUtf16 was not found. I believe the reason is, that (at the time of writing) the respective function in KString.cpp is inside a namespace {...} block, such that it cannot be used from another file. That's why I ended up mimicking the function as shown above.

Android JNI: pass std::string to Java and back to C++

I have a relative large size std::string. And I want to pass it to Java without copying. And then pass back to another JNI lib. What is the best approach?
jlong some_jni_call() {
string str = createLargeString(); // say this is from 3rd lib only returns string
string* strInHeap = new string(str); // this should just increase the reference count?
jlong handle = (long)strInHeap;
return handle;
}
Then I can pass back to JNI:
void use_string(jlong handle) {
string* str = (string*)handle;
// use the str...
delete str; // doesn't look so nice, since people can forget to delete
}
Is this a good approach?
This is surely a feasible approach; you can even use similar trick to pass function pointers back and forth.

coco2d-x call function with multiple arguments?

I'm working on a cocos2d-x project and got stuck. I want to call a function, delay and than call the same function again. I'm using cocos2d-x 2.2.5. and developing for Android.
This is what I got so far:
CCArray *arr = CCArray::create();
arr->addObject(pSprite);
arr->addObject(pGetal);
CCFiniteTimeAction *fun1 = CCCallFuncO::create(this, callfuncO_selector(GameLayer::animateFlip), arr);
CCDelayTime *delay = CCDelayTime::create(1.0);
pSprite->runAction(CCSequence::create(fun1, delay, fun1, NULL));
The method I want to call:
void GameLayer::animateFlip(CCObject *pSObj, CCObject *pGObj){
CCSprite *pSprite = (CCSprite *) pSObj;
CCLabelTTF *pGetal = (CCLabelTTF *) pSObj;
...
...
}
The function is in the same class and requires two arguments. I've tried putting both arguments (CCSprite and CCLabelTTF) in an array, but it crashes on runtime...
When I call the function just like this no errors occur:
this->animateFlip(sprite1, getal1);
Anyone any idea?
Thanks for your answers, I've created a struct for my buttons like Joachim suggested, which has the sprite and the label in it. All the buttons are put in an array. I also added the functions animateFlip and showFlip to it. AnimateFlip works fine and I'm able to call the function from the gamelayer for each indevidial button.
void GameLayer::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
CCTouch *touch = (CCTouch *)pTouches->anyObject();
CCPoint location = touch->getLocationInView();
location = CCDirector::sharedDirector()->convertToGL(location);
for(int i = 0; i < 12; i++){
if(butArr[i].sprite->boundingBox().containsPoint(location)){
butArr[i].animateFlip();
break;
}
}
}
The struct:
struct Button
{
cocos2d::CCSprite *sprite;
cocos2d::CCLabelTTF *getal;
int getalINT;
void showFlip();
void animateFlip();
};
But as rule number one in programming tells us, where one problem is solved, two shall arise, I've stumbled upon a new problem:
void Button::showFlip()
{
CCFiniteTimeAction *fun1 = CCCallFunc::create(this, callfunc_selector(Button::animateFlip));
CCFiniteTimeAction *fun1 = CCCallFunc::create(this, callfunc_selector(Button::animateFlip));
CCDelayTime *delay = CCDelayTime::create(1.0f);
this->runAction(CCSequence::create(fun1, delay, fun2, NULL));
}
The CCCallFunc::create() request a context (I guess?), which is usually 'this', but 'this' in my struct point to Button. How can I get the context of the gamelayer?
Thanks again!

Sending boolean to Pebble with PebbleKit

I've never really done much C and am a bit stumped on the best way to send a boolean from an Android app to the Pebble Watch.
I have strings working fine, but there doesn't seem to be an addBoolean method on PebbleDictionary. As a work around I am trying to use addUint8 to send a 1 or 0, but am having trouble handling the message on the Pebble.
Here is my Android code:
PebbleDictionary data = new PebbleDictionary();
if (isGPSFix()){
data.addUint8(GPS_HAS_FIX_KEY, Byte.valueOf("1"));
} else {
data.addUint8(GPS_HAS_FIX_KEY, Byte.valueOf("0"));
}
PebbleKit.sendDataToPebble(app.getContext(), UUID, data);
And in my Pebble I have a data struct:
static struct MyData {
uint8_t haveGPS[1];
.... // other stuff ommitted
AppSync sync;
uint8_t sync_buffer[256];
} s_data;
And then I am trying to compare it like this in my sync_tuple_changed callback.
static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) {
(void) old_tuple;
switch (key) {
case GPS_HAS_FIX_KEY:
if (memcmp(s_data.haveGPS, new_tuple->value->data, 8) == 0){
memcpy(s_data.haveGPS,new_tuple->value->data, new_tuple->length);
vibes_short_pulse();
}
break;
default:
return;
}
}
The watch doesn't crash, it just never vibrates when the phone drops or acquires GPS.
Things look good on the Android side. I think this is more of an AppSync problem.
Here are a few things to check in the watch application:
Make sure you create a list of tuples with initial values on the watch. This list needs to to contain your key GPS_HAS_FIX_KEY;
Tuplet initial_values[] = {
TupletInteger(GPS_HAS_FIX_KEY, (uint8_t) 0),
/* Other tuplets that you will synchronize */
};
Make sure you pass those tuplets to the app_sync_init() function:
app_sync_init(&sync, sync_buffer, sizeof(sync_buffer),
initial_values, ARRAY_LENGTH(initial_values),
sync_tuple_changed_callback, sync_error_callback, NULL);
Those two steps are required for app_sync to work (cf AppSync reference documentation).
As far as I understand you have to send an Dictionary of keys and objects to the watch. In Objective C it looks like this:
NSDictionary *update = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
Keys are all integer and objects I would think can be of Boolean. In my case only String what works well.
In a loop I perform something like this on the watch:
t=dict_find(received,count);
strcpy(strs[count], dict_find(received,count)->value->cstring);
But I have to tell you that I am still a rookie.
Thanks to sarfata's accepted answer above, I found that I had not added this new item to the Tuple that the Pebble was expecting.
Once I added that, my switch statement started working and I just had to get the memory compare to work. Here is the working code for my memory compare in case it helps anyone.
case GPS_HAS_FIX_KEY:
if (memcmp(s_data.haveGPS, new_tuple->value->data, 1) != 0){
memcpy(s_data.haveGPS,new_tuple->value->data, 1);
vibes_short_pulse();
}
break;
It really was a simple as expecting one byte (not 8 bytes - I thought it was doing a bit compare) and negating the logic for the case where the new value is NOT like the old one.

Categories

Resources