1

so I have this CanvasView which shows debugging information of my app. Its basically overlayed view with transparant background so everything drawn in the canvas is floating in the screen. Since I need some information from native c++ which returns wchar_t*, how can I use env->NewString since android now makes wchar_t is 4 bytes while jchar is 2 bytes?

My java code that calls native c++ function in my lib:

private static String get_Name();
private class CanvasView extends View{
    public CanvasView(Context context){
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Paint paintText = new Paint();
        paintText.setStyle(Paint.Style.FILL);
        paintText.setColor(Color.WHITE);
        paintText.setShadowLayer(5.5f, 0, 0, Color.BLACK);
        paintText.setTextSize(convertSizeToDp(7.5f));
        paintText.setTextAlign(Paint.Align.LEFT);

        paintText.drawText(get_Name(), x, y, paintText);


        // the rest of the code
        // ...
    }
}

get_Name basically return a jstring which come from NewString((const jchar* )myWchar, myLen)

The return results sometimes are non unicode string or even my app is crashing when NewString is called.

Botje
  • 26,269
  • 3
  • 31
  • 41
  • Does this answer your question? [Android Canvas.drawText](https://stackoverflow.com/questions/2655402/android-canvas-drawtext) – TruthSeeker Jan 24 '20 at 09:43
  • 1
    @TruthSeeker no it doesn't, my issue is to convert wchar_t* to jstring. – Yuuki Kuroyama Jan 24 '20 at 09:45
  • if I understand from android.developer site [`Paint`](https://developer.android.com/reference/android/graphics/Paint?hl=en) doesn't expose any public method `drawText` but [`Canvas`](https://developer.android.com/reference/android/graphics/Canvas). Make it `canvas.drawText(get_Name(), x, y, paintText);` – TruthSeeker Jan 24 '20 at 10:03

2 Answers2

1

First, allocate a ByteBuffer using JNI:

wchar_t *input = ...;
jobject bb = env->NewDirectByteBuffer((void *) input, wcslen(input) * sizeof(wchar_t));

Next, invoke Charset.forName("UTF-32LE").decode(bb).toString(): (each paragraph is one step)

jclass cls_Charset = env->FindClass("java/nio/charset/Charset");
jmethodID mid_Charset_forName = env->GetStaticMethodID(cls_Charset, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;");
jobject charset = env->CallStaticObjectMethod(cls_Charset, mid_Charset_forName, env->NewStringUTF("UTF-32LE"));

jmethodID mid_Charset_decode = env->GetMethodID(cls_Charset, "decode", "(Ljava/nio/ByteBuffer;)Ljava/nio/CharBuffer;");
jobject cb = env->CallObjectMethod(charset, mid_Charset_decode, bb);

jclass cls_CharBuffer = env->FindClass("java/nio/CharBuffer");
jmethodID mid_CharBuffer_toString = env->GetMethodID(cls_CharBuffer, "toString", "()Ljava/lang/String;");
jstring ret = env->CallObjectMethod(cb, mid_CharBuffer_toString);

return ret;

Note: this depends on the endianness of the platform you are on. From this answer it seems all Android platforms are little-endian. You may need to use UTF-32BE instead if you are on a big-endian platform.

Botje
  • 26,269
  • 3
  • 31
  • 41
0

The only platform where wchar_t is 2 bytes is Windows. On other platforms, wchar_t is 4 bytes. env->NewString() takes UTF-16 data as input, so you are going to have to convert your wchar_t data from UTF-32 to UTF-16 before calling env->NewString(). Otherwise, you will have to use JNI to invoke the Java String constructor that takes a byte[] array and charset as input, so you can create a string directly from UTF-32 data.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770