Skip to content

Commit 996274e

Browse files
committed
Release v3.7.0: Groq API separation, OfflineLite cleanup, and UI fixes
1 parent 10416fd commit 996274e

File tree

15 files changed

+199
-86
lines changed

15 files changed

+199
-86
lines changed

app/build.gradle.kts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ android {
2222
applicationId = "helium314.keyboardl"
2323
minSdk = 21
2424
targetSdk = 35
25-
versionCode = 3609
26-
versionName = "3.6.9"
25+
versionCode = 3700
26+
versionName = "3.7.0"
2727

2828
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
2929
}
@@ -109,12 +109,12 @@ android {
109109
compose = true
110110
}
111111

112-
// externalNativeBuild {
113-
// ndkBuild {
114-
// path = File("src/main/jni/Android.mk")
115-
// }
116-
// }
117-
//// ndkVersion = "28.0.13004108"
112+
externalNativeBuild {
113+
ndkBuild {
114+
path = File("src/main/jni/Android.mk")
115+
}
116+
}
117+
// ndkVersion = "28.0.13004108"
118118

119119
packaging {
120120
jniLibs {

app/src/main/java/helium314/keyboard/latin/dictionary/ExpandableBinaryDictionary.java

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,17 @@
3939
import java.util.concurrent.locks.ReentrantReadWriteLock;
4040

4141
/**
42-
* Abstract base class for an expandable dictionary that can be created and updated dynamically
43-
* during runtime. When updated it automatically generates a new binary dictionary to handle future
44-
* queries in native code. This binary dictionary is written to internal storage.
42+
* Abstract base class for an expandable dictionary that can be created and
43+
* updated dynamically
44+
* during runtime. When updated it automatically generates a new binary
45+
* dictionary to handle future
46+
* queries in native code. This binary dictionary is written to internal
47+
* storage.
4548
* <p>
46-
* A class that extends this abstract class must have a static factory method named
47-
* getDictionary(Context context, Locale locale, File dictFile, String dictNamePrefix)
49+
* A class that extends this abstract class must have a static factory method
50+
* named
51+
* getDictionary(Context context, Locale locale, File dictFile, String
52+
* dictNamePrefix)
4853
*/
4954
abstract public class ExpandableBinaryDictionary extends Dictionary {
5055
private static final boolean DEBUG = false;
@@ -60,25 +65,25 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
6065
/**
6166
* The maximum length of a word in this dictionary.
6267
*/
63-
protected static final int MAX_WORD_LENGTH =
64-
DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH;
68+
protected static final int MAX_WORD_LENGTH = DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH;
6569

6670
private static final int DICTIONARY_FORMAT_VERSION = FormatSpec.VERSION4;
6771

68-
private static final WordProperty[] DEFAULT_WORD_PROPERTIES_FOR_SYNC =
69-
new WordProperty[0] /* default */;
72+
private static final WordProperty[] DEFAULT_WORD_PROPERTIES_FOR_SYNC = new WordProperty[0] /* default */;
7073

7174
/** The application context. */
7275
public final Context mContext;
7376

7477
/**
75-
* The binary dictionary generated dynamically from the fusion dictionary. This is used to
78+
* The binary dictionary generated dynamically from the fusion dictionary. This
79+
* is used to
7680
* answer unigram and bigram queries.
7781
*/
7882
private BinaryDictionary mBinaryDictionary;
7983

8084
/**
81-
* The name of this dictionary, used as a part of the filename for storing the binary
85+
* The name of this dictionary, used as a part of the filename for storing the
86+
* binary
8287
* dictionary.
8388
*/
8489
private final String mDictName;
@@ -107,8 +112,10 @@ static boolean matchesExpectedBinaryDictFormatVersionForThisType(final int forma
107112
}
108113

109114
private static boolean needsToMigrateDictionary(final int formatVersion) {
110-
// When we bump up the dictionary format version, the old version should be added to here
111-
// for supporting migration. Note that native code has to support reading such formats.
115+
// When we bump up the dictionary format version, the old version should be
116+
// added to here
117+
// for supporting migration. Note that native code has to support reading such
118+
// formats.
112119
return formatVersion == FormatSpec.VERSION402;
113120
}
114121

@@ -119,13 +126,14 @@ public boolean isValidDictionaryLocked() {
119126
/**
120127
* Creates a new expandable binary dictionary.
121128
*
122-
* @param context The application context of the parent.
129+
* @param context The application context of the parent.
123130
* @param dictName The name of the dictionary. Multiple instances with the same
124-
* name is supported.
125-
* @param locale the dictionary locale.
131+
* name is supported.
132+
* @param locale the dictionary locale.
126133
* @param dictType the dictionary type, as a human-readable string
127-
* @param dictFile dictionary file path. if null, use default dictionary path based on
128-
* dictionary type.
134+
* @param dictFile dictionary file path. if null, use default dictionary path
135+
* based on
136+
* dictionary type.
129137
*/
130138
public ExpandableBinaryDictionary(final Context context, final String dictName,
131139
final Locale locale, final String dictType, final File dictFile) {
@@ -174,6 +182,9 @@ BinaryDictionary getBinaryDictionary() {
174182
public int getFrequency(final String word) {
175183
if (mLock.readLock().tryLock()) {
176184
try {
185+
if (mBinaryDictionary == null) {
186+
return NOT_A_PROBABILITY;
187+
}
177188
return mBinaryDictionary.getFrequency(word);
178189
} finally {
179190
mLock.readLock().unlock();
@@ -266,7 +277,8 @@ private void updateDictionaryWithWriteLock(@NonNull final Runnable updateTask) {
266277
}
267278

268279
/**
269-
* Adds unigram information of a word to the dictionary. May overwrite an existing entry.
280+
* Adds unigram information of a word to the dictionary. May overwrite an
281+
* existing entry.
270282
*/
271283
public void addUnigramEntry(final String word, final int frequency,
272284
final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
@@ -304,7 +316,8 @@ public void removeUnigramEntryDynamically(final String word) {
304316
}
305317

306318
/**
307-
* Adds n-gram information of a word to the dictionary. May overwrite an existing entry.
319+
* Adds n-gram information of a word to the dictionary. May overwrite an
320+
* existing entry.
308321
*/
309322
public void addNgramEntry(@NonNull final NgramContext ngramContext, final String word,
310323
final int frequency, final int timestamp) {
@@ -362,12 +375,12 @@ public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedDa
362375
if (mBinaryDictionary == null) {
363376
return null;
364377
}
365-
final ArrayList<SuggestedWordInfo> suggestions =
366-
mBinaryDictionary.getSuggestions(composedData, ngramContext,
367-
proximityInfoHandle, settingsValuesForSuggestion, sessionId,
368-
weightForLocale, inOutWeightOfLangModelVsSpatialModel);
378+
final ArrayList<SuggestedWordInfo> suggestions = mBinaryDictionary.getSuggestions(composedData,
379+
ngramContext,
380+
proximityInfoHandle, settingsValuesForSuggestion, sessionId,
381+
weightForLocale, inOutWeightOfLangModelVsSpatialModel);
369382
if (mBinaryDictionary.isCorrupted()) {
370-
Log.i(TAG, "Dictionary (" + mDictName +") is corrupted. "
383+
Log.i(TAG, "Dictionary (" + mDictName + ") is corrupted. "
371384
+ "Remove and regenerate it.");
372385
removeBinaryDictionary();
373386
}
@@ -407,7 +420,8 @@ public boolean isInDictionary(final String word) {
407420
}
408421

409422
protected boolean isInDictionaryLocked(final String word) {
410-
if (mBinaryDictionary == null) return false;
423+
if (mBinaryDictionary == null)
424+
return false;
411425
return mBinaryDictionary.isInDictionary(word);
412426
}
413427

@@ -434,14 +448,15 @@ public int getMaxFrequencyOfExactMatches(final String word) {
434448
return NOT_A_PROBABILITY;
435449
}
436450

437-
438451
/**
439-
* Loads the current binary dictionary from internal storage. Assumes the dictionary file
452+
* Loads the current binary dictionary from internal storage. Assumes the
453+
* dictionary file
440454
* exists.
441455
*/
442456
void loadBinaryDictionaryLocked() {
443457
if (DBG_STRESS_TEST) {
444-
// Test if this class does not cause problems when it takes long time to load binary
458+
// Test if this class does not cause problems when it takes long time to load
459+
// binary
445460
// dictionary.
446461
try {
447462
Log.w(TAG, "Start stress in loading: " + mDictName);
@@ -493,13 +508,18 @@ boolean isNeededToRecreate() {
493508
}
494509

495510
/**
496-
* Load the current binary dictionary from internal storage. If the dictionary file doesn't
497-
* exists or needs to be regenerated, the new dictionary file will be asynchronously generated.
498-
* However, the dictionary itself is accessible even before the new dictionary file is actually
499-
* generated. It may return a null result for getSuggestions() in that case by design.
511+
* Load the current binary dictionary from internal storage. If the dictionary
512+
* file doesn't
513+
* exists or needs to be regenerated, the new dictionary file will be
514+
* asynchronously generated.
515+
* However, the dictionary itself is accessible even before the new dictionary
516+
* file is actually
517+
* generated. It may return a null result for getSuggestions() in that case by
518+
* design.
500519
*/
501520
public final void reloadDictionaryIfRequired() {
502-
if (!isReloadRequired()) return;
521+
if (!isReloadRequired())
522+
return;
503523
asyncReloadDictionary();
504524
}
505525

@@ -531,7 +551,8 @@ private void asyncReloadDictionary() {
531551
final BinaryDictionary binaryDictionary = getBinaryDictionary();
532552
if (binaryDictionary != null && !(isValidDictionaryLocked()
533553
// TODO: remove the check below
534-
&& matchesExpectedBinaryDictFormatVersionForThisType(binaryDictionary.getFormatVersion()))) {
554+
&& matchesExpectedBinaryDictFormatVersionForThisType(
555+
binaryDictionary.getFormatVersion()))) {
535556
// Binary dictionary or its format version is not valid. Regenerate
536557
// the dictionary file. createNewDictionaryLocked will remove the
537558
// existing files if appropriate.
@@ -567,8 +588,7 @@ public DictionaryStats getDictionaryStats() {
567588
reloadDictionaryIfRequired();
568589
final String dictName = mDictName;
569590
final File dictFile = mDictFile;
570-
final AsyncResultHolder<DictionaryStats> result =
571-
new AsyncResultHolder<>("DictionaryStats");
591+
final AsyncResultHolder<DictionaryStats> result = new AsyncResultHolder<>("DictionaryStats");
572592
asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() {
573593
@Override
574594
public void run() {
@@ -597,8 +617,7 @@ public void dumpAllWordsForDebug() {
597617
}
598618
int token = 0;
599619
do {
600-
final BinaryDictionary.GetNextWordPropertyResult result =
601-
binaryDictionary.getNextWordProperty(token);
620+
final BinaryDictionary.GetNextWordPropertyResult result = binaryDictionary.getNextWordProperty(token);
602621
final WordProperty wordProperty = result.mWordProperty;
603622
if (wordProperty == null) {
604623
Log.d(tag, " dictionary is empty.");
@@ -615,8 +634,7 @@ public void dumpAllWordsForDebug() {
615634
*/
616635
public WordProperty[] getWordPropertiesForSyncing() {
617636
reloadDictionaryIfRequired();
618-
final AsyncResultHolder<WordProperty[]> result =
619-
new AsyncResultHolder<>("WordPropertiesForSync");
637+
final AsyncResultHolder<WordProperty[]> result = new AsyncResultHolder<>("WordPropertiesForSync");
620638
asyncExecuteTaskWithLock(mLock.readLock(), () -> {
621639
final ArrayList<WordProperty> wordPropertyList = new ArrayList<>();
622640
final BinaryDictionary binaryDictionary = getBinaryDictionary();
@@ -626,8 +644,8 @@ public WordProperty[] getWordPropertiesForSyncing() {
626644
int token = 0;
627645
do {
628646
// TODO: We need a new API that returns *new* un-synced data.
629-
final BinaryDictionary.GetNextWordPropertyResult nextWordPropertyResult =
630-
binaryDictionary.getNextWordProperty(token);
647+
final BinaryDictionary.GetNextWordPropertyResult nextWordPropertyResult = binaryDictionary
648+
.getNextWordProperty(token);
631649
final WordProperty wordProperty = nextWordPropertyResult.mWordProperty;
632650
if (wordProperty == null) {
633651
break;

app/src/main/java/helium314/keyboard/latin/utils/GroqModels.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ object GroqModels {
1111
"groq/compound-mini",
1212
"canopylabs/orpheus-v1-english"
1313
)
14-
const val DEFAULT_MODEL = "llama-3.3-70b-versatile"
14+
const val DEFAULT_MODEL = "llama-3.1-8b-instant"
1515
}

app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,21 @@ enum class ToolbarMode {
121121

122122
val toolbarKeyStrings = entries.associateWithTo(EnumMap(ToolbarKey::class.java)) { it.toString().lowercase(Locale.US) }
123123

124+
private val excludedKeys by lazy {
125+
if (helium314.keyboard.latin.BuildConfig.FLAVOR == "offlinelite")
126+
listOf(CLOSE_HISTORY, PROOFREAD, TRANSLATE)
127+
else
128+
listOf(CLOSE_HISTORY)
129+
}
130+
124131
val defaultToolbarPref by lazy {
125132
val default = when (helium314.keyboard.latin.BuildConfig.FLAVOR) {
126133
"offline" -> listOf(SETTINGS, VOICE, CLIPBOARD, UNDO, INCOGNITO, COPY, PASTE, PROOFREAD, TRANSLATE)
127134
"offlinelite" -> listOf(SETTINGS, VOICE, CLIPBOARD, UNDO, INCOGNITO, COPY, PASTE)
128135
else -> listOf(SETTINGS, VOICE, CLIPBOARD, UNDO, PROOFREAD, TRANSLATE, INCOGNITO, COPY, PASTE)
129136
}
130-
val others = entries.filterNot { it in default || it == CLOSE_HISTORY }
137+
138+
val others = entries.filterNot { it in default || it in excludedKeys }
131139
default.joinToString(Separators.ENTRY) { it.name + Separators.KV + true } + Separators.ENTRY +
132140
others.joinToString(Separators.ENTRY) { it.name + Separators.KV + false }
133141
}
@@ -137,7 +145,8 @@ val defaultPinnedToolbarPref by lazy {
137145
"offlinelite" -> listOf(CLIPBOARD)
138146
else -> listOf(CLIPBOARD, PROOFREAD)
139147
}
140-
entries.filterNot { it == CLOSE_HISTORY }.joinToString(Separators.ENTRY) {
148+
149+
entries.filterNot { it in excludedKeys }.joinToString(Separators.ENTRY) {
141150
it.name + Separators.KV + (it in pinnedDefault)
142151
}
143152
}

app/src/main/java/helium314/keyboard/settings/SearchScreen.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,22 @@ fun SearchSettingsScreen(
104104
},
105105
filteredItems = {
106106
SettingsActivity.settingsContainer.filter(it).filter { setting ->
107-
if (helium314.keyboard.latin.BuildConfig.FLAVOR == "offline") {
108-
setting.key.contains("gemini", ignoreCase = true).not()
109-
} else {
110-
true
107+
val key = setting.key
108+
when (helium314.keyboard.latin.BuildConfig.FLAVOR) {
109+
"offlinelite" -> {
110+
!key.startsWith("gemini") &&
111+
!key.startsWith("groq") &&
112+
!key.startsWith("huggingface") &&
113+
!key.startsWith("ai_provider") &&
114+
!key.startsWith("offline_model_path")
115+
}
116+
"offline" -> {
117+
!key.startsWith("gemini") &&
118+
!key.startsWith("groq") &&
119+
!key.startsWith("huggingface") &&
120+
!key.startsWith("ai_provider")
121+
}
122+
else -> true
111123
}
112124
}
113125
},

app/src/main/java/helium314/keyboard/settings/screens/AIIntegrationScreen.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ private val providerState = MutableStateFlow<String?>(null)
1919
fun AIIntegrationScreen(
2020
onClickBack: () -> Unit,
2121
) {
22+
// Hide AI settings completely in offlinelite flavor
23+
if (BuildConfig.FLAVOR == "offlinelite") {
24+
onClickBack()
25+
return
26+
}
27+
2228
if (BuildConfig.FLAVOR == "standard") {
2329
StandardAIIntegrationScreen(onClickBack)
2430
} else {

0 commit comments

Comments
 (0)