Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import java.io.File
import java.util.Properties
import org.gradle.api.GradleException

fun String.escapeForBuildConfig(): String =
replace("\\", "\\\\").replace("\"", "\\\"")

// Config di firma letta da release/keystore.properties (non tracciato) o da env vars
val keystorePropertiesFileCandidates = listOf(
rootProject.file("release/keystore.properties"),
Expand Down Expand Up @@ -82,6 +85,7 @@ android {
val nightlyVersionCode = providers.gradleProperty("PASTIERA_NIGHTLY_VERSION_CODE").orNull?.toIntOrNull()
val nightlyVersionNameSuffix = providers.gradleProperty("PASTIERA_NIGHTLY_VERSION_SUFFIX").orNull ?: "-nightly"
val isFdroidBuild = gradleBooleanProperty("PASTIERA_FDROID_BUILD")
val klipyApiKey = providers.gradleProperty("PASTIERA_KLIPY_API_KEY").orNull ?: ""

defaultConfig {
applicationId = "it.palsoftware.pastiera"
Expand All @@ -91,6 +95,7 @@ android {
versionName = ciVersionName ?: defaultVersionName

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "KLIPY_API_KEY", "\"${klipyApiKey.escapeForBuildConfig()}\"")
}

signingConfigs {
Expand Down Expand Up @@ -262,6 +267,7 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.documentfile:documentfile:1.0.1")
implementation("androidx.compose.material:material-icons-extended")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("androidx.work:work-runtime-ktx:2.9.1")
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@
android:label="@string/online_layouts_title"
android:theme="@style/Theme.Pastiera" />

<activity
android:name=".LocalMediaPickerActivity"
android:exported="false"
android:excludeFromRecents="true"
android:theme="@style/Theme.Pastiera" />

<!-- Activity for trackpad debug -->
<activity
android:name=".TrackpadDebugActivity"
Expand Down Expand Up @@ -202,6 +208,16 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.gifprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/gif_file_paths" />
</provider>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package it.palsoftware.pastiera

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle

class LocalMediaPickerActivity : Activity() {
companion object {
const val ACTION_FOLDER_SELECTED = "it.palsoftware.pastiera.LOCAL_MEDIA_FOLDER_SELECTED"
const val EXTRA_URI = "uri"
private const val REQUEST_OPEN_TREE = 1001
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
)
}
startActivityForResult(intent, REQUEST_OPEN_TREE)
}

@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != REQUEST_OPEN_TREE) {
finish()
return
}
if (resultCode == RESULT_OK) {
val uri = data?.data
if (uri != null) {
persistUri(uri, data)
SettingsManager.setLocalMediaFolderUri(this, uri.toString())
sendBroadcast(Intent(ACTION_FOLDER_SELECTED).apply {
setPackage(packageName)
putExtra(EXTRA_URI, uri.toString())
})
}
}
finish()
}

private fun persistUri(uri: Uri, data: Intent?) {
val flags = (data?.flags ?: 0) and
(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
contentResolver.takePersistableUriPermission(uri, flags or Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/it/palsoftware/pastiera/SettingsManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ object SettingsManager {
private const val KEY_RESTORE_SYM_PAGE = "restore_sym_page" // SYM page to restore when returning from settings
private const val KEY_PENDING_RESTORE_SYM_PAGE = "pending_restore_sym_page" // Temporary SYM page state saved when opening settings
private const val KEY_SYM_PAGES_CONFIG = "sym_pages_config" // Order/enabled pages for SYM
private const val KEY_KLIPY_API_KEY = "klipy_api_key" // API key used for GIF search
private const val KEY_LOCAL_MEDIA_FOLDER_URI = "local_media_folder_uri" // Persisted SAF tree uri for local media tab
private const val KEY_SYM_AUTO_CLOSE = "sym_auto_close" // Auto-close SYM layout after key press
private const val KEY_SYM_AUTO_CLOSE_ON_TOUCH = "sym_auto_close_on_touch" // Auto-close SYM layout after tapping on-screen SYM keys
private const val KEY_SHIFT_TAP_LATCHES = "shift_tap_latches"
Expand Down Expand Up @@ -279,6 +281,7 @@ object SettingsManager {
private const val DEFAULT_LAYOUT_AWARE_CTRL_SHORTCUTS = false
private const val DEFAULT_AUTO_CORRECT_ENABLED = true
private const val DEFAULT_SUGGESTIONS_ENABLED = true
private const val DEFAULT_LOCAL_MEDIA_FOLDER_URI = ""
private const val DEFAULT_ACCENT_MATCHING_ENABLED = true
private const val DEFAULT_AUTO_REPLACE_ON_SPACE_ENTER = false
private const val DEFAULT_MAX_AUTO_REPLACE_DISTANCE = 1
Expand Down Expand Up @@ -4187,6 +4190,27 @@ object SettingsManager {
Log.e(TAG, "Error saving SYM pages config", e)
}
}

fun getKlipyApiKey(context: Context): String {
return getPreferences(context).getString(KEY_KLIPY_API_KEY, "") ?: ""
}

fun setKlipyApiKey(context: Context, apiKey: String) {
getPreferences(context).edit()
.putString(KEY_KLIPY_API_KEY, apiKey.trim())
.apply()
}

fun getLocalMediaFolderUri(context: Context): String {
return getPreferences(context).getString(KEY_LOCAL_MEDIA_FOLDER_URI, DEFAULT_LOCAL_MEDIA_FOLDER_URI)
?: DEFAULT_LOCAL_MEDIA_FOLDER_URI
}

fun setLocalMediaFolderUri(context: Context, uri: String) {
getPreferences(context).edit()
.putString(KEY_LOCAL_MEDIA_FOLDER_URI, uri.trim())
.apply()
}

/**
* Gets whether SYM layout should auto-close after key press.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ fun SymCustomizationScreen(
SymPagesConfig.PAGE_SYMBOLS -> context.getString(R.string.sym_enable_symbols_page_title)
SymPagesConfig.PAGE_CLIPBOARD -> context.getString(R.string.sym_enable_clipboard_page_title)
SymPagesConfig.PAGE_EMOJI_PICKER -> context.getString(R.string.sym_enable_emoji_picker_page_title)
SymPagesConfig.PAGE_GIF_PICKER -> context.getString(R.string.sym_enable_gif_picker_page_title)
else -> pageId
}
var draggingPageId by remember { mutableStateOf<String?>(null) }
Expand Down Expand Up @@ -697,6 +698,48 @@ fun SymCustomizationScreen(
}
}

// GIF picker page toggle
Surface(
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Icon(
imageVector = Icons.Filled.Keyboard,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(24.dp)
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(R.string.sym_enable_gif_picker_page_title),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Medium,
maxLines = 1
)
Text(
text = stringResource(R.string.sym_enable_gif_picker_page_description),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 2
)
}
Switch(
checked = symPagesConfig.gifPickerEnabled,
onCheckedChange = { enabled ->
persistSymPagesConfig(symPagesConfig.copy(gifPickerEnabled = enabled))
}
)
}
}

// Page order control
Surface(
modifier = Modifier
Expand Down
5 changes: 4 additions & 1 deletion app/src/main/java/it/palsoftware/pastiera/SymPagesConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ data class SymPagesConfig(
val symbolsEnabled: Boolean = true,
val clipboardEnabled: Boolean = false,
val emojiPickerEnabled: Boolean = false,
val gifPickerEnabled: Boolean = false,
val symPageOrder: List<String> = DEFAULT_ORDER
) {
companion object {
const val PAGE_EMOJI = "emoji"
const val PAGE_SYMBOLS = "symbols"
const val PAGE_CLIPBOARD = "clipboard"
const val PAGE_EMOJI_PICKER = "emoji_picker"
val DEFAULT_ORDER = listOf(PAGE_EMOJI, PAGE_SYMBOLS, PAGE_CLIPBOARD, PAGE_EMOJI_PICKER)
const val PAGE_GIF_PICKER = "gif_picker"
val DEFAULT_ORDER = listOf(PAGE_EMOJI, PAGE_SYMBOLS, PAGE_CLIPBOARD, PAGE_EMOJI_PICKER, PAGE_GIF_PICKER)
}

fun normalizedOrder(): List<String> {
Expand All @@ -38,6 +40,7 @@ data class SymPagesConfig(
PAGE_SYMBOLS -> symbolsEnabled
PAGE_CLIPBOARD -> clipboardEnabled
PAGE_EMOJI_PICKER -> emojiPickerEnabled
PAGE_GIF_PICKER -> gifPickerEnabled
else -> false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.size
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.windowInsetsPadding
Expand Down Expand Up @@ -119,6 +120,10 @@ fun TextInputSettingsScreen(
var backspaceAtStartDelete by remember {
mutableStateOf(SettingsManager.getBackspaceAtStartDelete(context))
}

var klipyApiKey by remember {
mutableStateOf(SettingsManager.getKlipyApiKey(context))
}

// Handle system back button
BackHandler { onBack() }
Expand Down Expand Up @@ -383,6 +388,18 @@ fun TextInputSettingsScreen(
}
)

SettingsSectionHeader(text = stringResource(R.string.text_input_section_integrations))
SettingsTextFieldRow(
title = stringResource(R.string.klipy_api_key_title),
description = stringResource(R.string.klipy_api_key_description),
value = klipyApiKey,
placeholder = stringResource(R.string.klipy_api_key_placeholder),
onValueChange = { value ->
klipyApiKey = value
SettingsManager.setKlipyApiKey(context, value)
}
)

SettingsSectionHeader(text = stringResource(R.string.text_input_section_delete))
Surface(
modifier = Modifier
Expand Down Expand Up @@ -492,6 +509,46 @@ fun TextInputSettingsScreen(
}
}

@Composable
private fun SettingsTextFieldRow(
title: String,
description: String,
value: String,
placeholder: String,
onValueChange: (String) -> Unit
) {
SettingsRowFrame(minHeight = 132.dp) {
Icon(
imageVector = Icons.Filled.TextFields,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(24.dp)
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Medium,
maxLines = 2
)
Text(
text = description,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = value,
onValueChange = onValueChange,
singleLine = true,
placeholder = { Text(placeholder) },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
)
}
}
}

@Composable
private fun SettingsSectionHeader(text: String) {
Column(modifier = Modifier.fillMaxWidth()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ object PreferenceSchemas {
"restore_sym_page" to PreferenceValueType.INT,
"pending_restore_sym_page" to PreferenceValueType.INT,
"sym_pages_config" to PreferenceValueType.STRING,
"local_media_folder_uri" to PreferenceValueType.STRING,
"sym_auto_close" to PreferenceValueType.BOOLEAN,
"emoji_picker_expanded_height" to PreferenceValueType.BOOLEAN,
"dismissed_releases" to PreferenceValueType.STRING,
Expand Down
Loading