am bcec82de: Clean up constructors
* commit 'bcec82de66f52655593dc233346f11468f5077a0': Clean up constructorsmain
commit
7b4531a16f
|
@ -18,7 +18,6 @@ package com.android.inputmethod.keyboard;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.FloatMath;
|
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
|
import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
|
||||||
import com.android.inputmethod.latin.JniUtils;
|
import com.android.inputmethod.latin.JniUtils;
|
||||||
|
@ -155,7 +154,9 @@ public class ProximityInfo {
|
||||||
final float radius = touchPositionCorrection.mRadii[row];
|
final float radius = touchPositionCorrection.mRadii[row];
|
||||||
sweetSpotCenterXs[i] = hitBox.exactCenterX() + x * hitBoxWidth;
|
sweetSpotCenterXs[i] = hitBox.exactCenterX() + x * hitBoxWidth;
|
||||||
sweetSpotCenterYs[i] = hitBox.exactCenterY() + y * hitBoxHeight;
|
sweetSpotCenterYs[i] = hitBox.exactCenterY() + y * hitBoxHeight;
|
||||||
sweetSpotRadii[i] = radius * FloatMath.sqrt(
|
// Note that, in recent versions of Android, FloatMath is actually slower than
|
||||||
|
// java.lang.Math due to the way the JIT optimizes java.lang.Math.
|
||||||
|
sweetSpotRadii[i] = radius * (float)Math.sqrt(
|
||||||
hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
|
hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.util.FloatMath;
|
|
||||||
|
|
||||||
import com.android.inputmethod.latin.Constants;
|
import com.android.inputmethod.latin.Constants;
|
||||||
import com.android.inputmethod.latin.InputPointers;
|
import com.android.inputmethod.latin.InputPointers;
|
||||||
|
@ -50,7 +49,7 @@ public class GestureStroke {
|
||||||
private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec
|
private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec
|
||||||
private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f);
|
private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f);
|
||||||
|
|
||||||
private static final float DOUBLE_PI = (float)(2 * Math.PI);
|
private static final float DOUBLE_PI = (float)(2.0f * Math.PI);
|
||||||
|
|
||||||
// Fade based on number of gesture samples, see MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT
|
// Fade based on number of gesture samples, see MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT
|
||||||
private static final int DRAWING_GESTURE_FADE_START = 10;
|
private static final int DRAWING_GESTURE_FADE_START = 10;
|
||||||
|
@ -158,8 +157,9 @@ public class GestureStroke {
|
||||||
final int p2x, final int p2y) {
|
final int p2x, final int p2y) {
|
||||||
final float dx = p1x - p2x;
|
final float dx = p1x - p2x;
|
||||||
final float dy = p1y - p2y;
|
final float dy = p1y - p2y;
|
||||||
// TODO: Optimize out this {@link FloatMath#sqrt(float)} call.
|
// Note that, in recent versions of Android, FloatMath is actually slower than
|
||||||
return FloatMath.sqrt(dx * dx + dy * dy);
|
// java.lang.Math due to the way the JIT optimizes java.lang.Math.
|
||||||
|
return (float)Math.sqrt(dx * dx + dy * dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float getAngle(final int p1x, final int p1y, final int p2x, final int p2y) {
|
private static float getAngle(final int p1x, final int p1y, final int p2x, final int p2y) {
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2012 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.inputmethod.latin;
|
|
||||||
|
|
||||||
public class NativeUtils {
|
|
||||||
static {
|
|
||||||
JniUtils.loadNativeLibrary();
|
|
||||||
}
|
|
||||||
|
|
||||||
private NativeUtils() {
|
|
||||||
// This utility class is not publicly instantiable.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method just calls up libm's powf() directly.
|
|
||||||
*/
|
|
||||||
public static native float powf(float x, float y);
|
|
||||||
}
|
|
|
@ -212,7 +212,7 @@ public class UserHistoryForgettingCurveUtils {
|
||||||
for (int j = 0; j < ELAPSED_TIME_MAX; ++j) {
|
for (int j = 0; j < ELAPSED_TIME_MAX; ++j) {
|
||||||
final float elapsedHours = j * ELAPSED_TIME_INTERVAL_HOURS;
|
final float elapsedHours = j * ELAPSED_TIME_INTERVAL_HOURS;
|
||||||
final float freq = initialFreq
|
final float freq = initialFreq
|
||||||
* NativeUtils.powf(initialFreq, elapsedHours / HALF_LIFE_HOURS);
|
* (float)Math.pow(initialFreq, elapsedHours / HALF_LIFE_HOURS);
|
||||||
final int intFreq = Math.min(FC_FREQ_MAX, Math.max(0, (int)freq));
|
final int intFreq = Math.min(FC_FREQ_MAX, Math.max(0, (int)freq));
|
||||||
SCORE_TABLE[i][j] = intFreq;
|
SCORE_TABLE[i][j] = intFreq;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ LATIN_IME_JNI_SRC_FILES := \
|
||||||
com_android_inputmethod_keyboard_ProximityInfo.cpp \
|
com_android_inputmethod_keyboard_ProximityInfo.cpp \
|
||||||
com_android_inputmethod_latin_BinaryDictionary.cpp \
|
com_android_inputmethod_latin_BinaryDictionary.cpp \
|
||||||
com_android_inputmethod_latin_DicTraverseSession.cpp \
|
com_android_inputmethod_latin_DicTraverseSession.cpp \
|
||||||
com_android_inputmethod_latin_NativeUtils.cpp \
|
|
||||||
jni_common.cpp
|
jni_common.cpp
|
||||||
|
|
||||||
LATIN_IME_CORE_SRC_FILES := \
|
LATIN_IME_CORE_SRC_FILES := \
|
||||||
|
|
|
@ -43,8 +43,8 @@ static void latinime_Keyboard_release(JNIEnv *env, jobject object, jlong proximi
|
||||||
|
|
||||||
static JNINativeMethod sKeyboardMethods[] = {
|
static JNINativeMethod sKeyboardMethods[] = {
|
||||||
{"setProximityInfoNative", "(Ljava/lang/String;IIIIII[II[I[I[I[I[I[F[F[F)J",
|
{"setProximityInfoNative", "(Ljava/lang/String;IIIIII[II[I[I[I[I[I[F[F[F)J",
|
||||||
(void*)latinime_Keyboard_setProximityInfo},
|
reinterpret_cast<void *>(latinime_Keyboard_setProximityInfo)},
|
||||||
{"releaseProximityInfoNative", "(J)V", (void*)latinime_Keyboard_release}
|
{"releaseProximityInfoNative", "(J)V", reinterpret_cast<void *>(latinime_Keyboard_release)}
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_ProximityInfo(JNIEnv *env) {
|
int register_ProximityInfo(JNIEnv *env) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
|
||||||
AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno);
|
AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
dictBuf = (void *)((char *)dictBuf + adjust);
|
dictBuf = reinterpret_cast<void *>(reinterpret_cast<char *>(dictBuf) + adjust);
|
||||||
#else // USE_MMAP_FOR_DICTIONARY
|
#else // USE_MMAP_FOR_DICTIONARY
|
||||||
/* malloc version */
|
/* malloc version */
|
||||||
FILE *file = 0;
|
FILE *file = 0;
|
||||||
|
@ -110,10 +110,11 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Dictionary *dictionary = 0;
|
Dictionary *dictionary = 0;
|
||||||
if (BinaryFormat::UNKNOWN_FORMAT == BinaryFormat::detectFormat((uint8_t*)dictBuf)) {
|
if (BinaryFormat::UNKNOWN_FORMAT
|
||||||
|
== BinaryFormat::detectFormat(reinterpret_cast<uint8_t *>(dictBuf))) {
|
||||||
AKLOGE("DICT: dictionary format is unknown, bad magic number");
|
AKLOGE("DICT: dictionary format is unknown, bad magic number");
|
||||||
#ifdef USE_MMAP_FOR_DICTIONARY
|
#ifdef USE_MMAP_FOR_DICTIONARY
|
||||||
releaseDictBuf(((char*)dictBuf) - adjust, adjDictSize, fd);
|
releaseDictBuf(reinterpret_cast<char *>(dictBuf) - adjust, adjDictSize, fd);
|
||||||
#else // USE_MMAP_FOR_DICTIONARY
|
#else // USE_MMAP_FOR_DICTIONARY
|
||||||
releaseDictBuf(dictBuf, 0, 0);
|
releaseDictBuf(dictBuf, 0, 0);
|
||||||
#endif // USE_MMAP_FOR_DICTIONARY
|
#endif // USE_MMAP_FOR_DICTIONARY
|
||||||
|
@ -227,8 +228,9 @@ static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jobject
|
||||||
jchar afterChars[afterLength];
|
jchar afterChars[afterLength];
|
||||||
env->GetCharArrayRegion(before, 0, beforeLength, beforeChars);
|
env->GetCharArrayRegion(before, 0, beforeLength, beforeChars);
|
||||||
env->GetCharArrayRegion(after, 0, afterLength, afterChars);
|
env->GetCharArrayRegion(after, 0, afterLength, afterChars);
|
||||||
return Correction::RankingAlgorithm::calcNormalizedScore((unsigned short*)beforeChars,
|
return Correction::RankingAlgorithm::calcNormalizedScore(
|
||||||
beforeLength, (unsigned short*)afterChars, afterLength, score);
|
reinterpret_cast<unsigned short *>(beforeChars), beforeLength,
|
||||||
|
reinterpret_cast<unsigned short *>(afterChars), afterLength, score);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object,
|
static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object,
|
||||||
|
@ -239,8 +241,9 @@ static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object,
|
||||||
jchar afterChars[afterLength];
|
jchar afterChars[afterLength];
|
||||||
env->GetCharArrayRegion(before, 0, beforeLength, beforeChars);
|
env->GetCharArrayRegion(before, 0, beforeLength, beforeChars);
|
||||||
env->GetCharArrayRegion(after, 0, afterLength, afterChars);
|
env->GetCharArrayRegion(after, 0, afterLength, afterChars);
|
||||||
return Correction::RankingAlgorithm::editDistance((unsigned short*)beforeChars, beforeLength,
|
return Correction::RankingAlgorithm::editDistance(
|
||||||
(unsigned short*)afterChars, afterLength);
|
reinterpret_cast<unsigned short *>(beforeChars), beforeLength,
|
||||||
|
reinterpret_cast<unsigned short *>(afterChars), afterLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong dict) {
|
static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong dict) {
|
||||||
|
@ -249,7 +252,9 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong d
|
||||||
void *dictBuf = dictionary->getDict();
|
void *dictBuf = dictionary->getDict();
|
||||||
if (!dictBuf) return;
|
if (!dictBuf) return;
|
||||||
#ifdef USE_MMAP_FOR_DICTIONARY
|
#ifdef USE_MMAP_FOR_DICTIONARY
|
||||||
releaseDictBuf((void *)((char *)dictBuf - dictionary->getDictBufAdjust()),
|
releaseDictBuf(
|
||||||
|
reinterpret_cast<void *>(
|
||||||
|
reinterpret_cast<char *>(dictBuf) - dictionary->getDictBufAdjust()),
|
||||||
dictionary->getDictSize() + dictionary->getDictBufAdjust(), dictionary->getMmapFd());
|
dictionary->getDictSize() + dictionary->getDictBufAdjust(), dictionary->getMmapFd());
|
||||||
#else // USE_MMAP_FOR_DICTIONARY
|
#else // USE_MMAP_FOR_DICTIONARY
|
||||||
releaseDictBuf(dictBuf, 0, 0);
|
releaseDictBuf(dictBuf, 0, 0);
|
||||||
|
@ -273,15 +278,19 @@ static void releaseDictBuf(void *dictBuf, const size_t length, int fd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static JNINativeMethod sMethods[] = {
|
static JNINativeMethod sMethods[] = {
|
||||||
{"openNative", "(Ljava/lang/String;JJIIIII)J", (void*)latinime_BinaryDictionary_open},
|
{"openNative", "(Ljava/lang/String;JJIIIII)J",
|
||||||
{"closeNative", "(J)V", (void*)latinime_BinaryDictionary_close},
|
reinterpret_cast<void *>(latinime_BinaryDictionary_open)},
|
||||||
|
{"closeNative", "(J)V", reinterpret_cast<void *>(latinime_BinaryDictionary_close)},
|
||||||
{"getSuggestionsNative", "(JJJ[I[I[I[I[IIIZ[IZ[C[I[I[I)I",
|
{"getSuggestionsNative", "(JJJ[I[I[I[I[IIIZ[IZ[C[I[I[I)I",
|
||||||
(void*) latinime_BinaryDictionary_getSuggestions},
|
reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)},
|
||||||
{"getFrequencyNative", "(J[I)I", (void*)latinime_BinaryDictionary_getFrequency},
|
{"getFrequencyNative", "(J[I)I",
|
||||||
{"isValidBigramNative", "(J[I[I)Z", (void*)latinime_BinaryDictionary_isValidBigram},
|
reinterpret_cast<void *>(latinime_BinaryDictionary_getFrequency)},
|
||||||
|
{"isValidBigramNative", "(J[I[I)Z",
|
||||||
|
reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram)},
|
||||||
{"calcNormalizedScoreNative", "([C[CI)F",
|
{"calcNormalizedScoreNative", "([C[CI)F",
|
||||||
(void*)latinime_BinaryDictionary_calcNormalizedScore},
|
reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore)},
|
||||||
{"editDistanceNative", "([C[C)I", (void*)latinime_BinaryDictionary_editDistance}
|
{"editDistanceNative", "([C[C)I",
|
||||||
|
reinterpret_cast<void *>(latinime_BinaryDictionary_editDistance)}
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_BinaryDictionary(JNIEnv *env) {
|
int register_BinaryDictionary(JNIEnv *env) {
|
||||||
|
|
|
@ -41,16 +41,18 @@ static void latinime_initDicTraverseSession(JNIEnv *env, jobject object, jlong t
|
||||||
DicTraverseWrapper::initDicTraverseSession(ts, dict, prevWord, previousWordLength);
|
DicTraverseWrapper::initDicTraverseSession(ts, dict, prevWord, previousWordLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void latinime_releaseDicTraverseSession(
|
static void latinime_releaseDicTraverseSession(JNIEnv *env, jobject object, jlong traverseSession) {
|
||||||
JNIEnv *env, jobject object, jlong traverseSession) {
|
|
||||||
void *ts = reinterpret_cast<void*>(traverseSession);
|
void *ts = reinterpret_cast<void*>(traverseSession);
|
||||||
DicTraverseWrapper::releaseDicTraverseSession(ts);
|
DicTraverseWrapper::releaseDicTraverseSession(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JNINativeMethod sMethods[] = {
|
static JNINativeMethod sMethods[] = {
|
||||||
{"setDicTraverseSessionNative", "(Ljava/lang/String;)J", (void*)latinime_setDicTraverseSession},
|
{"setDicTraverseSessionNative", "(Ljava/lang/String;)J",
|
||||||
{"initDicTraverseSessionNative", "(JJ[II)V", (void*)latinime_initDicTraverseSession},
|
reinterpret_cast<void *>(latinime_setDicTraverseSession)},
|
||||||
{"releaseDicTraverseSessionNative", "(J)V", (void*)latinime_releaseDicTraverseSession}
|
{"initDicTraverseSessionNative", "(JJ[II)V",
|
||||||
|
reinterpret_cast<void *>(latinime_initDicTraverseSession)},
|
||||||
|
{"releaseDicTraverseSessionNative", "(J)V",
|
||||||
|
reinterpret_cast<void *>(latinime_releaseDicTraverseSession)}
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_DicTraverseSession(JNIEnv *env) {
|
int register_DicTraverseSession(JNIEnv *env) {
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2012, The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "com_android_inputmethod_latin_NativeUtils.h"
|
|
||||||
#include "jni.h"
|
|
||||||
#include "jni_common.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace latinime {
|
|
||||||
|
|
||||||
static float latinime_NativeUtils_powf(float x, float y) {
|
|
||||||
return powf(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static JNINativeMethod sMethods[] = {
|
|
||||||
{"powf", "(FF)F", (void*)latinime_NativeUtils_powf}
|
|
||||||
};
|
|
||||||
|
|
||||||
int register_NativeUtils(JNIEnv *env) {
|
|
||||||
const char *const kClassPathName = "com/android/inputmethod/latin/NativeUtils";
|
|
||||||
return registerNativeMethods(env, kClassPathName, sMethods,
|
|
||||||
sizeof(sMethods) / sizeof(sMethods[0]));
|
|
||||||
}
|
|
||||||
} // namespace latinime
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2012, The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H
|
|
||||||
#define _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H
|
|
||||||
|
|
||||||
#include "jni.h"
|
|
||||||
|
|
||||||
namespace latinime {
|
|
||||||
|
|
||||||
int register_NativeUtils(JNIEnv *env);
|
|
||||||
|
|
||||||
} // namespace latinime
|
|
||||||
#endif // _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "com_android_inputmethod_keyboard_ProximityInfo.h"
|
#include "com_android_inputmethod_keyboard_ProximityInfo.h"
|
||||||
#include "com_android_inputmethod_latin_BinaryDictionary.h"
|
#include "com_android_inputmethod_latin_BinaryDictionary.h"
|
||||||
#include "com_android_inputmethod_latin_DicTraverseSession.h"
|
#include "com_android_inputmethod_latin_DicTraverseSession.h"
|
||||||
#include "com_android_inputmethod_latin_NativeUtils.h"
|
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "jni.h"
|
#include "jni.h"
|
||||||
#include "jni_common.h"
|
#include "jni_common.h"
|
||||||
|
@ -56,11 +55,6 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!register_NativeUtils(env)) {
|
|
||||||
AKLOGE("ERROR: NativeUtils native registration failed");
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* success -- return valid version number */
|
/* success -- return valid version number */
|
||||||
result = JNI_VERSION_1_6;
|
result = JNI_VERSION_1_6;
|
||||||
|
|
||||||
|
|
|
@ -60,13 +60,14 @@ bool BigramDictionary::addWordBigram(unsigned short *word, int length, int frequ
|
||||||
AKLOGI("Bigram: InsertAt -> %d MAX_PREDICTIONS: %d", insertAt, MAX_PREDICTIONS);
|
AKLOGI("Bigram: InsertAt -> %d MAX_PREDICTIONS: %d", insertAt, MAX_PREDICTIONS);
|
||||||
}
|
}
|
||||||
if (insertAt < MAX_PREDICTIONS) {
|
if (insertAt < MAX_PREDICTIONS) {
|
||||||
memmove((char*) bigramFreq + (insertAt + 1) * sizeof(bigramFreq[0]),
|
memmove(reinterpret_cast<char *>(bigramFreq) + (insertAt + 1) * sizeof(bigramFreq[0]),
|
||||||
(char*) bigramFreq + insertAt * sizeof(bigramFreq[0]),
|
reinterpret_cast<char *>(bigramFreq) + insertAt * sizeof(bigramFreq[0]),
|
||||||
(MAX_PREDICTIONS - insertAt - 1) * sizeof(bigramFreq[0]));
|
(MAX_PREDICTIONS - insertAt - 1) * sizeof(bigramFreq[0]));
|
||||||
bigramFreq[insertAt] = frequency;
|
bigramFreq[insertAt] = frequency;
|
||||||
outputTypes[insertAt] = Dictionary::KIND_PREDICTION;
|
outputTypes[insertAt] = Dictionary::KIND_PREDICTION;
|
||||||
memmove((char*) bigramChars + (insertAt + 1) * MAX_WORD_LENGTH * sizeof(short),
|
memmove(reinterpret_cast<char *>(bigramChars)
|
||||||
(char*) bigramChars + (insertAt ) * MAX_WORD_LENGTH * sizeof(short),
|
+ (insertAt + 1) * MAX_WORD_LENGTH * sizeof(short),
|
||||||
|
reinterpret_cast<char *>(bigramChars) + insertAt * MAX_WORD_LENGTH * sizeof(short),
|
||||||
(MAX_PREDICTIONS - insertAt - 1) * sizeof(short) * MAX_WORD_LENGTH);
|
(MAX_PREDICTIONS - insertAt - 1) * sizeof(short) * MAX_WORD_LENGTH);
|
||||||
unsigned short *dest = bigramChars + (insertAt ) * MAX_WORD_LENGTH;
|
unsigned short *dest = bigramChars + (insertAt ) * MAX_WORD_LENGTH;
|
||||||
while (length--) {
|
while (length--) {
|
||||||
|
|
|
@ -885,16 +885,17 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static int compare_pair_capital(const void *a, const void *b) {
|
static int compare_pair_capital(const void *a, const void *b) {
|
||||||
return (int)(*(unsigned short *)a)
|
return static_cast<int>(*reinterpret_cast<const unsigned short *>(a))
|
||||||
- (int)((struct LatinCapitalSmallPair*)b)->capital;
|
- static_cast<int>(
|
||||||
|
(reinterpret_cast<const struct LatinCapitalSmallPair *>(b))->capital);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short latin_tolower(unsigned short c) {
|
unsigned short latin_tolower(unsigned short c) {
|
||||||
struct LatinCapitalSmallPair *p =
|
struct LatinCapitalSmallPair *p =
|
||||||
(struct LatinCapitalSmallPair *)bsearch(&c, SORTED_CHAR_MAP,
|
reinterpret_cast<struct LatinCapitalSmallPair *>(bsearch(&c, SORTED_CHAR_MAP,
|
||||||
sizeof(SORTED_CHAR_MAP) / sizeof(SORTED_CHAR_MAP[0]),
|
sizeof(SORTED_CHAR_MAP) / sizeof(SORTED_CHAR_MAP[0]),
|
||||||
sizeof(SORTED_CHAR_MAP[0]),
|
sizeof(SORTED_CHAR_MAP[0]),
|
||||||
compare_pair_capital);
|
compare_pair_capital));
|
||||||
return p ? p->small : c;
|
return p ? p->small : c;
|
||||||
}
|
}
|
||||||
} // namespace latinime
|
} // namespace latinime
|
||||||
|
|
|
@ -1092,7 +1092,7 @@ int Correction::RankingAlgorithm::editDistance(const unsigned short *before,
|
||||||
// In dictionary.cpp, getSuggestion() method,
|
// In dictionary.cpp, getSuggestion() method,
|
||||||
// suggestion scores are computed using the below formula.
|
// suggestion scores are computed using the below formula.
|
||||||
// original score
|
// original score
|
||||||
// := pow(mTypedLetterMultiplier (this is defined 2),
|
// := powf(mTypedLetterMultiplier (this is defined 2),
|
||||||
// (the number of matched characters between typed word and suggested word))
|
// (the number of matched characters between typed word and suggested word))
|
||||||
// * (individual word's score which defined in the unigram dictionary,
|
// * (individual word's score which defined in the unigram dictionary,
|
||||||
// and this score is defined in range [0, 255].)
|
// and this score is defined in range [0, 255].)
|
||||||
|
@ -1104,11 +1104,11 @@ int Correction::RankingAlgorithm::editDistance(const unsigned short *before,
|
||||||
// capitalization, then treat it as if the score was 255.
|
// capitalization, then treat it as if the score was 255.
|
||||||
// - If before.length() == after.length()
|
// - If before.length() == after.length()
|
||||||
// => multiply by mFullWordMultiplier (this is defined 2))
|
// => multiply by mFullWordMultiplier (this is defined 2))
|
||||||
// So, maximum original score is pow(2, min(before.length(), after.length())) * 255 * 2 * 1.2
|
// So, maximum original score is powf(2, min(before.length(), after.length())) * 255 * 2 * 1.2
|
||||||
// For historical reasons we ignore the 1.2 modifier (because the measure for a good
|
// For historical reasons we ignore the 1.2 modifier (because the measure for a good
|
||||||
// autocorrection threshold was done at a time when it didn't exist). This doesn't change
|
// autocorrection threshold was done at a time when it didn't exist). This doesn't change
|
||||||
// the result.
|
// the result.
|
||||||
// So, we can normalize original score by dividing pow(2, min(b.l(),a.l())) * 255 * 2.
|
// So, we can normalize original score by dividing powf(2, min(b.l(),a.l())) * 255 * 2.
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
float Correction::RankingAlgorithm::calcNormalizedScore(const unsigned short *before,
|
float Correction::RankingAlgorithm::calcNormalizedScore(const unsigned short *before,
|
||||||
|
@ -1130,7 +1130,7 @@ float Correction::RankingAlgorithm::calcNormalizedScore(const unsigned short *be
|
||||||
}
|
}
|
||||||
|
|
||||||
const float maxScore = score >= S_INT_MAX ? S_INT_MAX : MAX_INITIAL_SCORE
|
const float maxScore = score >= S_INT_MAX ? S_INT_MAX : MAX_INITIAL_SCORE
|
||||||
* pow(static_cast<float>(TYPED_LETTER_MULTIPLIER),
|
* powf(static_cast<float>(TYPED_LETTER_MULTIPLIER),
|
||||||
static_cast<float>(min(beforeLength, afterLength - spaceCount)))
|
static_cast<float>(min(beforeLength, afterLength - spaceCount)))
|
||||||
* FULL_WORD_MULTIPLIER;
|
* FULL_WORD_MULTIPLIER;
|
||||||
|
|
||||||
|
|
|
@ -58,11 +58,12 @@ static inline void LOGI_S16_PLUS(unsigned short *string, const unsigned int leng
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void printDebug(const char *tag, int *codes, int codesSize, int MAX_PROXIMITY_CHARS) {
|
static inline void printDebug(const char *tag, int *codes, int codesSize, int MAX_PROXIMITY_CHARS) {
|
||||||
unsigned char *buf = (unsigned char*)malloc((1 + codesSize) * sizeof(*buf));
|
unsigned char *buf = reinterpret_cast<unsigned char *>(malloc((1 + codesSize) * sizeof(*buf)));
|
||||||
|
|
||||||
buf[codesSize] = 0;
|
buf[codesSize] = 0;
|
||||||
while (--codesSize >= 0)
|
while (--codesSize >= 0) {
|
||||||
buf[codesSize] = (unsigned char)codes[codesSize * MAX_PROXIMITY_CHARS];
|
buf[codesSize] = static_cast<unsigned char>(codes[codesSize * MAX_PROXIMITY_CHARS]);
|
||||||
|
}
|
||||||
AKLOGI("%s, WORD = %s", tag, buf);
|
AKLOGI("%s, WORD = %s", tag, buf);
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
|
@ -32,8 +32,8 @@ namespace latinime {
|
||||||
Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust,
|
Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust,
|
||||||
int typedLetterMultiplier, int fullWordMultiplier,
|
int typedLetterMultiplier, int fullWordMultiplier,
|
||||||
int maxWordLength, int maxWords, int maxPredictions)
|
int maxWordLength, int maxWords, int maxPredictions)
|
||||||
: mDict((unsigned char*) dict),
|
: mDict(reinterpret_cast<unsigned char *>(dict)),
|
||||||
mOffsetDict(((unsigned char*) dict) + BinaryFormat::getHeaderSize(mDict)),
|
mOffsetDict((reinterpret_cast<unsigned char *>(dict)) + BinaryFormat::getHeaderSize(mDict)),
|
||||||
mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust) {
|
mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust) {
|
||||||
if (DEBUG_DICT) {
|
if (DEBUG_DICT) {
|
||||||
if (MAX_WORD_LENGTH_INTERNAL < maxWordLength) {
|
if (MAX_WORD_LENGTH_INTERNAL < maxWordLength) {
|
||||||
|
|
|
@ -55,8 +55,12 @@ class Dictionary {
|
||||||
|
|
||||||
int getFrequency(const int32_t *word, int length) const;
|
int getFrequency(const int32_t *word, int length) const;
|
||||||
bool isValidBigram(const int32_t *word1, int length1, const int32_t *word2, int length2) const;
|
bool isValidBigram(const int32_t *word1, int length1, const int32_t *word2, int length2) const;
|
||||||
void *getDict() const { return (void *)mDict; } // required to release dictionary buffer
|
void *getDict() const { // required to release dictionary buffer
|
||||||
void *getOffsetDict() const { return (void *)mOffsetDict; }
|
return reinterpret_cast<void *>(const_cast<unsigned char *>(mDict));
|
||||||
|
}
|
||||||
|
void *getOffsetDict() const {
|
||||||
|
return reinterpret_cast<void *>(const_cast<unsigned char *>(mOffsetDict));
|
||||||
|
}
|
||||||
int getDictSize() const { return mDictSize; }
|
int getDictSize() const { return mDictSize; }
|
||||||
int getMmapFd() const { return mMmapFd; }
|
int getMmapFd() const { return mMmapFd; }
|
||||||
int getDictBufAdjust() const { return mDictBufAdjust; }
|
int getDictBufAdjust() const { return mDictBufAdjust; }
|
||||||
|
@ -87,8 +91,9 @@ class Dictionary {
|
||||||
inline int Dictionary::wideStrLen(unsigned short *str) {
|
inline int Dictionary::wideStrLen(unsigned short *str) {
|
||||||
if (!str) return 0;
|
if (!str) return 0;
|
||||||
unsigned short *end = str;
|
unsigned short *end = str;
|
||||||
while (*end)
|
while (*end) {
|
||||||
end++;
|
end++;
|
||||||
|
}
|
||||||
return end - str;
|
return end - str;
|
||||||
}
|
}
|
||||||
} // namespace latinime
|
} // namespace latinime
|
||||||
|
|
|
@ -20,36 +20,43 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#define MAX_DISTANCE 10000000
|
#define MAX_DISTANCE 10000000
|
||||||
#define KEY_NUM 27
|
|
||||||
#define SPACE_KEY 26
|
|
||||||
#define MAX_PATHS 2
|
#define MAX_PATHS 2
|
||||||
|
|
||||||
#define DEBUG_DECODER false
|
#define DEBUG_DECODER false
|
||||||
|
|
||||||
|
#define M_PI_F 3.14159265f
|
||||||
|
|
||||||
namespace latinime {
|
namespace latinime {
|
||||||
|
|
||||||
static inline float sqr(float x) {
|
static inline float sqrf(float x) {
|
||||||
return x * x;
|
return x * x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline float getNormalizedSqrDistance(int x1, int y1, int x2, int y2, int scale) {
|
static inline float getNormalizedSqrDistanceFloat(int x1, int y1, int x2, int y2, int scale) {
|
||||||
return sqr((x1 - x2) * 1.0 / scale) + sqr((y1 - y2) * 1.0 / scale);
|
return sqrf(static_cast<float>(x1 - x2) / static_cast<float>(scale))
|
||||||
|
+ sqrf(static_cast<float>(y1 - y2) / static_cast<float>(scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int getDistance(int x1, int y1, int x2, int y2) {
|
static inline float getDistanceSqrFloat(float x1, float y1, float x2, float y2) {
|
||||||
return (int) sqrt(sqr(x2 - x1) + sqr(y2 - y1));
|
return sqrf(x2 - x1) + sqrf(y2 - y1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline float getDistanceSq(float x1, float y1, float x2, float y2) {
|
static inline int getDistanceInt(int x1, int y1, int x2, int y2) {
|
||||||
return sqr(x2 - x1) + sqr(y2 - y1);
|
return static_cast<int>(
|
||||||
|
sqrtf(getDistanceSqrFloat(
|
||||||
|
static_cast<float>(x1), static_cast<float>(y1),
|
||||||
|
static_cast<float>(x2), static_cast<float>(y2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline float getAngle(int x1, int y1, int x2, int y2) {
|
static inline float getAngle(int x1, int y1, int x2, int y2) {
|
||||||
float dx = x1 - x2;
|
int dx = x1 - x2;
|
||||||
float dy = y1 - y2;
|
int dy = y1 - y2;
|
||||||
if (dx == 0 && dy == 0)
|
if (dx == 0 && dy == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
return atan2(dy, dx);
|
}
|
||||||
|
float dxf = static_cast<float>(dx);
|
||||||
|
float dyf = static_cast<float>(dy);
|
||||||
|
return atan2f(dyf, dxf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline float angleDiff(float a1, float a2) {
|
static inline float angleDiff(float a1, float a2) {
|
||||||
|
@ -57,13 +64,14 @@ static inline float angleDiff(float a1, float a2) {
|
||||||
if (diff < 0) {
|
if (diff < 0) {
|
||||||
diff = -diff;
|
diff = -diff;
|
||||||
}
|
}
|
||||||
if (diff > M_PI) {
|
if (diff > M_PI_F) {
|
||||||
return 2 * M_PI - diff;
|
return 2.0f * M_PI_F - diff;
|
||||||
}
|
}
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
//static float pointToLineDistanceSq(float x, float y, float x1, float y1, float x2, float y2) {
|
// static float pointToLineDistanceSqrFloat(
|
||||||
|
// float x, float y, float x1, float y1, float x2, float y2) {
|
||||||
// float A = x - x1;
|
// float A = x - x1;
|
||||||
// float B = y - y1;
|
// float B = y - y1;
|
||||||
// float C = x2 - x1;
|
// float C = x2 - x1;
|
||||||
|
@ -71,7 +79,7 @@ static inline float angleDiff(float a1, float a2) {
|
||||||
// return abs(A * D - C * B) / sqrt(C * C + D * D);
|
// return abs(A * D - C * B) / sqrt(C * C + D * D);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
static inline float pointToLineSegDistanceSq(
|
static inline float pointToLineSegDistanceSqrFloat(
|
||||||
float x, float y, float x1, float y1, float x2, float y2) {
|
float x, float y, float x1, float y1, float x2, float y2) {
|
||||||
float ray1x = x - x1;
|
float ray1x = x - x1;
|
||||||
float ray1y = y - y1;
|
float ray1y = y - y1;
|
||||||
|
@ -93,9 +101,7 @@ static inline float pointToLineSegDistanceSq(
|
||||||
projectionX = x1 + projectionLengthSq * ray2x;
|
projectionX = x1 + projectionLengthSq * ray2x;
|
||||||
projectionY = y1 + projectionLengthSq * ray2y;
|
projectionY = y1 + projectionLengthSq * ray2y;
|
||||||
}
|
}
|
||||||
|
return getDistanceSqrFloat(x, y, projectionX, projectionY);
|
||||||
float dist = getDistanceSq(x, y, projectionX, projectionY);
|
|
||||||
return dist;
|
|
||||||
}
|
}
|
||||||
} // namespace latinime
|
} // namespace latinime
|
||||||
#endif // LATINIME_INCREMENTAL_GEOMETRY_UTILS_H
|
#endif // LATINIME_INCREMENTAL_GEOMETRY_UTILS_H
|
||||||
|
|
|
@ -62,7 +62,7 @@ class GestureDecoderWrapper : public IncrementalDecoderInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(GestureDecoderWrapper);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(GestureDecoderWrapper);
|
||||||
static IncrementalDecoderInterface *getGestureDecoderInstance(int maxWordLength, int maxWords) {
|
static IncrementalDecoderInterface *getGestureDecoderInstance(int maxWordLength, int maxWords) {
|
||||||
if (sGestureDecoderFactoryMethod) {
|
if (sGestureDecoderFactoryMethod) {
|
||||||
return sGestureDecoderFactoryMethod(maxWordLength, maxWords);
|
return sGestureDecoderFactoryMethod(maxWordLength, maxWords);
|
||||||
|
|
|
@ -34,9 +34,10 @@ class IncrementalDecoderInterface {
|
||||||
int *outputIndices, int *outputTypes) = 0;
|
int *outputIndices, int *outputTypes) = 0;
|
||||||
virtual void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram,
|
virtual void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram,
|
||||||
const uint8_t *dictRoot, int rootPos) = 0;
|
const uint8_t *dictRoot, int rootPos) = 0;
|
||||||
|
IncrementalDecoderInterface() { };
|
||||||
virtual ~IncrementalDecoderInterface() { };
|
virtual ~IncrementalDecoderInterface() { };
|
||||||
private:
|
private:
|
||||||
//DISALLOW_COPY_AND_ASSIGN(IncrementalDecoderInterface);
|
DISALLOW_COPY_AND_ASSIGN(IncrementalDecoderInterface);
|
||||||
};
|
};
|
||||||
} // namespace latinime
|
} // namespace latinime
|
||||||
#endif // LATINIME_INCREMENTAL_DECODER_INTERFACE_H
|
#endif // LATINIME_INCREMENTAL_DECODER_INTERFACE_H
|
||||||
|
|
|
@ -62,7 +62,7 @@ class IncrementalDecoderWrapper : public IncrementalDecoderInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(IncrementalDecoderWrapper);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(IncrementalDecoderWrapper);
|
||||||
static IncrementalDecoderInterface *getIncrementalDecoderInstance(int maxWordLength,
|
static IncrementalDecoderInterface *getIncrementalDecoderInstance(int maxWordLength,
|
||||||
int maxWords) {
|
int maxWords) {
|
||||||
if (sIncrementalDecoderFactoryMethod) {
|
if (sIncrementalDecoderFactoryMethod) {
|
||||||
|
|
|
@ -248,7 +248,7 @@ void ProximityInfo::initializeG() {
|
||||||
for (int i = 0; i < KEY_COUNT; i++) {
|
for (int i = 0; i < KEY_COUNT; i++) {
|
||||||
mKeyKeyDistancesG[i][i] = 0;
|
mKeyKeyDistancesG[i][i] = 0;
|
||||||
for (int j = i + 1; j < KEY_COUNT; j++) {
|
for (int j = i + 1; j < KEY_COUNT; j++) {
|
||||||
mKeyKeyDistancesG[i][j] = getDistance(
|
mKeyKeyDistancesG[i][j] = getDistanceInt(
|
||||||
mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
|
mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
|
||||||
mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
|
mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const {
|
||||||
void ProximityInfo::getCenters(int *centerXs, int *centerYs, int *codeToKeyIndex,
|
void ProximityInfo::getCenters(int *centerXs, int *centerYs, int *codeToKeyIndex,
|
||||||
int *keyToCodeIndex, int *keyCount, int *keyWidth) const {
|
int *keyToCodeIndex, int *keyCount, int *keyWidth) const {
|
||||||
*keyCount = KEY_COUNT;
|
*keyCount = KEY_COUNT;
|
||||||
*keyWidth = sqrt(static_cast<float>(MOST_COMMON_KEY_WIDTH_SQUARE));
|
*keyWidth = sqrtf(static_cast<float>(MOST_COMMON_KEY_WIDTH_SQUARE));
|
||||||
|
|
||||||
for (int i = 0; i < KEY_COUNT; ++i) {
|
for (int i = 0; i < KEY_COUNT; ++i) {
|
||||||
const int code = mKeyCharCodes[i];
|
const int code = mKeyCharCodes[i];
|
||||||
|
|
|
@ -140,12 +140,13 @@ class WordsPriorityQueue {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const unsigned int wordLength = sw->mWordLength;
|
const unsigned int wordLength = sw->mWordLength;
|
||||||
char *targetAdr = (char*) outputChars + i * MAX_WORD_LENGTH * sizeof(short);
|
char *targetAddress = reinterpret_cast<char *>(outputChars)
|
||||||
|
+ i * MAX_WORD_LENGTH * sizeof(short);
|
||||||
frequencies[i] = sw->mScore;
|
frequencies[i] = sw->mScore;
|
||||||
outputTypes[i] = sw->mType;
|
outputTypes[i] = sw->mType;
|
||||||
memcpy(targetAdr, sw->mWord, (wordLength) * sizeof(short));
|
memcpy(targetAddress, sw->mWord, (wordLength) * sizeof(short));
|
||||||
if (wordLength < MAX_WORD_LENGTH) {
|
if (wordLength < MAX_WORD_LENGTH) {
|
||||||
((unsigned short*) targetAdr)[wordLength] = 0;
|
reinterpret_cast<unsigned short *>(targetAddress)[wordLength] = 0;
|
||||||
}
|
}
|
||||||
sw->mUsed = false;
|
sw->mUsed = false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue