diff --git a/android-smsmms/build.gradle b/android-smsmms/build.gradle index 505404cb3..673ead30d 100644 --- a/android-smsmms/build.gradle +++ b/android-smsmms/build.gradle @@ -25,15 +25,14 @@ android { defaultConfig { minSdkVersion 21 targetSdkVersion 25 - versionCode 1 - versionName "1.0" } - lintOptions { - abortOnError false - } useLibrary 'org.apache.http.legacy' + namespace 'com.klinker.android.send_message' + lint { + abortOnError false + } } dependencies { diff --git a/android-smsmms/src/main/AndroidManifest.xml b/android-smsmms/src/main/AndroidManifest.xml index c87baa9a5..9cfdcee78 100755 --- a/android-smsmms/src/main/AndroidManifest.xml +++ b/android-smsmms/src/main/AndroidManifest.xml @@ -1,7 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest - package="com.klinker.android.send_message" - xmlns:android="http://schemas.android.com/apk/res/android"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <queries> + <intent> + <action android:name="android.intent.action.TTS_SERVICE" /> + </intent> + </queries> <application> diff --git a/build.gradle b/build.gradle index f7a1e2c8f..1191b05bb 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ buildscript { ext.exoplayer_version = "2.8.1" ext.glide_version = "4.8.0" ext.junit_version = '4.12' - ext.kotlin_version = '1.3.60' + ext.kotlin_version = '1.6.21' ext.lifecycle_version = '2.1.0' ext.material_version = '1.0.0' ext.mockito_version = '2.18.3' @@ -42,9 +42,9 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.4' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1' - classpath 'com.google.gms:google-services:4.2.0' + classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.2' + classpath 'com.google.gms:google-services:4.3.10' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.realm:realm-gradle-plugin:$realm_version" } diff --git a/common/build.gradle b/common/build.gradle index a893d1c18..3f8be346c 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -28,6 +28,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + namespace 'com.moez.QKSMS.common' } dependencies { diff --git a/common/src/main/AndroidManifest.xml b/common/src/main/AndroidManifest.xml index 969a751c5..21edd1fd3 100644 --- a/common/src/main/AndroidManifest.xml +++ b/common/src/main/AndroidManifest.xml @@ -16,4 +16,4 @@ ~ You should have received a copy of the GNU General Public License ~ along with QKSMS. If not, see <http://www.gnu.org/licenses/>. --> -<manifest package="com.moez.QKSMS.common" /> +<manifest /> diff --git a/common/src/main/java/com/moez/QKSMS/common/util/extensions/ContextExtensions.kt b/common/src/main/java/com/moez/QKSMS/common/util/extensions/ContextExtensions.kt index f5534a9fc..256c7cdca 100644 --- a/common/src/main/java/com/moez/QKSMS/common/util/extensions/ContextExtensions.kt +++ b/common/src/main/java/com/moez/QKSMS/common/util/extensions/ContextExtensions.kt @@ -20,6 +20,7 @@ package com.moez.QKSMS.common.util.extensions import android.app.job.JobScheduler import android.content.Context +import android.content.pm.PackageInfo import android.content.res.ColorStateList import android.graphics.Color import android.util.TypedValue @@ -27,6 +28,7 @@ import android.widget.Toast import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.content.getSystemService +import androidx.core.content.pm.PackageInfoCompat import com.moez.QKSMS.util.tryOrNull fun Context.getColorCompat(colorRes: Int): Int { @@ -85,7 +87,7 @@ fun Context.isInstalled(packageName: String): Boolean { } val Context.versionCode: Int - get() = packageManager.getPackageInfo(packageName, 0).versionCode + get() = PackageInfoCompat.getLongVersionCode(PackageInfo()).toInt() val Context.jobScheduler: JobScheduler get() = getSystemService()!! diff --git a/data/build.gradle b/data/build.gradle index a76aff2b4..74ac944cb 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -42,6 +42,7 @@ android { withAnalytics { dimension "analytics" } noAnalytics { dimension "analytics" } } + namespace 'com.moez.QKSMS.data' } dependencies { diff --git a/data/src/main/AndroidManifest.xml b/data/src/main/AndroidManifest.xml index 3c6184a70..21edd1fd3 100644 --- a/data/src/main/AndroidManifest.xml +++ b/data/src/main/AndroidManifest.xml @@ -16,4 +16,4 @@ ~ You should have received a copy of the GNU General Public License ~ along with QKSMS. If not, see <http://www.gnu.org/licenses/>. --> -<manifest package="com.moez.QKSMS.data" /> +<manifest /> diff --git a/domain/build.gradle b/domain/build.gradle index 85a69fd8d..6cb92629f 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -34,6 +34,7 @@ android { minSdkVersion 21 targetSdkVersion 29 } + namespace 'com.moez.QKSMS.domain' } dependencies { diff --git a/domain/src/main/AndroidManifest.xml b/domain/src/main/AndroidManifest.xml index 00485b51a..21edd1fd3 100644 --- a/domain/src/main/AndroidManifest.xml +++ b/domain/src/main/AndroidManifest.xml @@ -16,4 +16,4 @@ ~ You should have received a copy of the GNU General Public License ~ along with QKSMS. If not, see <http://www.gnu.org/licenses/>. --> -<manifest package="com.moez.QKSMS.domain" /> +<manifest /> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4b9cec46b..0299a449a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/presentation/build.gradle b/presentation/build.gradle index 7dee8d3c8..10734f0b9 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -24,7 +24,6 @@ apply plugin: 'kotlin-kapt' android { compileSdkVersion 29 - buildToolsVersion "29.0.3" flavorDimensions "analytics" defaultConfig { @@ -61,14 +60,15 @@ android { jvmTarget = "1.8" } - lintOptions { - abortOnError false - } productFlavors { withAnalytics { dimension "analytics" } noAnalytics { dimension "analytics" } } + namespace 'com.moez.QKSMS' + lint { + abortOnError false + } if (System.getenv("CI") == "true") { signingConfigs.release.storeFile = file("../keystore") diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml index 796dc6a33..351f1de03 100644 --- a/presentation/src/main/AndroidManifest.xml +++ b/presentation/src/main/AndroidManifest.xml @@ -17,8 +17,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with QKSMS. If not, see <http://www.gnu.org/licenses/>. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.moez.QKSMS"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" /> diff --git a/presentation/src/main/java/com/moez/QKSMS/common/widget/TightTextView.kt b/presentation/src/main/java/com/moez/QKSMS/common/widget/TightTextView.kt index 0c104b81c..36d091956 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/widget/TightTextView.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/widget/TightTextView.kt @@ -20,6 +20,7 @@ package com.moez.QKSMS.common.widget import android.content.Context import android.util.AttributeSet +import kotlin.math.ceil class TightTextView @JvmOverloads constructor( context: Context, @@ -35,11 +36,9 @@ class TightTextView @JvmOverloads constructor( return } - val maxLineWidth = (0 until layout.lineCount) - .map(layout::getLineWidth) - .max() ?: 0f + val maxLineWidth = (0 until layout.lineCount).maxOfOrNull(layout::getLineWidth) ?: 0f - val width = Math.ceil(maxLineWidth.toDouble()).toInt() + compoundPaddingLeft + compoundPaddingRight + val width = ceil(maxLineWidth.toDouble()).toInt() + compoundPaddingLeft + compoundPaddingRight if (width < measuredWidth) { val widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec)) super.onMeasure(widthSpec, heightMeasureSpec) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupPresenter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupPresenter.kt index b547ec306..0a89c0380 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupPresenter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupPresenter.kt @@ -64,7 +64,7 @@ class BackupPresenter @Inject constructor( .distinctUntilChanged() .switchMap { backupRepo.getBackups() } .doOnNext { backups -> newState { copy(backups = backups) } } - .map { backups -> backups.map { it.date }.max() ?: 0L } + .map { backups -> backups.maxOfOrNull { it.date } ?: 0L } .map { lastBackup -> when (lastBackup) { 0L -> context.getString(R.string.backup_never) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt index 3b42535b6..2bdb9b05d 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com> + * Copyright (C) 2017,2021 Moez Bhatti <moez.bhatti@gmail.com>,to268 * * This file is part of QKSMS. * @@ -30,7 +30,9 @@ import android.os.Build import android.os.Bundle import android.provider.ContactsContract import android.provider.MediaStore +import android.speech.tts.TextToSpeech import android.text.format.DateFormat +import android.util.Log import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AlertDialog @@ -66,12 +68,16 @@ import io.reactivex.Observable import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.Subject import kotlinx.android.synthetic.main.compose_activity.* +import kotlinx.android.synthetic.main.compose_activity.attachments +import kotlinx.android.synthetic.main.compose_activity.sim +import kotlinx.android.synthetic.main.compose_activity.simIndex +import kotlinx.android.synthetic.main.message_list_item_in.* import java.text.SimpleDateFormat import java.util.* import javax.inject.Inject import kotlin.collections.HashMap -class ComposeActivity : QkThemedActivity(), ComposeView { +class ComposeActivity : QkThemedActivity(), ComposeView, TextToSpeech.OnInitListener { companion object { private const val SelectContactRequestCode = 0 @@ -82,12 +88,18 @@ class ComposeActivity : QkThemedActivity(), ComposeView { private const val CameraDestinationKey = "camera_destination" } - @Inject lateinit var attachmentAdapter: AttachmentAdapter - @Inject lateinit var chipsAdapter: ChipsAdapter - @Inject lateinit var dateFormatter: DateFormatter - @Inject lateinit var messageAdapter: MessagesAdapter - @Inject lateinit var navigator: Navigator - @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + @Inject + lateinit var attachmentAdapter: AttachmentAdapter + @Inject + lateinit var chipsAdapter: ChipsAdapter + @Inject + lateinit var dateFormatter: DateFormatter + @Inject + lateinit var messageAdapter: MessagesAdapter + @Inject + lateinit var navigator: Navigator + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory override val activityVisibleIntent: Subject<Boolean> = PublishSubject.create() override val chipsSelectedIntent: Subject<HashMap<String, String?>> = PublishSubject.create() @@ -119,6 +131,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory)[ComposeViewModel::class.java] } private var cameraDestination: Uri? = null + private var tts: TextToSpeech? = null override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) @@ -160,6 +173,9 @@ class ComposeActivity : QkThemedActivity(), ComposeView { if (Build.VERSION.SDK_INT <= 22) { messageBackground.setBackgroundTint(resolveThemeColor(R.attr.bubbleColor)) } + + // Set tts info + tts = TextToSpeech(this, this) } override fun onStart() { @@ -206,6 +222,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { toolbar.menu.findItem(R.id.details)?.isVisible = !state.editingMode && state.selectedMessages == 1 toolbar.menu.findItem(R.id.delete)?.isVisible = !state.editingMode && state.selectedMessages > 0 toolbar.menu.findItem(R.id.forward)?.isVisible = !state.editingMode && state.selectedMessages == 1 + toolbar.menu.findItem(R.id.speech)?.isVisible = !state.editingMode && state.selectedMessages == 1 toolbar.menu.findItem(R.id.previous)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty() toolbar.menu.findItem(R.id.next)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty() toolbar.menu.findItem(R.id.clear)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty() @@ -251,6 +268,10 @@ class ComposeActivity : QkThemedActivity(), ComposeView { .show() } + override fun speechText(text: String) { + tts!!.speak(text, TextToSpeech.QUEUE_FLUSH, null, "") + } + override fun requestDefaultSms() { navigator.showDefaultSmsDialog(this) } @@ -399,4 +420,27 @@ class ComposeActivity : QkThemedActivity(), ComposeView { override fun onBackPressed() = backPressedIntent.onNext(Unit) + // Text to speech + override fun onInit(status: Int) { + if (status == TextToSpeech.SUCCESS) { + val result = tts!!.setLanguage(Locale.getDefault()) + + if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { + Log.e("TTS", "The default language is not supported !") + } + + } else { + Log.e("TTS", "Initialization failed !") + } + } + + override fun onDestroy() { + if (tts != null) { + tts!!.stop() + tts!!.shutdown() + } + + super.onDestroy() + } + } diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt index d8a78eec2..50fd3ea3b 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt @@ -58,6 +58,7 @@ interface ComposeView : QkView<ComposeState> { fun clearSelection() fun showDetails(details: String) + fun speechText(text: String) fun requestDefaultSms() fun requestStoragePermission() fun requestSmsPermission() diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt index ba601f2f7..9fac4b364 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com> + * Copyright (C) 2017,2021 Moez Bhatti <moez.bhatti@gmail.com>,to268 * * This file is part of QKSMS. * @@ -74,29 +74,29 @@ import javax.inject.Inject import javax.inject.Named class ComposeViewModel @Inject constructor( - @Named("query") private val query: String, - @Named("threadId") private val threadId: Long, - @Named("addresses") private val addresses: List<String>, - @Named("text") private val sharedText: String, - @Named("attachments") private val sharedAttachments: Attachments, - private val contactRepo: ContactRepository, - private val context: Context, - private val activeConversationManager: ActiveConversationManager, - private val addScheduledMessage: AddScheduledMessage, - private val billingManager: BillingManager, - private val cancelMessage: CancelDelayedMessage, - private val conversationRepo: ConversationRepository, - private val deleteMessages: DeleteMessages, - private val markRead: MarkRead, - private val messageDetailsFormatter: MessageDetailsFormatter, - private val messageRepo: MessageRepository, - private val navigator: Navigator, - private val permissionManager: PermissionManager, - private val phoneNumberUtils: PhoneNumberUtils, - private val prefs: Preferences, - private val retrySending: RetrySending, - private val sendMessage: SendMessage, - private val subscriptionManager: SubscriptionManagerCompat + @Named("query") private val query: String, + @Named("threadId") private val threadId: Long, + @Named("addresses") private val addresses: List<String>, + @Named("text") private val sharedText: String, + @Named("attachments") private val sharedAttachments: Attachments, + private val contactRepo: ContactRepository, + private val context: Context, + private val activeConversationManager: ActiveConversationManager, + private val addScheduledMessage: AddScheduledMessage, + private val billingManager: BillingManager, + private val cancelMessage: CancelDelayedMessage, + private val conversationRepo: ConversationRepository, + private val deleteMessages: DeleteMessages, + private val markRead: MarkRead, + private val messageDetailsFormatter: MessageDetailsFormatter, + private val messageRepo: MessageRepository, + private val navigator: Navigator, + private val permissionManager: PermissionManager, + private val phoneNumberUtils: PhoneNumberUtils, + private val prefs: Preferences, + private val retrySending: RetrySending, + private val sendMessage: SendMessage, + private val subscriptionManager: SubscriptionManagerCompat ) : QkViewModel<ComposeView, ComposeState>(ComposeState( editingMode = threadId == 0L && addresses.isEmpty(), threadId = threadId, @@ -340,6 +340,16 @@ class ComposeViewModel @Inject constructor( .autoDisposable(view.scope()) .subscribe { view.showDetails(it) } + // Speech text + view.optionsItemIntent + .filter { it == R.id.speech } + .withLatestFrom(view.messagesSelectedIntent) { _, messages -> messages } + .mapNotNull { messages -> messages.firstOrNull().also { view.clearSelection() } } + .mapNotNull(messageRepo::getMessage) + .mapNotNull(Message::getText) + .autoDisposable(view.scope()) + .subscribe { view.speechText(it) } + // Delete the messages view.optionsItemIntent .filter { it == R.id.delete } diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/plus/experiment/UpgradeButtonExperiment.kt b/presentation/src/main/java/com/moez/QKSMS/feature/plus/experiment/UpgradeButtonExperiment.kt index c954a5592..656ea55b1 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/plus/experiment/UpgradeButtonExperiment.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/plus/experiment/UpgradeButtonExperiment.kt @@ -29,7 +29,7 @@ import javax.inject.Inject class UpgradeButtonExperiment @Inject constructor( context: Context, analytics: AnalyticsManager -) : Experiment<@StringRes Int>(context, analytics) { +) : Experiment<@receiver:StringRes Int>(context, analytics) { override val key: String = "Upgrade Button" diff --git a/presentation/src/main/res/drawable/ic_speech_black_24dp.xml b/presentation/src/main/res/drawable/ic_speech_black_24dp.xml new file mode 100644 index 000000000..cba949793 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_speech_black_24dp.xml @@ -0,0 +1,30 @@ +<!-- + ~ Copyright (C) 2021 to268 + ~ + ~ This file is part of QKSMS. + ~ + ~ QKSMS is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ (at your option) any later version. + ~ + ~ QKSMS is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with QKSMS. If not, see <http://www.gnu.org/licenses/>. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M9,9m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"/> + <path + android:fillColor="#FF000000" + android:pathData="M9,15c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4zM16.76,5.36l-1.68,1.69c0.84,1.18 0.84,2.71 0,3.89l1.68,1.69c2.02,-2.02 2.02,-5.07 0,-7.27zM20.07,2l-1.63,1.63c2.77,3.02 2.77,7.56 0,10.74L20.07,16c3.9,-3.89 3.91,-9.95 0,-14z"/> +</vector> diff --git a/presentation/src/main/res/menu/compose.xml b/presentation/src/main/res/menu/compose.xml index c39f25ac2..e98db8d52 100644 --- a/presentation/src/main/res/menu/compose.xml +++ b/presentation/src/main/res/menu/compose.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2019 Moez Bhatti <moez.bhatti@gmail.com> + ~ Copyright (C) 2019,2021 Moez Bhatti <moez.bhatti@gmail.com>,to268 ~ ~ This file is part of QKSMS. ~ @@ -69,6 +69,13 @@ android:visible="false" app:showAsAction="ifRoom" /> + <item + android:id="@+id/speech" + android:icon="@drawable/ic_speech_black_24dp" + android:title="@string/compose_menu_speech" + android:visible="false" + app:showAsAction="ifRoom" /> + <item android:id="@+id/previous" android:icon="@drawable/ic_keyboard_arrow_up_black_24dp" diff --git a/presentation/src/main/res/values-fr/strings.xml b/presentation/src/main/res/values-fr/strings.xml index 27ae8db75..8e62e1ca2 100644 --- a/presentation/src/main/res/values-fr/strings.xml +++ b/presentation/src/main/res/values-fr/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2019 Moez Bhatti <moez.bhatti@gmail.com> + ~ Copyright (C) 2019,2021 Moez Bhatti <moez.bhatti@gmail.com>,to268 ~ ~ This file is part of QKSMS. ~ @@ -102,6 +102,7 @@ <string name="compose_hint">Écrire un message…</string> <string name="compose_menu_copy">Copier le texte</string> <string name="compose_menu_forward">Transférer</string> + <string name="compose_menu_speech">Énoncer le texte</string> <string name="compose_menu_delete">Supprimer</string> <string name="compose_menu_previous">Précédent</string> <string name="compose_menu_next">Suivant</string> diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 2c7e1b838..82ebd278e 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ <!-- - ~ Copyright (C) 2019 Moez Bhatti <moez.bhatti@gmail.com> + ~ Copyright (C) 2019,2021 Moez Bhatti <moez.bhatti@gmail.com>,to268 ~ ~ This file is part of QKSMS. ~ @@ -116,6 +116,7 @@ <string name="compose_hint">Write a message…</string> <string name="compose_menu_copy">Copy text</string> <string name="compose_menu_forward">Forward</string> + <string name="compose_menu_speech" translatable="false">Speech text</string> <string name="compose_menu_delete">Delete</string> <string name="compose_menu_previous">Previous</string> <string name="compose_menu_next">Next</string> @@ -382,7 +383,7 @@ <string name="about_changelog" translatable="false">https://github.com/moezbhatti/qksms/releases</string> <string name="about_contact" translatable="false">moez@qklabs.com</string> <string name="about_license" translatable="false">GNU General Public License v3.0</string> - <string name="about_copyright" translatable="false">© 2014–2019</string> + <string name="about_copyright" translatable="false">© 2014–2021</string> <string name="changelog_title" translatable="false">New in QKSMS</string> <string name="changelog_version" translatable="false">Version %s</string>