AndroidTypeface (const Font& font) : Typeface (font.getTypefaceName()), ascent (0), descent (0) { jint flags = 0; if (font.isBold()) flags = 1; if (font.isItalic()) flags += 2; JNIEnv* env = getEnv(); File fontFile (File ("/system/fonts").getChildFile (name).withFileExtension (".ttf")); if (fontFile.exists()) typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile, javaString (fontFile.getFullPathName()).get())); else typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create, javaString (getName()).get(), flags)); rect = GlobalRef (env->NewObject (RectClass, RectClass.constructor, 0, 0, 0, 0)); paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality)); const LocalRef<jobject> ignored (paint.callObjectMethod (Paint.setTypeface, typeface.get())); const float standardSize = 256.0f; paint.callVoidMethod (Paint.setTextSize, standardSize); ascent = std::abs (paint.callFloatMethod (Paint.ascent)) / standardSize; descent = paint.callFloatMethod (Paint.descent) / standardSize; const float height = ascent + descent; unitsToHeightScaleFactor = 1.0f / 256.0f; }
AndroidTypeface (const Font& font) : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), ascent (0), descent (0), heightToPointsFactor (1.0f) { JNIEnv* const env = getEnv(); // First check whether there's an embedded asset with this font name: typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromAsset, javaString ("fonts/" + name).get())); if (typeface.get() == nullptr) { const bool isBold = style.contains ("Bold"); const bool isItalic = style.contains ("Italic"); File fontFile (getFontFile (name, style)); if (! fontFile.exists()) fontFile = findFontFile (name, isBold, isItalic); if (fontFile.exists()) typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile, javaString (fontFile.getFullPathName()).get())); else typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create, javaString (getName()).get(), (isBold ? 1 : 0) + (isItalic ? 2 : 0))); } initialise (env); }
//============================================================================== WebInputStream (String address, bool isPost, const MemoryBlock& postData, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers, int timeOutMs, StringPairArray* responseHeaders) { if (! address.contains ("://")) address = "http://" + address; JNIEnv* env = getEnv(); jbyteArray postDataArray = 0; if (postData.getSize() > 0) { postDataArray = env->NewByteArray (postData.getSize()); env->SetByteArrayRegion (postDataArray, 0, postData.getSize(), (const jbyte*) postData.getData()); } LocalRef<jobject> responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor)); stream = GlobalRef (env->CallStaticObjectMethod (BeastAppActivity, BeastAppActivity.createHTTPStream, javaString (address).get(), (jboolean) isPost, postDataArray, javaString (headers).get(), (jint) timeOutMs, responseHeaderBuffer.get())); if (postDataArray != 0) env->DeleteLocalRef (postDataArray); if (stream != 0) { StringArray headerLines; { LocalRef<jstring> headersString ((jstring) env->CallObjectMethod (responseHeaderBuffer.get(), StringBuffer.toString)); headerLines.addLines (beastString (env, headersString)); } if (responseHeaders != 0) { for (int i = 0; i < headerLines.size(); ++i) { const String& header = headerLines[i]; const String key (header.upToFirstOccurrenceOf (": ", false, false)); const String value (header.fromFirstOccurrenceOf (": ", false, false)); const String previousValue ((*responseHeaders) [key]); responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); } } } }
jstring JavaStringTest::nativeGetJavaStringUtf8(JNIEnv *env, jobject javaThis) { #if HAS_RAW_STRING_LITERALS JavaString javaString(TEST_UTF8_STRING); return javaString.toJavaString(env).leak(); #else return NULL; #endif }
static void unpairBluetoothMidiDevice (const String& bluetoothAddress) { JNIEnv* env = getEnv(); LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager)); if (btManager.get() != nullptr) env->CallVoidMethod (btManager.get(), AndroidBluetoothManager.unpairBluetoothMidiDevice, javaString (bluetoothAddress).get()); }
int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { jassert (callback != 0); // on android, all alerts must be non-modal!! android.activity.callVoidMethod (JuceAppActivity.showYesNoCancelBox, javaString (title).get(), javaString (message).get(), (jlong) (pointer_sized_int) callback); return 0; }
//============================================================================== static bool isBluetoothDevicePaired (const String& address) { JNIEnv* env = getEnv(); LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager)); if (btManager.get() == nullptr) return false; return env->CallBooleanMethod (btManager.get(), AndroidBluetoothManager.isBluetoothDevicePaired, javaString (address).get()); }
//============================================================================== static bool pairBluetoothMidiDevice (const String& bluetoothAddress) { JNIEnv* env = getEnv(); LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager)); if (btManager.get() == nullptr) return false; jboolean result = env->CallBooleanMethod (btManager.get(), AndroidBluetoothManager.pairBluetoothMidiDevice, javaString (bluetoothAddress).get()); return result; }
AndroidTypeface (const Font& font) : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), ascent (0), descent (0), heightToPointsFactor (1.0f) { JNIEnv* const env = getEnv(); const bool isBold = style.contains ("Bold"); const bool isItalic = style.contains ("Italic"); File fontFile (getFontFile (name, style)); if (! fontFile.exists()) fontFile = findFontFile (name, isBold, isItalic); if (fontFile.exists()) typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile, javaString (fontFile.getFullPathName()).get())); else typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create, javaString (getName()).get(), (isBold ? 1 : 0) + (isItalic ? 2 : 0))); rect = GlobalRef (env->NewObject (RectClass, RectClass.constructor, 0, 0, 0, 0)); paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality)); const LocalRef<jobject> ignored (paint.callObjectMethod (Paint.setTypeface, typeface.get())); paint.callVoidMethod (Paint.setTextSize, referenceFontSize); const float fullAscent = std::abs (paint.callFloatMethod (Paint.ascent)); const float fullDescent = paint.callFloatMethod (Paint.descent); const float totalHeight = fullAscent + fullDescent; ascent = fullAscent / totalHeight; descent = fullDescent / totalHeight; heightToPointsFactor = referenceFontSize / totalHeight; }
static bool setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const UnicodeString& s) { // Fill in whatever we got. We don't use the display names if they're "GMT[+-]xx:xx" // because icu4c doesn't use the up-to-date time zone transition data, so it gets these // wrong. TimeZone.getDisplayName creates accurate names on demand. // TODO: investigate whether it's worth doing that work once in the Java wrapper instead of on-demand. static const UnicodeString kGmt("GMT", 3, US_INV); if (!s.isBogus() && !s.startsWith(kGmt)) { ScopedLocalRef<jstring> javaString(env, env->NewString(s.getBuffer(), s.length())); if (javaString.get() == NULL) { return false; } env->SetObjectArrayElement(array, i, javaString.get()); } return true; }
float getStringWidth (const String& text) override { JNIEnv* env = getEnv(); const int numChars = text.length(); jfloatArray widths = env->NewFloatArray (numChars); const int numDone = paint.callIntMethod (Paint.getTextWidths, javaString (text).get(), widths); HeapBlock<jfloat> localWidths (numDone); env->GetFloatArrayRegion (widths, 0, numDone, localWidths); env->DeleteLocalRef (widths); float x = 0; for (int i = 0; i < numDone; ++i) x += localWidths[i]; return x * referenceFontToUnits; }
//============================================================================== static String getHumanReadableStringForBluetoothAddress (const String& address) { JNIEnv* env = getEnv(); LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager)); if (btManager.get() == nullptr) return address; LocalRef<jstring> string ((jstring) env->CallObjectMethod (btManager.get(), AndroidBluetoothManager.getHumanReadableStringForBluetoothAddress, javaString (address).get())); if (string.get() == nullptr) return address; return juceString (string); }
void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) override { JNIEnv* env = getEnv(); const int numChars = text.length(); jfloatArray widths = env->NewFloatArray (numChars); const int numDone = paint.callIntMethod (Paint.getTextWidths, javaString (text).get(), widths); HeapBlock<jfloat> localWidths (numDone); env->GetFloatArrayRegion (widths, 0, numDone, localWidths); env->DeleteLocalRef (widths); String::CharPointerType s (text.getCharPointer()); xOffsets.add (0); float x = 0; for (int i = 0; i < numDone; ++i) { glyphs.add ((int) s.getAndAdvance()); x += localWidths[i]; xOffsets.add (x * referenceFontToUnits); } }
String getSystemProperty (const String& name) { return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (SystemClass, SystemClass.getProperty, javaString (name).get()))); }
void dismissPendingTextInput() override { view.callVoidMethod (ComponentPeerView.showKeyboard, javaString ("").get()); }
void textInputRequired (Point<int>, TextInputTarget& target) override { view.callVoidMethod (ComponentPeerView.showKeyboard, javaString (getVirtualKeyboardType (target.getKeyboardType())).get()); }
jstring JavaStringTest::nativeGetJavaString(JNIEnv *env, jobject javaThis) { JavaString javaString(TEST_STRING); return javaString.toJavaString(env).leak(); }
jstring JavaStringTest::nativeGetJavaStringWithNullChar(JNIEnv *env, jobject javaThis) { JavaString javaString(TEST_STRING_WITH_NULL_CHAR); return javaString.toJavaString(env).leak(); }
void onMediaScannerConnected() override { auto* env = getEnv(); env->CallVoidMethod (msc.get(), MediaScannerConnection.scanFile, javaString (file).get(), 0); }
static void setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const UnicodeString& s) { ScopedLocalRef<jstring> javaString(env, env->NewString((jchar*)s.getBuffer(), s.length())); env->SetObjectArrayElement(array, i, javaString.get()); }
WebInputStream (String address, bool isPost, const MemoryBlock& postData, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers, int timeOutMs, StringPairArray* responseHeaders) : statusCode (0) { if (! address.contains ("://")) address = "http://" + address; JNIEnv* env = getEnv(); jbyteArray postDataArray = 0; if (postData.getSize() > 0) { postDataArray = env->NewByteArray (postData.getSize()); env->SetByteArrayRegion (postDataArray, 0, postData.getSize(), (const jbyte*) postData.getData()); } LocalRef<jobject> responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor)); // Annoyingly, the android HTTP functions will choke on this call if you try to do it on the message // thread. You'll need to move your networking code to a background thread to keep it happy.. jassert (Thread::getCurrentThread() != nullptr); jintArray statusCodeArray = env->NewIntArray (1); jassert (statusCodeArray != 0); stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity, JuceAppActivity.createHTTPStream, javaString (address).get(), (jboolean) isPost, postDataArray, javaString (headers).get(), (jint) timeOutMs, statusCodeArray, responseHeaderBuffer.get())); jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); statusCode = statusCodeElements[0]; env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); env->DeleteLocalRef (statusCodeArray); if (postDataArray != 0) env->DeleteLocalRef (postDataArray); if (stream != 0) { StringArray headerLines; { LocalRef<jstring> headersString ((jstring) env->CallObjectMethod (responseHeaderBuffer.get(), StringBuffer.toString)); headerLines.addLines (juceString (env, headersString)); } if (responseHeaders != 0) { for (int i = 0; i < headerLines.size(); ++i) { const String& header = headerLines[i]; const String key (header.upToFirstOccurrenceOf (": ", false, false)); const String value (header.fromFirstOccurrenceOf (": ", false, false)); const String previousValue ((*responseHeaders) [key]); responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); } } } }
void JavaStringTest::createJavaStringFromStdString(JNIEnv *env, jobject javaThis) { std::string stdString = TEST_STRING; JavaString javaString(stdString); JUNIT_ASSERT_EQUALS_STRING(TEST_STRING, javaString.get()); }
//============================================================================== void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, const String& title, const String& message, Component* associatedComponent) { android.activity.callVoidMethod (JuceAppActivity.showMessageBox, javaString (title).get(), javaString (message).get(), (jlong) 0); }