diff --git a/app/build.gradle b/app/build.gradle index 1b1c9f225..a48cdc51c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +apply plugin: 'org.jetbrains.kotlin.android' apply plugin: 'witness' apply plugin: 'com.google.devtools.ksp' apply plugin: 'kotlin-parcelize' @@ -13,8 +13,8 @@ apply plugin: 'org.jetbrains.kotlin.plugin.compose' configurations.all { exclude module: "commons-logging" } -def canonicalVersionCode = 99 -def canonicalVersionName = "2.9.8" +def canonicalVersionCode = 103 +def canonicalVersionName = "3.0.0" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, @@ -22,6 +22,8 @@ def abiPostFix = ['armeabi-v7a' : 1, 'x86_64' : 4, 'universal' : 5] +def splitApksEnabled = project.properties["splitApks"] == "true" + android { useLibrary 'org.apache.http.legacy' @@ -49,13 +51,19 @@ android { splits { abi { - enable true + enable splitApksEnabled reset() include 'armeabi-v7a', 'arm64-v8a', 'x86_64' universalApk true } } + bundle { + language { + enableSplit = false + } + } + defaultConfig { versionCode canonicalVersionCode versionName canonicalVersionName @@ -90,6 +98,9 @@ android { cppFlags '-std=c++17' } } + ndk { + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64' + } applicationId 'io.beldex.bchat' } diff --git a/app/buildGradle/dependencies.gradle b/app/buildGradle/dependencies.gradle index de60014a4..f66c2c349 100644 --- a/app/buildGradle/dependencies.gradle +++ b/app/buildGradle/dependencies.gradle @@ -16,8 +16,8 @@ dependencies { implementation "androidx.lifecycle:lifecycle-process:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation "androidx.paging:paging-runtime-ktx:$pagingVersion" - implementation 'androidx.activity:activity-ktx:1.13.0' - implementation 'androidx.activity:activity-compose:1.13.0' + implementation 'androidx.activity:activity-ktx:1.10.1' + implementation 'androidx.activity:activity-compose:1.10.1' implementation 'androidx.fragment:fragment-ktx:1.9.0' implementation "androidx.core:core-ktx:$coreVersion" implementation "androidx.work:work-runtime-ktx:2.11.2" @@ -40,7 +40,7 @@ dependencies { implementation 'androidx.legacy:legacy-support-v4:1.0.0' ksp "com.github.bumptech.glide:ksp:$glideVersion" implementation 'com.makeramen:roundedimageview:2.1.0' - implementation 'org.greenrobot:eventbus:3.0.0' + implementation 'org.greenrobot:eventbus:3.3.1' implementation 'com.vanniktech:android-image-cropper:4.5.0' implementation("com.google.dagger:hilt-android:$daggerHiltVersion") ksp("com.google.dagger:hilt-compiler:$daggerHiltVersion") diff --git a/app/playMainNet/release/output-metadata.json b/app/playMainNet/release/output-metadata.json index dc784d625..980923ebe 100644 --- a/app/playMainNet/release/output-metadata.json +++ b/app/playMainNet/release/output-metadata.json @@ -8,51 +8,12 @@ "variantName": "playMainNetRelease", "elements": [ { - "type": "UNIVERSAL", + "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 99, - "versionName": "2.9.8", - "outputFile": "Bchat-2.9.8-universal.apk" - }, - { - "type": "ONE_OF_MANY", - "filters": [ - { - "filterType": "ABI", - "value": "armeabi-v7a" - } - ], - "attributes": [], - "versionCode": 99, - "versionName": "2.9.8", - "outputFile": "Bchat-2.9.8-armeabi-v7a.apk" - }, - { - "type": "ONE_OF_MANY", - "filters": [ - { - "filterType": "ABI", - "value": "x86_64" - } - ], - "attributes": [], - "versionCode": 99, - "versionName": "2.9.8", - "outputFile": "Bchat-2.9.8-x86_64.apk" - }, - { - "type": "ONE_OF_MANY", - "filters": [ - { - "filterType": "ABI", - "value": "arm64-v8a" - } - ], - "attributes": [], - "versionCode": 99, - "versionName": "2.9.8", - "outputFile": "Bchat-2.9.8-arm64-v8a.apk" + "versionCode": 103, + "versionName": "3.0.0", + "outputFile": "Bchat-3.0.0-universal.apk" } ], "elementType": "File", @@ -61,20 +22,14 @@ "minApi": 28, "maxApi": 30, "baselineProfiles": [ - "baselineProfiles/1/Bchat-2.9.8-universal.dm", - "baselineProfiles/1/Bchat-2.9.8-armeabi-v7a.dm", - "baselineProfiles/1/Bchat-2.9.8-x86_64.dm", - "baselineProfiles/1/Bchat-2.9.8-arm64-v8a.dm" + "baselineProfiles/1/Bchat-3.0.0-universal.dm" ] }, { "minApi": 31, "maxApi": 2147483647, "baselineProfiles": [ - "baselineProfiles/0/Bchat-2.9.8-universal.dm", - "baselineProfiles/0/Bchat-2.9.8-armeabi-v7a.dm", - "baselineProfiles/0/Bchat-2.9.8-x86_64.dm", - "baselineProfiles/0/Bchat-2.9.8-arm64-v8a.dm" + "baselineProfiles/0/Bchat-3.0.0-universal.dm" ] } ], diff --git a/app/src/main/assets/about.txt b/app/src/main/assets/about.txt deleted file mode 100644 index 32e41b506..000000000 --- a/app/src/main/assets/about.txt +++ /dev/null @@ -1,18 +0,0 @@ -BChat is a decentralized peer to peer messaging application built on top of the Beldex blockchain. BChat preserves your confidentiality as it doesn't collect or store your personal information such as your name, age, email ID, phone number, username, location, contacts, IP address, etc. All you need to create a BChat account is the internet. - -When you create an account on BChat, a unique BChat ID and recovery phrase (seed key) are generated. The BChat ID is your public key which you can use to chat with other people on BChat. Your BChat ID is also connected to a wallet address which is generated each time you create a new account. - -The recovery key (seed key) is your private key which you can use to restore your account on any device. Your recovery key is similar to your password. Do not share it with anyone. Do not store a digital copy of it. Write it down on paper and keep it safe. If you lose your recovery key, you will lose access to your account. - -On BChat, your messages are routed through the nodes on the Beldex network and are only stored locally on your device. Your messages and their associated metadata are not stored anywhere on the Beldex blockchain including the masternode storage servers. - -You own your data. You can choose to delete your data at any time with a single click of a button. To clear your data, click the profile icon at the top right corner, select ‘My Account > Clear Data > Delete>Ok’ and it’s gone. - -BChat is built and maintained by the Beldex foundation. It is open-source and censorship-free. Anybody can contribute to its development. The Beldex foundation is an organization building confidential applications for the decentralized web. The products that are currently being researched and developed include BChat, BelNet (a decentralized VPN service), Beldex Browser (a decentralized ad-free browser), and the Beldex protocol (a protocol to anonymize the transactions of other blockchain assets) - -BChat is Beldex's flagship product. The initial version of BChat will be a messenger that lets you send truly anonymous messages. You can create secret and social groups. Secret groups allow a maximum of 100 participants, while social groups do not have an upper limit. Anyone can create a secret group but you can only add your BChat contacts to it. Social groups require a dedicated server that needs to be maintained by its creator. Future versions of BChat may incorporate voice and video calls, and let users send and receive BDX, the native cryptocurrency of the Beldex network. The “pay as you chat” feature will allow anyone to transfer BDX to their contacts right from their chatbox. - -The Beldex Research Labs, the research wing of the Beldex foundation is researching the possibility of implementing zk-SNARKs (as an alternative to ring signatures) to increase the anonymity set and data sharding to improve scalability on the Beldex network, which may render BChat several times faster than current decentralized applications and put it on track for global adoption. - -Credits: -The Beldex network and BChat uses several protocols that were designed by the open-source projects Monero, Signal, and Session. \ No newline at end of file diff --git a/app/src/main/assets/changeLog.json b/app/src/main/assets/changeLog.json index 441b6aa99..556347dfe 100644 --- a/app/src/main/assets/changeLog.json +++ b/app/src/main/assets/changeLog.json @@ -647,6 +647,17 @@ "description":"Minor bug fixes" } ] + }, + { + "title": "3.0.0", + "descriptions": [ + { + "description":"Introducing Multi language support in BChat" + }, + { + "description":"Minor bug fixes" + } + ] } ] } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/ApplicationContext.kt b/app/src/main/java/io/beldex/bchat/ApplicationContext.kt index 55c96f528..1385ee7f3 100644 --- a/app/src/main/java/io/beldex/bchat/ApplicationContext.kt +++ b/app/src/main/java/io/beldex/bchat/ApplicationContext.kt @@ -4,6 +4,7 @@ import android.app.Application import android.app.KeyguardManager import android.content.Context import android.content.Intent +import android.content.res.Configuration import android.os.AsyncTask import android.os.Handler import android.os.HandlerThread @@ -26,6 +27,7 @@ import com.beldex.libbchat.utilities.ProfilePictureUtilities.upload import com.beldex.libbchat.utilities.SSKEnvironment.Companion.configure import com.beldex.libbchat.utilities.TextSecurePreferences import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.clearAll +import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.getAppSelectedLanguage import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.getLanguage import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.getLastProfilePictureUpload import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.getLocalNumber @@ -39,6 +41,7 @@ import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.setRefreshD import com.beldex.libbchat.utilities.Util.runOnMain import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper import com.beldex.libbchat.utilities.dynamiclanguage.LocaleParser.Companion.configure +import com.beldex.libbchat.utilities.dynamiclanguage.LocaleParser.Companion.findBestMatchingLocaleForLanguage import com.beldex.libsignal.utilities.HTTP.isConnectedToNetwork import com.beldex.libsignal.utilities.JsonUtil import com.beldex.libsignal.utilities.Log @@ -81,6 +84,7 @@ import io.beldex.bchat.service.KeyCachingService import io.beldex.bchat.sskenvironment.ProfileManager import io.beldex.bchat.sskenvironment.ReadReceiptManager import io.beldex.bchat.sskenvironment.TypingStatusRepository +import io.beldex.bchat.util.AppLanguageEvent import io.beldex.bchat.util.Broadcaster import io.beldex.bchat.util.FirebaseRemoteConfigUtil import io.beldex.bchat.util.UiModeUtilities.setupUiModeToUserSelected @@ -356,17 +360,26 @@ class ApplicationContext: Application(), DefaultLifecycleObserver { } } - override fun attachBaseContext(base : Context?) { + override fun attachBaseContext(base: Context?) { initializeLocaleParser() super.attachBaseContext( DynamicLanguageContextWrapper.updateContext( - base, getLanguage( + base, getAppSelectedLanguage( base!! ) ) ) } + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + val appSettingLanguage = findBestMatchingLocaleForLanguage(getLanguage(this)) + TextSecurePreferences.setDeviceLanguage(this, appSettingLanguage?.language) + appSettingLanguage?.let { it -> + AppLanguageEvent.update(it.language) + } + } + private class ProviderInitializationException() : RuntimeException() diff --git a/app/src/main/java/io/beldex/bchat/BaseActionBarActivity.java b/app/src/main/java/io/beldex/bchat/BaseActionBarActivity.java index cda0b2735..b269b7e90 100644 --- a/app/src/main/java/io/beldex/bchat/BaseActionBarActivity.java +++ b/app/src/main/java/io/beldex/bchat/BaseActionBarActivity.java @@ -58,7 +58,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override protected void onResume() { super.onResume(); - DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getLanguage(this)); + DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getAppSelectedLanguage(this)); String name = getResources().getString(R.string.app_name); Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground); int color = getResources().getColor(R.color.app_icon_background); @@ -78,7 +78,7 @@ protected final boolean handleNavigateUp() { @Override protected void attachBaseContext(Context newBase) { - super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, TextSecurePreferences.getLanguage(newBase))); + super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, TextSecurePreferences.getAppSelectedLanguage(newBase))); } diff --git a/app/src/main/java/io/beldex/bchat/BaseActivity.java b/app/src/main/java/io/beldex/bchat/BaseActivity.java index 7d69b5e99..8bce588af 100644 --- a/app/src/main/java/io/beldex/bchat/BaseActivity.java +++ b/app/src/main/java/io/beldex/bchat/BaseActivity.java @@ -15,7 +15,7 @@ public abstract class BaseActivity extends FragmentActivity { @Override protected void onResume() { super.onResume(); - DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getLanguage(this)); + DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getAppSelectedLanguage(this)); String name = getResources().getString(R.string.app_name); Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground); int color = getResources().getColor(R.color.app_icon_background); @@ -24,6 +24,6 @@ protected void onResume() { @Override protected void attachBaseContext(Context newBase) { - super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, TextSecurePreferences.getLanguage(newBase))); + super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, TextSecurePreferences.getAppSelectedLanguage(newBase))); } } diff --git a/app/src/main/java/io/beldex/bchat/BaseAppCompatActivity.kt b/app/src/main/java/io/beldex/bchat/BaseAppCompatActivity.kt new file mode 100644 index 000000000..5e0abd989 --- /dev/null +++ b/app/src/main/java/io/beldex/bchat/BaseAppCompatActivity.kt @@ -0,0 +1,27 @@ +package io.beldex.bchat + +import android.content.Context +import androidx.appcompat.app.AppCompatActivity +import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.getAppSelectedLanguage +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageActivityHelper +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper + + +open class BaseAppCompatActivity : AppCompatActivity() { + protected override fun onResume() { + super.onResume() + DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage( + this, + getAppSelectedLanguage(this) + ) + } + + protected override fun attachBaseContext(newBase: Context) { + super.attachBaseContext( + DynamicLanguageContextWrapper.updateContext( + newBase, + getAppSelectedLanguage(newBase) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/BaseComponentActivity.kt b/app/src/main/java/io/beldex/bchat/BaseComponentActivity.kt new file mode 100644 index 000000000..2e15ec45b --- /dev/null +++ b/app/src/main/java/io/beldex/bchat/BaseComponentActivity.kt @@ -0,0 +1,26 @@ +package io.beldex.bchat + +import android.content.Context +import androidx.activity.ComponentActivity +import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.getAppSelectedLanguage +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageActivityHelper +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper + +open class BaseComponentActivity : ComponentActivity() { + protected override fun onResume() { + super.onResume() + DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage( + this, + getAppSelectedLanguage(this) + ) + } + + protected override fun attachBaseContext(newBase: Context) { + super.attachBaseContext( + DynamicLanguageContextWrapper.updateContext( + newBase, + getAppSelectedLanguage(newBase) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/MediaGalleryAdapter.java b/app/src/main/java/io/beldex/bchat/MediaGalleryAdapter.java index 4b02da825..fde4201c6 100644 --- a/app/src/main/java/io/beldex/bchat/MediaGalleryAdapter.java +++ b/app/src/main/java/io/beldex/bchat/MediaGalleryAdapter.java @@ -103,7 +103,7 @@ public ItemViewHolder onCreateItemViewHolder(ViewGroup parent, int itemType) { @Override public void onBindHeaderViewHolder(StickyHeaderGridAdapter.HeaderViewHolder viewHolder, int section) { - ((HeaderHolder)viewHolder).textView.setText(media.getName(section, locale)); + ((HeaderHolder)viewHolder).textView.setText(media.getName(section, context)); } @Override diff --git a/app/src/main/java/io/beldex/bchat/MediaOverviewActivity.java b/app/src/main/java/io/beldex/bchat/MediaOverviewActivity.java index d6e85e7c1..c27e68a7d 100644 --- a/app/src/main/java/io/beldex/bchat/MediaOverviewActivity.java +++ b/app/src/main/java/io/beldex/bchat/MediaOverviewActivity.java @@ -61,13 +61,23 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager.widget.ViewPager; +import com.beldex.libbchat.messaging.messages.control.DataExtractionNotification; +import com.beldex.libbchat.messaging.sending_receiving.MessageSender; import com.beldex.libbchat.mnode.MnodeAPI; +import com.beldex.libbchat.utilities.Address; +import com.beldex.libbchat.utilities.Util; +import com.beldex.libbchat.utilities.ViewUtil; +import com.beldex.libbchat.utilities.recipients.Recipient; +import com.beldex.libbchat.utilities.task.ProgressDialogAsyncTask; +import com.bumptech.glide.Glide; import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager; import com.google.android.material.tabs.TabLayout; -import com.beldex.libbchat.messaging.messages.control.DataExtractionNotification; -import com.beldex.libbchat.messaging.sending_receiving.MessageSender; -import com.beldex.libbchat.utilities.Address; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; import io.beldex.bchat.conversation.v2.ConversationActivityV2; import io.beldex.bchat.database.CursorRecyclerViewAdapter; @@ -75,9 +85,7 @@ import io.beldex.bchat.database.loaders.BucketedThreadMediaLoader; import io.beldex.bchat.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia; import io.beldex.bchat.database.loaders.ThreadMediaLoader; -import com.bumptech.glide.Glide; import io.beldex.bchat.permissions.Permissions; -import com.beldex.libbchat.utilities.recipients.Recipient; import io.beldex.bchat.util.AttachmentUtil; import io.beldex.bchat.util.GridSpaceItemDecoration; import io.beldex.bchat.util.SaveAttachmentTask; @@ -182,7 +190,8 @@ public Fragment getItem(int position) { Bundle args = new Bundle(); args.putString(MediaOverviewGalleryFragment.ADDRESS_EXTRA, recipient.getAddress().serialize()); - args.putSerializable(MediaOverviewGalleryFragment.LOCALE_EXTRA, Locale.getDefault()); + Locale appLocale = getResources().getConfiguration().getLocales().get(0); + args.putSerializable(MediaOverviewGalleryFragment.LOCALE_EXTRA, appLocale); fragment.setArguments(args); @@ -429,10 +438,10 @@ private void sendMediaSavedNotificationIfNeeded() { private void handleDeleteMedia(@NonNull Collection mediaRecords) { int recordCount = mediaRecords.size(); Resources res = getContext().getResources(); - String confirmTitle = res.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_title, + String confirmTitle = res.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, recordCount, recordCount); - String confirmMessage = res.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_message, + String confirmMessage = res.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, recordCount, recordCount); diff --git a/app/src/main/java/io/beldex/bchat/ShortcutLauncherActivity.java b/app/src/main/java/io/beldex/bchat/ShortcutLauncherActivity.java index 54a0f635c..03018c00b 100644 --- a/app/src/main/java/io/beldex/bchat/ShortcutLauncherActivity.java +++ b/app/src/main/java/io/beldex/bchat/ShortcutLauncherActivity.java @@ -4,22 +4,16 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.TaskStackBuilder; -import androidx.appcompat.app.AppCompatActivity; -import android.widget.Toast; - -import io.beldex.bchat.util.CommunicationActions; import com.beldex.libbchat.utilities.Address; -import io.beldex.bchat.home.HomeActivity; import com.beldex.libbchat.utilities.recipients.Recipient; import io.beldex.bchat.onboarding.PasswordActivity; import io.beldex.bchat.util.CommunicationActions; -import io.beldex.bchat.R; - -public class ShortcutLauncherActivity extends AppCompatActivity { +public class ShortcutLauncherActivity extends BaseAppCompatActivity { private static final String KEY_SERIALIZED_ADDRESS = "serialized_address"; diff --git a/app/src/main/java/io/beldex/bchat/applock/AppLockDetailsActivity.kt b/app/src/main/java/io/beldex/bchat/applock/AppLockDetailsActivity.kt index 311615cdb..4da2d3234 100644 --- a/app/src/main/java/io/beldex/bchat/applock/AppLockDetailsActivity.kt +++ b/app/src/main/java/io/beldex/bchat/applock/AppLockDetailsActivity.kt @@ -18,10 +18,12 @@ import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.setScreenLo import com.beldex.libsignal.utilities.Log import io.beldex.bchat.PassphraseRequiredActionBarActivity import io.beldex.bchat.crypto.IdentityKeyUtil +import io.beldex.bchat.my_account.ui.ScreenTimeoutOptions import io.beldex.bchat.service.KeyCachingService import io.beldex.bchat.util.push import java.util.* import java.util.concurrent.TimeUnit +import androidx.core.graphics.drawable.toDrawable class AppLockDetailsActivity : io.beldex.bchat.PassphraseRequiredActionBarActivity() { @@ -30,35 +32,38 @@ class AppLockDetailsActivity : io.beldex.bchat.PassphraseRequiredActionBarActivi super.onCreate(savedInstanceState, isReady) binding = ActivityAppLockDetailsBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("App Lock") + setUpActionBarBchatLogo(getString(R.string.activity_settings_app_lock_button_title)) with(binding) { - appLockDetailsScreenLockInActivityTimeOut.text = io.beldex.bchat.crypto.IdentityKeyUtil.retrieve( - this@AppLockDetailsActivity, - io.beldex.bchat.crypto.IdentityKeyUtil.SCREEN_TIMEOUT_VALUES_KEY - ) ?: "None" + val currentIndex = + IdentityKeyUtil.retrieve( + this@AppLockDetailsActivity, + IdentityKeyUtil.SCREEN_TIMEOUT_KEY + )?.toIntOrNull() ?: 0 + + val currentOption = ScreenTimeoutOptions.entries.getOrElse(currentIndex) { + ScreenTimeoutOptions.None + } + + appLockDetailsScreenLockInActivityTimeOut.text = + currentOption.displayValue(this@AppLockDetailsActivity) appLockDetailsChangePasswordCard.setOnClickListener { } appLockDetailsScreenLockInActivityTimeOutCard.setOnClickListener { - if (io.beldex.bchat.crypto.IdentityKeyUtil.retrieve( + + if (IdentityKeyUtil.retrieve( this@AppLockDetailsActivity, - io.beldex.bchat.crypto.IdentityKeyUtil.SCREEN_TIMEOUT_KEY - ) != null + IdentityKeyUtil.SCREEN_TIMEOUT_KEY + ) == null ) { - numberPickerDialog() - } else { - io.beldex.bchat.crypto.IdentityKeyUtil.save( + IdentityKeyUtil.save( this@AppLockDetailsActivity, - io.beldex.bchat.crypto.IdentityKeyUtil.SCREEN_TIMEOUT_KEY, + IdentityKeyUtil.SCREEN_TIMEOUT_KEY, "0" ) - io.beldex.bchat.crypto.IdentityKeyUtil.save( - this@AppLockDetailsActivity, - io.beldex.bchat.crypto.IdentityKeyUtil.SCREEN_TIMEOUT_VALUES_KEY, - "None" - ) - numberPickerDialog() } + + numberPickerDialog() } changePassword.setOnClickListener() { @@ -69,171 +74,90 @@ class AppLockDetailsActivity : io.beldex.bchat.PassphraseRequiredActionBarActivi } private fun numberPickerDialog() { - /* val builder = AlertDialog.Builder(this) - builder.setTitle(this.getString(R.string.ExpirationDialog_disappearing_messages)) - builder.setView(view) - builder.setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, which: Int -> - val selected = - (view.findViewById(R.id.expiration_number_picker) as NumberPickerView).value - listener.onClick( - context.getResources().getIntArray(R.array.expiration_times).get(selected) - ) - } - builder.setNegativeButton(android.R.string.cancel, null) - builder.show()*/ - val d: AlertDialog.Builder = AlertDialog.Builder(this) - val inflater = this.layoutInflater - val dialogView: View = inflater.inflate(R.layout.number_picker_dialog, null) - - d.setView(dialogView) - val numberPicker = dialogView.findViewById(R.id.dialog_number_picker) - val numberPickerCancel = dialogView.findViewById(R.id.number_picker_dialog_cancel) - val numberPickerOk = dialogView.findViewById(R.id.number_picker_dialog_ok) - val screenTimeOutArray: Array = - this.resources.getStringArray(R.array.screen_lock_timeout) - val screenTimeOutValues = arrayOfNulls(screenTimeOutArray.size) - val currentIndex: Int = - io.beldex.bchat.crypto.IdentityKeyUtil.retrieve(this, io.beldex.bchat.crypto.IdentityKeyUtil.SCREEN_TIMEOUT_KEY).toInt() - var selectedIndex: Int = currentIndex - Log.d("selected Index", "$selectedIndex") - Log.d("ScreenTimeOut", screenTimeOutArray[currentIndex]) - - for (i in screenTimeOutArray.indices) { - screenTimeOutValues[i] = - ExpirationUtil.getTimeOutDisplayValue(this, screenTimeOutArray.get(i).toString()) - if (i == currentIndex) { - selectedIndex = i - } - } - numberPicker.displayedValues = screenTimeOutValues + + val dialogView = layoutInflater.inflate( + R.layout.number_picker_dialog, + null + ) + + val numberPicker = + dialogView.findViewById(R.id.dialog_number_picker) + + val cancel = + dialogView.findViewById(R.id.number_picker_dialog_cancel) + + val ok = + dialogView.findViewById(R.id.number_picker_dialog_ok) + + val options = ScreenTimeoutOptions.entries + + val displayedValues = options.map { + it.displayValue(this) + }.toTypedArray() + + val currentIndex = + IdentityKeyUtil.retrieve( + this, + IdentityKeyUtil.SCREEN_TIMEOUT_KEY + )?.toIntOrNull() ?: 0 + numberPicker.minValue = 0 - numberPicker.maxValue = screenTimeOutArray.size - 1 + numberPicker.maxValue = displayedValues.lastIndex + numberPicker.displayedValues = displayedValues + numberPicker.value = currentIndex numberPicker.wrapSelectorWheel = true - val listener = - NumberPicker.OnValueChangeListener { picker: NumberPicker, oldVal: Int, newVal: Int -> - if (newVal == 0) { - //textView.setText(R.string.ExpirationDialog_your_messages_will_not_expire) - } else { - /* textView.setText( - context.getString( - R.string.ExpirationDialog_your_messages_will_disappear_s_after_they_have_been_seen, - picker.displayedValues[newVal] - ) - )*/ - selectedIndex = newVal - Log.d("selected Index", "$selectedIndex") - } - } - numberPicker.setOnValueChangedListener(listener) - Log.d("selected Index", "$selectedIndex") - numberPicker.value = selectedIndex - listener.onValueChange(numberPicker, selectedIndex, selectedIndex) - - /*d.setPositiveButton("Ok", - DialogInterface.OnClickListener { dialogInterface, i -> - IdentityKeyUtil.save( - this, - IdentityKeyUtil.SCREEN_TIMEOUT_KEY, - numberPicker.value.toString() - ) - IdentityKeyUtil.save( - this, - IdentityKeyUtil.SCREEN_TIMEOUT_VALUES_KEY, - screenTimeOutArray[numberPicker.value] - ) - Log.d( - "TAG", - "onClick: " + numberPicker.value - ) - binding.appLockDetailsScreenLockInActivityTimeOut.text = - screenTimeOutArray[numberPicker.value] - }) - d.setNegativeButton("Cancel", - DialogInterface.OnClickListener { dialogInterface, i -> })*/ - val alertDialog: AlertDialog = d.create() - alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - alertDialog.show() - numberPickerCancel.setOnClickListener { - alertDialog.cancel() + val dialog = AlertDialog.Builder(this) + .setView(dialogView) + .create() + + dialog.window?.setBackgroundDrawable( + Color.TRANSPARENT.toDrawable() + ) + + cancel.setOnClickListener { + dialog.dismiss() } - numberPickerOk.setOnClickListener { - io.beldex.bchat.crypto.IdentityKeyUtil.save( - this, - io.beldex.bchat.crypto.IdentityKeyUtil.SCREEN_TIMEOUT_KEY, - numberPicker.value.toString() - ) - io.beldex.bchat.crypto.IdentityKeyUtil.save( + + ok.setOnClickListener { + + val selectedIndex = numberPicker.value + val selectedOption = options[selectedIndex] + + IdentityKeyUtil.save( this, - io.beldex.bchat.crypto.IdentityKeyUtil.SCREEN_TIMEOUT_VALUES_KEY, - screenTimeOutArray[numberPicker.value] + IdentityKeyUtil.SCREEN_TIMEOUT_KEY, + selectedIndex.toString() ) - Log.d("TAG", "onClick: " + numberPicker.value) + binding.appLockDetailsScreenLockInActivityTimeOut.text = - screenTimeOutArray[numberPicker.value] - - //New Line - val enabled = true - - setScreenLockEnabled(this, enabled) - - val intent = Intent(this, io.beldex.bchat.service.KeyCachingService::class.java) - intent.action = io.beldex.bchat.service.KeyCachingService.LOCK_TOGGLED_EVENT - this.startService(intent) - Log.d("numberPicker Value 0 ", numberPicker.value.toString()) - if(screenTimeOutArray[numberPicker.value] == "None") { - setScreenLockTimeout(this, 950400) - Log.d("numberPicker Value 1 ", numberPicker.value.toString()) - } else if (screenTimeOutArray[numberPicker.value] == "30 seconds") { - Log.d("numberPicker Value 2 ", numberPicker.value.toString()) - - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(30000) - Log.d("numberPicker Value 2i ", timeoutSeconds.toString()) - setScreenLockTimeout(this, timeoutSeconds) - } else if (screenTimeOutArray[numberPicker.value] == "1 Minute") { - Log.d("numberPicker Value 3 ", numberPicker.value.toString()) - - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(60000) - setScreenLockTimeout(this, timeoutSeconds) - } else if (screenTimeOutArray[numberPicker.value] == "2 Minutes") { - Log.d("numberPicker Value 4 ", numberPicker.value.toString()) - - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(120000) - setScreenLockTimeout(this, timeoutSeconds) - } else if (screenTimeOutArray[numberPicker.value] == "5 Minutes") { - Log.d("numberPicker Value 5 ", numberPicker.value.toString()) - - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(300000) - setScreenLockTimeout(this, timeoutSeconds) - } else if (screenTimeOutArray[numberPicker.value] == "15 Minutes") { - Log.d("numberPicker Value 6 ", numberPicker.value.toString()) - - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(900000) - setScreenLockTimeout(this, timeoutSeconds) - } else if (screenTimeOutArray[numberPicker.value] == "30 Minutes") { - Log.d("numberPicker Value 7 ", numberPicker.value.toString()) - - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(1800000) - setScreenLockTimeout(this, timeoutSeconds) + selectedOption.displayValue(this) + + setScreenLockEnabled(this, true) + + val intent = Intent( + this, + KeyCachingService::class.java + ).apply { + action = KeyCachingService.LOCK_TOGGLED_EVENT } - // initializeScreenLockTimeoutSummary() + startService(intent) - alertDialog.cancel() - } - } + val timeoutSeconds = + if (selectedOption == ScreenTimeoutOptions.None) { + 950400 + } else { + TimeUnit.MILLISECONDS.toSeconds( + selectedOption.timeoutMillis + ) + } - /*private fun initializeVisibility() { - if (isPasswordDisabled(this)) { - val keyguardManager = this.getSystemService(KEYGUARD_SERVICE) as KeyguardManager - if (!keyguardManager.isKeyguardSecure) { - (this.findPreference(TextSecurePreferences.SCREEN_LOCK) as SwitchPreferenceCompat).isChecked = - false - findPreference(TextSecurePreferences.SCREEN_LOCK).setEnabled(false) - } - } else { - findPreference(TextSecurePreferences.SCREEN_LOCK).setVisible(false) - findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setVisible(false) + setScreenLockTimeout(this, timeoutSeconds) + + dialog.dismiss() } - }*/ + + dialog.show() + } } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/applock/ChangePasswordActivity.kt b/app/src/main/java/io/beldex/bchat/applock/ChangePasswordActivity.kt index f54ba0430..8342a6be1 100644 --- a/app/src/main/java/io/beldex/bchat/applock/ChangePasswordActivity.kt +++ b/app/src/main/java/io/beldex/bchat/applock/ChangePasswordActivity.kt @@ -28,7 +28,7 @@ class ChangePasswordActivity : BaseActionBarActivity() { super.onCreate(savedInstanceState) binding = ActivityChangePasswordBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("Change Password") + setUpActionBarBchatLogo(getString(R.string.change_password)) val oldPassword = TextSecurePreferences.getMyPassword(this) with(binding) { @@ -40,14 +40,14 @@ class ChangePasswordActivity : BaseActionBarActivity() { when { oldPassword != oldEnteredPassword -> { oldPasswordEditTxtLayout.isErrorEnabled = true - oldPasswordEditTxtLayout.error = "Invalid old password" + oldPasswordEditTxtLayout.error = getString(R.string.invalid_old_password) newPasswordEditTxtLayout.isErrorEnabled = false } oldEnteredPassword.isEmpty() -> { oldPasswordEditTxtLayout.isErrorEnabled = false Toast.makeText( this@ChangePasswordActivity, - "Must set your Old PIN.", + getString(R.string.must_set_your_old_pin), Toast.LENGTH_LONG ).show() } @@ -56,21 +56,21 @@ class ChangePasswordActivity : BaseActionBarActivity() { oldPasswordEditTxtLayout.isErrorEnabled = false } newPasswordEditTxtLayout.isErrorEnabled = true - newPasswordEditTxtLayout.error = "Both are Same" + newPasswordEditTxtLayout.error = getString(R.string.both_are_same) } newEnteredPassword.isEmpty() -> { oldPasswordEditTxtLayout.isErrorEnabled = false newPasswordEditTxtLayout.isErrorEnabled = false Toast.makeText( this@ChangePasswordActivity, - "Must set your New PIN.", + getString(R.string.must_set_your_new_pin), Toast.LENGTH_LONG ).show() } newEnteredPassword.length < 4 -> { oldPasswordEditTxtLayout.isErrorEnabled = false newPasswordEditTxtLayout.isErrorEnabled = true - newPasswordEditTxtLayout.error = "Please enter 4 digit PIN." + newPasswordEditTxtLayout.error = getString(R.string.please_enter_4_digit_pin) } oldEnteredPassword != newEnteredPassword -> { TextSecurePreferences.setMyPassword( diff --git a/app/src/main/java/io/beldex/bchat/calls/WebRtcCallActivity.kt b/app/src/main/java/io/beldex/bchat/calls/WebRtcCallActivity.kt index 10a9e3406..eb7528864 100644 --- a/app/src/main/java/io/beldex/bchat/calls/WebRtcCallActivity.kt +++ b/app/src/main/java/io/beldex/bchat/calls/WebRtcCallActivity.kt @@ -331,7 +331,7 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { if(binding.callActivityVideoStatus.isVisible){ binding.callActivityAudioStatus.text = "" }else{ - binding.callActivityAudioStatus.text = "Call Muted" + binding.callActivityAudioStatus.text = getString(R.string.call_muted) } Log.d("Remote Audio Enabled","false") }else{ @@ -550,7 +550,7 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { private fun getUserDisplayName(publicKey: String): String { val contact = DatabaseComponent.get(this).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, this) ?: publicKey } override fun onStop() { diff --git a/app/src/main/java/io/beldex/bchat/changelog/ChangeLogActivity.kt b/app/src/main/java/io/beldex/bchat/changelog/ChangeLogActivity.kt index f4925b002..5994fe9ad 100644 --- a/app/src/main/java/io/beldex/bchat/changelog/ChangeLogActivity.kt +++ b/app/src/main/java/io/beldex/bchat/changelog/ChangeLogActivity.kt @@ -1,14 +1,10 @@ package io.beldex.bchat.changelog import android.os.Bundle -import android.util.Log -import android.view.View -import android.widget.ExpandableListView import io.beldex.bchat.R import io.beldex.bchat.databinding.ActivityChangeLogBinding import org.json.JSONException import org.json.JSONObject -import io.beldex.bchat.BaseActionBarActivity import io.beldex.bchat.util.setUpActionBarBchatLogo import java.io.IOException import java.io.InputStream @@ -25,7 +21,7 @@ class ChangeLogActivity : io.beldex.bchat.BaseActionBarActivity() { super.onCreate(savedInstanceState) binding = ActivityChangeLogBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("Changelog") + setUpActionBarBchatLogo(getString(R.string.changelog)) // preparing list data prepareListData() diff --git a/app/src/main/java/io/beldex/bchat/components/ComposeText.java b/app/src/main/java/io/beldex/bchat/components/ComposeText.java index 93d7fb698..76fe5e59f 100644 --- a/app/src/main/java/io/beldex/bchat/components/ComposeText.java +++ b/app/src/main/java/io/beldex/bchat/components/ComposeText.java @@ -78,7 +78,7 @@ protected void onSelectionChanged(int selStart, int selEnd) { private CharSequence ellipsizeToWidth(CharSequence text) { return TextUtils.ellipsize(text, getPaint(), - getWidth() - getPaddingLeft() - getPaddingRight(), + getWidth() - getPaddingStart() - getPaddingEnd(), TruncateAt.END); } diff --git a/app/src/main/java/io/beldex/bchat/components/ProfilePictureView.kt b/app/src/main/java/io/beldex/bchat/components/ProfilePictureView.kt index f2ca88d9b..04c935ddb 100644 --- a/app/src/main/java/io/beldex/bchat/components/ProfilePictureView.kt +++ b/app/src/main/java/io/beldex/bchat/components/ProfilePictureView.kt @@ -56,7 +56,7 @@ class ProfilePictureView @JvmOverloads constructor( fun update(recipient: Recipient,groupImage: Boolean = false, fromEditGroup : Boolean= false) { fun getUserDisplayName(publicKey: String): String { val contact = DatabaseComponent.get(context).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } fun isOpenGroupWithProfilePicture(recipient: Recipient): Boolean { diff --git a/app/src/main/java/io/beldex/bchat/components/QuoteView.java b/app/src/main/java/io/beldex/bchat/components/QuoteView.java index ba28de484..5c59da992 100644 --- a/app/src/main/java/io/beldex/bchat/components/QuoteView.java +++ b/app/src/main/java/io/beldex/bchat/components/QuoteView.java @@ -202,13 +202,13 @@ private void setQuoteAuthor(@NonNull Recipient author) { Contact contact = contactDB.getContactWithBchatID(senderHexEncodedPublicKey); if (contact != null) { Contact.ContactContext context = (this.conversationRecipient.isOpenGroupRecipient()) ? Contact.ContactContext.OPEN_GROUP : Contact.ContactContext.REGULAR; - quoteeDisplayName = contact.displayName(context); + quoteeDisplayName = contact.displayName(context, getContext()); } else { quoteeDisplayName = senderHexEncodedPublicKey; } } - authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you) : quoteeDisplayName); + authorView.setText(isOwnNumber ? getContext().getString(R.string.MediaPreviewActivity_you) : quoteeDisplayName); // We use the raw color resource because Android 4.x was struggling with tints here int colorID = UiModeUtilities.isDayUiMode(getContext()) ? R.color.black : R.color.accent; diff --git a/app/src/main/java/io/beldex/bchat/components/RemovableEditableMediaView.java b/app/src/main/java/io/beldex/bchat/components/RemovableEditableMediaView.java index a37c09a25..dd3fde051 100644 --- a/app/src/main/java/io/beldex/bchat/components/RemovableEditableMediaView.java +++ b/app/src/main/java/io/beldex/bchat/components/RemovableEditableMediaView.java @@ -56,7 +56,7 @@ public void display(@Nullable View view, boolean editable) { if (current != null) current.setVisibility(View.GONE); if (view != null) { - view.setPadding(view.getPaddingLeft(), removeSize / 2, removeSize / 2, (int)(8 * getResources().getDisplayMetrics().density)); + view.setPadding(view.getPaddingStart(), removeSize / 2, removeSize / 2, (int)(8 * getResources().getDisplayMetrics().density)); edit.setPadding(0, 0, removeSize / 2, 0); view.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/io/beldex/bchat/components/emoji/AsciiEmojiView.java b/app/src/main/java/io/beldex/bchat/components/emoji/AsciiEmojiView.java index 9e8f4cab4..db1a449f4 100644 --- a/app/src/main/java/io/beldex/bchat/components/emoji/AsciiEmojiView.java +++ b/app/src/main/java/io/beldex/bchat/components/emoji/AsciiEmojiView.java @@ -46,7 +46,7 @@ protected void onDraw(Canvas canvas) { int xPos = (getWidth() / 2); int yPos = (int) ((getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2)); - float overflow = paint.measureText(emoji) / (getWidth() - getPaddingLeft() - getPaddingRight()); + float overflow = paint.measureText(emoji) / (getWidth() - getPaddingStart() - getPaddingEnd()); if (overflow > 1f) { paint.setTextSize(targetFontSize / overflow); yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2)); diff --git a/app/src/main/java/io/beldex/bchat/components/emoji/EmojiPageView.java b/app/src/main/java/io/beldex/bchat/components/emoji/EmojiPageView.java index 0872f6d53..199e978bc 100644 --- a/app/src/main/java/io/beldex/bchat/components/emoji/EmojiPageView.java +++ b/app/src/main/java/io/beldex/bchat/components/emoji/EmojiPageView.java @@ -116,9 +116,9 @@ public int getSpanSize(int position) { } public void presentForEmojiKeyboard() { - setPadding(getPaddingLeft(), + setPadding(getPaddingStart(), getPaddingTop(), - getPaddingRight(), + getPaddingEnd(), getPaddingBottom() + ViewUtil.dpToPx(56)); setClipToPadding(false); diff --git a/app/src/main/java/io/beldex/bchat/compose_utils/ChatTextField.kt b/app/src/main/java/io/beldex/bchat/compose_utils/ChatTextField.kt index 8a3b8461b..4362e73c4 100644 --- a/app/src/main/java/io/beldex/bchat/compose_utils/ChatTextField.kt +++ b/app/src/main/java/io/beldex/bchat/compose_utils/ChatTextField.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview @@ -80,7 +81,7 @@ fun ChatTextField( singleLine = true, placeholder = { Text( - text = "Write here...", + text = stringResource(R.string.write_here), style = MaterialTheme.typography.bodyMedium.copy( fontWeight = FontWeight.Light ) diff --git a/app/src/main/java/io/beldex/bchat/compose_utils/Colors.kt b/app/src/main/java/io/beldex/bchat/compose_utils/Colors.kt index 613aaa07b..4a6494439 100644 --- a/app/src/main/java/io/beldex/bchat/compose_utils/Colors.kt +++ b/app/src/main/java/io/beldex/bchat/compose_utils/Colors.kt @@ -7,20 +7,14 @@ import androidx.compose.ui.graphics.Color val TextColor = Color(0xFFEBEBEB) val TextColorLight = Color(0xFF333333) -val BackgroundColor = Color(0xFF11111A) val Primary = Color(0xFF1C1C26) val PrimaryLight = Color(0xFFF0F0F0) -val OnPrimary = Color(0xFFFFFFFF) -val Background = Color(0xFF1C1C26) -val BackgroundLight = Color(0xFFF0F0F0) -val OnBackgroundLight = Color(0xFF333333) val Outline = Color(0xFF4B4B64) val OutlineLight = Color(0xFFA7A7BA) val SurfaceLight = Color(0xFFF8F8F8) class Colors( val primaryButtonColor: Color, - val secondaryButtonColor: Color, val backgroundColor: Color, val secondaryContentColor: Color, val editTextPlaceholder: Color, @@ -62,59 +56,26 @@ class Colors( val textFieldDescriptionColor: Color, val cancelButtonTextColor: Color, val textColor: Color, - val walletInfoHighlightColor: Color, - val walletInfoBackground: Color, - val walletInfoBackgroundBorder: Color, val beldexAddressBackground: Color, - val receiveCardBackground: Color, val qrCodeBackground: Color, val errorMessageColor: Color, val textFiledBorderColor: Color, - val totalBalanceColor: Color, val textHint: Color, val dividerColor: Color, val popUpAddressBackground: Color, - val walletDashboardShowBalanceCardBackground: Color, val walletDashboardMainMenuCardBackground: Color, - val walletDashboardQRButtonBackground: Color, - val walletDashboardSendButtonBackground: Color, val walletDashboardReceiveButtonBackground: Color, - val walletDashboardRescanButtonBackground: Color, - val transactionFilterBackground: Color, - val transactionFilterIcon: Color, - val transactionHistoryCardBackground: Color, val walletSyncingIcon: Color, - val walletSyncingSubTitle: Color, - val transactionTypeTitle: Color, - val transactionHistoryCardDivider: Color, - val transactionDateTitle: Color, val transactionSubTitle: Color, - val transactionHistoryArrowForwardIconColor: Color, - val transactionHistoryArrowBackIconColor: Color, - val copyIcon: Color, - val selectDateRangeText: Color, - val cancelButtonColor: Color, - val secondaryColor: Color, - val currentBlockHeightContainerBackground: Color, - val addNodeHintColor : Color, - val disableButtonContentColor : Color, - val transactionFilterEnableColor : Color, - val disableDateColor : Color, val iconBackground: Color, - val clearColor: Color, - val cancelColor: Color, val loaderBackground: Color, val callBottomBackground: Color, val circularProgressBarBackground : Color, - val disablePrimaryButtonContent : Color, - val noAddressIcon : Color, val numberPickerDivider: Color, - val optionalTextfieldBackground: Color, + val optionalTextFieldBackground: Color, val clearDataSubTitle: Color, val textSelectionColor: Color, val disabledNextButtonColor : Color, - val disableWalletButtonColor : Color, - val disableWalletButtonContentColor : Color, val listItemBackground : Color, val bnsDialogBackground: Color, val linkBnsDisabledButtonContent: Color, @@ -122,16 +83,11 @@ class Colors( val profileAddressCardBackground: Color, val profileCameraIconBackgroundWithBnsTag: Color, val profileCameraIconBackground: Color, - val disableAddButtonContainer: Color, - val disableAddButtonContent: Color, - val cancelButtonContainer: Color, - val cancelButtonContent: Color, val disabledCreateButtonContainer: Color, val archiveChatCountBackground: Color, val deleteOptionColor: Color, val archiveChatCardBackground: Color, val archiveChatIconBackground: Color, - val userBottomSheetBorder: Color, val floatingActionButtonBackground: Color, val negativeGreenButton: Color, val negativeGreenButtonBorder: Color, @@ -139,17 +95,16 @@ class Colors( val negativeRedButtonBorder: Color, val negativeGreenButtonText: Color, val textGreen: Color, - val textOldGreen: Color, val disabledLetsBchatButton: Color, val disabledLetsBchatContent: Color, val newChatCardBackground: Color, val secretGroupInfoBackground: Color, - val textSelectionBackgroundColor: Color + val textSelectionBackgroundColor: Color, + val scrollBarColor: Color ) val lightColors = Colors( primaryButtonColor = Color(0xFF078720), - secondaryButtonColor = Color(0xFFEFEFEF), backgroundColor = Color(0xFFEBEBEB), secondaryContentColor = Color(0xFF333333), editTextPlaceholder = Color(0xFFA7A7BA), @@ -191,59 +146,26 @@ val lightColors = Colors( textFieldDescriptionColor = Color(0xFF333333), cancelButtonTextColor = Color(0xFF8A8A9D), textColor = Color(0xFF111111), - walletInfoHighlightColor = Color(0xFF00C304), - walletInfoBackground = Color(0xFFFFFFFF), - walletInfoBackgroundBorder = Color(0xFF111119), beldexAddressBackground = Color(0xFFF8F8F8), - receiveCardBackground = Color(0xFFEBEBEB), qrCodeBackground = Color(0xFFF8F8F8), errorMessageColor = Color(0xFFFF3E3E), textFiledBorderColor = Color(0xFFA7A7BA), - totalBalanceColor = Color(0xFFACACAC), textHint = Color(0xFFA7A7BA), dividerColor = Color(0XFFA7A7BA), popUpAddressBackground = Color(0xFFF8F8F8), - walletDashboardShowBalanceCardBackground = Color(0xffFFFFFF), walletDashboardMainMenuCardBackground = Color(0xffF0F0F0), - walletDashboardQRButtonBackground = Color(0xffFFFFFF), - walletDashboardSendButtonBackground = Color(0xff2979FB), walletDashboardReceiveButtonBackground = Color(0xff078720), - walletDashboardRescanButtonBackground = Color(0xffE0E0E0), - transactionFilterBackground = Color(0xff333333), - transactionFilterIcon = Color(0xffFFFFFF), - transactionHistoryCardBackground = Color(0xffEBEBEB), walletSyncingIcon = Color(0xffA7A7BA), - walletSyncingSubTitle = Color(0xff82828D), - transactionTypeTitle = Color(0xff82828D), - transactionHistoryCardDivider = Color(0xffA7A7BA), - transactionDateTitle = Color(0xff333333), transactionSubTitle = Color(0xff82828D), - transactionHistoryArrowForwardIconColor = Color(0xff717194), - transactionHistoryArrowBackIconColor = Color(0xff333333), - copyIcon = Color(0xff00BD40), - selectDateRangeText = Color(0xff222222), - cancelButtonColor = Color(0xffECECEC), - secondaryColor = Color(0xff2929FB), - currentBlockHeightContainerBackground = Color(0xffF8F8F8), - addNodeHintColor = Color(0xFFA7A7BA), - disableButtonContentColor = Color(0xffA7A7BA), - transactionFilterEnableColor = Color(0xffA7A7BA), - disableDateColor = Color(0xffA7A7BA), iconBackground = Color(0xFFE0E0E0), - clearColor = Color(0xFFFF3E3E), - cancelColor = Color(0xFFACACAC), loaderBackground = Color(0xA4000000), callBottomBackground = Color(0xFFE8E8E8), circularProgressBarBackground = Color(0xffEBEBEB), - disablePrimaryButtonContent = Color(0xFF8A8A9D), - noAddressIcon = Color(0xffA3A3B2), numberPickerDivider = Color(0xFFA7A7BA), - optionalTextfieldBackground = Color(0xFFF4F4F4), + optionalTextFieldBackground = Color(0xFFF4F4F4), clearDataSubTitle = Color(0XFF6A6A77), textSelectionColor= Color(0xFF008D06), disabledNextButtonColor = Color(0xFFA7A7BA), - disableWalletButtonColor = Color(0xFFE0E0E0), - disableWalletButtonContentColor = Color(0xFF000000), listItemBackground = Color(0xFFF8F8F8), bnsDialogBackground = Color(0xFFEBEBEB), linkBnsDisabledButtonContent = Color(0xFFACACAC), @@ -251,16 +173,11 @@ val lightColors = Colors( profileAddressCardBackground = Color(0xFFFFFFFF), profileCameraIconBackgroundWithBnsTag = Color(0xFFEBEBEB), profileCameraIconBackground = Color(0xFFF8F8F8), - disableAddButtonContainer = Color(0xFFECECEC), - disableAddButtonContent = Color(0xFFA7A7BA), - cancelButtonContainer = Color(0xFFECECEC), - cancelButtonContent = Color(0xFF6C6C78), disabledCreateButtonContainer = Color(0xFFECECEC), archiveChatCountBackground = Color(0xFFFFFFFF), deleteOptionColor = Color(0xFFFF3E3E), archiveChatCardBackground = Color(0xFFF8F8F8), archiveChatIconBackground = Color(0xFFECECEC), - userBottomSheetBorder = Color(0xFFA7A7BA), floatingActionButtonBackground = Color(0xFF078720), negativeGreenButton = Color(0x0D078727), negativeGreenButtonBorder = Color(0xFF078720), @@ -268,17 +185,16 @@ val lightColors = Colors( negativeRedButtonBorder = Color(0xFFFF3E3E), negativeGreenButtonText = Color(0xFF333333), textGreen = Color(0xFF078720), - textOldGreen = Color(0xFF00BD40), disabledLetsBchatButton = Color(0xFFF8F8F8), disabledLetsBchatContent = Color(0xFF6C6C78), newChatCardBackground = Color(0xFFECECEC), secretGroupInfoBackground = Color(0xFFF8F8F8), - textSelectionBackgroundColor = Color(0xFFA7A7BA) + textSelectionBackgroundColor = Color(0xFFA7A7BA), + scrollBarColor = Color(0xFFCDCDCD) ) val darkColors = Colors( primaryButtonColor = Color(0xFF078720), - secondaryButtonColor = Color(0xFF282836), backgroundColor = Color(0xFF11111A), secondaryContentColor = Color(0xFFFFFFFF), editTextPlaceholder = Color(0xFFA7A7BA), @@ -320,59 +236,26 @@ val darkColors = Colors( textFieldDescriptionColor = Color(0xFFA7A7BA), cancelButtonTextColor = Color(0xFFACACAC), textColor = Color(0xFFFFFFFF), - walletInfoHighlightColor = Color(0xFF00C304), - walletInfoBackground = Color(0xFF111119), - walletInfoBackgroundBorder = Color(0xFF24242F), beldexAddressBackground = Color(0xFF1C1C26), - receiveCardBackground = Color(0xFF111119), qrCodeBackground = Color(0xFF2C2C3B), errorMessageColor = Color(0xFFFF3E3E), textFiledBorderColor = Color(0xFF4B4B64), - totalBalanceColor = Color(0xFFACACAC), textHint = Color(0xFFA7A7BA), dividerColor = Color(0XFF4B4B64), popUpAddressBackground = Color(0xFF282836), - walletDashboardShowBalanceCardBackground = Color(0xff2B2B3A), walletDashboardMainMenuCardBackground = Color(0xff1C1C26), - walletDashboardQRButtonBackground = Color(0xffEBEBEB), - walletDashboardSendButtonBackground = Color(0xff2979FB), walletDashboardReceiveButtonBackground = Color(0xff078720), - walletDashboardRescanButtonBackground = Color(0xff333343), - transactionFilterBackground = Color(0xffEBEBEB), - transactionFilterIcon = Color(0xff1C1C26), - transactionHistoryCardBackground = Color(0xff11111A), walletSyncingIcon = Color(0xff65656E), - walletSyncingSubTitle = Color(0xff82828D), - transactionTypeTitle = Color(0xffACACAC), - transactionHistoryCardDivider = Color(0xff4B4B64), - transactionDateTitle = Color(0xffFFFFFF), transactionSubTitle = Color(0xffA7A7BA), - transactionHistoryArrowForwardIconColor = Color(0xff717194), - transactionHistoryArrowBackIconColor = Color(0xffFFFFFF), - copyIcon = Color(0xff00BD40), - selectDateRangeText = Color(0xffFFFFFF), - cancelButtonColor = Color(0xff1C1C26), - secondaryColor = Color(0xff2929FB), - currentBlockHeightContainerBackground = Color(0xff1C1C26), - addNodeHintColor = Color(0xFF77778B), - disableButtonContentColor = Color(0xffA7A7BA), - transactionFilterEnableColor = Color(0xffACACAC), - disableDateColor = Color(0xff333343), iconBackground = Color(0xFF30303F), - clearColor = Color(0xFFFF3E3E), - cancelColor = Color(0xFFACACAC), loaderBackground = Color(0xA4000000), callBottomBackground = Color(0xFF11111A), circularProgressBarBackground = Color(0xff30303F), - disablePrimaryButtonContent = Color(0xFF6C6C78), - noAddressIcon = Color(0xff65656E), numberPickerDivider = Color(0xFF4B4B64), - optionalTextfieldBackground = Color(0xFF1C1C26), + optionalTextFieldBackground = Color(0xFF1C1C26), clearDataSubTitle = Color(0XFF6A6A77), textSelectionColor= Color(0xFF008D06), disabledNextButtonColor = Color(0xFF6C6C78), - disableWalletButtonColor = Color(0xFF333343), - disableWalletButtonContentColor = Color(0xFF000000), listItemBackground = Color(0xFF1C1C26), bnsDialogBackground = Color(0xFF282836), linkBnsDisabledButtonContent = Color(0xFF6C6C78), @@ -380,16 +263,11 @@ val darkColors = Colors( profileAddressCardBackground = Color(0xFF11111A), profileCameraIconBackgroundWithBnsTag = Color(0xFF11111A), profileCameraIconBackground = Color(0xFF111119), - disableAddButtonContainer = Color(0xFF1C1C26), - disableAddButtonContent = Color(0xFF6C6C78), - cancelButtonContainer = Color(0xFF1C1C26), - cancelButtonContent = Color(0xFFACACAC), disabledCreateButtonContainer = Color(0xFF282836), archiveChatCountBackground = Color(0xFF42425F), deleteOptionColor = Color(0xFFFF3E3E), archiveChatCardBackground = Color(0xFF282836), archiveChatIconBackground = Color(0xFF1C1C26), - userBottomSheetBorder = Color(0xFF2C2C3B), floatingActionButtonBackground = Color(0xFF078720), negativeGreenButton = Color(0x1A078727), negativeGreenButtonBorder = Color(0xFF078720), @@ -397,12 +275,12 @@ val darkColors = Colors( negativeRedButtonBorder = Color(0xFFFF3E3E), negativeGreenButtonText = Color(0xFFACACAC), textGreen = Color(0xFF078720), - textOldGreen = Color(0xFF00BD40), disabledLetsBchatButton = Color(0xFF272733), disabledLetsBchatContent = Color(0xFF6C6C78), newChatCardBackground = Color(0xFF111119), secretGroupInfoBackground = Color(0xFF1C1C26), - textSelectionBackgroundColor = Color(0xFFFFFFFF) + textSelectionBackgroundColor = Color(0xFFFFFFFF), + scrollBarColor = Color(0xFF282836) ) diff --git a/app/src/main/java/io/beldex/bchat/compose_utils/ComposeDialogContainer.kt b/app/src/main/java/io/beldex/bchat/compose_utils/ComposeDialogContainer.kt index 3a52f3afe..13cfa394c 100644 --- a/app/src/main/java/io/beldex/bchat/compose_utils/ComposeDialogContainer.kt +++ b/app/src/main/java/io/beldex/bchat/compose_utils/ComposeDialogContainer.kt @@ -160,7 +160,7 @@ class ComposeDialogContainer( val bchatID = TextSecurePreferences.getLocalNumber(context) val isCurrentUserAdmin = admins.any { it.toString() == bchatID } val message = if (isCurrentUserAdmin) { - "Because you are the creator of this group it will be deleted for everyone. This cannot be undone." + context.resources.getString(R.string.group_delete_confirmation_message) } else { context.resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group) } diff --git a/app/src/main/java/io/beldex/bchat/compose_utils/ui/ScreenContainer.kt b/app/src/main/java/io/beldex/bchat/compose_utils/ui/ScreenContainer.kt index 9ec262e70..b67066fbf 100644 --- a/app/src/main/java/io/beldex/bchat/compose_utils/ui/ScreenContainer.kt +++ b/app/src/main/java/io/beldex/bchat/compose_utils/ui/ScreenContainer.kt @@ -10,9 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.material.IconButton -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.IconButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text diff --git a/app/src/main/java/io/beldex/bchat/contacts/BlockedContactActivity.kt b/app/src/main/java/io/beldex/bchat/contacts/BlockedContactActivity.kt index aad007efa..0ed0b10f6 100644 --- a/app/src/main/java/io/beldex/bchat/contacts/BlockedContactActivity.kt +++ b/app/src/main/java/io/beldex/bchat/contacts/BlockedContactActivity.kt @@ -1,13 +1,11 @@ package io.beldex.bchat.contacts -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.view.View import androidx.loader.app.LoaderManager import androidx.loader.content.Loader import androidx.recyclerview.widget.LinearLayoutManager -import io.beldex.bchat.PassphraseRequiredActionBarActivity import com.bumptech.glide.Glide import io.beldex.bchat.R import io.beldex.bchat.databinding.ActivityBlockedContactBinding diff --git a/app/src/main/java/io/beldex/bchat/contacts/BlockedContactAdapter.kt b/app/src/main/java/io/beldex/bchat/contacts/BlockedContactAdapter.kt index eee0c0b56..2cfd3f315 100644 --- a/app/src/main/java/io/beldex/bchat/contacts/BlockedContactAdapter.kt +++ b/app/src/main/java/io/beldex/bchat/contacts/BlockedContactAdapter.kt @@ -11,6 +11,7 @@ import com.beldex.libbchat.utilities.recipients.Recipient import io.beldex.bchat.database.RecipientDatabase import io.beldex.bchat.dependencies.DatabaseComponent import com.bumptech.glide.RequestManager +import io.beldex.bchat.R import javax.inject.Inject class BlockedContactAdapter( @@ -51,7 +52,7 @@ class BlockedContactAdapter( ), false ) TextSecurePreferences.setUnBlockStatus(context, true) - Toast.makeText(context, "UnBlocked this contact", Toast.LENGTH_SHORT).show() + Toast.makeText(context, R.string.unblocked_this_contact, Toast.LENGTH_SHORT).show() listener.onBlockedContactClick(position) } viewHolder.view.bind( diff --git a/app/src/main/java/io/beldex/bchat/contacts/BlockedView.kt b/app/src/main/java/io/beldex/bchat/contacts/BlockedView.kt index a4a369127..af478475e 100644 --- a/app/src/main/java/io/beldex/bchat/contacts/BlockedView.kt +++ b/app/src/main/java/io/beldex/bchat/contacts/BlockedView.kt @@ -8,8 +8,6 @@ import android.widget.LinearLayout import com.beldex.libbchat.messaging.contacts.Contact import com.beldex.libbchat.utilities.recipients.Recipient import io.beldex.bchat.conversation.v2.utilities.MentionManagerUtilities -import io.beldex.bchat.database.RecipientDatabase -import io.beldex.bchat.database.model.ThreadRecord import io.beldex.bchat.dependencies.DatabaseComponent import com.bumptech.glide.RequestManager; import io.beldex.bchat.databinding.ActivityBlockedViewBinding @@ -48,7 +46,8 @@ class BlockedView : LinearLayout { fun getUserDisplayName(publicKey: String): String { val contact = DatabaseComponent.get(context).bchatContactDatabase() .getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context + ) ?: publicKey } val threadID = diff --git a/app/src/main/java/io/beldex/bchat/contacts/ContactSelectionListFragment.kt b/app/src/main/java/io/beldex/bchat/contacts/ContactSelectionListFragment.kt index 845f52622..e7efe26cc 100644 --- a/app/src/main/java/io/beldex/bchat/contacts/ContactSelectionListFragment.kt +++ b/app/src/main/java/io/beldex/bchat/contacts/ContactSelectionListFragment.kt @@ -31,7 +31,7 @@ class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks> { - return ContactSelectionListLoader(requireActivity(), - requireActivity().intent.getIntExtra(DISPLAY_MODE, io.beldex.bchat.contacts.ContactsCursorLoader.DisplayMode.FLAG_ALL), - cursorFilter) + return ContactSelectionListLoader( + requireContext(), + requireActivity().intent.getIntExtra( + DISPLAY_MODE, + ContactsCursorLoader.DisplayMode.FLAG_ALL + ), + cursorFilter + ) } override fun onLoadFinished(loader: Loader>, items: List) { diff --git a/app/src/main/java/io/beldex/bchat/contacts/ContactSelectionListLoader.kt b/app/src/main/java/io/beldex/bchat/contacts/ContactSelectionListLoader.kt index 2b4fe5726..6397209f2 100644 --- a/app/src/main/java/io/beldex/bchat/contacts/ContactSelectionListLoader.kt +++ b/app/src/main/java/io/beldex/bchat/contacts/ContactSelectionListLoader.kt @@ -1,11 +1,12 @@ package io.beldex.bchat.contacts import android.content.Context +import android.content.res.Configuration import com.beldex.libbchat.utilities.recipients.Recipient import io.beldex.bchat.dependencies.DatabaseComponent import io.beldex.bchat.util.AsyncLoader import io.beldex.bchat.R -import io.beldex.bchat.util.ContactUtilities +import java.util.Locale sealed class ContactSelectionListItem { class Header(val name: String) : ContactSelectionListItem() @@ -65,21 +66,29 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St return list } + val localizedContext: Context + get() { + val currentLocale = Locale.getDefault() + val config = Configuration(context.resources.configuration) + config.setLocale(currentLocale) + return context.createConfigurationContext(config) + } + private fun getContacts(contacts: List): List { - return getItems(contacts, context.getString(R.string.fragment_contact_selection_contacts_title)) { + return getItems(contacts, localizedContext.getString(R.string.fragment_contact_selection_contacts_title)) { !it.isGroupRecipient && it.isApproved && !it.isBlocked && it.hasApprovedMe() } } private fun getClosedGroups(contacts: List): List { - return getItems(contacts, context.getString(R.string.fragment_contact_selection_closed_groups_title)) { + return getItems(contacts, localizedContext.getString(R.string.fragment_contact_selection_closed_groups_title)) { it.address.isClosedGroup && DatabaseComponent.get(context) .groupDatabase().isActive(it.address.toGroupString()) } } private fun getOpenGroups(contacts: List): List { - return getItems(contacts, context.getString(R.string.fragment_contact_selection_open_groups_title)) { + return getItems(contacts, localizedContext.getString(R.string.fragment_contact_selection_open_groups_title)) { it.address.isOpenGroup } } diff --git a/app/src/main/java/io/beldex/bchat/contacts/SelectContactsActivity.kt b/app/src/main/java/io/beldex/bchat/contacts/SelectContactsActivity.kt index ebe381c80..ff1b055bf 100644 --- a/app/src/main/java/io/beldex/bchat/contacts/SelectContactsActivity.kt +++ b/app/src/main/java/io/beldex/bchat/contacts/SelectContactsActivity.kt @@ -130,7 +130,7 @@ class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderMana private fun getUserDisplayName(publicKey: String): String { val contact = DatabaseComponent.get(this).bchatContactDatabase() .getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, this) ?: publicKey } override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { diff --git a/app/src/main/java/io/beldex/bchat/contacts/UserView.kt b/app/src/main/java/io/beldex/bchat/contacts/UserView.kt index 500626efc..c6e435f3a 100644 --- a/app/src/main/java/io/beldex/bchat/contacts/UserView.kt +++ b/app/src/main/java/io/beldex/bchat/contacts/UserView.kt @@ -56,7 +56,7 @@ class UserView : LinearLayout { } else { val contact=DatabaseComponent.get(context).bchatContactDatabase() .getContactWithBchatID(publicKey) - contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } } diff --git a/app/src/main/java/io/beldex/bchat/contacts/blocked/BlockedContactsAdapter.kt b/app/src/main/java/io/beldex/bchat/contacts/blocked/BlockedContactsAdapter.kt index 36531e488..d472e9723 100644 --- a/app/src/main/java/io/beldex/bchat/contacts/blocked/BlockedContactsAdapter.kt +++ b/app/src/main/java/io/beldex/bchat/contacts/blocked/BlockedContactsAdapter.kt @@ -87,7 +87,7 @@ class BlockedContactsAdapter(private val context: BlockedContactsActivity) : Lis fun getUserDisplayName(publicKey: String, context: Context): String { val contact = DatabaseComponent.get(context).bchatContactDatabase() .getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } } diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationActivityV2.kt index 35ea84613..e9986a599 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationActivityV2.kt @@ -41,7 +41,6 @@ import androidx.activity.viewModels import androidx.annotation.DimenRes import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat @@ -59,6 +58,7 @@ import androidx.loader.app.LoaderManager import androidx.loader.content.Loader import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.beldex.libbchat.BuildConfig import com.beldex.libbchat.messaging.jobs.AttachmentDownloadJob import com.beldex.libbchat.messaging.jobs.JobQueue import com.beldex.libbchat.messaging.mentions.Mention @@ -96,6 +96,7 @@ import com.bumptech.glide.Glide import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint import io.beldex.bchat.ApplicationContext +import io.beldex.bchat.BaseAppCompatActivity import io.beldex.bchat.CheckOnline import io.beldex.bchat.MediaOverviewActivity import io.beldex.bchat.R @@ -207,10 +208,9 @@ import kotlin.math.abs import kotlin.math.min import kotlin.math.roundToInt import kotlin.math.sqrt -import kotlin.text.indexOfAny @AndroidEntryPoint -class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, +class ConversationActivityV2 : BaseAppCompatActivity(), InputBarDelegate, InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ConversationActionModeCallbackDelegate, RecipientModifiedListener, @@ -769,7 +769,7 @@ class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, ) ) binding.callActionBarView.isVisible=false - Toast.makeText(this, "Call ended", Toast.LENGTH_SHORT) + Toast.makeText(this, getString(R.string.call_ended), Toast.LENGTH_SHORT) .show() } binding.callActionBarView.setOnClickListener { @@ -1092,10 +1092,16 @@ class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, profileManager.setName(applicationContext, recipient, bnsName) } + // ---------- Report issue title ---------- + val isReportIssueRecipient = recipient.address.toString() == BuildConfig.REPORT_ISSUE_ID + val localizedReportIssueName = getString(R.string.report_issue).capitalizeFirstLetter() + // ---------- Title ---------- val title = when { recipient.isLocalNumber -> getString(R.string.note_to_self).capitalizeFirstLetter() + isReportIssueRecipient -> + localizedReportIssueName else -> recipient.toShortString().capitalizeFirstLetter() } @@ -1141,7 +1147,11 @@ class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, } // ---------- Back button ---------- - binding.backToHomeBtn.setOnClickListener { + binding.conversationActivityToolbar.setNavigationIcon( + R.drawable.ic_back_arrow + ) + + binding.conversationActivityToolbar.setNavigationOnClickListener { handleBackPressed() onBackPressedDispatcher.onBackPressed() } @@ -1466,9 +1476,12 @@ class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, val groupID : String=recipient.address.toGroupString() val members=groupRepository?.getGroupMembers(groupID) val memberCount=members?.members?.size ?: 0 - binding.conversationSubtitleView.isVisible=true - binding.conversationSubtitleView.text= - if (memberCount > 1) "$memberCount members" else "$memberCount member" + binding.conversationSubtitleView.isVisible = true + binding.conversationSubtitleView.text = resources.getQuantityString( + R.plurals.group_member_count, + memberCount, + memberCount + ) } else { binding.conversationSubtitleView.isVisible=false } @@ -2229,7 +2242,8 @@ class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, val candidates = MentionsManager.getMentionCandidates( query, viewModel.threadId, - recipient.isOpenGroupRecipient + recipient.isOpenGroupRecipient, + this ) if (!isShowingMentionCandidatesView) { @@ -2437,7 +2451,7 @@ class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, if (binding.inputBar.text.length > 4096) { Toast.makeText( this, - "Text limit exceed: Maximum limit of messages is 4096 characters", + getString(R.string.text_limit_exceed_warnings), Toast.LENGTH_SHORT ).show() } else { @@ -3299,7 +3313,7 @@ class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, AlertDialog.Builder(this, R.style.BChatAlertDialog_ForBan) .setTitle(R.string.ConversationFragment_ban_selected_user) .setMessage( - "This will ban the selected user from this room. It won't ban them from other rooms." + R.string.ban_message ) .setCancelable(true) .setPositiveButton(R.string.ban) { _, _ -> @@ -3990,11 +4004,17 @@ class ConversationActivityV2 : AppCompatActivity(), InputBarDelegate, val names = flattenData(contact.name).ifEmpty { addresses } val displayName = when (names.size) { - 0 -> "No Name" + 0 -> getString(R.string.no_name) 1 -> names.first().capitalizeFirstLetter() - 2 -> "${shortNameAndAddress(names[0], addresses[0])} and 1 other" - else -> - "${shortNameAndAddress(names.first(), addresses.first())} and ${names.size - 1} others" + else -> { + val othersCount = names.size - 1 + resources.getQuantityString( + R.plurals.contact_others, + othersCount, + shortNameAndAddress(names.first(), addresses.first()), + othersCount + ) + } } arguments = Bundle().apply { diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationReactionOverlay.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationReactionOverlay.kt index d6618e074..5aea809f9 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationReactionOverlay.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationReactionOverlay.kt @@ -102,6 +102,7 @@ class ConversationReactionOverlay : FrameLayout { private val scope = CoroutineScope(Dispatchers.Default) private var job: Job? = null private var systemInsets: Insets = Insets.NONE + private val scrubberGap = DimensionUnit.DP.toPixels(6f) constructor(context: Context) : super(context) @@ -230,6 +231,13 @@ class ConversationReactionOverlay : FrameLayout { val conversationItemSnapshot = selectedConversationModel.bitmap val isWideLayout = contextMenu.getMaxWidth() + scrubberWidth < width val availableHeight = getAvailableScreenHeight() + val availableTop = systemInsets.top.toFloat() + val topContentPadding = DimensionUnit.DP.toPixels(8f) + + val minSafeTop = maxOf( + availableTop - statusBarHeight, + topContentPadding + ) val bubbleWidth = selectedConversationModel.bubbleWidth var endApparentTop = endY @@ -237,98 +245,231 @@ class ConversationReactionOverlay : FrameLayout { val menuPadding = DimensionUnit.DP.toPixels(12f) val reactionBarTopPadding = DimensionUnit.DP.toPixels(32f) val reactionBarHeight = backgroundView.height + // The visible scrubber is the opaque bar (the emoji row sits on it), so the message must + // clear the bar's bottom edge plus a minimum gap. + val scrubberContentTop = 0f + val scrubberContentBottom = reactionBarHeight.toFloat() + val scrubberContentClearance = scrubberContentBottom + scrubberGap var reactionBarBackgroundY: Float val actualMenuHeight = contextMenu.getMaxHeight() if (isWideLayout) { - val everythingFitsVertically = reactionBarHeight + menuPadding + reactionBarTopPadding + conversationItemSnapshot.height < availableHeight - if (everythingFitsVertically) { - val reactionBarFitsAboveItem = conversationItem.y > reactionBarHeight + menuPadding + reactionBarTopPadding + val totalHeight = + scrubberContentClearance + + conversationItemSnapshot.height + + val spaceBelowTop = availableHeight - minSafeTop + + if (totalHeight <= spaceBelowTop) { + val reactionBarFitsAboveItem = + conversationItem.y - scrubberContentClearance >= minSafeTop + if (reactionBarFitsAboveItem) { - reactionBarBackgroundY = conversationItem.y - menuPadding - reactionBarHeight + reactionBarBackgroundY = + conversationItem.y - + scrubberContentClearance } else { - endY = reactionBarHeight + menuPadding + reactionBarTopPadding - reactionBarBackgroundY = reactionBarTopPadding + reactionBarBackgroundY = minSafeTop + endY = + reactionBarBackgroundY + + scrubberContentClearance } } else { - val spaceAvailableForItem = availableHeight - reactionBarHeight - menuPadding - reactionBarTopPadding - endScale = spaceAvailableForItem / conversationItem.height - endX += Util.halfOffsetFromScale(conversationItemSnapshot.width, endScale) * if (isMessageOnLeft) -1 else 1 - endY = reactionBarHeight + menuPadding + reactionBarTopPadding - Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) - reactionBarBackgroundY = reactionBarTopPadding + val spaceAvailableForItem = + spaceBelowTop - + scrubberContentClearance + + endScale = + (spaceAvailableForItem / conversationItem.height) + .coerceIn(0.2f, 1f) + + endX += Util.halfOffsetFromScale( + conversationItemSnapshot.width, + endScale + ) * if (isMessageOnLeft) -1 else 1 + + endY = + minSafeTop + + scrubberContentClearance - + Util.halfOffsetFromScale( + conversationItemSnapshot.height, + endScale + ) + + reactionBarBackgroundY = minSafeTop } } else { + // Portrait positioning val reactionBarOffset = DimensionUnit.DP.toPixels(48f) - val spaceForReactionBar = Math.max(reactionBarHeight + reactionBarOffset, 0f) - val everythingFitsVertically = actualMenuHeight + conversationItemSnapshot.height + menuPadding + spaceForReactionBar < availableHeight + + val spaceForReactionBar = + scrubberContentClearance + + val everythingFitsVertically = + actualMenuHeight + + conversationItemSnapshot.height + + menuPadding + + spaceForReactionBar + + minSafeTop <= availableHeight if (everythingFitsVertically) { - val bubbleBottom = selectedConversationModel.bubbleY + conversationItemSnapshot.height - val menuFitsBelowItem = bubbleBottom + menuPadding + actualMenuHeight <= availableHeight + statusBarHeight + val bubbleBottom = + selectedConversationModel.bubbleY + + conversationItemSnapshot.height + + val menuFitsBelowItem = + bubbleBottom + + menuPadding + + actualMenuHeight <= + availableHeight + statusBarHeight if (menuFitsBelowItem) { - if (conversationItem.y < systemInsets.top) { - endY = systemInsets.top.toFloat() + if (conversationItem.y < minSafeTop) { + endY = minSafeTop } - val contextMenuTop = endY + conversationItemSnapshot.height - reactionBarBackgroundY = getReactionBarOffsetForTouch( - selectedConversationModel.bubbleY, - contextMenuTop, - menuPadding, - reactionBarOffset, - reactionBarHeight, - reactionBarTopPadding, - endY - ) + + val contextMenuTop = + endY + conversationItemSnapshot.height + + reactionBarBackgroundY = + getReactionBarOffsetForTouch( + selectedConversationModel.bubbleY, + contextMenuTop, + menuPadding, + reactionBarOffset, + reactionBarHeight, + reactionBarTopPadding, + endY + ) + + reactionBarBackgroundY = + maxOf( + reactionBarBackgroundY, + minSafeTop + ) + if (reactionBarBackgroundY <= reactionBarTopPadding) { - endY = backgroundView.height + menuPadding + reactionBarTopPadding + endY = + reactionBarBackgroundY + + scrubberContentClearance } } else { - // Keep the menu just above the message's original position so it never covers it. - val originalCardTop = selectedConversationModel.bubbleY - statusBarHeight + val originalCardTop = + selectedConversationModel.bubbleY - + statusBarHeight + endY = maxOf( - originalCardTop - actualMenuHeight - 2 * menuPadding - conversationItemSnapshot.height, - systemInsets.top.toFloat() + originalCardTop - + actualMenuHeight - + 2 * menuPadding - + conversationItemSnapshot.height, + minSafeTop ) - reactionBarBackgroundY = endY - reactionBarHeight - menuPadding + + reactionBarBackgroundY = + maxOf( + endY - + scrubberContentClearance, + minSafeTop + ) } + endApparentTop = endY - } else if (reactionBarOffset + reactionBarHeight + actualMenuHeight + menuPadding < availableHeight) { - val spaceAvailableForItem = availableHeight.toFloat() - actualMenuHeight - menuPadding - spaceForReactionBar - endScale = spaceAvailableForItem / conversationItemSnapshot.height - endX += Util.halfOffsetFromScale(conversationItemSnapshot.width, endScale) * if (isMessageOnLeft) -1 else 1 - endY = spaceForReactionBar - Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) - reactionBarBackgroundY = reactionBarTopPadding - endApparentTop = endY + Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) } else { - // Calculate how much we need to scale the bubble to fit everything - val spaceAvailableForItem = availableHeight.toFloat() - actualMenuHeight - menuPadding * 2 - reactionBarHeight - reactionBarTopPadding + val spaceAvailableForItem = + availableHeight.toFloat() - + actualMenuHeight - + menuPadding * 2 - + scrubberContentClearance - + minSafeTop if (spaceAvailableForItem > 0) { - endScale = spaceAvailableForItem / conversationItemSnapshot.height - endX += Util.halfOffsetFromScale(conversationItemSnapshot.width, endScale) * if (isMessageOnLeft) -1 else 1 - endY = reactionBarHeight - Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) + menuPadding + reactionBarTopPadding - reactionBarBackgroundY = reactionBarTopPadding - endApparentTop = reactionBarHeight + menuPadding + reactionBarTopPadding + endScale = + (spaceAvailableForItem / + conversationItemSnapshot.height) + .coerceIn(0.2f, 1f) + + endX += Util.halfOffsetFromScale( + conversationItemSnapshot.width, + endScale + ) * if (isMessageOnLeft) -1 else 1 + + endY = + minSafeTop + + scrubberContentClearance - + Util.halfOffsetFromScale( + conversationItemSnapshot.height, + endScale + ) + + reactionBarBackgroundY = minSafeTop + + endApparentTop = + endY + + Util.halfOffsetFromScale( + conversationItemSnapshot.height, + endScale + ) } else { - // If we can't fit everything even with scaling, use a minimum scale - val minScale = 0.2f // Minimum readable scale + val minScale = 0.2f endScale = minScale - endX += Util.halfOffsetFromScale(conversationItemSnapshot.width, endScale) * if (isMessageOnLeft) -1 else 1 - endY = reactionBarHeight - Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) + menuPadding + reactionBarTopPadding - reactionBarBackgroundY = reactionBarTopPadding - endApparentTop = reactionBarHeight + menuPadding + reactionBarTopPadding + + endX += Util.halfOffsetFromScale( + conversationItemSnapshot.width, + endScale + ) * if (isMessageOnLeft) -1 else 1 + + endY = + minSafeTop + + scrubberContentClearance - + Util.halfOffsetFromScale( + conversationItemSnapshot.height, + endScale + ) + + reactionBarBackgroundY = minSafeTop + + endApparentTop = + minSafeTop + + scrubberContentClearance } } } // Adjust for system insets - reactionBarBackgroundY = maxOf(reactionBarBackgroundY, systemInsets.top.toFloat() - statusBarHeight) - + reactionBarBackgroundY = maxOf( + reactionBarBackgroundY, + minSafeTop + ) + // The visible scrubber is the opaque bar, so resolve any overlap between the bar and + // the message bubble, keeping them a minimum distance apart. + val scrubberStripTop = reactionBarBackgroundY + scrubberContentTop + val scrubberStripBottom = reactionBarBackgroundY + scrubberContentBottom + val bubbleVisualTop = + endY + Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) + val bubbleVisualBottom = + bubbleVisualTop + conversationItemSnapshot.height * endScale + + if (scrubberStripTop < bubbleVisualBottom && scrubberStripBottom > bubbleVisualTop) { + val liftedBarY = bubbleVisualTop - scrubberContentClearance + if (liftedBarY >= minSafeTop) { + reactionBarBackgroundY = liftedBarY + } else { + reactionBarBackgroundY = minSafeTop + val pushedBubbleTop = reactionBarBackgroundY + scrubberContentClearance + endY = pushedBubbleTop - Util.halfOffsetFromScale(conversationItemSnapshot.height, endScale) + endApparentTop = pushedBubbleTop + } + } // Now that endScale is final, clamp the bubble X so it stays fully within the visual edges. val minBubbleX = leftEdge - val maxBubbleX = rightEdge - endX = endX.coerceIn(minBubbleX, maxBubbleX) + val maxBubbleX = + rightEdge - conversationItem.width * endScale + + endX = endX.coerceIn( + minBubbleX, + maxBubbleX + ) // Ensure initial position is corrected before making the overlay visible. conversationItem.x = endX conversationItem.y = endY @@ -367,7 +508,11 @@ class ConversationReactionOverlay : FrameLayout { scrubberX - contextMenu.getMaxWidth() - menuPadding } - val menuYInOverlay = minOf(backgroundView.y, maxMenuY) + val menuYInOverlay = backgroundView.y + .coerceIn( + minSafeTop, + maxMenuY + ) // Convert overlay-local to anchor relative as expected by ConversationContextMenu.show() val (xOffset, yOffset) = toAnchorOffsets(menuXInOverlay, menuYInOverlay) @@ -383,7 +528,7 @@ class ConversationReactionOverlay : FrameLayout { val menuTop = endApparentTop + conversationItemSnapshot.height * endScale val menuYInOverlay = (menuTop + menuPadding) .coerceIn( - systemInsets.top.toFloat(), + minSafeTop, maxMenuY ) @@ -422,7 +567,8 @@ class ConversationReactionOverlay : FrameLayout { val offsetToMakeReactionBarOffsetMatchMenuPadding = reactionBarOffset - contextMenuPadding reactionStartingPoint = messageTop + offsetToMakeReactionBarOffsetMatchMenuPadding } - return Math.max(reactionStartingPoint - reactionBarOffset - reactionBarHeight, spaceNeededBetweenTopOfScreenAndTopOfReactionBar) + val contentClearance = reactionBarHeight + scrubberGap + return Math.max(reactionStartingPoint - contentClearance, spaceNeededBetweenTopOfScreenAndTopOfReactionBar) } private fun updateSystemUiOnShow(activity: Activity) { val window = activity.window @@ -650,7 +796,7 @@ class ConversationReactionOverlay : FrameLayout { } // Reply if (!message.isPending && !message.isFailed && !isDeleteOnly) { - items += ActionItem(R.attr.menu_reply_icon, context.resources.getString(R.string.accessibilityId_reply), { handleActionItemClicked(Action.REPLY) }, R.string.accessibilityId_reply) + items += ActionItem(R.attr.menu_reply_icon, context.resources.getString(R.string.MessageNotifier_reply), { handleActionItemClicked(Action.REPLY) }, R.string.MessageNotifier_reply) } // Copy message text if (!containsControlMessage && hasText && !isSharedContact) { diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationViewModel.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationViewModel.kt index 08f7ecb45..f016d5b87 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/ConversationViewModel.kt @@ -227,7 +227,7 @@ class ConversationViewModel ( stopPlayingAudioMessage(message) } .onFailure { - showMessage("Couldn't delete message due to error: $it") + showMessage(application.getString(R.string.delete_message_failed_with_error, it)) } } @@ -235,7 +235,7 @@ class ConversationViewModel ( fun deleteMessagesWithoutUnsendRequest(messages: Set) = viewModelScope.launch { repository.deleteMessageWithoutUnsendRequest(threadId, messages) .onFailure { - showMessage("Couldn't delete message due to error: $it") + showMessage(application.getString(R.string.delete_message_failed_with_error, it)) } } diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/DeleteOptionsBottomSheet.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/DeleteOptionsBottomSheet.kt index 51e024770..06b9ccef0 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/DeleteOptionsBottomSheet.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/DeleteOptionsBottomSheet.kt @@ -29,7 +29,7 @@ class DeleteOptionsBottomSheet : BottomSheetDialogFragment(), View.OnClickListen val senderId = recipient.address.serialize() // this dialog won't show for social group contacts contactDatabase.getContactWithBchatID(senderId) - ?.displayName(Contact.ContactContext.REGULAR) + ?.displayName(Contact.ContactContext.REGULAR, requireContext()) } var onDeleteForMeTapped: (() -> Unit?)? = null diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/ViewUtil.java b/app/src/main/java/io/beldex/bchat/conversation/v2/ViewUtil.java index 869587bcc..c2fece228 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/ViewUtil.java +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/ViewUtil.java @@ -287,11 +287,11 @@ public static int getWidth(@NonNull View view) { } public static void setPaddingTop(@NonNull View view, int padding) { - view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(), view.getPaddingBottom()); + view.setPadding(view.getPaddingStart(), padding, view.getPaddingEnd(), view.getPaddingBottom()); } public static void setPaddingBottom(@NonNull View view, int padding) { - view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), padding); + view.setPadding(view.getPaddingStart(), view.getPaddingTop(), view.getPaddingEnd(), padding); } public static void setPadding(@NonNull View view, int padding) { @@ -300,17 +300,17 @@ public static void setPadding(@NonNull View view, int padding) { public static void setPaddingStart(@NonNull View view, int padding) { if (isLtr(view)) { - view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); + view.setPadding(padding, view.getPaddingTop(), view.getPaddingEnd(), view.getPaddingBottom()); } else { - view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding, view.getPaddingBottom()); + view.setPadding(view.getPaddingStart(), view.getPaddingTop(), padding, view.getPaddingBottom()); } } public static void setPaddingEnd(@NonNull View view, int padding) { if (isLtr(view)) { - view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding, view.getPaddingBottom()); + view.setPadding(view.getPaddingStart(), view.getPaddingTop(), padding, view.getPaddingBottom()); } else { - view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); + view.setPadding(padding, view.getPaddingTop(), view.getPaddingEnd(), view.getPaddingBottom()); } } diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ContactSharingActivity.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ContactSharingActivity.kt index 8670b2ecb..8722b5b57 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ContactSharingActivity.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ContactSharingActivity.kt @@ -5,7 +5,6 @@ import android.app.Activity import android.content.Intent import android.os.Build import android.os.Bundle -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -32,12 +31,13 @@ import androidx.core.view.WindowInsetsControllerCompat import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseComponentActivity import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.util.UiMode import io.beldex.bchat.util.UiModeUtilities @AndroidEntryPoint -class ContactSharingActivity: ComponentActivity() { +class ContactSharingActivity: BaseComponentActivity() { @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ContactsScreen.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ContactsScreen.kt index 467611c02..251779fe7 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ContactsScreen.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ContactsScreen.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -101,7 +102,7 @@ fun ContactsScreen( .imePadding() ) { Text( - text=stringResource(id=R.string.send), + text=stringResource(id=R.string.conversation_activity__send), style=MaterialTheme.typography.bodyMedium.copy( color=Color.White, fontWeight=FontWeight(400), @@ -149,18 +150,29 @@ fun ContactsScreen( Spacer(modifier = Modifier.height(16.dp)) if (contacts.isEmpty()) { Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center + modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { - Image( - painter = painterResource( - id = if (isDarkTheme) - R.drawable.ic_no_contact_found - else - R.drawable.ic_no_contact_found_white - ), - contentDescription = "no contact found", - ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Image( + painter = painterResource( + id = if (isDarkTheme) R.drawable.ic_no_contact_found + else R.drawable.ic_no_contact_found_white + ), + contentDescription = stringResource(id = R.string.no_contact_found), + ) + Text( + text = stringResource(id = R.string.no_contact_found), + style = MaterialTheme.typography.bodyMedium.copy( + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.appColors.editTextPlaceholder + ), + textAlign = TextAlign.Center + ) + } } } else { LazyColumn( diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/SharedContactView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/SharedContactView.kt index 361c1139f..bf1a56e42 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/SharedContactView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/SharedContactView.kt @@ -169,10 +169,17 @@ fun SharedContactContent( } ?: emptyList() val displayName = when (contactList.size) { - 0 -> "No Name" + 0 -> context.getString(R.string.no_name) 1 -> contactList[0].name.capitalizeFirstLetter() - 2 -> "${shortNameAndAddress(contactList[0].name, contactList[0].address)} and ${contactList.size - 1} other" - else -> "${shortNameAndAddress(contactList[0].name, contactList[0].address)} and ${contactList.size - 1} others" + else -> { + val othersCount = contactList.size - 1 + context.resources.getQuantityString( + R.plurals.contact_others, + othersCount, + shortNameAndAddress(contactList[0].name, contactList[0].address), + othersCount + ) + } } diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ViewContactsActivity.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ViewContactsActivity.kt index a39563557..dbd772380 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ViewContactsActivity.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/contact_sharing/ViewContactsActivity.kt @@ -3,7 +3,6 @@ package io.beldex.bchat.conversation.v2.contact_sharing import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent -import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -47,6 +46,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.beldex.libbchat.utilities.Address import com.beldex.libbchat.utilities.recipients.Recipient +import io.beldex.bchat.BaseAppCompatActivity import io.beldex.bchat.R import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.compose_utils.DialogContainer @@ -62,7 +62,7 @@ import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -class ViewAllContactsActivity : AppCompatActivity() { +class ViewAllContactsActivity : BaseAppCompatActivity() { private var contact: ContactModel? = null @@ -230,7 +230,7 @@ fun ChatWithContactPopUp(name: String, address : String, onDismiss: () -> Unit){ mutableStateOf(name) } val context=LocalContext.current - val activity = context as AppCompatActivity + val activity = context as BaseAppCompatActivity fun moveToChat(address: String) { val addressForThread = Address.fromSerialized(address) diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/BlockedDialog.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/BlockedDialog.kt index eb9bc8124..044ee9058 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/BlockedDialog.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/BlockedDialog.kt @@ -21,7 +21,7 @@ class BlockedDialog(private val recipient: Recipient) : BaseDialog() { val contactDB = DatabaseComponent.get(requireContext()).bchatContactDatabase() val bchatID = recipient.address.toString() val contact = contactDB.getContactWithBchatID(bchatID) - val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: bchatID + val name = contact?.displayName(Contact.ContactContext.REGULAR, requireContext()) ?: bchatID val title = resources.getString(R.string.dialog_blocked_title, name) binding.blockedTitleTextView.text = title val explanation = resources.getString(R.string.dialog_blocked_explanation, name) diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/DownloadDialog.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/DownloadDialog.kt index 07000c82e..9f36e86f9 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/DownloadDialog.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/DownloadDialog.kt @@ -29,7 +29,7 @@ class DownloadDialog(private val recipient: Recipient) : BaseDialog() { val binding = DialogDownloadBinding.inflate(LayoutInflater.from(requireContext())) val bchatID = recipient.address.toString() val contact = contactDB.getContactWithBchatID(bchatID) - val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: bchatID + val name = contact?.displayName(Contact.ContactContext.REGULAR, requireContext()) ?: bchatID val title = resources.getString(R.string.dialog_download_title, name) binding.downloadTitleTextView.text = title val explanation = resources.getString(R.string.dialog_download_explanation, name) diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/UnblockUserDialog.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/UnblockUserDialog.kt index 2a8ac24f6..39a92bf2f 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/UnblockUserDialog.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/dialogs/UnblockUserDialog.kt @@ -91,7 +91,7 @@ fun UnblockUserDialog( style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.appColors.negativeGreenButtonText, fontWeight = FontWeight(400), - fontSize = 14.sp + fontSize = 12.sp ), modifier = Modifier.padding( vertical = 8.dp @@ -111,7 +111,7 @@ fun UnblockUserDialog( text = positiveButtonTitle, style = MaterialTheme.typography.bodyMedium.copy( fontWeight = FontWeight(400), - fontSize = 14.sp, + fontSize = 12.sp, color = Color.White ), modifier = Modifier.padding( diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/input_bar/InputBar.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/input_bar/InputBar.kt index 4d8c44798..d3c2ad47e 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/input_bar/InputBar.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/input_bar/InputBar.kt @@ -234,17 +234,16 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li private fun showOrHideInputIfNeeded() { if (showInput) { - setOf( binding.inputBarEditText, attachmentsButton, binding.microphoneOrSendButtonContainer ).forEach { it.isVisible = true } + setOf( binding.inputBarEditText, attachmentsButton, binding.microphoneOrSendButtonContainer, binding.containerCardView ).forEach { it.isVisible = true } microphoneButton.isVisible = text.isEmpty() || text.isBlank() sendButton.isVisible = text.isNotEmpty() && text.isNotBlank() binding.noLongerParticipantTextView.isVisible = false } else { cancelQuoteDraft(2) cancelLinkPreviewDraft(2) - val views = setOf( binding.inputBarEditText, attachmentsButton, microphoneButton, sendButton, binding.microphoneOrSendButtonContainer ) + val views = setOf( binding.inputBarEditText, attachmentsButton, microphoneButton, sendButton, binding.microphoneOrSendButtonContainer, binding.containerCardView ) views.forEach { it.isVisible = false } binding.noLongerParticipantTextView.isVisible = true -// binding.containerCardView.setContentPadding(16, 16, 16, 16) } } /*Hales63*/ diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/DeletedMessageView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/DeletedMessageView.kt index 6cc5fc872..8f93b5880 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/DeletedMessageView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/DeletedMessageView.kt @@ -23,7 +23,11 @@ class DeletedMessageView : LinearLayout { fun bind(message: MessageRecord, @ColorInt textColor: Int) { assert(message.isDeleted) // set the text to the message's body if it is set, else use a fallback - binding.deleteTitleTextView.text = message.body.ifEmpty { context.resources.getQuantityString(R.plurals.deleteMessageDeleted, 1, 1) } + binding.deleteTitleTextView.text = when { + message.body == context.getString(R.string.deleteMessageDeletedGloballyDefault) -> context.getString(R.string.deleteMessageDeletedGlobally) + message.body.isEmpty() -> context.resources.getQuantityString(R.plurals.deleteMessageDeleted, 1, 1) + else -> message.body + } binding.deleteTitleTextView.setTextColor(textColor) binding.deletedMessageViewIconImageView.imageTintList = ColorStateList.valueOf(textColor) diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/PaymentCardView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/PaymentCardView.kt index 07c466dd1..a7727078f 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/PaymentCardView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/PaymentCardView.kt @@ -87,7 +87,7 @@ class PaymentCardView : LinearLayout { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) context.startActivity(intent) } catch (e: Exception) { - Toast.makeText(context, "Can't open URL", Toast.LENGTH_LONG).show() + Toast.makeText(context, context.getString(R.string.cannot_open_url), Toast.LENGTH_LONG).show() } } diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/QuoteView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/QuoteView.kt index c36ea04b5..a61fc3085 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/QuoteView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/QuoteView.kt @@ -8,7 +8,6 @@ import android.util.TypedValue import android.view.View import android.widget.LinearLayout import androidx.annotation.ColorInt -import androidx.compose.ui.res.colorResource import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.use @@ -97,8 +96,8 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? val localNumber = TextSecurePreferences.getLocalNumber(context) val quoteIsLocalUser = localNumber != null && authorPublicKey == localNumber val authorDisplayName = - if (quoteIsLocalUser) context.getString(R.string.QuoteView_you) - else author?.displayName(Contact.contextForRecipient(thread)) ?: "${authorPublicKey.take(4)}...${authorPublicKey.takeLast(4)}" + if (quoteIsLocalUser) context.getString(R.string.MediaPreviewActivity_you) + else author?.displayName(Contact.contextForRecipient(thread), context) ?: "${authorPublicKey.take(4)}...${authorPublicKey.takeLast(4)}" binding.quoteViewAuthorTextView.text = authorDisplayName.capitalizeFirstLetter() binding.quoteViewAuthorTextView.setTextColor(if(quoteIsLocalUser && !outgoing){ ResourcesCompat.getColor(resources, R.color.button_green, context.theme) @@ -143,10 +142,17 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? val addresses = flattenData(data.address) val names = flattenData(data.name).ifEmpty { addresses } val displayName = when(names.size) { - 0 -> "No Name" + 0 -> context.getString(R.string.no_name) 1 -> names.first().capitalizeFirstLetter() - 2 -> "${shortNameAndAddress(names[0],addresses[0])} and ${names.size - 1} other" - else -> "${shortNameAndAddress(names.first(), addresses.first())} and ${names.size - 1} others" + else -> { + val othersCount = names.size - 1 + context.resources.getQuantityString( + R.plurals.contact_others, + othersCount, + shortNameAndAddress(names.first(), addresses.first()), + othersCount + ) + } } binding.contactName.text = displayName diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/UntrustedAttachmentView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/UntrustedAttachmentView.kt index caed9dd43..cceb2ae8f 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/UntrustedAttachmentView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/UntrustedAttachmentView.kt @@ -39,7 +39,7 @@ class UntrustedAttachmentView: LinearLayout { val (iconRes, stringRes) = when (attachmentType) { AttachmentType.AUDIO -> R.drawable.ic_microphone to R.string.Slide_audio AttachmentType.DOCUMENT -> R.drawable.ic_document_large_light to R.string.document - AttachmentType.MEDIA -> R.drawable.ic_image_attachment to R.string.media + AttachmentType.MEDIA -> R.drawable.ic_image_attachment to R.string.MediaOverviewActivity_Media } val iconDrawable = ContextCompat.getDrawable(context,iconRes)!! iconDrawable.mutate().setTint(textColor) diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VisibleMessageContentView.kt index ff169a396..d9dad8ad4 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VisibleMessageContentView.kt @@ -377,7 +377,7 @@ class VisibleMessageContentView : MaterialCardView { if (documentSlide?.uri == null) { Toast.makeText( context, - "Please wait until file downloaded", + context.getString(R.string.please_wait_until_file_downloaded), Toast.LENGTH_SHORT ).show() return@setOnClickListener @@ -560,7 +560,7 @@ class VisibleMessageContentView : MaterialCardView { val isList= isListMessage(message.body) binding.bodyTextView.setPadding( - binding.bodyTextView.paddingLeft, + binding.bodyTextView.paddingStart, binding.bodyTextView.paddingTop, dpToPx(12), if (isList) dpToPx(12) else dpToPx(6) @@ -908,18 +908,17 @@ class VisibleMessageContentView : MaterialCardView { visibleMessageView: VisibleMessageView, position: Int ) { - val suffix = "... Read more" - - val result = SpannableStringBuilder(fullText).let { - if (it.length <= 705) it - else SpannableStringBuilder(it.subSequence(0, 705)).append(suffix) - } + val suffix = context.getString(R.string.read_more_suffix) if (fullText.length <= 705) { - textView.text = result + textView.text = fullText return } + val result = SpannableStringBuilder(fullText.subSequence(0, 705)).append(suffix) + val leadingSkip = suffix.indexOfFirst { it.isLetterOrDigit() } + val spanStart = result.length - suffix.length + (if (leadingSkip >= 0) leadingSkip else 0) + val clickableSpan = object : ClickableSpan() { override fun onClick(view: View) { addReadLess(fullText, textView, message, delegate, visibleMessageView, position) @@ -942,7 +941,7 @@ class VisibleMessageContentView : MaterialCardView { result.setSpan( clickableSpan, - result.length - 10, + spanStart, result.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE ) @@ -963,7 +962,7 @@ class VisibleMessageContentView : MaterialCardView { visibleMessageView: VisibleMessageView, position: Int ) { - val suffix = " Read less" + val suffix = context.getString(R.string.read_less_suffix) val result = SpannableStringBuilder(fullText).append(suffix) val clickableSpan = object : ClickableSpan() { diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VisibleMessageView.kt index 1e9392533..531b08005 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VisibleMessageView.kt @@ -188,7 +188,7 @@ class VisibleMessageView : LinearLayout { binding.senderNameTextView.isVisible = !message.isOutgoing && (isStartOfMessageCluster && (isGroupThread || snIsSelected)) val contactContext = if (thread.isOpenGroupRecipient) ContactContext.OPEN_GROUP else ContactContext.REGULAR - binding.senderNameTextView.text = contact?.displayName(contactContext) ?: senderBChatID + binding.senderNameTextView.text = contact?.displayName(contactContext, context) ?: senderBChatID // Date break val showDateBreak = (isStartOfMessageCluster || snIsSelected) && !isSameDayMessage(message, previous) if (showDateBreak) { diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VoiceMessageView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VoiceMessageView.kt index 272d4777b..d28f648b5 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VoiceMessageView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/messages/VoiceMessageView.kt @@ -246,7 +246,7 @@ class VoiceMessageView : RelativeLayout, AudioSlidePlayer.Listener { if (TextSecurePreferences.getRecordingStatus(context)) { Toast.makeText( context, - "Unable to play audio while recording", + context.getString(R.string.unable_to_play_audio_while_recording), Toast.LENGTH_SHORT ).show() return diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/AttachmentManager.java b/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/AttachmentManager.java index 29ba48c31..6056bbab4 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/AttachmentManager.java +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/AttachmentManager.java @@ -62,6 +62,7 @@ import java.util.List; import io.beldex.bchat.R; +import io.beldex.bchat.util.ToastMessageUtil; public class AttachmentManager { @@ -344,7 +345,7 @@ private boolean areConstraintsSatisfied(final @NonNull Context context, // Note: This file size test must come BEFORE the `constraints.isSatisfied` check below because // it is a more specific type of check. if (slide.asAttachment().getSize() > MAX_ATTACHMENTS_FILE_SIZE_BYTES) { - Toast.makeText(context, R.string.MediaSendActivity_an_item_was_removed_because_it_exceeded_the_size_limit, Toast.LENGTH_LONG).show(); + ToastMessageUtil.showLong(context, R.string.MediaSendActivity_an_item_was_removed_because_it_exceeded_the_size_limit); return false; } diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/MentionUtilities.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/MentionUtilities.kt index f0fb7f26d..57da25bdd 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/MentionUtilities.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/MentionUtilities.kt @@ -64,7 +64,7 @@ object MentionUtilities { else Contact.ContactContext.REGULAR - contact?.displayName(ctx) + contact?.displayName(ctx, context) } if (userDisplayName != null) { diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/TextUtilities.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/TextUtilities.kt index 3ed7909f9..aaa5d23cc 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/TextUtilities.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/TextUtilities.kt @@ -42,7 +42,7 @@ object TextUtilities { val textSpannable = text.toSpannable() return (0 until textLayout.lineCount).flatMap { line -> textLayout.getLineBounds(line, lineRect) - lineRect.offset(offset[0] + totalPaddingLeft, offset[1] + totalPaddingTop) + lineRect.offset(offset[0] + totalPaddingStart, offset[1] + totalPaddingTop) if (lineRect.contains(hitRect)) { // calculate the url span intersected with (if any) val off = textLayout.getOffsetForHorizontal(line, hitRect.left.toFloat()) // left and right will be the same diff --git a/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/ThumbnailView.kt b/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/ThumbnailView.kt index c870e3137..2a1687c7e 100644 --- a/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/ThumbnailView.kt +++ b/app/src/main/java/io/beldex/bchat/conversation/v2/utilities/ThumbnailView.kt @@ -75,7 +75,7 @@ open class ThumbnailView: FrameLayout { return super.onMeasure(widthMeasureSpec, heightMeasureSpec) } - val finalWidth: Int = adjustedDimens[WIDTH] + paddingLeft + paddingRight + val finalWidth: Int = adjustedDimens[WIDTH] + paddingStart + paddingEnd val finalHeight: Int = adjustedDimens[HEIGHT] + paddingTop + paddingBottom super.onMeasure( diff --git a/app/src/main/java/io/beldex/bchat/conversation_v2/CreateSecretGroupScreen.kt b/app/src/main/java/io/beldex/bchat/conversation_v2/CreateSecretGroupScreen.kt index 6597df534..968308548 100644 --- a/app/src/main/java/io/beldex/bchat/conversation_v2/CreateSecretGroupScreen.kt +++ b/app/src/main/java/io/beldex/bchat/conversation_v2/CreateSecretGroupScreen.kt @@ -412,7 +412,7 @@ private fun GroupContact( fun getUserDisplayName(publicKey: String, context: Context): String { val contact = DatabaseComponent.get(context).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } private fun createClosedGroup( diff --git a/app/src/main/java/io/beldex/bchat/conversation_v2/NewChatConversationActivity.kt b/app/src/main/java/io/beldex/bchat/conversation_v2/NewChatConversationActivity.kt index 1416006d1..2fc62b6c6 100644 --- a/app/src/main/java/io/beldex/bchat/conversation_v2/NewChatConversationActivity.kt +++ b/app/src/main/java/io/beldex/bchat/conversation_v2/NewChatConversationActivity.kt @@ -4,7 +4,6 @@ import android.app.Activity import android.content.Intent import android.os.Build import android.os.Bundle -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Surface import androidx.compose.runtime.SideEffect @@ -15,13 +14,14 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowInsetsControllerCompat import androidx.hilt.navigation.compose.hiltViewModel -import io.beldex.bchat.compose_utils.BChatTheme -import io.beldex.bchat.util.UiMode -import io.beldex.bchat.util.UiModeUtilities import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseComponentActivity import io.beldex.bchat.PassphraseRequiredActionBarActivity +import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.conversation.v2.ConversationActivityV2 import io.beldex.bchat.dms.NewChatScreen +import io.beldex.bchat.util.UiMode +import io.beldex.bchat.util.UiModeUtilities enum class OpenActivity(val destination: String) { NewChat("new_chat"), @@ -33,7 +33,7 @@ enum class OpenActivity(val destination: String) { } @AndroidEntryPoint -class NewChatConversationActivity: ComponentActivity() { +class NewChatConversationActivity: BaseComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/io/beldex/bchat/conversation_v2/NewGroupConversationActivity.kt b/app/src/main/java/io/beldex/bchat/conversation_v2/NewGroupConversationActivity.kt index 5b14b653e..a78b80a54 100644 --- a/app/src/main/java/io/beldex/bchat/conversation_v2/NewGroupConversationActivity.kt +++ b/app/src/main/java/io/beldex/bchat/conversation_v2/NewGroupConversationActivity.kt @@ -4,7 +4,6 @@ import android.app.Activity import android.content.Intent import android.os.Build import android.os.Bundle -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.background import androidx.compose.foundation.layout.WindowInsets @@ -36,15 +35,16 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.beldex.libbchat.messaging.open_groups.OpenGroupAPIV2 import com.beldex.libbchat.utilities.recipients.Recipient +import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseComponentActivity import io.beldex.bchat.PassphraseRequiredActionBarActivity +import io.beldex.bchat.R import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.compose_utils.ui.ScreenContainer import io.beldex.bchat.conversation.v2.ConversationActivityV2 import io.beldex.bchat.util.State import io.beldex.bchat.util.UiMode import io.beldex.bchat.util.UiModeUtilities -import dagger.hilt.android.AndroidEntryPoint -import io.beldex.bchat.R enum class NewGroupConversationType(val destination: String) { SecretGroup("secret_group"), @@ -52,7 +52,7 @@ enum class NewGroupConversationType(val destination: String) { } @AndroidEntryPoint -class NewGroupConversationActivity: ComponentActivity() { +class NewGroupConversationActivity: BaseComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/io/beldex/bchat/database/loaders/BucketedThreadMediaLoader.java b/app/src/main/java/io/beldex/bchat/database/loaders/BucketedThreadMediaLoader.java index 558918973..0889582e2 100644 --- a/app/src/main/java/io/beldex/bchat/database/loaders/BucketedThreadMediaLoader.java +++ b/app/src/main/java/io/beldex/bchat/database/loaders/BucketedThreadMediaLoader.java @@ -90,10 +90,10 @@ public static class BucketedThreadMedia { private final TimeBucket[] TIME_SECTIONS; public BucketedThreadMedia(@NonNull Context context) { - this.TODAY = new TimeBucket(context.getString(R.string.BucketedThreadMedia_Today), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, 1000)); - this.YESTERDAY = new TimeBucket(context.getString(R.string.BucketedThreadMedia_Yesterday), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1)); - this.THIS_WEEK = new TimeBucket(context.getString(R.string.BucketedThreadMedia_This_week), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2)); - this.THIS_MONTH = new TimeBucket(context.getString(R.string.BucketedThreadMedia_This_month), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -30), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7)); + this.TODAY = new TimeBucket(R.string.BucketedThreadMedia_Today, TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, 1000)); + this.YESTERDAY = new TimeBucket(R.string.BucketedThreadMedia_Yesterday, TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1)); + this.THIS_WEEK = new TimeBucket(R.string.BucketedThreadMedia_This_week, TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2)); + this.THIS_MONTH = new TimeBucket(R.string.BucketedThreadMedia_This_month, TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -30), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7)); this.TIME_SECTIONS = new TimeBucket[]{TODAY, YESTERDAY, THIS_WEEK, THIS_MONTH}; this.OLDER = new MonthBuckets(); } @@ -131,11 +131,11 @@ public MediaDatabase.MediaRecord get(int section, int item) { else return OLDER.getItem(section - activeTimeBuckets.size(), item); } - public String getName(int section, Locale locale) { + public String getName(int section, Context context) { List activeTimeBuckets = Stream.of(TIME_SECTIONS).filter(timeBucket -> !timeBucket.isEmpty()).toList(); - if (section < activeTimeBuckets.size()) return activeTimeBuckets.get(section).getName(); - else return OLDER.getName(section - activeTimeBuckets.size(), locale); + if (section < activeTimeBuckets.size()) return activeTimeBuckets.get(section).getName(context); + else return OLDER.getName(section - activeTimeBuckets.size(), context); } private static class TimeBucket { @@ -144,10 +144,10 @@ private static class TimeBucket { private final long startTime; private final long endtime; - private final String name; + private final int nameResId; - TimeBucket(String name, long startTime, long endtime) { - this.name = name; + TimeBucket(int nameResId, long startTime, long endtime) { + this.nameResId = nameResId; this.startTime = startTime; this.endtime = endtime; } @@ -172,8 +172,8 @@ MediaDatabase.MediaRecord getItem(int position) { return records.get(position); } - String getName() { - return name; + String getName(Context context) { + return context.getString(nameResId); } static long addToCalendar(int field, int amount) { @@ -223,9 +223,9 @@ Date getSection(int section) { return keys.get(section); } - String getName(int section, Locale locale) { + String getName(int section, Context context) { Date sectionDate = getSection(section); - + Locale locale = context.getResources().getConfiguration().getLocales().get(0); return new SimpleDateFormat("MMMM, yyyy", locale).format(sectionDate); } } diff --git a/app/src/main/java/io/beldex/bchat/database/model/ThreadRecord.java b/app/src/main/java/io/beldex/bchat/database/model/ThreadRecord.java index 9896c1e08..0af2e1b8c 100644 --- a/app/src/main/java/io/beldex/bchat/database/model/ThreadRecord.java +++ b/app/src/main/java/io/beldex/bchat/database/model/ThreadRecord.java @@ -110,10 +110,9 @@ public SpannableString getDisplayBody(@NonNull Context context) { } Boolean isOutGoing = MmsSmsColumns.Types.isOutgoingMessageType(type); if (isOutGoing) { - direction = context.getString(R.string.send); + direction = context.getString(R.string.conversation_activity__send); } else { direction = context.getString(R.string.message_details_header__received); - ; } return emphasisAdded(context.getString(R.string.ThreadRecord_payment ,amount,direction)); } else if (SmsDatabase.Types.isFailedDecryptType(type)) { diff --git a/app/src/main/java/io/beldex/bchat/dms/CreateNewPrivateChatActivity.kt b/app/src/main/java/io/beldex/bchat/dms/CreateNewPrivateChatActivity.kt index b33f73d0f..c3be53f7d 100644 --- a/app/src/main/java/io/beldex/bchat/dms/CreateNewPrivateChatActivity.kt +++ b/app/src/main/java/io/beldex/bchat/dms/CreateNewPrivateChatActivity.kt @@ -84,7 +84,7 @@ class CreateNewPrivateChatActivity : PassphraseRequiredActionBarActivity() { if (publicKeyEditText.text.isEmpty()) { Toast.makeText( this@CreateNewPrivateChatActivity, - "Please enter BChat ID", + R.string.bchat_id_warning_message, Toast.LENGTH_SHORT ).show() } else { diff --git a/app/src/main/java/io/beldex/bchat/dms/CreatePrivateChatActivity.kt b/app/src/main/java/io/beldex/bchat/dms/CreatePrivateChatActivity.kt index f85e53693..9b2ba3b25 100644 --- a/app/src/main/java/io/beldex/bchat/dms/CreatePrivateChatActivity.kt +++ b/app/src/main/java/io/beldex/bchat/dms/CreatePrivateChatActivity.kt @@ -153,7 +153,7 @@ private class CreatePrivateChatActivityAdapter(val activity: CreatePrivateChatAc override fun getPageTitle(index: Int): CharSequence? { return when (index) { 0 -> activity.resources.getString(R.string.activity_create_private_chat_enter_bchat_id_tab_title) - 1 -> activity.resources.getString(R.string.activity_create_private_chat_scan_qr_code_tab_title) + 1 -> activity.resources.getString(R.string.scan_qr_code) else -> throw IllegalStateException() } } diff --git a/app/src/main/java/io/beldex/bchat/dms/NewChatScreen.kt b/app/src/main/java/io/beldex/bchat/dms/NewChatScreen.kt index 7847c7d43..9b59410c2 100644 --- a/app/src/main/java/io/beldex/bchat/dms/NewChatScreen.kt +++ b/app/src/main/java/io/beldex/bchat/dms/NewChatScreen.kt @@ -39,6 +39,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.TextAutoSize import androidx.compose.foundation.text.selection.TextSelectionColors import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear @@ -322,7 +323,7 @@ fun NewChatScreen( onClickScanQRCode = {} ) Text( - text = "Contact list", + text = stringResource(R.string.contact_list), style = TextStyle( fontSize = 14.sp, fontWeight = FontWeight(400), @@ -596,8 +597,7 @@ fun NewChatPopUp(context: Context, onDismiss: () -> Unit, onClick: (String) -> U }, modifier = Modifier .fillMaxWidth() - .height(115.dp) - .padding(end=10.dp), + .height(115.dp), shape = RoundedCornerShape(16.dp), colors = TextFieldDefaults.colors( unfocusedContainerColor = MaterialTheme.appColors.editTextBackground, @@ -634,10 +634,10 @@ fun NewChatPopUp(context: Context, onDismiss: () -> Unit, onClick: (String) -> U text=stringResource(id=R.string.cancel), style=MaterialTheme.typography.bodyMedium.copy( fontWeight = FontWeight(400), - fontSize = 14.sp, + fontSize = 12.sp, color = MaterialTheme.appColors.negativeGreenButtonText ), - modifier=Modifier.padding(10.dp) + modifier=Modifier.padding(top = 4.dp, bottom = 4.dp) ) } @@ -649,7 +649,7 @@ fun NewChatPopUp(context: Context, onDismiss: () -> Unit, onClick: (String) -> U if (bChatId.isEmpty()) { Toast.makeText( context, - "Please enter BChat ID", + context.getString(R.string.bchat_id_warning_message), Toast.LENGTH_SHORT ).show() } else { @@ -679,9 +679,16 @@ fun NewChatPopUp(context: Context, onDismiss: () -> Unit, onClick: (String) -> U MaterialTheme.appColors.disabledLetsBchatContent }, fontWeight = FontWeight(400), - fontSize = 14.sp + fontSize = 12.sp, + textAlign = TextAlign.Center ), - modifier=Modifier.padding(10.dp) + autoSize = TextAutoSize.StepBased( + minFontSize = 8.sp, + maxFontSize = 12.sp + ), + maxLines = 1, + overflow = TextOverflow.Clip, + modifier=Modifier.padding(top = 4.dp, bottom = 4.dp) ) } } diff --git a/app/src/main/java/io/beldex/bchat/emoji/EmojiCategory.kt b/app/src/main/java/io/beldex/bchat/emoji/EmojiCategory.kt index c459c61b6..c2620511d 100644 --- a/app/src/main/java/io/beldex/bchat/emoji/EmojiCategory.kt +++ b/app/src/main/java/io/beldex/bchat/emoji/EmojiCategory.kt @@ -31,7 +31,7 @@ enum class EmojiCategory(val priority: Int, val key: String, @AttrRes val icon: @StringRes fun getCategoryLabel(@AttrRes iconAttr: Int): Int { return when (iconAttr) { - R.attr.emoji_category_people -> R.string.emojiCategorySmileys + R.attr.emoji_category_people -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__smileys_and_people R.attr.emoji_category_nature -> R.string.emojiCategoryAnimals R.attr.emoji_category_foods -> R.string.emojiCategoryFood R.attr.emoji_category_activity -> R.string.emojiCategoryActivities diff --git a/app/src/main/java/io/beldex/bchat/groups/EditClosedGroupActivity.kt b/app/src/main/java/io/beldex/bchat/groups/EditClosedGroupActivity.kt index c5ae7de54..5c529b5ba 100644 --- a/app/src/main/java/io/beldex/bchat/groups/EditClosedGroupActivity.kt +++ b/app/src/main/java/io/beldex/bchat/groups/EditClosedGroupActivity.kt @@ -103,7 +103,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportActionBar!!.setHomeAsUpIndicator( ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable)) - binding.toolbar.title = getString(R.string.activity_edit_closed_group_title) + supportActionBar!!.title = getString(R.string.activity_edit_closed_group_title) groupID = intent.getStringExtra(groupIDKey)!! val groupInfo = DatabaseComponent.get(this).groupDatabase().getGroup(groupID).get() @@ -373,7 +373,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_missing_error, Toast.LENGTH_SHORT).show() } if (name.length >= 26) { - return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_too_long_error, Toast.LENGTH_SHORT).show() + return Toast.makeText(this, R.string.activity_create_closed_group_group_name_too_long_error, Toast.LENGTH_SHORT).show() } if(name == originalName){ return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_same_name_error,Toast.LENGTH_SHORT).show() @@ -432,7 +432,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { val userAsRecipient = Recipient.from(this, Address.fromSerialized(userPublicKey), false) if (!members.contains(userAsRecipient) && !members.map { it.address.toString() }.containsAll(originalMembers.minus(userPublicKey))) { - val message = "Can't leave while adding or removing other members." + val message = getString(R.string.group_leave_during_member_update_error) return Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show() } @@ -462,7 +462,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { setResult(RESULT_OK, returnIntent) finish() }.failUi { exception -> - val message = if (exception is MessageSender.Error) exception.description else "An error occurred" + val message = if (exception is MessageSender.Error) exception.description else getString(R.string.an_error_occurred) Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show() binding.loaderContainer.fadeOut() isLoading = false diff --git a/app/src/main/java/io/beldex/bchat/groups/JoinPublicChatActivity.kt b/app/src/main/java/io/beldex/bchat/groups/JoinPublicChatActivity.kt index 1bf639033..a4dc9225d 100644 --- a/app/src/main/java/io/beldex/bchat/groups/JoinPublicChatActivity.kt +++ b/app/src/main/java/io/beldex/bchat/groups/JoinPublicChatActivity.kt @@ -168,7 +168,7 @@ private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity override fun getPageTitle(index: Int): CharSequence { return when (index) { 0 -> activity.resources.getString(R.string.group_url) - 1 -> activity.resources.getString(R.string.activity_join_public_chat_scan_qr_code_tab_title) + 1 -> activity.resources.getString(R.string.scan_qr_code) else -> throw IllegalStateException() } } diff --git a/app/src/main/java/io/beldex/bchat/groups/OpenGroupGuidelinesActivity.kt b/app/src/main/java/io/beldex/bchat/groups/OpenGroupGuidelinesActivity.kt index 6a67ba8ed..c472a596c 100644 --- a/app/src/main/java/io/beldex/bchat/groups/OpenGroupGuidelinesActivity.kt +++ b/app/src/main/java/io/beldex/bchat/groups/OpenGroupGuidelinesActivity.kt @@ -18,26 +18,8 @@ class OpenGroupGuidelinesActivity : BaseActionBarActivity() { title.text = getString(R.string.ConversationActivity_open_group_guidelines) } WindowInsetsUtil.applyTopInset(binding.root) - - binding.communityGuidelinesTextView.text = """ -BChat is a decentralized messaging platform that protects your privacy. When you're using BChat, you own your conversations and data. It does not collect or store any of your personal information. BChat is where you chat with freedom. - -BChat is also more than a messaging application. You can send and receive BDX right from your chat box. - -BChat is built on top of the Beldex network. Masternodes on the Beldex network store and relay encrypted messages between clients. Other projects that are currently being researched or developed by Beldex include BelNet (a decentralized VPN service), Beldex Browser (an ad-free Web3 browser), and the Beldex Privacy Protocol (to anonymize every other asset). - -To know more, visit: https://www.beldex.io -For collabs or marketing proposals, contact marketing@beldex.io. -For investments, contact invest.bchat@beldex.io - -To protect and preserve this community, kindly follow the group rules and guidelines. - -Be civil. You can share your opinions and constructive criticisms but harassment is not permitted. -Don't promote or shill your token/project. This group is dedicated to BChat and the Beldex ecosystem. -Spammers will be banned. -Do not share NSFW content or use profane language. - -Beware of scammers. Admins will not DM you first.""".trimIndent() + + binding.communityGuidelinesTextView.text = getString(R.string.community_guidelines) } } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/groups/SecretGroupInfoComposeActivity.kt b/app/src/main/java/io/beldex/bchat/groups/SecretGroupInfoComposeActivity.kt index d7ff8f9c0..643c7d1d1 100644 --- a/app/src/main/java/io/beldex/bchat/groups/SecretGroupInfoComposeActivity.kt +++ b/app/src/main/java/io/beldex/bchat/groups/SecretGroupInfoComposeActivity.kt @@ -10,7 +10,7 @@ import android.content.Intent import android.os.Build import android.os.Bundle import android.widget.Toast -import androidx.activity.ComponentActivity +import io.beldex.bchat.BaseComponentActivity import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent @@ -33,13 +33,12 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ModalBottomSheetLayout -import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowForwardIos import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Search -import androidx.compose.material.rememberModalBottomSheetState +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.CardDefaults import androidx.compose.material3.Divider import androidx.compose.material3.Icon @@ -77,6 +76,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalView @@ -106,18 +106,16 @@ import io.beldex.bchat.compose_utils.appColors import io.beldex.bchat.conversation.v2.ConversationActivityV2 import io.beldex.bchat.conversation.v2.dialogs.LeaveGroupDialog import io.beldex.bchat.dependencies.DatabaseComponent -import io.beldex.bchat.home.HomeActivity import io.beldex.bchat.home.NotificationSettingDialog import io.beldex.bchat.my_account.ui.CardContainer import io.beldex.bchat.my_account.ui.dialogs.LockOptionsDialog import io.beldex.bchat.util.UiMode import io.beldex.bchat.util.UiModeUtilities -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import java.io.IOException @AndroidEntryPoint -class SecretGroupInfoComposeActivity : ComponentActivity() { +class SecretGroupInfoComposeActivity : BaseComponentActivity() { companion object { const val secretGroupID = "secret_group_id" @@ -184,8 +182,12 @@ class SecretGroupInfoComposeActivity : ComponentActivity() { groupMembers = groupMembers, listenerCallback = listenerCallback, secretGroupInfoViewModel = secretGroupInfoViewModel, - showSearchView = { - tileName = context.getString(R.string.search_member_title) + showSearchView = { show -> + tileName = if(show) { + context.getString(R.string.search_member_title) + } else { + context.getString(R.string.group_info) + } } ) } @@ -205,7 +207,7 @@ fun GroupDetailsScreen( groupMembers : GroupMembers?, listenerCallback : SecretGroupInfoComposeActivity.SocialGroupInfoInterface?, secretGroupInfoViewModel : SecretGroupInfoViewModel, - showSearchView : () -> Unit + showSearchView : (Boolean) -> Unit ) { lateinit var groupID : String val context=LocalContext.current @@ -324,7 +326,7 @@ fun GroupDetailsScreen( val bchatID=getLocalNumber(context) val isCurrentUserAdmin=admins.any { it.toString() == bchatID } val message=if (isCurrentUserAdmin) { - "Because you are the creator of this group it will be deleted for everyone. This cannot be undone." + context.resources.getString(R.string.group_delete_confirmation_message) } else { context.resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group) } @@ -448,7 +450,7 @@ fun GroupDetailsScreen( } else { val contact=DatabaseComponent.get(context).bchatContactDatabase() .getContactWithBchatID(publicKey) - contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } } @@ -491,11 +493,6 @@ fun GroupDetailsScreen( return this.replaceFirstChar { it.uppercase() } } - val modalSheetState = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Hidden, - confirmValueChange = { it != ModalBottomSheetValue.HalfExpanded }, - skipHalfExpanded = true - ) var isShowSearchBottomSheet by remember { mutableStateOf(false) } @@ -503,11 +500,20 @@ fun GroupDetailsScreen( isShowSearchBottomSheet = isVisible } + @OptIn(ExperimentalMaterial3Api::class) if (isShowSearchBottomSheet) { - ModalBottomSheetLayout( - sheetState=modalSheetState, - sheetShape=RoundedCornerShape(topStart=12.dp, topEnd=12.dp), - sheetContent={} + + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) + + ModalBottomSheet( + onDismissRequest = { + showSearchView(false) + secretGroupInfoViewModel.updateVisibleBottomSheet(false) + }, + sheetState = sheetState, + shape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp) ) { val focusRequester=remember { FocusRequester() } val coroutineScope=rememberCoroutineScope() @@ -568,13 +574,13 @@ fun GroupDetailsScreen( Row( modifier=Modifier .fillMaxWidth() - .padding(vertical=8.dp) + .padding(vertical = 8.dp) .clickable { - if (getLocalNumber(context) != member) { - selectedItem=member - showMemberOptionDialog=true - } - }, + if (getLocalNumber(context) != member) { + selectedItem = member + showMemberOptionDialog = true + } + }, verticalAlignment=Alignment.CenterVertically ) { @@ -617,7 +623,7 @@ fun GroupDetailsScreen( item { if (membersToDisplay.isEmpty()) { Text( - text="No records found! ", + text= stringResource(R.string.no_records_found), style=MaterialTheme.typography.bodyMedium.copy( fontSize=14.sp, color=MaterialTheme.appColors.textColor @@ -699,7 +705,7 @@ fun GroupDetailsScreen( ) ) { NavigationItem( - "All Media", + stringResource(R.string.all_media), painterResource(id=R.drawable.ic_all_media), onItemClick={ listenerCallback?.showAllMedia(recipient) }, checked=false, @@ -707,7 +713,7 @@ fun GroupDetailsScreen( null ) NavigationItem( - "Disappearing Messages", + stringResource(R.string.disappearing_messages), painterResource(id=R.drawable.ic_disappearing_message), onItemClick={ if (isSecretGroupIsActive()) { @@ -725,7 +731,7 @@ fun GroupDetailsScreen( subTitle=context.getString(R.string.disappearing_info) ) NavigationItem( - "Edit Group", + stringResource(R.string.activity_edit_closed_group_title), painterResource(id=R.drawable.ic_block_request), onItemClick={ if (isSecretGroupIsActive()) { @@ -743,7 +749,7 @@ fun GroupDetailsScreen( null ) NavigationItem( - "Notify for Mentions Only", + stringResource(R.string.notify_for_mentions_only), painterResource(id=R.drawable.ic_mention_only), onItemClick={ if (isSecretGroupIsActive()) { @@ -757,7 +763,7 @@ fun GroupDetailsScreen( } }, - checked=showNotificationSettingsItem == "Mentions", + checked=showNotificationSettingsItem == context.getString(R.string.notify_type_mentions), showSwitch=true, subTitle=context.getString(R.string.notification_info) ) @@ -769,7 +775,7 @@ fun GroupDetailsScreen( .padding(16.dp) .clickable { if (isSecretGroupIsActive()) { - showLeaveGroupDialog=true + showLeaveGroupDialog = true } else { Toast .makeText( @@ -789,7 +795,7 @@ fun GroupDetailsScreen( ) Spacer(modifier=Modifier.width(16.dp)) Text( - text="Leave Group", + text= stringResource(R.string.leave_group), color=Color.Red, style=MaterialTheme.typography.titleSmall ) @@ -817,7 +823,7 @@ fun GroupDetailsScreen( modifier=Modifier.padding(16.dp) ) { Text( - text="$memberCount members", + text= stringResource(R.string.ConversationActivity_member_count, memberCount), color=Color.Gray, style=MaterialTheme.typography.titleSmall.copy( color=MaterialTheme.appColors.editTextHint, @@ -833,7 +839,7 @@ fun GroupDetailsScreen( modifier=Modifier .size(20.dp) .clickable { - showSearchView() + showSearchView(true) secretGroupInfoViewModel.updateVisibleBottomSheet(true) } ) @@ -851,8 +857,8 @@ fun GroupDetailsScreen( .padding(8.dp) .clickable { if (getLocalNumber(context) != member) { - selectedItem=member - showMemberOptionDialog=true + selectedItem = member + showMemberOptionDialog = true } }, verticalAlignment=Alignment.CenterVertically, @@ -976,7 +982,7 @@ fun NavigationItem( ), modifier=Modifier .size(30.dp) - .padding(end=4.dp) + .padding(end = 4.dp) ) } } @@ -1090,7 +1096,7 @@ fun MemberDetailsDialog( } else { val contact=DatabaseComponent.get(context).bchatContactDatabase() .getContactWithBchatID(publicKey) - contact?.displayName(Contact.ContactContext.REGULAR) ?: truncatedPublicKey(publicKey) + contact?.displayName(Contact.ContactContext.REGULAR, context) ?: truncatedPublicKey(publicKey) } } @@ -1108,7 +1114,7 @@ fun MemberDetailsDialog( Column( Modifier .fillMaxWidth() - .padding(start=20.dp, end=20.dp, top=25.dp, bottom=25.dp), + .padding(start = 20.dp, end = 20.dp, top = 25.dp, bottom = 25.dp), Arrangement.Center, Alignment.CenterHorizontally ) { @@ -1124,7 +1130,7 @@ fun MemberDetailsDialog( Column( modifier=Modifier .fillMaxSize() - .padding(vertical=4.dp), + .padding(vertical = 4.dp), verticalArrangement=Arrangement.Center, horizontalAlignment=Alignment.Start, ) { @@ -1142,21 +1148,21 @@ fun MemberDetailsDialog( .clickable { when (index) { 0 -> { - val recipient=Recipient.from( + val recipient = Recipient.from( context, Address.fromSerialized(member), false ) - val existingThread=DatabaseComponent + val existingThread = DatabaseComponent .get(context) .threadDatabase() .getThreadIdIfExistsFor(recipient) if (activity != null) { createConversation( - threadId=existingThread, - address=recipient.address, - activity=activity, - context=context + threadId = existingThread, + address = recipient.address, + activity = activity, + context = context ) } onDismiss() @@ -1177,7 +1183,7 @@ fun MemberDetailsDialog( contentDescription="", tint=MaterialTheme.appColors.editTextColor, modifier=Modifier - .padding(end=8.dp, bottom=16.dp) + .padding(end = 8.dp, bottom = 16.dp) .clickable { onDismiss() } diff --git a/app/src/main/java/io/beldex/bchat/groups/SecretGroupInfoViewModel.kt b/app/src/main/java/io/beldex/bchat/groups/SecretGroupInfoViewModel.kt index 0d6f603bb..f0afb9500 100644 --- a/app/src/main/java/io/beldex/bchat/groups/SecretGroupInfoViewModel.kt +++ b/app/src/main/java/io/beldex/bchat/groups/SecretGroupInfoViewModel.kt @@ -49,7 +49,7 @@ class SecretGroupInfoViewModel @Inject constructor(private val groupID: String, fun getUserDisplayName(publicKey : String,context : Context) : String { val contact= DatabaseComponent.get(context).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } fun updateNotificationType(selectOption: String) { diff --git a/app/src/main/java/io/beldex/bchat/home/ArchiveChatActivity.kt b/app/src/main/java/io/beldex/bchat/home/ArchiveChatActivity.kt index e1b42c53a..b697a7fac 100644 --- a/app/src/main/java/io/beldex/bchat/home/ArchiveChatActivity.kt +++ b/app/src/main/java/io/beldex/bchat/home/ArchiveChatActivity.kt @@ -5,7 +5,6 @@ import android.app.Activity import android.content.Intent import android.os.Build import android.os.Bundle -import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent import androidx.compose.foundation.background @@ -30,7 +29,6 @@ import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults @@ -62,6 +60,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import app.cash.copper.flow.observeQuery import com.beldex.libbchat.utilities.TextSecurePreferences import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseComponentActivity import io.beldex.bchat.R import io.beldex.bchat.archivechats.ArchiveChatViewModel import io.beldex.bchat.compose_utils.BChatTheme @@ -83,7 +82,7 @@ import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class ArchiveChatActivity : ComponentActivity() { +class ArchiveChatActivity : BaseComponentActivity() { @Inject lateinit var groupDatabase : GroupDatabase @@ -161,7 +160,7 @@ class ArchiveChatActivity : ComponentActivity() { Box { Icon( imageVector=Icons.Default.MoreVert, - contentDescription="Menu", + contentDescription=stringResource(R.string.menu), tint=MaterialTheme.appColors.editTextColor, modifier=Modifier.clickable { expanded=true } ) @@ -171,7 +170,7 @@ class ArchiveChatActivity : ComponentActivity() { onDismissRequest={ expanded=false } ) { DropdownMenuItem( - text={ Text("Archive Settings") }, + text={ Text(stringResource(R.string.archive_settings)) }, onClick={ expanded=false showSettings=true diff --git a/app/src/main/java/io/beldex/bchat/home/ArchiveChatView.kt b/app/src/main/java/io/beldex/bchat/home/ArchiveChatView.kt index 05e62fa24..9c922cefe 100644 --- a/app/src/main/java/io/beldex/bchat/home/ArchiveChatView.kt +++ b/app/src/main/java/io/beldex/bchat/home/ArchiveChatView.kt @@ -55,7 +55,7 @@ fun ArchiveChatView( ) { Image( painterResource(id= if(isDarkTheme) R.drawable.ic_archive else R.drawable.ic_archive_icon_white), - contentDescription="Archived Chats", + contentDescription=stringResource(R.string.archive_chat), modifier=Modifier.size(26.dp) ) diff --git a/app/src/main/java/io/beldex/bchat/home/ChooseLanguageComposeScreen.kt b/app/src/main/java/io/beldex/bchat/home/ChooseLanguageComposeScreen.kt new file mode 100644 index 000000000..3095e67cd --- /dev/null +++ b/app/src/main/java/io/beldex/bchat/home/ChooseLanguageComposeScreen.kt @@ -0,0 +1,240 @@ +package io.beldex.bchat.home + +import android.content.Context +import android.content.Intent +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.RadioButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.beldex.libbchat.utilities.TextSecurePreferences +import io.beldex.bchat.BaseComponentActivity +import io.beldex.bchat.compose_utils.appColors +import io.beldex.bchat.my_account.ui.MyAccountViewModel +import io.beldex.bchat.service.KeyCachingService +import io.beldex.bchat.service.WebRtcCallService +import io.beldex.bchat.util.AppLanguageEvent + + +@Composable +fun ChooseLanguage( + viewModel: MyAccountViewModel, + onBack: () -> Unit +) { + val scrollState = rememberLazyListState() + val selectedCode by viewModel.selectedLanguageCode.collectAsState() + val deviceLanguageCode = AppLanguageEvent.deviceLanguageState.collectAsState().value + val context = LocalContext.current + + val onLanguageSelected: (String) -> Unit = { code -> + if (code != selectedCode) { + viewModel.selectLanguage(code, context) + } + onBack() + } + + val languages = listOf( + Language("Chinese, Simplified", "简体中文", "zh"), + Language("English", "English", "en"), + Language("German", "Deutsch", "de"), + Language("Japanese", "日本語", "ja"), + Language("Korean", "한국어", "ko"), + Language("Portuguese (Brazil)", "Português (Brasil)", "pt"), + Language("Russian", "Русский", "ru"), + Language("Spanish", "Español", "es"), + Language("Turkish", "Türkçe", "tr"), + Language("Vietnamese", "Tiếng Việt", "vi") + ) + + fun updateSelectedLanguage(context: Context, language: Language) { + TextSecurePreferences.setAppSelectedLanguage(context, language.code) + val keyCachingIntent = Intent(context, KeyCachingService::class.java) + keyCachingIntent.action = KeyCachingService.LANGUAGE_CHANGED_ACTION + context.startService(keyCachingIntent) + val webRtcIntent = Intent(context, WebRtcCallService::class.java) + webRtcIntent.action = WebRtcCallService.ACTION_LANGUAGE_CHANGED + context.startService(webRtcIntent) + onLanguageSelected(language.code) + } + + Box( + modifier = + Modifier + .fillMaxSize() + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + LazyColumn( + state = scrollState, + verticalArrangement = Arrangement.spacedBy(8.dp), + contentPadding = PaddingValues(vertical = 16.dp), + modifier = Modifier + .fillMaxSize() + .padding(end = 16.dp) + + ) { + items(languages, key = { it.code }) { language -> + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .border( + width = if (language.code == selectedCode) 1.dp else 0.dp, + color = if (language.code == selectedCode) + MaterialTheme.appColors.primaryButtonColor + else + MaterialTheme.appColors.editTextBackground, + shape = RoundedCornerShape(12.dp), + ) + .background( + color = if (selectedCode == language.code) MaterialTheme.appColors.contactCardBackground else MaterialTheme.appColors.editTextBackground + ) + .clickable { + updateSelectedLanguage(context, language) + } + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + RadioButton( + selected = language.code == selectedCode, + onClick = { + updateSelectedLanguage(context, language) + }, + colors = RadioButtonDefaults.colors( + selectedColor = MaterialTheme.appColors.primaryButtonColor, + unselectedColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f), + ), + ) + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + horizontalAlignment = Alignment.Start, + modifier = Modifier.weight(1f) + ) { + Text( + text = language.nativeName, + color = MaterialTheme.appColors.textColor, + fontSize = 16.sp, + fontWeight = FontWeight.Normal, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = if (language.code.contains(deviceLanguageCode)) "(device's language)" else language.englishName, + color = MaterialTheme.appColors.transactionSubTitle, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + } + } + + LazyColumnScrollbar( + listState = scrollState, + thumbColor = MaterialTheme.appColors.scrollBarColor, + modifier = Modifier + .align(Alignment.CenterEnd) + .padding(start = 8.dp, top = 16.dp, end = 2.dp, bottom = 8.dp) + ) + } +} + +@Composable +fun LazyColumnScrollbar( + listState: LazyListState, + thumbColor: Color, + trackColor: Color = Color.Transparent, + modifier: Modifier = Modifier + +) { + Canvas( + modifier = modifier + .fillMaxHeight() + .width(8.dp) + ) { + val layoutInfo = listState.layoutInfo + + if (layoutInfo.totalItemsCount == 0) return@Canvas + + drawRoundRect( + color = trackColor, + cornerRadius = CornerRadius(size.width) + ) + + val visibleItems = layoutInfo.visibleItemsInfo + if (visibleItems.isEmpty()) return@Canvas + + val firstItem = visibleItems.first() + + val estimatedItemHeight = firstItem.size.toFloat() + + val totalContentHeight = + layoutInfo.totalItemsCount * estimatedItemHeight + + val viewportHeight = size.height + + val thumbHeight = + (viewportHeight * viewportHeight / totalContentHeight) + .coerceIn(40f, viewportHeight) + + val scrollY = + (listState.firstVisibleItemIndex * estimatedItemHeight) + + listState.firstVisibleItemScrollOffset + + val maxScroll = + (totalContentHeight - viewportHeight) + .coerceAtLeast(1f) + + val thumbOffset = + ((viewportHeight - thumbHeight) * (scrollY / maxScroll)) + .coerceIn(0f, viewportHeight - thumbHeight) + + drawRoundRect( + color = thumbColor, + topLeft = Offset(0f, thumbOffset), + size = Size(size.width, thumbHeight), + cornerRadius = CornerRadius(size.width) + ) + } +} + +data class Language( + val englishName: String, + val nativeName: String, + val code: String +) diff --git a/app/src/main/java/io/beldex/bchat/home/ConversationActionDialog.kt b/app/src/main/java/io/beldex/bchat/home/ConversationActionDialog.kt index 3fcfb7662..02ed13fb8 100644 --- a/app/src/main/java/io/beldex/bchat/home/ConversationActionDialog.kt +++ b/app/src/main/java/io/beldex/bchat/home/ConversationActionDialog.kt @@ -129,7 +129,7 @@ class ConversationActionDialog: DialogFragment() { UnblockUserDialog( title = stringResource(id = R.string.unblock_contact), message = stringResource(id = R.string.unblock_user_confirmation), - positiveButtonTitle = stringResource(id = R.string.unblock), + positiveButtonTitle = stringResource(id = R.string.ConversationActivity_unblock), onAccept = { dismiss() listener?.onConfirm(dialogType, threadRecord, threadPosition) @@ -147,7 +147,7 @@ class ConversationActionDialog: DialogFragment() { ) { val timesOption = context.resources.getStringArray(R.array.mute_durations) LockOptionsDialog( - title = stringResource(R.string.mute_notification), + title = stringResource(R.string.conversation_unmuted__mute_notifications), options = timesOption.toList(), currentValue = timesOption[argument3], onDismiss = { diff --git a/app/src/main/java/io/beldex/bchat/home/ConversationView.kt b/app/src/main/java/io/beldex/bchat/home/ConversationView.kt index 66245553b..a23ca99bb 100644 --- a/app/src/main/java/io/beldex/bchat/home/ConversationView.kt +++ b/app/src/main/java/io/beldex/bchat/home/ConversationView.kt @@ -34,7 +34,7 @@ class ConversationView : LinearLayout { var isReportIssueID: Boolean = false private val reportIssueBChatID = BuildConfig.REPORT_ISSUE_ID - val attachmentRegex = Regex("""📷\s*Attachment:""") + val attachmentRegex = Regex("""📷\s*\S+:""") // region Lifecycle constructor(context: Context) : super(context) { initialize() } @@ -112,10 +112,17 @@ class ConversationView : LinearLayout { val addresses = flattenData(data.address) val names = flattenData(data.name).ifEmpty { addresses } val displayName = when(names.size) { - 0 -> "No Name" + 0 -> context.getString(R.string.no_name) 1 -> names.first().capitalizeFirstLetter() - 2 -> "${shortNameAndAddress(names[0],addresses[0])} and ${names.size - 1} other" - else -> "${shortNameAndAddress(names.first(), addresses.first())} and ${names.size - 1} others" + else -> { + val othersCount = names.size - 1 + context.resources.getQuantityString( + R.plurals.contact_others, + othersCount, + shortNameAndAddress(names.first(), addresses.first()), + othersCount + ) + } } binding.contactName.text = displayName } @@ -127,8 +134,10 @@ class ConversationView : LinearLayout { val snippet = if (thread.isGroupUpdateMessage) { rawSnippet } else { + val localized = relocalizeAttachmentLabel(rawSnippet.toString()) + // Format basic styles (bold, italic, lists, etc.) - val formatted = TextFormatter.formatForSentMessage(context, rawSnippet) + val formatted = TextFormatter.formatForSentMessage(context, localized) // Apply mentions val mentionFormatted = highlightMentionsSpannableString( @@ -169,6 +178,25 @@ class ConversationView : LinearLayout { binding.profilePictureView.root.recycle() } + private fun relocalizeAttachmentLabel(body: String): String { + // Attachment / voice message bodies are stored as " " (and + // optionally ": ") using the language that was active when the message was + // sent. Rebuild the label from the emoji prefix so it always reflects the current + // app language, preserving any caption suffix. + val prefix = body.substringBefore(' ') + if (prefix.isEmpty()) return body + + val currentLabel = when (prefix) { + "🎤" -> context.getString(R.string.attachment_type_voice_message) + "📷", "🎥", "🎧", "📎", "🎡" -> context.getString(R.string.attachment) + else -> return body + } + + val colonIdx = body.indexOf(": ") + val suffix = if (colonIdx > 0) body.substring(colonIdx) else "" + return "$prefix $currentLabel$suffix" + } + private fun getUserDisplayName(recipient: Recipient): String? { isReportIssueID = recipient.address.toString() == reportIssueBChatID return when { diff --git a/app/src/main/java/io/beldex/bchat/home/HomeActivity.kt b/app/src/main/java/io/beldex/bchat/home/HomeActivity.kt index 9093dcc96..2efd52dea 100644 --- a/app/src/main/java/io/beldex/bchat/home/HomeActivity.kt +++ b/app/src/main/java/io/beldex/bchat/home/HomeActivity.kt @@ -24,6 +24,7 @@ import android.text.style.ForegroundColorSpan import android.view.MenuItem import android.view.MotionEvent import android.view.View +import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.TextView import android.widget.Toast @@ -64,6 +65,8 @@ import com.beldex.libbchat.utilities.GroupRecord import com.beldex.libbchat.utilities.GroupUtil import com.beldex.libbchat.utilities.ProfilePictureModifiedEvent import com.beldex.libbchat.utilities.TextSecurePreferences +import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.getLanguage +import com.beldex.libbchat.utilities.dynamiclanguage.LocaleParser.Companion.findBestMatchingLocaleForLanguage import com.beldex.libbchat.utilities.recipients.Recipient import com.beldex.libsignal.utilities.Log import com.beldex.libsignal.utilities.ThreadUtils @@ -159,6 +162,7 @@ import timber.log.Timber import java.io.IOException import java.util.concurrent.TimeUnit import javax.inject.Inject +import kotlin.jvm.java import kotlin.math.abs import kotlin.math.roundToInt @@ -226,17 +230,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele } private lateinit var adapter: NavigationRVAdapter - - private var items = arrayListOf( - NavigationItemModel(R.drawable.ic_settings_outline, "Settings"), - NavigationItemModel(R.drawable.ic_notification_outline, "Notification"), - NavigationItemModel(R.drawable.ic_msg_rqst_outline, "Message Requests"), - NavigationItemModel(R.drawable.ic_recovery_seed_outline, "Recovery Seed"), - NavigationItemModel(R.drawable.ic_report_issue_outline,"Report Issue"), - NavigationItemModel(R.drawable.ic_help_outline, "Help"), - NavigationItemModel(R.drawable.ic_invite_outline, "Invite"), - NavigationItemModel(R.drawable.ic_about_outline, "About") - ) private val hexEncodedPublicKey: String get() { return TextSecurePreferences.getLocalNumber(this)!! @@ -295,6 +288,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele @Inject lateinit var threadDb: ThreadDatabase private val reactWithAnyEmojiStartPage = -1 + private lateinit var items: ArrayList // region Lifecycle override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { @@ -306,6 +300,18 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele setupToolbarInsets() setSupportActionBar(binding.toolbar) + items = arrayListOf( + NavigationItemModel(R.drawable.ic_settings_outline, getString(R.string.activity_settings_title)), + NavigationItemModel(R.drawable.ic_notification_outline, getString(R.string.notification)), + NavigationItemModel(R.drawable.ic_msg_rqst_outline, getString(R.string.activity_message_requests_title)), + NavigationItemModel(R.drawable.ic_recovery_seed_outline, getString(R.string.activity_settings_recovery_phrase_button_title)), + NavigationItemModel(R.drawable.ic_language, getString(R.string.language)), + NavigationItemModel(R.drawable.ic_report_issue_outline,getString(R.string.report_issue)), + NavigationItemModel(R.drawable.ic_help_outline, getString(R.string.help)), + NavigationItemModel(R.drawable.ic_invite_outline, getString(R.string.invite)), + NavigationItemModel(R.drawable.ic_about_outline, getString(R.string.about)) + ) + glide = Glide.with(this) binding.profileButton.root.glide = glide @@ -334,19 +340,23 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele showSeed() } 4 -> { + // # Choose Language Activity + showChooseLanguage() + } + 5 -> { // # Support sendMessageToSupport() binding.drawerLayout.closeDrawer(GravityCompat.END) } - 5 -> { + 6 -> { // # Help Activity help() } - 6 -> { + 7 -> { // # Invite Activity sendInvitation(hexEncodedPublicKey) } - 7 -> { + 8 -> { // # About Activity showAbout() } @@ -671,7 +681,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) startActivity(intent) } catch (e: Exception) { - Toast.makeText(this, "Can't open URL", Toast.LENGTH_LONG).show() + Toast.makeText(this, getString(R.string.cannot_open_url), Toast.LENGTH_LONG).show() } }*/ //New Line App Update @@ -732,21 +742,21 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele RESULT_CANCELED -> { Toast.makeText( applicationContext, - "Update canceled by user! Result Code: $resultCode", Toast.LENGTH_LONG + getString(R.string.update_canceled_by_user), Toast.LENGTH_LONG ).show() finish() } RESULT_OK -> { Toast.makeText( applicationContext, - "Update success! Result Code: $resultCode", + getString(R.string.update_success), Toast.LENGTH_LONG ).show() } else -> { Toast.makeText( applicationContext, - "Update Failed! Result Code: $resultCode", + getString(R.string.update_failed), Toast.LENGTH_LONG ).show() checkUpdate() @@ -844,7 +854,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele if (messageResults.isNotEmpty()) { messageResults.add( 0, - GlobalSearchAdapter.Model.Header(R.string.global_search_messages) + GlobalSearchAdapter.Model.Header(R.string.NotificationChannel_group_messages) ) } @@ -1013,7 +1023,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele val menu = MenuBuilder(this) menuInflater.inflate(R.menu.menu_conversation_v2, menu) val item : MenuItem= menu.findItem(R.id.menu_delete) - val s=SpannableString("Delete") + val s=SpannableString(getString(R.string.delete)) s.setSpan(ForegroundColorSpan(this.getColor(R.color.red)), 0, s.length, 0) item.setTitle(s) with(menu) { @@ -1045,6 +1055,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele // space available inside the chat list area, so it never grows over the views above (e.g. // the global search box) nor past the bottom of the screen. val menuAdapter = MenuAdapter(menu, layoutInflater, true, androidx.appcompat.R.layout.abc_popup_menu_item_layout) + menuAdapter.setForceShowIcon(true) showChatOptionsMenu(menuAdapter, view, thread, position) } @@ -1054,6 +1065,10 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele var maxItemWidth = 0 for (i in 0 until menuAdapter.count) { val itemView = menuAdapter.getView(i, null, null) + itemView.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) itemView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) @@ -1179,8 +1194,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele override fun hideMessageRequests() { val dialog = AlertDialog.Builder(this, R.style.BChatAlertDialog_New) - .setTitle("Hide message requests?") - .setMessage("Once they are hidden, you can access them from Settings > Message Requests") + .setTitle(R.string.hide_message_request) + .setMessage(R.string.message_requests_hidden_info) .setPositiveButton(R.string.yes) { _, _ -> textSecurePreferences.setHasHiddenMessageRequests() homeViewModel.tryUpdateChannel() @@ -1303,7 +1318,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele private fun sendInvitation(hexEncodedPublicKey:String) { val intent = Intent() intent.action = Intent.ACTION_SEND - val invitation = String.format(this.resources.getString(R.string.invitation_msg), hexEncodedPublicKey) + val invitation = getString(R.string.invitation_msg, hexEncodedPublicKey) intent.putExtra(Intent.EXTRA_TEXT, invitation) intent.type = "text/plain" val chooser = Intent.createChooser(intent, getString(R.string.activity_settings_invite_button_title)) @@ -1327,15 +1342,19 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele } private fun showSeed() { -// Intent(requireContext(), SeedPermissionActivity::class.java).also { -// show(it) -// } Intent(this, MyAccountActivity::class.java).also { it.putExtra(MyAccountActivity.extraStartDestination, MyAccountScreens.RecoverySeedScreen.route) startActivity(it) } } + private fun showChooseLanguage() { + Intent(this, MyAccountActivity::class.java).also { + it.putExtra(MyAccountActivity.extraStartDestination, MyAccountScreens.LanguageChooseScreen.route) + startActivity(it) + } + } + private fun handlePopUpMenuClickListener(item: MenuItem, thread: ThreadRecord, position : Int) { when (item.itemId) { R.id.menu_details -> { @@ -1475,9 +1494,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele val group = groupDb.getGroup(recipient.address.toString()).orNull() if (group != null && group.admins.map { it.toString() } .contains(TextSecurePreferences.getLocalNumber(this))) { - "Because you are the creator of this group it will be deleted for everyone. This cannot be undone." + resources.getString(R.string.group_delete_confirmation_message) } else { - resources.getString(R.string.activity_home_leave_group_dialog_message) + resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group) } } else { resources.getString(R.string.activity_home_delete_conversation_dialog_message) @@ -1612,7 +1631,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele binding.hanUpCall.setOnClickListener { this.startService(WebRtcCallService.hangupIntent(this)) binding.toolbarCall.isVisible = false - Toast.makeText(this, "Call ended", Toast.LENGTH_SHORT).show() + Toast.makeText(this, getString(R.string.call_ended), Toast.LENGTH_SHORT).show() } binding.toolbarCall.setOnClickListener { toolBarCall() @@ -1689,6 +1708,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) + if (!::binding.isInitialized) return updateEmptyState() val wasDrawerOpen = binding.drawerLayout.isDrawerVisible(GravityCompat.END) updateDrawerWidth() @@ -1719,6 +1739,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), SeedReminderViewDele override fun onResume() { super.onResume() Timber.d("onResume()-->") + val appSettingLanguage = findBestMatchingLocaleForLanguage(getLanguage(this)) + TextSecurePreferences.setDeviceLanguage(this, appSettingLanguage?.language) updateDrawerWidth() if (binding.drawerLayout.isDrawerVisible(GravityCompat.END)) { binding.drawerLayout.closeDrawer(GravityCompat.END, false) diff --git a/app/src/main/java/io/beldex/bchat/home/MessageRequestsView.kt b/app/src/main/java/io/beldex/bchat/home/MessageRequestsView.kt index 1e5994949..5749a2732 100644 --- a/app/src/main/java/io/beldex/bchat/home/MessageRequestsView.kt +++ b/app/src/main/java/io/beldex/bchat/home/MessageRequestsView.kt @@ -354,7 +354,7 @@ fun RequestItem( private fun getDisplayName(context: Context, publicKey: String): String { val contact = DatabaseComponent.get(context).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } @Preview diff --git a/app/src/main/java/io/beldex/bchat/home/NewConversationButtonSetView.kt b/app/src/main/java/io/beldex/bchat/home/NewConversationButtonSetView.kt index 237626902..615bccd24 100644 --- a/app/src/main/java/io/beldex/bchat/home/NewConversationButtonSetView.kt +++ b/app/src/main/java/io/beldex/bchat/home/NewConversationButtonSetView.kt @@ -73,7 +73,7 @@ class NewConversationButtonSetView : RelativeLayout { TextView(context).apply { setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f) typeface = ResourcesCompat.getFont(this.context, R.font.open_sans_bold) - setText(R.string.home_screen_new_chat_title) + setText(R.string.activity_create_private_chat_title) isAllCaps = true } } diff --git a/app/src/main/java/io/beldex/bchat/home/NotificationSetting.kt b/app/src/main/java/io/beldex/bchat/home/NotificationSetting.kt index f48c5ff03..0834ae180 100644 --- a/app/src/main/java/io/beldex/bchat/home/NotificationSetting.kt +++ b/app/src/main/java/io/beldex/bchat/home/NotificationSetting.kt @@ -62,12 +62,12 @@ fun NotificationSettingDialog( Column( Modifier .fillMaxWidth() - .padding(start=20.dp, end=20.dp, top=25.dp, bottom=25.dp), Arrangement.Center, Alignment.CenterHorizontally) { + .padding(start=16.dp, end=16.dp, top=25.dp, bottom=25.dp), Arrangement.Center, Alignment.CenterHorizontally) { Row(modifier=Modifier.padding(bottom=20.dp)) { Text(text=stringResource(id=R.string.RecipientPreferenceActivity_notification_settings), style=MaterialTheme.typography.titleMedium.copy( - fontSize=16.sp, + fontSize=15.sp, fontWeight=FontWeight(700), color=MaterialTheme.appColors.secondaryContentColor), textAlign=TextAlign.Center, @@ -123,7 +123,7 @@ fun NotificationSettingDialog( ) { Text(text=item, style=MaterialTheme.typography.titleMedium.copy( - color=if (index == selectedItemIndex) MaterialTheme.appColors.secondaryContentColor else MaterialTheme.appColors.secondaryTextColor, fontSize=16.sp, fontWeight=FontWeight(400) + color=if (index == selectedItemIndex) MaterialTheme.appColors.secondaryContentColor else MaterialTheme.appColors.secondaryTextColor, fontSize=14.sp, fontWeight=FontWeight(400) ), modifier=Modifier.padding(10.dp)) } } diff --git a/app/src/main/java/io/beldex/bchat/home/PathActivity.kt b/app/src/main/java/io/beldex/bchat/home/PathActivity.kt index ac1f36a9a..1613755a9 100644 --- a/app/src/main/java/io/beldex/bchat/home/PathActivity.kt +++ b/app/src/main/java/io/beldex/bchat/home/PathActivity.kt @@ -97,7 +97,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() { val isGuardMnode = (OnionRequestAPI.guardMnodes.contains(mnode)) getPathRow(mnode, LineView.Location.Middle, index.toLong() * 1000 + 2000, dotAnimationRepeatInterval, isGuardMnode) } - val youRow = getPathRow(resources.getString(R.string.activity_path_device_row_title), null, LineView.Location.Top, 1000, dotAnimationRepeatInterval) + val youRow = getPathRow(resources.getString(R.string.MediaPreviewActivity_you), null, LineView.Location.Top, 1000, dotAnimationRepeatInterval) val destinationRow = getPathRow(resources.getString(R.string.activity_path_destination_row_title), null, LineView.Location.Bottom, path.count().toLong() * 1000 + 2000, dotAnimationRepeatInterval) val rows = listOf( youRow ) + pathRows + listOf( destinationRow ) for (row in rows) { diff --git a/app/src/main/java/io/beldex/bchat/home/SearchActivity.kt b/app/src/main/java/io/beldex/bchat/home/SearchActivity.kt index 8bf54c502..ec3bd4fbb 100644 --- a/app/src/main/java/io/beldex/bchat/home/SearchActivity.kt +++ b/app/src/main/java/io/beldex/bchat/home/SearchActivity.kt @@ -7,7 +7,6 @@ import android.content.res.Configuration import android.os.Build import android.os.Bundle import android.text.SpannableStringBuilder -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -71,6 +70,9 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.beldex.libbchat.utilities.Address import com.beldex.libbchat.utilities.recipients.Recipient +import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseComponentActivity +import io.beldex.bchat.R import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.compose_utils.ProfilePictureComponent import io.beldex.bchat.compose_utils.ProfilePictureMode @@ -82,19 +84,17 @@ import io.beldex.bchat.home.search.GlobalSearchViewModel import io.beldex.bchat.home.search.getSearchName import io.beldex.bchat.search.SearchActivityResults import io.beldex.bchat.search.SearchResults +import io.beldex.bchat.textformatter.TextFormatter +import io.beldex.bchat.textformatter.TextFormatter.toAnnotatedString import io.beldex.bchat.util.DateUtils import io.beldex.bchat.util.UiMode import io.beldex.bchat.util.UiModeUtilities -import dagger.hilt.android.AndroidEntryPoint -import io.beldex.bchat.R -import io.beldex.bchat.textformatter.TextFormatter -import io.beldex.bchat.textformatter.TextFormatter.toAnnotatedString import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.util.Locale @AndroidEntryPoint -class SearchActivity : ComponentActivity() { +class SearchActivity : BaseComponentActivity() { companion object { const val EXTRA_SEARCH_DATA = "io.beldex.bchat.EXTRA_SEARCH_DATA" @@ -251,7 +251,7 @@ private fun SearchView( leadingIcon = { Icon( Icons.Default.Search, - contentDescription = "search", + contentDescription = stringResource(R.string.SearchToolbar_search), tint = MaterialTheme.appColors.iconTint ) }, @@ -259,7 +259,7 @@ private fun SearchView( if(searchQuery.isNotEmpty()) { Icon( Icons.Default.Close, - contentDescription = "clear", + contentDescription = stringResource(R.string.clear), tint = MaterialTheme.appColors.iconTint, modifier = Modifier.clickable( onClick = { diff --git a/app/src/main/java/io/beldex/bchat/home/SplashHomeScreenActivity.kt b/app/src/main/java/io/beldex/bchat/home/SplashHomeScreenActivity.kt index f907d0e3b..d392f5891 100644 --- a/app/src/main/java/io/beldex/bchat/home/SplashHomeScreenActivity.kt +++ b/app/src/main/java/io/beldex/bchat/home/SplashHomeScreenActivity.kt @@ -6,9 +6,10 @@ import android.os.Handler import android.os.Looper import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat +import io.beldex.bchat.BaseAppCompatActivity import io.beldex.bchat.databinding.ActivitySplashScreenBinding -class SplashHomeScreenActivity : AppCompatActivity() { +class SplashHomeScreenActivity : BaseAppCompatActivity() { private lateinit var binding: ActivitySplashScreenBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/io/beldex/bchat/home/UserDetailsBottomSheet.kt b/app/src/main/java/io/beldex/bchat/home/UserDetailsBottomSheet.kt index 9c5b8b9f9..4b8ac3013 100644 --- a/app/src/main/java/io/beldex/bchat/home/UserDetailsBottomSheet.kt +++ b/app/src/main/java/io/beldex/bchat/home/UserDetailsBottomSheet.kt @@ -16,23 +16,25 @@ import androidx.core.view.isVisible import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BuildConfig import io.beldex.bchat.R import io.beldex.bchat.databinding.FragmentUserDetailsBottomSheetBinding import com.beldex.libbchat.messaging.MessagingModuleConfiguration import com.beldex.libbchat.messaging.contacts.Contact import com.beldex.libbchat.utilities.Address +import com.beldex.libbchat.utilities.SSKEnvironment import com.beldex.libbchat.utilities.recipients.Recipient import io.beldex.bchat.database.ThreadDatabase import io.beldex.bchat.dependencies.DatabaseComponent import com.bumptech.glide.Glide; import io.beldex.bchat.util.UiModeUtilities +import io.beldex.bchat.util.unicodeNamePattern import javax.inject.Inject @AndroidEntryPoint class UserDetailsBottomSheet : BottomSheetDialogFragment() { @Inject lateinit var threadDb: ThreadDatabase - private val nicknameRegex = Regex("^[a-zA-Z0-9 ]+$") private lateinit var binding: FragmentUserDetailsBottomSheetBinding companion object { @@ -106,7 +108,7 @@ class UserDetailsBottomSheet : BottomSheetDialogFragment() { else -> return@setOnEditorActionListener false } } - nameTextView.text = recipient.name ?: publicKey // Uses the Contact API internally + nameTextView.text = if (publicKey == BuildConfig.REPORT_ISSUE_ID) getString(R.string.report_issue) else recipient.name ?: publicKey // Uses the Contact API internally publicKeyTextView.isVisible = !threadRecipient.isOpenGroupRecipient messageButton.isVisible = !threadRecipient.isOpenGroupRecipient @@ -114,7 +116,7 @@ class UserDetailsBottomSheet : BottomSheetDialogFragment() { publicKeyTextView.setOnLongClickListener { val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText("Bchat ID", publicKey) + val clip = ClipData.newPlainText("BChat ID", publicKey) clipboard.setPrimaryClip(clip) Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT) .show() @@ -195,10 +197,18 @@ class UserDetailsBottomSheet : BottomSheetDialogFragment() { ).show() } - !nicknameRegex.matches(nickname) -> { + nickname.toByteArray().size > SSKEnvironment.ProfileManagerProtocol.Companion.NAME_PADDED_LENGTH -> { Toast.makeText( context, - R.string.nickname_special_char_not_allowed, + R.string.nick_name_too_long_error, + Toast.LENGTH_SHORT + ).show() + } + + !nickname.matches(unicodeNamePattern.toRegex()) -> { + Toast.makeText( + context, + R.string.display_name_validation, Toast.LENGTH_SHORT ).show() } @@ -220,7 +230,13 @@ class UserDetailsBottomSheet : BottomSheetDialogFragment() { contact.nickname = nickname contactDB.setContact(contact) + val previousName = nameTextView.text.toString() + nameTextView.text = recipient.name ?: publicKey + + if(previousName != nameTextView.text.toString()) { + updateProfilePictureView(recipient) + } } } } @@ -236,4 +252,9 @@ class UserDetailsBottomSheet : BottomSheetDialogFragment() { val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager imm?.hideSoftInputFromWindow(binding.nicknameEditText.windowToken, 0) } + + private fun updateProfilePictureView(recipient: Recipient) { + binding.profilePictureView.root.recycle() + binding.profilePictureView.root.update(recipient) + } } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/home/search/GlobalSearchViewModel.kt b/app/src/main/java/io/beldex/bchat/home/search/GlobalSearchViewModel.kt index e1ff9d86f..8c9828851 100644 --- a/app/src/main/java/io/beldex/bchat/home/search/GlobalSearchViewModel.kt +++ b/app/src/main/java/io/beldex/bchat/home/search/GlobalSearchViewModel.kt @@ -134,7 +134,7 @@ class GlobalSearchViewModel @Inject constructor( if (messageResults.isNotEmpty()) { messageResults.add( 0, - SearchResults.Header(R.string.global_search_messages) + SearchResults.Header(R.string.NotificationChannel_group_messages) ) } _searchResults.update { contactResults + messageResults } diff --git a/app/src/main/java/io/beldex/bchat/home/search/RecyclerViewDivider.kt b/app/src/main/java/io/beldex/bchat/home/search/RecyclerViewDivider.kt index 6cc3afd07..bb35211e6 100644 --- a/app/src/main/java/io/beldex/bchat/home/search/RecyclerViewDivider.kt +++ b/app/src/main/java/io/beldex/bchat/home/search/RecyclerViewDivider.kt @@ -16,8 +16,8 @@ class RecyclerViewDivider( private val divider: Drawable? = ContextCompat.getDrawable(context, resId) override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { divider?.let { - val left = parent.paddingLeft + marginStart - val right = parent.width - parent.paddingRight - marginEnd + val left = parent.paddingStart + marginStart + val right = parent.width - parent.paddingEnd - marginEnd val childCount = parent.childCount for (i in 0 until childCount - 1) { val child = parent.getChildAt(i) diff --git a/app/src/main/java/io/beldex/bchat/keyboard/layouts/NumberDecimalKeyboardLayout.kt b/app/src/main/java/io/beldex/bchat/keyboard/layouts/NumberDecimalKeyboardLayout.kt index b2ebadf83..ba13b749c 100644 --- a/app/src/main/java/io/beldex/bchat/keyboard/layouts/NumberDecimalKeyboardLayout.kt +++ b/app/src/main/java/io/beldex/bchat/keyboard/layouts/NumberDecimalKeyboardLayout.kt @@ -3,6 +3,7 @@ package io.beldex.bchat.keyboard.layouts import android.content.Context import android.view.View import android.widget.LinearLayout +import io.beldex.bchat.R import io.beldex.bchat.keyboard.controllers.KeyboardController class NumberDecimalKeyboardLayout(context: Context, controller: KeyboardController?) : @@ -25,9 +26,9 @@ class NumberDecimalKeyboardLayout(context: Context, controller: KeyboardControll rowTwo.add(createButton("5", columnWidth, '5')) rowTwo.add(createButton("6", columnWidth, '6')) if (hasNextFocus) { - rowTwo.add(createButton("Next", columnWidth, KeyboardController.SpecialKey.NEXT)) + rowTwo.add(createButton(context.getString(R.string.next), columnWidth, KeyboardController.SpecialKey.NEXT)) } else { - rowTwo.add(createButton("Done", columnWidth, KeyboardController.SpecialKey.DONE)) + rowTwo.add(createButton(context.getString(R.string.menu_done_button), columnWidth, KeyboardController.SpecialKey.DONE)) } val rowThree = ArrayList() @@ -40,7 +41,7 @@ class NumberDecimalKeyboardLayout(context: Context, controller: KeyboardControll rowFour.add(createButton("⇦", columnWidth, KeyboardController.SpecialKey.BACK)) rowFour.add(createButton("0", columnWidth, '0')) rowFour.add(createButton("⇨", columnWidth, KeyboardController.SpecialKey.FORWARD)) - rowFour.add(createButton("Clear", columnWidth, KeyboardController.SpecialKey.CLEAR)) + rowFour.add(createButton(context.getString(R.string.clear), columnWidth, KeyboardController.SpecialKey.CLEAR)) val rows = ArrayList() rows.add(createRow(rowOne)) diff --git a/app/src/main/java/io/beldex/bchat/keyboard/layouts/NumberKeyboardLayout.kt b/app/src/main/java/io/beldex/bchat/keyboard/layouts/NumberKeyboardLayout.kt index dcc5068cf..fd4a55dcb 100644 --- a/app/src/main/java/io/beldex/bchat/keyboard/layouts/NumberKeyboardLayout.kt +++ b/app/src/main/java/io/beldex/bchat/keyboard/layouts/NumberKeyboardLayout.kt @@ -3,6 +3,7 @@ package io.beldex.bchat.keyboard.layouts import android.content.Context import android.view.View import android.widget.LinearLayout +import io.beldex.bchat.R import io.beldex.bchat.keyboard.controllers.KeyboardController class NumberKeyboardLayout(context: Context, controller: KeyboardController?) : @@ -33,7 +34,7 @@ class NumberKeyboardLayout(context: Context, controller: KeyboardController?) : rowFour.add(createButton("⌫", columnWidth, KeyboardController.SpecialKey.BACKSPACE)) rowFour.add(createButton("0", columnWidth, '0')) if (hasNextFocus) { - rowFour.add(createButton("Next", columnWidth, KeyboardController.SpecialKey.NEXT)) + rowFour.add(createButton(context.getString(R.string.next), columnWidth, KeyboardController.SpecialKey.NEXT)) } else { rowFour.add(createButton("✓", columnWidth, KeyboardController.SpecialKey.DONE)) } diff --git a/app/src/main/java/io/beldex/bchat/keyboard/layouts/QwertyKeyboardLAyout.kt b/app/src/main/java/io/beldex/bchat/keyboard/layouts/QwertyKeyboardLAyout.kt index 2b5b09552..53552e5ca 100644 --- a/app/src/main/java/io/beldex/bchat/keyboard/layouts/QwertyKeyboardLAyout.kt +++ b/app/src/main/java/io/beldex/bchat/keyboard/layouts/QwertyKeyboardLAyout.kt @@ -5,6 +5,7 @@ import android.graphics.Color import android.view.View import android.widget.Button import android.widget.LinearLayout +import io.beldex.bchat.R import io.beldex.bchat.keyboard.controllers.KeyboardController import io.beldex.bchat.keyboard.listeners.KeyboardListener import io.beldex.bchat.keyboard.utils.ComponentUtils @@ -134,10 +135,10 @@ class QwertyKeyboardLayout(context: Context, controller: KeyboardController?) : rowThree.add(createButton("l", columnWidth, 'l')) if (hasNextFocus) { rowThree.add(createButton( - "Next", (columnWidth * 1.5f), KeyboardController.SpecialKey.NEXT)) + context.getString(R.string.next), (columnWidth * 1.5f), KeyboardController.SpecialKey.NEXT)) } else { rowThree.add(createButton( - "Done", (columnWidth * 1.5f), KeyboardController.SpecialKey.DONE)) + context.getString(R.string.menu_done_button), (columnWidth * 1.5f), KeyboardController.SpecialKey.DONE)) } val rowFour = ArrayList() @@ -197,10 +198,10 @@ class QwertyKeyboardLayout(context: Context, controller: KeyboardController?) : rowThree.add(createButton("L", columnWidth, 'L')) if (hasNextFocus) { rowThree.add(createButton( - "Next", (columnWidth * 1.5f), KeyboardController.SpecialKey.NEXT)) + context.getString(R.string.next), (columnWidth * 1.5f), KeyboardController.SpecialKey.NEXT)) } else { rowThree.add(createButton( - "Done", (columnWidth * 1.5f), KeyboardController.SpecialKey.DONE)) + context.getString(R.string.menu_done_button), (columnWidth * 1.5f), KeyboardController.SpecialKey.DONE)) } val rowFour = ArrayList() @@ -260,10 +261,10 @@ class QwertyKeyboardLayout(context: Context, controller: KeyboardController?) : rowThree.add(createButton(")", columnWidth, ')')) if (hasNextFocus) { rowThree.add(createButton( - "Next", (columnWidth * 1.5f), KeyboardController.SpecialKey.NEXT)) + context.getString(R.string.next), (columnWidth * 1.5f), KeyboardController.SpecialKey.NEXT)) } else { rowThree.add(createButton( - "Done", (columnWidth * 1.5f), KeyboardController.SpecialKey.DONE)) + context.getString(R.string.menu_done_button), (columnWidth * 1.5f), KeyboardController.SpecialKey.DONE)) } val rowFour = ArrayList() @@ -324,10 +325,10 @@ class QwertyKeyboardLayout(context: Context, controller: KeyboardController?) : rowThree.add(createButton("♧", columnWidth, '♧')) if (hasNextFocus) { rowThree.add(createButton( - "Next", (columnWidth * 1.5f), KeyboardController.SpecialKey.NEXT)) + context.getString(R.string.next), (columnWidth * 1.5f), KeyboardController.SpecialKey.NEXT)) } else { rowThree.add(createButton( - "Done", (columnWidth * 1.5f), KeyboardController.SpecialKey.DONE)) + context.getString(R.string.menu_done_button), (columnWidth * 1.5f), KeyboardController.SpecialKey.DONE)) } val rowFour = ArrayList() diff --git a/app/src/main/java/io/beldex/bchat/keys/RecoveryGetKeysDetailsActivity.kt b/app/src/main/java/io/beldex/bchat/keys/RecoveryGetKeysDetailsActivity.kt index 0ba1a3f56..681664ecb 100644 --- a/app/src/main/java/io/beldex/bchat/keys/RecoveryGetKeysDetailsActivity.kt +++ b/app/src/main/java/io/beldex/bchat/keys/RecoveryGetKeysDetailsActivity.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.os.Bundle import io.beldex.bchat.databinding.ActivityRecoveryGetKeysDetailsBinding import io.beldex.bchat.BaseActionBarActivity +import io.beldex.bchat.R import io.beldex.bchat.onboarding.AppLockActivity import io.beldex.bchat.util.push import io.beldex.bchat.util.setUpActionBarBchatLogo @@ -14,7 +15,7 @@ class RecoveryGetKeysDetailsActivity : BaseActionBarActivity() { super.onCreate(savedInstanceState) binding= ActivityRecoveryGetKeysDetailsBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("Restore from Keys") + setUpActionBarBchatLogo("Restore From Keys") with(binding){ restoreKeysRestoreButton.setOnClickListener { val intent = Intent(this@RecoveryGetKeysDetailsActivity, AppLockActivity::class.java) diff --git a/app/src/main/java/io/beldex/bchat/mediasend/MediaPickerFolderFragment.java b/app/src/main/java/io/beldex/bchat/mediasend/MediaPickerFolderFragment.java index 5e59af664..228cd0167 100644 --- a/app/src/main/java/io/beldex/bchat/mediasend/MediaPickerFolderFragment.java +++ b/app/src/main/java/io/beldex/bchat/mediasend/MediaPickerFolderFragment.java @@ -1,28 +1,24 @@ package io.beldex.bchat.mediasend; -import androidx.appcompat.app.ActionBar; -import androidx.lifecycle.ViewModelProvider; - import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; -import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.Toolbar; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; - -import com.bumptech.glide.Glide; import com.beldex.libbchat.utilities.recipients.Recipient; import com.beldex.libsignal.utilities.guava.Optional; - +import com.bumptech.glide.Glide; import io.beldex.bchat.R; import io.beldex.bchat.WindowInsetsUtil; @@ -110,8 +106,8 @@ public void onConfigurationChanged(Configuration newConfig) { onScreenWidthChanged(getScreenWidth()); } private void initToolbar(Toolbar toolbar) { - ((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar); - ActionBar actionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar(); + ((MediaSendActivity) requireActivity()).setSupportActionBar(toolbar); + ActionBar actionBar = ((MediaSendActivity) requireActivity()).getSupportActionBar(); actionBar.setTitle(getString(R.string.MediaPickerActivity_send_to, recipientName)); actionBar.setHomeAsUpIndicator(R.drawable.ic_back); actionBar.setDisplayHomeAsUpEnabled(true); diff --git a/app/src/main/java/io/beldex/bchat/mediasend/MediaPickerItemFragment.java b/app/src/main/java/io/beldex/bchat/mediasend/MediaPickerItemFragment.java index d420e551a..50e60a07e 100644 --- a/app/src/main/java/io/beldex/bchat/mediasend/MediaPickerItemFragment.java +++ b/app/src/main/java/io/beldex/bchat/mediasend/MediaPickerItemFragment.java @@ -1,18 +1,9 @@ package io.beldex.bchat.mediasend; -import androidx.appcompat.app.ActionBar; -import androidx.lifecycle.ViewModelProvider; import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -20,13 +11,18 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.Toast; - -import com.bumptech.glide.Glide; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.widget.Toolbar; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.beldex.libbchat.utilities.Util; - +import com.bumptech.glide.Glide; import java.util.ArrayList; import java.util.List; - import io.beldex.bchat.R; import io.beldex.bchat.WindowInsetsUtil; @@ -170,7 +166,7 @@ public void onMediaSelectionOverflow(int maxSelection) { } private void initToolbar(Toolbar toolbar) { - AppCompatActivity activity = (AppCompatActivity) requireActivity(); + MediaSendActivity activity = (MediaSendActivity) requireActivity(); activity.setSupportActionBar(toolbar); ActionBar actionBar = activity.getSupportActionBar(); actionBar.setTitle(folderTitle); diff --git a/app/src/main/java/io/beldex/bchat/mediasend/MediaSendActivity.java b/app/src/main/java/io/beldex/bchat/mediasend/MediaSendActivity.java index 6073c8549..341b96df1 100644 --- a/app/src/main/java/io/beldex/bchat/mediasend/MediaSendActivity.java +++ b/app/src/main/java/io/beldex/bchat/mediasend/MediaSendActivity.java @@ -23,6 +23,7 @@ import io.beldex.bchat.PassphraseRequiredActionBarActivity; import io.beldex.bchat.providers.BlobProvider; +import io.beldex.bchat.util.ToastMessageUtil; import com.beldex.libbchat.utilities.Address; import com.beldex.libbchat.utilities.MediaTypes; @@ -347,7 +348,7 @@ private void initializeErrorObserver() { switch (error) { case ITEM_TOO_LARGE: - Toast.makeText(this, R.string.MediaSendActivity_an_item_was_removed_because_it_exceeded_the_size_limit, Toast.LENGTH_LONG).show(); + ToastMessageUtil.showLong(this, R.string.MediaSendActivity_an_item_was_removed_because_it_exceeded_the_size_limit); break; case TOO_MANY_ITEMS: int maxSelection = viewModel.getMaxSelection(); diff --git a/app/src/main/java/io/beldex/bchat/messagerequests/MessageRequestsActivity.kt b/app/src/main/java/io/beldex/bchat/messagerequests/MessageRequestsActivity.kt index e40f772bc..adfe270ba 100644 --- a/app/src/main/java/io/beldex/bchat/messagerequests/MessageRequestsActivity.kt +++ b/app/src/main/java/io/beldex/bchat/messagerequests/MessageRequestsActivity.kt @@ -83,7 +83,7 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat val dialog = AlertDialog.Builder(this,R.style.BChatAlertDialog_Clear_All) dialog.setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) .setMessage(R.string.message_requests_block_message) - .setPositiveButton(R.string.recipient_preferences__block) { _, _ -> + .setPositiveButton(R.string.RecipientPreferenceActivity_block) { _, _ -> viewModel.blockMessageRequest(thread) LoaderManager.getInstance(this).restartLoader(0, null, this) } @@ -120,7 +120,7 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat private fun deleteAllAndBlock() { val dialog = AlertDialog.Builder(this,R.style.BChatAlertDialog_Clear_All) dialog.setMessage(resources.getString(R.string.message_requests_clear_all_message)) - dialog.setPositiveButton(R.string.message_requests_clear) { _, _ -> + dialog.setPositiveButton(R.string.clear) { _, _ -> viewModel.clearAllMessageRequests() LoaderManager.getInstance(this).restartLoader(0, null, this) lifecycleScope.launch(Dispatchers.IO) { diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/AboutBNSScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/AboutBNSScreen.kt index 582f92aab..3023dd611 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/AboutBNSScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/AboutBNSScreen.kt @@ -33,13 +33,15 @@ fun AboutBNSScreen( fontSize = 16.sp, letterSpacing = 0.2.sp, )){ - append("Pricing:") + append(stringResource(id = R.string.about_bns_pricing_label)) } withStyle(style = SpanStyle(fontFamily = FontFamily.SansSerif, fontWeight = FontWeight.Normal, fontSize = 16.sp, letterSpacing = 0.2.sp,)) { - append(" Users can register their BNS names for 1, 2, 5, and 10 years for as low as ") + append(" ") + append(stringResource(id = R.string.about_bns_pricing_text)) + append(" ") } withStyle(style = SpanStyle( fontFamily = FontFamily.SansSerif, @@ -47,13 +49,14 @@ fun AboutBNSScreen( fontSize = 16.sp, letterSpacing = 0.2.sp, )){ - append("650 BDX, 1000 BDX, 2000 BDX, and 4000 BDX") + append(stringResource(id = R.string.about_bns_pricing_values)) } withStyle(style = SpanStyle(fontFamily = FontFamily.SansSerif, fontWeight = FontWeight.Normal, fontSize = 16.sp, letterSpacing = 0.2.sp,)) { - append(" respectively.") + append(" ") + append(stringResource(id = R.string.about_bns_pricing_suffix)) } } Column( @@ -63,21 +66,21 @@ fun AboutBNSScreen( ) ) { Text( - text = "BNS: Your Decentralized Identity in the Beldex Ecosystem", + text = stringResource(id = R.string.about_bns_title), style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.SemiBold, ), modifier = Modifier.padding(bottom = 15.dp) ) Text( - text = "BNS (Beldex Name Service) is your gateway to a seamless experience within the Beldex ecosystem. With BNS, you can create a unique, easy-to-remember name that links to your various Beldex identities.", + text = stringResource(id = R.string.about_bns_description_1), style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.Normal, ), modifier = Modifier.padding(bottom = 15.dp) ) Text( - text = "Key Benefits:", + text = stringResource(id = R.string.about_bns_key_benefits), style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.SemiBold, ), @@ -109,14 +112,14 @@ fun AboutBNSScreen( modifier = Modifier.padding(bottom = 15.dp) ) Text( - text = "Using BNS names enhances your privacy, security, and convenience. Whether you're sending a message, making a transaction, or using decentralized services, your BNS name ensures a consistent and simplified experience.", + text = stringResource(id = R.string.about_bns_description_7), style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.Normal, ), modifier = Modifier.padding(bottom = 15.dp) ) Text( - text = "Get started with your BNS name today and enjoy the benefits of a decentralized identity across all your Beldex services!", + text = stringResource(id = R.string.about_bns_description_8), style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.Normal, ), diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/AppLockScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/AppLockScreen.kt index 809806be3..65ba0e902 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/AppLockScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/AppLockScreen.kt @@ -50,20 +50,26 @@ fun AppLockScreen() { var showLockOptionsDialog by remember { mutableStateOf(false) } - val lockOptions = remember { - ScreenTimeoutOptions.entries.map { it.displayValue }.toList() - } - var selectedLockOptions by remember { - mutableStateOf(IdentityKeyUtil.retrieve( - context, - IdentityKeyUtil.SCREEN_TIMEOUT_VALUES_KEY - ) ?: "None") + val lockOptions = ScreenTimeoutOptions.entries + var selectedLockOption by remember { + mutableStateOf( + lockOptions.getOrNull( + IdentityKeyUtil.retrieve( + context, + IdentityKeyUtil.SCREEN_TIMEOUT_KEY + )?.toIntOrNull() ?: 0 + ) ?: ScreenTimeoutOptions.None + ) } - val onLockTimerChanged: (String, Int) -> Unit = { value, index -> + + val noTimeoutSeconds = 950400L + + val onLockTimerChanged: (ScreenTimeoutOptions, Int) -> Unit = { option, index -> showLockOptionsDialog = false + IdentityKeyUtil.save(context, IdentityKeyUtil.SCREEN_TIMEOUT_KEY, index.toString()) - IdentityKeyUtil.save(context, IdentityKeyUtil.SCREEN_TIMEOUT_VALUES_KEY, value) - selectedLockOptions = value + + selectedLockOption = option TextSecurePreferences.setScreenLockEnabled(context, true) @@ -71,48 +77,37 @@ fun AppLockScreen() { intent.action = KeyCachingService.LOCK_TOGGLED_EVENT context.startService(intent) - when (value) { - "None" -> { - TextSecurePreferences.setScreenLockTimeout(context, 950400) - } - "30 Seconds" -> { - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(30000) - TextSecurePreferences.setScreenLockTimeout(context, timeoutSeconds) - } - "1 Minute" -> { - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(60000) - TextSecurePreferences.setScreenLockTimeout(context, timeoutSeconds) - } - "2 Minutes" -> { - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(120000) - TextSecurePreferences.setScreenLockTimeout(context, timeoutSeconds) - } - "5 Minutes" -> { - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(300000) - TextSecurePreferences.setScreenLockTimeout(context, timeoutSeconds) - } - "15 Minutes" -> { - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(900000) - TextSecurePreferences.setScreenLockTimeout(context, timeoutSeconds) + val timeoutSeconds = + if (option == ScreenTimeoutOptions.None) { + noTimeoutSeconds + } else { + TimeUnit.MILLISECONDS.toSeconds(option.timeoutMillis) } - "30 Minutes" -> { - val timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(1800000) - TextSecurePreferences.setScreenLockTimeout(context, timeoutSeconds) - } - else -> Unit - } + + TextSecurePreferences.setScreenLockTimeout( + context, + timeoutSeconds + ) } if (showLockOptionsDialog) { LockOptionsDialog( title = stringResource(R.string.screen_inactivity_timeout), - options = lockOptions, - currentValue = selectedLockOptions, + + options = lockOptions.map { + stringResource(it.labelRes) + }, + + currentValue = stringResource(selectedLockOption.labelRes), + onDismiss = { showLockOptionsDialog = false }, + onValueChanged = { value, index -> - onLockTimerChanged(value, index) + lockOptions.getOrNull(index)?.let { option -> + onLockTimerChanged(option, index) + } } ) } @@ -182,7 +177,7 @@ fun AppLockScreen() { ) { Icon( painter = painterResource(id = R.drawable.ic_screen_lock), - contentDescription = "", + contentDescription = null, tint = MaterialTheme.appColors.iconTint ) @@ -199,7 +194,7 @@ fun AppLockScreen() { ) Text( - text = selectedLockOptions, + text = stringResource(selectedLockOption.labelRes), style = MaterialTheme.typography.bodySmall.copy( color = MaterialTheme.appColors.lockTimerColor, fontWeight = FontWeight(400), diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/ArchiveChatScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/ArchiveChatScreen.kt index 1955cb7e6..26f886342 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/ArchiveChatScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/ArchiveChatScreen.kt @@ -16,11 +16,13 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Divider +import androidx.compose.material3.Divider import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -96,7 +98,7 @@ fun ArchiveChatScreen( var showMenu by remember { mutableStateOf(false) } val itemPositions = remember { mutableStateMapOf() } var menuPositionState by remember { mutableStateOf(MenuPositionState()) } - val popupOffset = rememberMenuPosition(menuPositionState) + val popupOffset = rememberMenuPosition(menuPositionState, 320.dp) var showBlockPopup by remember { mutableStateOf(false) @@ -143,7 +145,7 @@ fun ArchiveChatScreen( ) { UnblockUserDialog(title=stringResource(id=R.string.unblock_contact), message=stringResource(id=R.string.unblock_user_confirmation), - positiveButtonTitle=stringResource(id=R.string.unblock), + positiveButtonTitle=stringResource(id=R.string.ConversationActivity_unblock), onAccept={ threadRecord?.let { thread -> archiveChatViewModel.onEvent( @@ -230,9 +232,9 @@ fun ArchiveChatScreen( val group=groupDatabase.getGroup(thread.recipient.address.toString()).orNull() if (group != null && group.admins.map { it.toString() } .contains(TextSecurePreferences.getLocalNumber(context))) { - "Because you are the creator of this group it will be deleted for everyone. This cannot be undone." + context.resources.getString(R.string.group_delete_confirmation_message) } else { - context.resources.getString(R.string.activity_home_leave_group_dialog_message) + context.resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group) } } else { context.resources.getString(R.string.activity_home_delete_conversation_dialog_message) @@ -265,7 +267,7 @@ fun ArchiveChatScreen( offset = popupOffset, onDismissRequest = { showMenu = false }, ) { - Card(modifier = Modifier.width(200.dp)) { + Card(modifier = Modifier.wrapContentWidth().widthIn(max = 320.dp)) { Column( modifier = Modifier .padding(16.dp) @@ -286,7 +288,10 @@ fun ArchiveChatScreen( showMenu = false showBlockPopup = true }) { - Text(stringResource(id = R.string.RecipientPreferenceActivity_block)) + Text( + stringResource(id = R.string.RecipientPreferenceActivity_block), + fontSize = 12.sp + ) } } } @@ -306,7 +311,10 @@ fun ArchiveChatScreen( showMenu = false showUnBlockPopup = true }) { - Text(stringResource(id = R.string.RecipientPreferenceActivity_unblock)) + Text( + stringResource(id=R.string.ConversationActivity_unblock), + fontSize = 12.sp + ) } } } @@ -325,7 +333,10 @@ fun ArchiveChatScreen( showMenu = false archiveChatViewModel.onEvent(ArchiveChatsEvents.UnArchiveChats(thread)) }) { - Text(stringResource(id = R.string.un_archive_chat_title)) + Text( + stringResource(id = R.string.un_archive_chat_title), + fontSize = 12.sp + ) } } if (thread.unreadCount > 0) { @@ -342,7 +353,10 @@ fun ArchiveChatScreen( showMenu = false archiveChatViewModel.onEvent(ArchiveChatsEvents.MarkAsRead(thread)) }) { - Text(stringResource(id = R.string.MessageNotifier_mark_all_as_read)) + Text( + stringResource(id = R.string.MessageNotifier_mark_all_as_read), + fontSize = 12.sp + ) } } } @@ -362,6 +376,7 @@ fun ArchiveChatScreen( Text( stringResource(id = R.string.delete), color = MaterialTheme.appColors.deleteOptionColor, + fontSize = 12.sp, ) } } @@ -529,12 +544,19 @@ fun ArchiveChatItem( val addresses = flattenData(data.address) val names = flattenData(data.name).ifEmpty { addresses } when(names.size) { - 0 -> "No Name" + 0 -> context.getString(R.string.no_name) 1 -> names.first().capitalizeFirstLetter() - 2 -> "${shortNameAndAddress(names[0],addresses[0])} and ${names.size - 1} other" - else -> "${shortNameAndAddress(names.first(), addresses.first())} and ${names.size - 1} others" + else -> { + val othersCount = names.size - 1 + context.resources.getQuantityString( + R.plurals.contact_others, + othersCount, + shortNameAndAddress(names.first(), addresses.first()), + othersCount + ) + } } - } ?: "No Name" + } ?: context.getString(R.string.no_name) Row( verticalAlignment = Alignment.CenterVertically ) { @@ -638,7 +660,7 @@ fun ArchiveChatItem( private fun getDisplayName(context : Context, publicKey : String) : String { val contact= DatabaseComponent.get(context).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } private fun getUserDisplayName(context : Context, recipient : Recipient?) : String? { diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/BlockedContactScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/BlockedContactScreen.kt index 39ac88948..13eb73505 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/BlockedContactScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/BlockedContactScreen.kt @@ -274,7 +274,7 @@ private fun BlockedContactItem( ) } else { Text( - text = "Unblock", + text = stringResource(R.string.ConversationActivity_unblock), style = MaterialTheme.typography.labelMedium.copy( fontWeight = FontWeight(600), fontSize = 12.sp diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/ChatSettingsScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/ChatSettingsScreen.kt index 98d5fe89f..4b871a3ac 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/ChatSettingsScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/ChatSettingsScreen.kt @@ -89,7 +89,7 @@ fun ChatSettingsScreen( Spacer(modifier = Modifier.height(24.dp)) Text( - text = "Message Timing", + text = stringResource(R.string.preferences_chats__message_trimming), style = MaterialTheme.typography.titleMedium.copy( color = MaterialTheme.appColors.textGreen, fontWeight = FontWeight(600), @@ -112,7 +112,7 @@ fun ChatSettingsScreen( .padding(16.dp) ) { SettingsItem( - settingTitle = "Delete Old Messages", + settingTitle = stringResource(R.string.preferences__delete_old_messages), settingIcon = painterResource(id = R.drawable.ic_delete_old), isEnabled = false, onSwitchChanged = {}, @@ -123,10 +123,10 @@ fun ChatSettingsScreen( Spacer(modifier = Modifier.height(16.dp)) SettingsItem( - settingTitle = "Conversation Length Limit", + settingTitle = stringResource(R.string.preferences__conversation_length_limit), painterResource(id = R.drawable.ic_conversation_length), containsSwitch = false, - settingDesc = "500 messages per conversation", + settingDesc = stringResource(R.string.default_value_messages_per_conversation), modifier = Modifier .fillMaxWidth(), ) @@ -134,7 +134,7 @@ fun ChatSettingsScreen( Spacer(modifier = Modifier.height(24.dp)) SettingsItem( - settingTitle = "Trim all conversations now", + settingTitle = stringResource(R.string.preferences__trim_all_conversations_now), painterResource(id = R.drawable.ic_trim_conversation), containsSwitch = false, modifier = Modifier diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/ContentScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/ContentScreen.kt index c18832eb3..17ed91316 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/ContentScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/ContentScreen.kt @@ -7,13 +7,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp +import io.beldex.bchat.R import io.beldex.bchat.compose_utils.appColors @Composable fun ContentScreen( - content: String, modifier: Modifier = Modifier ) { val scrollState = rememberScrollState() @@ -24,7 +25,7 @@ fun ContentScreen( ) ) { Text( - text = content, + text = stringResource(R.string.about_content), style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.appColors.editTextColor, fontWeight = FontWeight(400), diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/ContentViewModel.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/ContentViewModel.kt deleted file mode 100644 index 46aa57faf..000000000 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/ContentViewModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.beldex.bchat.my_account.ui - -import androidx.lifecycle.ViewModel -import io.beldex.bchat.util.AssetFileHelper -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import javax.inject.Inject - -@HiltViewModel -class ContentViewModel @Inject constructor( - private val assetHelper: AssetFileHelper -): ViewModel() { - - private val _content = MutableStateFlow("") - val content = _content.asStateFlow() - - init { - _content.value = assetHelper.loadAboutContent() ?: "" - } - -} \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/HopsScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/HopsScreen.kt index d9f26afe6..62c0d931c 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/HopsScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/HopsScreen.kt @@ -58,7 +58,7 @@ fun HopsScreen( ) ) { Text( - text = stringResource(id = R.string.activity_path_device_row_title), + text = stringResource(id = R.string.MediaPreviewActivity_you), style = MaterialTheme.typography.bodySmall ) diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/MessageRequestsScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/MessageRequestsScreen.kt index 60a48b4cd..972bc9685 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/MessageRequestsScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/MessageRequestsScreen.kt @@ -2,7 +2,6 @@ package io.beldex.bchat.my_account.ui import android.app.Activity import android.content.Context -import android.view.WindowManager import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -42,9 +41,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.lifecycle.lifecycleScope import com.beldex.libbchat.messaging.contacts.Contact -import com.beldex.libbchat.utilities.TextSecurePreferences import com.beldex.libbchat.utilities.recipients.Recipient import io.beldex.bchat.compose_utils.ProfilePictureComponent import io.beldex.bchat.compose_utils.ProfilePictureMode @@ -103,7 +100,7 @@ fun MessageRequestsScreen( RequestBlockConfirmationDialog( title = stringResource(id = R.string.block_request), message = stringResource(id = R.string.message_requests_block_message), - actionTitle = stringResource(id = R.string.recipient_preferences__block), + actionTitle = stringResource(id = R.string.RecipientPreferenceActivity_block), onConfirmation = { threadRecord?.let { onEvent(MessageRequestEvents.BlockRequest(it)) @@ -331,7 +328,7 @@ fun MessageRequestItem( private fun getDisplayName(context: Context, publicKey: String): String { val contact = DatabaseComponent.get(context).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } private fun getUserDisplayName(context: Context, recipient: Recipient?): String? { diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountActivity.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountActivity.kt index 07da42601..5fa307061 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountActivity.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountActivity.kt @@ -16,7 +16,6 @@ import android.os.Handler import android.os.Looper import android.os.SystemClock import android.widget.Toast -import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.result.contract.ActivityResultContracts @@ -37,6 +36,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -114,6 +114,7 @@ import com.beldex.libsignal.utilities.hexEncodedPrivateKey import com.canhub.cropper.CropImage import com.canhub.cropper.CropImageContract import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseComponentActivity import io.beldex.bchat.CheckOnline import io.beldex.bchat.PassphraseRequiredActionBarActivity import io.beldex.bchat.R @@ -132,6 +133,7 @@ import io.beldex.bchat.crypto.IdentityKeyUtil import io.beldex.bchat.crypto.MnemonicUtilities import io.beldex.bchat.database.DatabaseContentProviders import io.beldex.bchat.database.GroupDatabase +import io.beldex.bchat.home.ChooseLanguage import io.beldex.bchat.messagerequests.MessageRequestsViewModel import io.beldex.bchat.my_account.ui.dialogs.BNSNameVerifySuccessDialog import io.beldex.bchat.my_account.ui.dialogs.ClearDataDialog @@ -151,6 +153,7 @@ import io.beldex.bchat.util.UiMode import io.beldex.bchat.util.UiModeUtilities import io.beldex.bchat.util.copyToClipBoard import io.beldex.bchat.util.toPx +import io.beldex.bchat.util.unicodeNamePattern import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach @@ -163,11 +166,10 @@ import java.io.File import java.io.FileOutputStream import java.security.SecureRandom import java.util.Date -import java.util.regex.Pattern import javax.inject.Inject @AndroidEntryPoint -class MyAccountActivity : ComponentActivity() { +class MyAccountActivity : BaseComponentActivity() { private var destination = MyAccountScreens.SettingsScreen.route @Inject @@ -422,11 +424,10 @@ fun MyAccountNavHost( } fun saveDisplayName(displayName: String, context : Context): Boolean { - val namePattern = Pattern.compile("[A-Za-z0-9\\s]+") if (displayName.isEmpty()) { Toast.makeText( context, - R.string.activity_settings_display_name_missing_error, + R.string.activity_display_name_display_name_missing_error, Toast.LENGTH_SHORT ).show() return false @@ -434,12 +435,12 @@ fun MyAccountNavHost( if (displayName.toByteArray().size > SSKEnvironment.ProfileManagerProtocol.Companion.NAME_PADDED_LENGTH) { Toast.makeText( context, - R.string.activity_settings_display_name_too_long_error, + R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT ).show() return false } - if (!displayName.matches(namePattern.toRegex())) { + if (!displayName.matches(unicodeNamePattern.toRegex())) { Toast.makeText( context, R.string.display_name_validation, @@ -493,7 +494,7 @@ fun MyAccountNavHost( MyAccountScreenContainer( title = stringResource(id = R.string.account_settings), onBackClick = { - (context as ComponentActivity).finish() + (context as BaseComponentActivity).finish() } ) { val state by viewModel.uiState.collectAsState() @@ -767,6 +768,7 @@ fun MyAccountNavHost( Box( modifier = Modifier .padding(start = 24.dp, top = 16.dp, end = 0.dp, bottom = 16.dp) + .widthIn(max = 120.dp) .background( color = if (showEditNameTextField) MaterialTheme.appColors.primaryButtonColor else MaterialTheme.appColors.listItemBackground, shape = RoundedCornerShape(16.dp) @@ -795,9 +797,10 @@ fun MyAccountNavHost( style = BChatTypography.bodySmall.copy( color = if(showEditNameTextField) Color.White else MaterialTheme.appColors.primaryButtonColor, fontWeight = FontWeight(600), - fontSize = 12.sp, + fontSize = 11.sp, ), - modifier = Modifier.padding(start = 12.dp, top = 4.dp, end = 12.dp, bottom = 4.dp) + textAlign = TextAlign.Center, + modifier = Modifier.padding(start = 6.dp, top = 4.dp, end = 6.dp, bottom = 4.dp) ) } @@ -853,8 +856,10 @@ fun MyAccountNavHost( ) } Box( - contentAlignment=Alignment.CenterEnd, - modifier=Modifier.align(alignment=Alignment.TopCenter) + contentAlignment = Alignment.BottomEnd, + modifier = Modifier + .size(ProfilePictureMode.LargePicture.size) + .align(alignment = Alignment.TopCenter) ) { if(isProfileChanged) { ProfilePictureComponent( @@ -862,7 +867,7 @@ fun MyAccountNavHost( displayName = state.profileName ?: state.publicKey, containerSize = ProfilePictureMode.LargePicture.size, pictureMode = ProfilePictureMode.LargePicture, - modifier = Modifier.align(alignment = Alignment.TopCenter) + modifier = Modifier.fillMaxSize() ) isProfileChanged = false }else{ @@ -871,14 +876,15 @@ fun MyAccountNavHost( displayName = state.profileName ?: state.publicKey, containerSize = ProfilePictureMode.LargePicture.size, pictureMode = ProfilePictureMode.LargePicture, - modifier = Modifier.align(alignment = Alignment.TopCenter) + modifier = Modifier.fillMaxSize() ) } if (showEditNameTextField) { Box( contentAlignment=Alignment.Center, modifier= Modifier - .padding(start = 150.dp) + .align(Alignment.BottomEnd) + .padding(end = 4.dp, bottom = 4.dp) .size(32.dp) .clip(CircleShape) .background( @@ -1030,7 +1036,7 @@ fun MyAccountNavHost( } catch (e: Exception) { Toast.makeText( context, - "Can't open URL", + context.getString(R.string.cannot_open_url), Toast.LENGTH_LONG ).show() } @@ -1157,7 +1163,8 @@ fun MyAccountNavHost( ) { Text( text = stringResource(id = R.string.no_blocked_contact), - style = MaterialTheme.typography.titleLarge + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Center ) } } else { @@ -1190,16 +1197,13 @@ fun MyAccountNavHost( composable( route = MyAccountScreens.AboutScreen.route ) { - val aboutViewModel: ContentViewModel = hiltViewModel() - val content by aboutViewModel.content.collectAsState() MyAccountScreenContainer( title = stringResource(R.string.about), onBackClick = { - (context as ComponentActivity).finish() + (context as BaseComponentActivity).finish() } ) { ContentScreen( - content = content, modifier = Modifier .fillMaxSize() .padding(16.dp) @@ -1219,7 +1223,7 @@ fun MyAccountNavHost( markedAsSafe = true } else { markedAsSafe = false - Toast.makeText(context, "Failed to authenticate", Toast.LENGTH_SHORT).show() + Toast.makeText(context, context.getString(R.string.failed_to_authenticate), Toast.LENGTH_SHORT).show() } } val verifyPin: () -> Unit = { @@ -1249,10 +1253,10 @@ fun MyAccountNavHost( } } MyAccountScreenContainer( - title = stringResource(R.string.recovery_seed), + title = stringResource(R.string.activity_settings_recovery_phrase_button_title), wrapInCard = markedAsSafe, onBackClick = { - (context as ComponentActivity).finish() + (context as BaseComponentActivity).finish() } ) { RecoverySeedScreen( @@ -1283,7 +1287,7 @@ fun MyAccountNavHost( MyAccountScreenContainer( title = stringResource(R.string.activity_message_requests_title), onBackClick = { - (context as ComponentActivity).finish() + (context as BaseComponentActivity).finish() } ) { MessageRequestsScreen( @@ -1338,7 +1342,7 @@ fun MyAccountNavHost( ArchiveChatScreenContainer( title = stringResource(R.string.archive_chat), onBackClick = { - (context as ComponentActivity).finish() + (context as BaseComponentActivity).finish() } ) { ArchiveChatScreen( @@ -1361,6 +1365,22 @@ fun MyAccountNavHost( ) } } + composable( + route = MyAccountScreens.LanguageChooseScreen.route + ) { + MyAccountScreenContainer( + title = stringResource(R.string.choose_language), + onBackClick = { + (context as BaseComponentActivity).finish() + }) { + ChooseLanguage( + viewModel, + onBack = { + (context as BaseComponentActivity).finish() + } + ) + } + } } } @@ -1481,7 +1501,7 @@ fun ProfileCard( horizontalArrangement = Arrangement.Center ) { Text( - text = "BNS Verified", + text = stringResource(R.string.bns_verified), style = MaterialTheme.typography.titleSmall.copy( color = MaterialTheme.appColors.primaryButtonColor, fontWeight = FontWeight(700), @@ -1497,7 +1517,7 @@ fun ProfileCard( Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly + horizontalArrangement = Arrangement.spacedBy(10.dp) ) { ProfileCardKeyContainer( isBnsHolder = isBnsHolder, @@ -1509,7 +1529,8 @@ fun ProfileCard( isBeldex = true, onShowDialog = { onShowDialog(0) - } + }, + modifier = Modifier.weight(1f) ) ProfileCardKeyContainer( @@ -1521,7 +1542,8 @@ fun ProfileCard( }, onShowDialog = { onShowDialog(1) - } + }, + modifier = Modifier.weight(1f) ) ProfileCardKeyContainer( @@ -1533,7 +1555,8 @@ fun ProfileCard( showCopyIcon = false, onShowDialog = { onShowDialog(2) - } + }, + modifier = Modifier.weight(1f) ) } } @@ -1547,9 +1570,11 @@ fun ProfileCardKeyContainer( onCopy: () -> Unit, showCopyIcon: Boolean = true, isBeldex: Boolean = false, - onShowDialog: () -> Unit + onShowDialog: () -> Unit, + modifier: Modifier = Modifier ) { Card( + modifier = modifier, elevation = CardDefaults.cardElevation( defaultElevation = if(!isBnsHolder.isNullOrEmpty()) 2.dp else 0.dp ), @@ -1559,31 +1584,34 @@ fun ProfileCardKeyContainer( ) { Box( modifier = Modifier + .fillMaxWidth() .padding(5.dp) .clickable { onShowDialog() - } + }, + contentAlignment = Alignment.Center ) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, - modifier = Modifier.padding( - top = 13.dp, - bottom = 5.dp, - start = if (isBeldex) 5.dp else 15.dp, - end = if (isBeldex) 5.dp else 15.dp - ) + modifier = Modifier + .fillMaxWidth() + .padding( + top = 13.dp, + bottom = 5.dp + ) ) { Image( painter = painterResource(id = image), contentDescription = "", modifier = Modifier - .size(25.dp), + .size(25.dp) + .align(Alignment.CenterHorizontally), colorFilter = if (!showCopyIcon) ColorFilter.tint(MaterialTheme.appColors.editTextColor) else null ) Text( text = title, style = MaterialTheme.typography.bodySmall.copy( - fontSize = 11.sp, + fontSize = 10.sp, color = MaterialTheme.appColors.editTextColor, fontWeight = FontWeight.Medium ), @@ -1611,7 +1639,7 @@ fun ProfileCardKeyContainer( } @Composable -private fun MyAccountScreenContainer( +fun MyAccountScreenContainer( title: String, wrapInCard: Boolean = true, onBackClick: () -> Unit, diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountScreen.kt index d7658399b..44325844a 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountScreen.kt @@ -158,7 +158,7 @@ fun MyAccountScreen( } if (showPermissionDialog) { PermissionSettingDialog( - message = "BChat needs library access to continue. You can enable access in the Settings page", + message = stringResource(R.string.library_access_required), onDismissRequest = {}, gotoSettings = {} ) diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountScreens.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountScreens.kt index 4def639a3..318eb0ac5 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountScreens.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountScreens.kt @@ -14,4 +14,5 @@ sealed class MyAccountScreens(val route: String) { data object StartWalletInfoScreen: MyAccountScreens("/start-wallet-info") data object AboutBNSScreen : MyAccountScreens("/about-bns") data object ArchiveChatScreen : MyAccountScreens("/archive_chats") + data object LanguageChooseScreen: MyAccountScreens("/language-choose") } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountViewModel.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountViewModel.kt index 122638f88..c0206a577 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountViewModel.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/MyAccountViewModel.kt @@ -1,23 +1,32 @@ package io.beldex.bchat.my_account.ui +import android.content.Context +import android.content.res.Configuration import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.beldex.libbchat.mnode.OnionRequestAPI +import com.beldex.libbchat.utilities.TextSecurePreferences +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper import com.beldex.libbchat.utilities.truncateIdForDisplay +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import io.beldex.bchat.R import io.beldex.bchat.my_account.domain.PathNodeModel +import io.beldex.bchat.util.AppLanguageEvent import io.beldex.bchat.util.IP2Country import io.beldex.bchat.util.ResourceProvider import io.beldex.bchat.util.SharedPreferenceUtil -import dagger.hilt.android.lifecycle.HiltViewModel -import io.beldex.bchat.R import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update +import java.util.Locale import javax.inject.Inject @HiltViewModel class MyAccountViewModel @Inject constructor( + @ApplicationContext context : Context, private val preferenceUtil: SharedPreferenceUtil, private val resourceProvider: ResourceProvider ): ViewModel() { @@ -41,6 +50,26 @@ class MyAccountViewModel @Inject constructor( private val _showLoader = MutableLiveData() val showLoader: LiveData get () = _showLoader + private val _selectedLanguageCode = MutableStateFlow(TextSecurePreferences.getAppSelectedLanguage(context) ?: Locale.getDefault().language) + + val selectedLanguageCode: StateFlow = + _selectedLanguageCode.asStateFlow() + + fun selectLanguage(code: String, context: Context) { + _selectedLanguageCode.value = code + val localizedContext = getLocalizedContext(context, code) + DynamicLanguageContextWrapper.updateContext( + context, code) + resourceProvider.updateContext(localizedContext) + } + + private fun getLocalizedContext(context: Context, code: String): Context { + val locale = Locale(code) + val config = Configuration(context.resources.configuration) + config.setLocale(locale) + return context.createConfigurationContext(config) + } + init { val publicKey = preferenceUtil.getPublicKey() _uiState.update { @@ -49,6 +78,7 @@ class MyAccountViewModel @Inject constructor( publicKey = publicKey ) } + AppLanguageEvent.deviceLanguage.value = TextSecurePreferences.getDeviceLanguage(context) ?: Locale.getDefault().language } fun getPathNodes() { diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/MyProfileActivity.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/MyProfileActivity.kt index 98f1ddedf..70c538226 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/MyProfileActivity.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/MyProfileActivity.kt @@ -12,7 +12,6 @@ import android.view.LayoutInflater import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth @@ -38,16 +37,21 @@ import com.beldex.libbchat.utilities.SSKEnvironment import com.beldex.libbchat.utilities.TextSecurePreferences import com.beldex.libbchat.utilities.truncateIdForDisplay import com.beldex.libsignal.utilities.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.RequestManager import com.canhub.cropper.CropImage import com.canhub.cropper.CropImageContract +import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseAppCompatActivity +import io.beldex.bchat.CheckOnline +import io.beldex.bchat.R import io.beldex.bchat.avatar.AvatarSelection import io.beldex.bchat.components.ProfilePictureView import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.compose_utils.ComposeDialogContainer import io.beldex.bchat.compose_utils.DialogType import io.beldex.bchat.compose_utils.appColors -import com.bumptech.glide.Glide; -import com.bumptech.glide.RequestManager +import io.beldex.bchat.databinding.ActivityMyProfileBinding import io.beldex.bchat.permissions.Permissions import io.beldex.bchat.profiles.ProfileMediaConstraints import io.beldex.bchat.util.BitmapDecodingException @@ -56,10 +60,7 @@ import io.beldex.bchat.util.ConfigurationMessageUtilities import io.beldex.bchat.util.FileProviderUtil import io.beldex.bchat.util.QRCodeUtilities import io.beldex.bchat.util.toPx -import io.beldex.bchat.CheckOnline -import dagger.hilt.android.AndroidEntryPoint -import io.beldex.bchat.R -import io.beldex.bchat.databinding.ActivityMyProfileBinding +import io.beldex.bchat.util.unicodeNamePattern import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -71,15 +72,13 @@ import java.io.File import java.io.FileOutputStream import java.security.SecureRandom import java.util.Date -import java.util.regex.Pattern @AndroidEntryPoint -class MyProfileActivity: AppCompatActivity() { +class MyProfileActivity: BaseAppCompatActivity() { private lateinit var glide: RequestManager private var tempFile: File? = null private lateinit var binding: ActivityMyProfileBinding - private val namePattern = Pattern.compile("[A-Za-z0-9\\s]+") private val hexEncodedPublicKey: String get() { return TextSecurePreferences.getLocalNumber(this)!! @@ -368,7 +367,7 @@ class MyProfileActivity: AppCompatActivity() { if (displayName.isEmpty()) { Toast.makeText( this, - R.string.activity_settings_display_name_missing_error, + R.string.activity_display_name_display_name_missing_error, Toast.LENGTH_SHORT ).show() return false @@ -376,12 +375,12 @@ class MyProfileActivity: AppCompatActivity() { if (displayName.toByteArray().size > SSKEnvironment.ProfileManagerProtocol.Companion.NAME_PADDED_LENGTH) { Toast.makeText( this, - R.string.activity_settings_display_name_too_long_error, + R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT ).show() return false } - if (!displayName.matches(namePattern.toRegex())) { + if (!displayName.matches(unicodeNamePattern.toRegex())) { Toast.makeText( this, R.string.display_name_validation, diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/RecoverySeedScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/RecoverySeedScreen.kt index 23da82c39..b3f99ef55 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/RecoverySeedScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/RecoverySeedScreen.kt @@ -201,7 +201,8 @@ private fun RecoveryWarningView( fontWeight = FontWeight(700), fontSize = 16.sp, color = MaterialTheme.appColors.secondaryContentColor - ) + ), + textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(16.dp)) @@ -212,7 +213,8 @@ private fun RecoveryWarningView( color = MaterialTheme.appColors.editTextColor, fontWeight = FontWeight(700), fontSize = 16.sp - ) + ), + textAlign = TextAlign.Center ) Text( @@ -252,7 +254,8 @@ private fun RecoveryWarningView( color = Color.White, fontWeight = FontWeight(400), fontSize = 14.sp - ) + ), + textAlign = TextAlign.Center ) } } diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/ScreenTimeoutOptions.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/ScreenTimeoutOptions.kt index 4f936fba5..173e398fd 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/ScreenTimeoutOptions.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/ScreenTimeoutOptions.kt @@ -1,11 +1,22 @@ package io.beldex.bchat.my_account.ui -enum class ScreenTimeoutOptions(val time: Long, val displayValue: String) { - None(0, "None"), - ThirtySeconds(30000, "30 Seconds"), - OneMinute(60000, "1 Minute"), - TwoMinute(120000, "2 Minutes"), - FiveMinute(300000, "5 Minutes"), - FifteenMinute(900000, "15 Minutes"), - ThirtyMinute(1800000, "30 Minutes"), +import android.content.Context +import androidx.annotation.StringRes +import io.beldex.bchat.R + +enum class ScreenTimeoutOptions( + val timeoutMillis: Long, + @StringRes val labelRes: Int +) { + None(0, R.string.none), + ThirtySeconds(30000, R.string.thirty_seconds), + OneMinute(60000, R.string.one_minute), + TwoMinutes(120000, R.string.two_minutes), + FiveMinutes(300000, R.string.five_minutes), + FifteenMinutes(900000, R.string.fifteen_minutes), + ThirtyMinutes(1800000, R.string.thirty_minutes); + + fun displayValue(context: Context): String { + return context.getString(labelRes) + } } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/SettingsScreen.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/SettingsScreen.kt index d81366a58..0f8487eff 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/SettingsScreen.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/SettingsScreen.kt @@ -90,14 +90,14 @@ fun SettingsScreen( SettingItem.FAQ -> false }, modifier =Modifier - .fillMaxWidth() - .padding( - vertical=16.dp, - horizontal=24.dp - ) - .clickable { - navigate(item) - } + .fillMaxWidth() + .padding( + vertical = 16.dp, + horizontal = 24.dp + ) + .clickable { + navigate(item) + } ) } } @@ -108,46 +108,51 @@ private fun MyAccountItem( title: String, icon: Painter, drawDot: Boolean, - expendArrow:Boolean, + expendArrow: Boolean, modifier: Modifier = Modifier ) { val context = LocalContext.current Row( - modifier = modifier + modifier = modifier, + verticalAlignment = Alignment.CenterVertically ) { Icon( - painter=icon, - contentDescription="", - tint=MaterialTheme.appColors.editTextColor + painter = icon, + contentDescription = "", + tint = MaterialTheme.appColors.editTextColor, + modifier = Modifier.size(20.dp) ) - Spacer(modifier=Modifier.width(16.dp)) + Spacer(modifier = Modifier.width(16.dp)) Row( - verticalAlignment=Alignment.CenterVertically, - modifier=Modifier - .weight(0.7f) + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) ) { Text( - text=title, - style=BChatTypography.titleMedium.copy( - color=MaterialTheme.appColors.editTextColor - ) + text = title, + style = BChatTypography.titleMedium.copy( + color = MaterialTheme.appColors.editTextColor + ) ) + if (drawDot) { - Spacer(modifier=Modifier.width(8.dp)) + Spacer(modifier = Modifier.width(8.dp)) - AndroidView(modifier=Modifier - .size(8.dp), factory={ - PathStatusView(context=context) - }) + AndroidView( + modifier = Modifier.size(8.dp), + factory = { + PathStatusView(context = context) + } + ) } } + if (expendArrow) { Icon( - Icons.Default.KeyboardArrowRight, - contentDescription="", - tint=MaterialTheme.appColors.editTextColor + Icons.Default.KeyboardArrowRight, + contentDescription = "", + tint = MaterialTheme.appColors.editTextColor ) } } diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/ShowQRDialog.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/ShowQRDialog.kt index fcc4f77d1..ef5a2e03d 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/ShowQRDialog.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/ShowQRDialog.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -112,7 +113,7 @@ fun ShowQRDialog( Button(onClick={ onShare() }, colors= ButtonDefaults.buttonColors(containerColor=MaterialTheme.appColors.primaryButtonColor)) { - Text(text="Share", style=MaterialTheme.typography.bodyMedium.copy(color=Color.White, fontWeight=FontWeight.Bold), modifier=Modifier.padding(start = 10.dp, end = 5.dp)) + Text(text= stringResource(R.string.share), style=MaterialTheme.typography.bodyMedium.copy(color=Color.White, fontWeight=FontWeight.Bold), modifier=Modifier.padding(start = 10.dp, end = 5.dp)) Icon(painter=painterResource(id= R.drawable.ic_baseline_share_24), contentDescription="Refresh", tint=Color.White, modifier = Modifier.size(14.dp)) } diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/ClearDataDialog.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/ClearDataDialog.kt index 21ef82cf6..2077203f5 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/ClearDataDialog.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/ClearDataDialog.kt @@ -193,10 +193,11 @@ fun ClearDataDialog( text = when (step) { Steps.INFO_PROMPT_DEFAULT, Steps.INFO_PROMPT -> { - stringResource(id = R.string.dialog_clear_all_data_title) + stringResource(id = R.string. + dialog_clear_all_data_title) } Steps.NETWORK_PROMPT -> { - stringResource(id = R.string.dialog_clear_all_data_clear_device_and_network_title) + stringResource(id = R.string.delete_entire_account) } Steps.DELETING -> { "" @@ -358,8 +359,7 @@ fun DeleteOption( color = MaterialTheme.appColors.clearDataSubTitle, fontWeight = FontWeight.Normal, fontSize = 14.sp - ), - textAlign = TextAlign.Center + ) ) } } diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/LinkYourBNSDialog.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/LinkYourBNSDialog.kt index e16a28b0b..431e41f44 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/LinkYourBNSDialog.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/LinkYourBNSDialog.kt @@ -144,7 +144,7 @@ fun LinkYourBNSDialog( .padding(if (isLandscape) 10.dp else 16.dp) ) { Text( - text = "Link BNS", + text = stringResource(R.string.link_bns), style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.editTextColor, fontSize = 20.sp, @@ -156,7 +156,7 @@ fun LinkYourBNSDialog( .padding(bottom=15.dp) ) Text( - text = "Your BChat ID", + text = stringResource(R.string.your_bchat_id), style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.editTextColor, fontSize = 14.sp, @@ -185,7 +185,7 @@ fun LinkYourBNSDialog( ) } Text( - text = "BNS Name", + text = stringResource(R.string.bns_name), style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.editTextColor, fontSize = 14.sp, diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/LockOptionsDialog.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/LockOptionsDialog.kt index 8d5750232..7cde14a71 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/LockOptionsDialog.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/LockOptionsDialog.kt @@ -83,7 +83,7 @@ fun LockOptionsDialog( textModifier = Modifier.padding(12.dp), textStyle = MaterialTheme.typography.titleMedium, modifier = Modifier - .fillMaxWidth(0.5f) + .fillMaxWidth() ) Spacer(modifier = Modifier.height(16.dp)) diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/MessageRequestDialogs.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/MessageRequestDialogs.kt index 5f938625f..f15fb2e21 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/MessageRequestDialogs.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/MessageRequestDialogs.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -91,7 +92,7 @@ fun RequestBlockConfirmationDialog( style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.appColors.negativeGreenButtonText, fontWeight = FontWeight(400), - fontSize = 14.sp + fontSize = 12.sp ) ) } @@ -111,7 +112,7 @@ fun RequestBlockConfirmationDialog( text = actionTitle, style = MaterialTheme.typography.bodyMedium.copy( fontWeight = FontWeight(400), - fontSize = 14.sp, + fontSize = 12.sp, color = Color.White ), ) @@ -139,7 +140,7 @@ fun IgnoreRequestDialog( .padding(16.dp) ) { Text( - text = stringResource(id = R.string.message_request), + text = stringResource(id = R.string.activity_message_requests_title), style = MaterialTheme.typography.titleMedium.copy( color = MaterialTheme.appColors.secondaryContentColor, fontWeight = FontWeight(700), @@ -178,16 +179,19 @@ fun IgnoreRequestDialog( .weight(1f) ) { Text( - text = stringResource(id = R.string.recipient_preferences__block), + text = stringResource(id = R.string.RecipientPreferenceActivity_block), style = MaterialTheme.typography.bodyMedium.copy( color = MaterialTheme.appColors.negativeRedButtonBorder, fontWeight = FontWeight(400), - fontSize = 14.sp - ) + fontSize = 12.sp + ), + maxLines = 1, + softWrap = false, + overflow = TextOverflow.Visible ) } - Spacer(modifier = Modifier.width(16.dp)) + Spacer(modifier = Modifier.width(8.dp)) Button( onClick ={ @@ -204,9 +208,12 @@ fun IgnoreRequestDialog( text = stringResource(id = R.string.delete), style = MaterialTheme.typography.bodyMedium.copy( fontWeight = FontWeight(400), - fontSize = 14.sp, + fontSize = 12.sp, color = Color.White ), + maxLines = 1, + softWrap = false, + overflow = TextOverflow.Visible ) } } diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/PermissionSettingDialog.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/PermissionSettingDialog.kt index ab8a280a6..60fb2d942 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/PermissionSettingDialog.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/PermissionSettingDialog.kt @@ -133,7 +133,7 @@ fun PermissionSettingDialog( @Composable fun PermissionSettingDialogPreview() { PermissionSettingDialog( - message = "BChat needs library access to continue. You can enable access in the Settings page", + message = stringResource(R.string.library_access_required), onDismissRequest = {}, gotoSettings = {} ) diff --git a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/ProfilePicturePopup.kt b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/ProfilePicturePopup.kt index 0f2962cf7..b5363644c 100644 --- a/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/ProfilePicturePopup.kt +++ b/app/src/main/java/io/beldex/bchat/my_account/ui/dialogs/ProfilePicturePopup.kt @@ -156,12 +156,12 @@ fun ProfilePicturePopup( colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.appColors.negativeGreenButton, contentColor = if(profilePictureStatus) MaterialTheme.appColors.negativeGreenButtonText else MaterialTheme.appColors.disabledButtonContent, - disabledContainerColor = MaterialTheme.appColors.optionalTextfieldBackground + disabledContainerColor = MaterialTheme.appColors.optionalTextFieldBackground ), modifier = Modifier .weight(1f).height(50.dp), shape = RoundedCornerShape(12.dp), - border = BorderStroke(width = 0.5.dp, color = if(profilePictureStatus) MaterialTheme.appColors.negativeGreenButtonBorder else MaterialTheme.appColors.optionalTextfieldBackground) + border = BorderStroke(width = 0.5.dp, color = if(profilePictureStatus) MaterialTheme.appColors.negativeGreenButtonBorder else MaterialTheme.appColors.optionalTextFieldBackground) ) { Text( text = stringResource(id = R.string.activity_settings_remove), diff --git a/app/src/main/java/io/beldex/bchat/notifications/DefaultMessageNotifier.java b/app/src/main/java/io/beldex/bchat/notifications/DefaultMessageNotifier.java index 9d422910c..0419bf8fb 100644 --- a/app/src/main/java/io/beldex/bchat/notifications/DefaultMessageNotifier.java +++ b/app/src/main/java/io/beldex/bchat/notifications/DefaultMessageNotifier.java @@ -45,6 +45,7 @@ import com.beldex.libbchat.utilities.Contact; import com.beldex.libbchat.utilities.ServiceUtil; import com.beldex.libbchat.utilities.TextSecurePreferences; +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper; import com.beldex.libbchat.utilities.recipients.Recipient; import com.beldex.libsignal.utilities.Log; import com.beldex.libsignal.utilities.Util; @@ -343,13 +344,16 @@ private void sendSingleThreadNotification(@NonNull Context context, { Log.i(TAG, "sendSingleThreadNotification() signal: " + signal + " bundled: " + bundled); + String language = TextSecurePreferences.getAppSelectedLanguage(context); + Context localizedContext = DynamicLanguageContextWrapper.updateContext(context, language); + if (notificationState.getNotifications().isEmpty()) { if (!bundled) cancelActiveNotifications(context); Log.i(TAG, "Empty notification state. Skipping."); return; } - SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(localizedContext, TextSecurePreferences.getNotificationPrivacy(context)); List notifications = notificationState.getNotifications(); Recipient messageOriginator = notifications.get(0).getRecipient(); int notificationId = (int) (SUMMARY_NOTIFICATION_ID + (bundled ? notifications.get(0).getThreadId() : 0)); @@ -444,7 +448,10 @@ private void sendMultipleThreadNotification(@NonNull Context context, { Log.i(TAG, "sendMultiThreadNotification() signal: " + signal); - MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context)); + String language = TextSecurePreferences.getAppSelectedLanguage(context); + Context localizedContext = DynamicLanguageContextWrapper.updateContext(context, language); + + MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(localizedContext, TextSecurePreferences.getNotificationPrivacy(context)); List notifications = notificationState.getNotifications(); builder.setMessageCount(notificationState.getNotificationCount(), notificationState.getThreadCount()); @@ -615,7 +622,7 @@ private NotificationState constructNotificationState(@NonNull Context context, e.printStackTrace(); } if (record.isOutgoing()) { - direction = context.getString(R.string.send); + direction = context.getString(R.string.conversation_activity__send); } else { direction = context.getString(R.string.message_details_header__received); } @@ -632,14 +639,18 @@ private NotificationState constructNotificationState(@NonNull Context context, } String displayName; - if (names.size() > 2) { - displayName = shortNameAndAddress(names.get(0), addresses.get(0)) + " and " + (names.size() - 1) + " others"; - } else if (names.size() == 2) { - displayName = shortNameAndAddress(names.get(0), addresses.get(0)) + " and " + (names.size() - 1) + " other"; + if (names.size() > 1) { + int othersCount = names.size() - 1; + displayName = context.getResources().getQuantityString( + R.plurals.contact_others, + othersCount, + shortNameAndAddress(names.get(0), addresses.get(0)), + othersCount + ); } else if (names.size() == 1) { displayName = capitalizeFirstLetter(names.get(0)); } else { - displayName = "No Name"; + displayName = context.getString(R.string.no_name); } body = "👤 " + displayName; } diff --git a/app/src/main/java/io/beldex/bchat/notifications/MultipleRecipientNotificationBuilder.java b/app/src/main/java/io/beldex/bchat/notifications/MultipleRecipientNotificationBuilder.java index a4634bb2e..148359746 100644 --- a/app/src/main/java/io/beldex/bchat/notifications/MultipleRecipientNotificationBuilder.java +++ b/app/src/main/java/io/beldex/bchat/notifications/MultipleRecipientNotificationBuilder.java @@ -116,7 +116,7 @@ private String getOpenGroupDisplayName(Recipient recipient) { String bchatID = recipient.getAddress().serialize(); Contact contact = contactDB.getContactWithBchatID(bchatID); if (contact == null) { return bchatID; } - String displayName = contact.displayName(Contact.ContactContext.OPEN_GROUP); + String displayName = contact.displayName(Contact.ContactContext.OPEN_GROUP, context); if (displayName == null) { return bchatID; } return displayName; } diff --git a/app/src/main/java/io/beldex/bchat/notifications/PushReceiver.kt b/app/src/main/java/io/beldex/bchat/notifications/PushReceiver.kt index 35db3cf01..e588ab202 100644 --- a/app/src/main/java/io/beldex/bchat/notifications/PushReceiver.kt +++ b/app/src/main/java/io/beldex/bchat/notifications/PushReceiver.kt @@ -22,6 +22,7 @@ import com.beldex.libbchat.utilities.bencode.BencodeList import com.beldex.libbchat.utilities.bencode.BencodeString import com.beldex.libsignal.utilities.Base64 import com.beldex.libsignal.utilities.Log +import io.beldex.bchat.R import io.beldex.bchat.crypto.IdentityKeyUtil import javax.inject.Inject private const val TAG = "PushHandler" @@ -55,7 +56,7 @@ class PushReceiver @Inject constructor(@ApplicationContext val context: Context) .setSmallIcon(io.beldex.bchat.R.drawable.ic_notification_) .setColor(context.getColor(io.beldex.bchat.R.color.textsecure_primary)) .setContentTitle("BChat") - .setContentText("You've got a new message.") + .setContentText(context.getString(R.string.you_have_got_a_new_message)) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true) NotificationManagerCompat.from(context).notify(11111, builder.build()) diff --git a/app/src/main/java/io/beldex/bchat/notifications/SingleRecipientNotificationBuilder.java b/app/src/main/java/io/beldex/bchat/notifications/SingleRecipientNotificationBuilder.java index d3df86c7f..d954f01bf 100644 --- a/app/src/main/java/io/beldex/bchat/notifications/SingleRecipientNotificationBuilder.java +++ b/app/src/main/java/io/beldex/bchat/notifications/SingleRecipientNotificationBuilder.java @@ -334,7 +334,7 @@ private String getOpenGroupDisplayName(Recipient recipient) { String bchatID = recipient.getAddress().serialize(); Contact contact = contactDB.getContactWithBchatID(bchatID); if (contact == null) { return bchatID; } - String displayName = contact.displayName(Contact.ContactContext.OPEN_GROUP); + String displayName = contact.displayName(Contact.ContactContext.OPEN_GROUP, context); if (displayName == null) { return bchatID; } return displayName; } diff --git a/app/src/main/java/io/beldex/bchat/onboarding/AboutActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/AboutActivity.kt index eebaed927..6f9096937 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/AboutActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/AboutActivity.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.util.Log import io.beldex.bchat.databinding.ActivityAboutBinding import io.beldex.bchat.BaseActionBarActivity +import io.beldex.bchat.R import io.beldex.bchat.util.setUpActionBarBchatLogo class AboutActivity : BaseActionBarActivity() { @@ -13,19 +14,9 @@ class AboutActivity : BaseActionBarActivity() { super.onCreate(savedInstanceState) binding = ActivityAboutBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("About",false) + setUpActionBarBchatLogo(getString(R.string.activity_settings_app_lock_button_title),false) with(binding){ - aboutPageContent.text = loadFileContents(this@AboutActivity) + aboutPageContent.text = this@AboutActivity.getString(R.string.about_content) } - - } - - public fun loadFileContents(context: Context,): String { - val inputStream = context.assets.open("about.txt") - val size = inputStream.available() - val buffer = ByteArray(size) - inputStream.read(buffer) - inputStream.close() - return String(buffer) } } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/onboarding/AppLockActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/AppLockActivity.kt index a7e56523d..df787763c 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/AppLockActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/AppLockActivity.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.os.Bundle import io.beldex.bchat.databinding.ActivityAppLockBinding import io.beldex.bchat.BaseActionBarActivity +import io.beldex.bchat.R import io.beldex.bchat.home.HomeActivity import io.beldex.bchat.util.push import io.beldex.bchat.util.setUpActionBarBchatLogo @@ -15,7 +16,7 @@ class AppLockActivity : BaseActionBarActivity() { super.onCreate(savedInstanceState) binding = ActivityAppLockBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("App Lock",true) + setUpActionBarBchatLogo(getString(R.string.activity_settings_app_lock_button_title),true) with(binding) { laterButton.setOnClickListener(){ later() diff --git a/app/src/main/java/io/beldex/bchat/onboarding/CheckPasswordActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/CheckPasswordActivity.kt index 2dcbce6e2..9b52e1d1e 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/CheckPasswordActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/CheckPasswordActivity.kt @@ -32,7 +32,7 @@ class CheckPasswordActivity : BaseActionBarActivity() { super.onCreate(savedInstanceState) binding = ActivityCheckPasswordBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("Password") + setUpActionBarBchatLogo(resources.getString(R.string.password)) page = intent.extras?.getInt("page") ?: 0 diff --git a/app/src/main/java/io/beldex/bchat/onboarding/CreatePasswordActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/CreatePasswordActivity.kt index 35be4cdfb..6cf187c8f 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/CreatePasswordActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/CreatePasswordActivity.kt @@ -2,15 +2,8 @@ package io.beldex.bchat.onboarding import android.content.Intent import android.os.Bundle -import android.os.Handler -import android.os.Looper -import android.text.Editable -import android.text.TextWatcher -import android.view.MotionEvent -import android.view.View -import android.view.WindowManager +import android.util.Log import android.view.inputmethod.EditorInfo -import android.view.inputmethod.InputConnection import android.widget.Toast import com.beldex.libbchat.utilities.TextSecurePreferences import io.beldex.bchat.BaseActionBarActivity @@ -20,6 +13,22 @@ import io.beldex.bchat.service.KeyCachingService import io.beldex.bchat.util.push import io.beldex.bchat.util.setUpActionBarBchatLogo import javax.inject.Inject +import android.view.inputmethod.InputConnection +import android.annotation.SuppressLint +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.text.InputType +import android.view.View +import android.view.WindowManager +import android.view.MotionEvent +import android.view.View.OnTouchListener +import androidx.core.widget.addTextChangedListener +import android.text.Editable + +import android.text.TextWatcher +import io.beldex.bchat.R +import org.w3c.dom.Text class CreatePasswordActivity : BaseActionBarActivity() { @@ -33,7 +42,7 @@ class CreatePasswordActivity : BaseActionBarActivity() { super.onCreate(savedInstanceState) binding = ActivityCreatePasswordBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("Create Password", true) + setUpActionBarBchatLogo(getString(R.string.create_password), true) callPage = intent?.extras?.getInt("callPage", 0) ?: 0 with(binding) { @@ -47,29 +56,29 @@ class CreatePasswordActivity : BaseActionBarActivity() { enterPinEditTxtLayout!!.isErrorEnabled = false Toast.makeText( this@CreatePasswordActivity, - "Must set your 4 digit PIN.", + getString(R.string.must_set_your_four_digit_pin), Toast.LENGTH_LONG ).show() //enterPinEditTxtLayout!!.error = "Must set your 4 digit PIN." } else if (enteredPIN.length < 4) { enterPinEditTxtLayout!!.isErrorEnabled = true - enterPinEditTxtLayout.error = "Please enter 4 digit PIN." + enterPinEditTxtLayout.error = getString(R.string.please_enter_4_digit_pin) } else if (reEnterPIN.isEmpty()) { enterPinEditTxtLayout!!.isErrorEnabled = false reEnterPinEditTxtLayout!!.isErrorEnabled = false Toast.makeText( this@CreatePasswordActivity, - "Must set your 4 digit PIN.", + getString(R.string.must_set_your_four_digit_pin), Toast.LENGTH_LONG ).show() //reEnterPinEditTxtLayout!!.error = "Must set your 4 digit PIN." } else if (reEnterPIN.length < 4) { enterPinEditTxtLayout!!.isErrorEnabled = false reEnterPinEditTxtLayout!!.isErrorEnabled = true - reEnterPinEditTxtLayout.error = "Please enter 4 digit PIN." + reEnterPinEditTxtLayout.error = getString(R.string.please_enter_4_digit_pin) } else if (enteredPIN != reEnterPIN) { reEnterPinEditTxtLayout!!.isErrorEnabled = true - reEnterPinEditTxtLayout!!.error = "Password is not matched" + reEnterPinEditTxtLayout!!.error = getString(R.string.password_is_not_matched) } else if (enteredPIN == reEnterPIN) { callPage(callPage) } diff --git a/app/src/main/java/io/beldex/bchat/onboarding/DisplayNameActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/DisplayNameActivity.kt index a77dce1ec..6a7714380 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/DisplayNameActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/DisplayNameActivity.kt @@ -31,6 +31,7 @@ import io.beldex.bchat.CheckOnline import io.beldex.bchat.R import io.beldex.bchat.WindowInsetsUtil import io.beldex.bchat.databinding.ActivityDisplayNameBinding +import io.beldex.bchat.util.englishNamePattern import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -39,7 +40,6 @@ import java.io.File import java.util.Collections import java.util.UUID import java.util.concurrent.Executor -import java.util.regex.Pattern class DisplayNameActivity : BaseActionBarActivity() { @@ -52,7 +52,6 @@ class DisplayNameActivity : BaseActionBarActivity() { private val PREF_DAEMON_TESTNET = "daemon_testnet" private val PREF_DAEMON_STAGENET = "daemon_stagenet" private val PREF_DAEMON_MAINNET = "daemon_mainnet" - private val namePattern = Pattern.compile("[A-Za-z0-9\\s]+") private var node: NodeInfo? = null @@ -343,7 +342,7 @@ class DisplayNameActivity : BaseActionBarActivity() { removeWallet() } } - if (!displayName.matches(namePattern.toRegex())) { + if (!displayName.matches(englishNamePattern.toRegex())) { return Toast.makeText( this, R.string.display_name_validation, diff --git a/app/src/main/java/io/beldex/bchat/onboarding/LandingActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/LandingActivity.kt index 9528616c5..a309a7faa 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/LandingActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/LandingActivity.kt @@ -19,17 +19,18 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import com.beldex.libbchat.utilities.TextSecurePreferences +import io.beldex.bchat.BaseAppCompatActivity +import io.beldex.bchat.R import io.beldex.bchat.crypto.IdentityKeyUtil +import io.beldex.bchat.databinding.ActivityLandingBinding import io.beldex.bchat.permissions.Permissions import io.beldex.bchat.service.KeyCachingService import io.beldex.bchat.util.UiModeUtilities import io.beldex.bchat.util.nodelistasync.DownloadNodeListFileAsyncTask import io.beldex.bchat.util.nodelistasync.NodeListConstants import io.beldex.bchat.util.push -import io.beldex.bchat.R -import io.beldex.bchat.databinding.ActivityLandingBinding -class LandingActivity : AppCompatActivity() { +class LandingActivity : BaseAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/io/beldex/bchat/onboarding/LandingScreenActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/LandingScreenActivity.kt index 6c3aff4c9..b4fcb20fe 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/LandingScreenActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/LandingScreenActivity.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.content.res.Configuration import android.os.Build import android.os.Bundle -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -44,6 +43,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.view.WindowInsetsControllerCompat +import io.beldex.bchat.BaseComponentActivity +import io.beldex.bchat.R import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.compose_utils.BChatTypography import io.beldex.bchat.compose_utils.OpenSans @@ -51,12 +52,11 @@ import io.beldex.bchat.compose_utils.PrimaryButton import io.beldex.bchat.compose_utils.appColors import io.beldex.bchat.onboarding.ui.OnBoardingActivity import io.beldex.bchat.onboarding.ui.OnBoardingScreens -import io.beldex.bchat.util.push -import io.beldex.bchat.R import io.beldex.bchat.util.UiMode import io.beldex.bchat.util.UiModeUtilities +import io.beldex.bchat.util.push -class LandingScreenActivity: ComponentActivity() { +class LandingScreenActivity: BaseComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -140,7 +140,7 @@ fun LandingScreen( ) ) { Text( - text = "Welcome to", + text = stringResource(id = R.string.welcome_to), style = BChatTypography.headlineSmall.copy( color = MaterialTheme.appColors.onMainContainerTextColor, fontWeight = FontWeight.ExtraBold diff --git a/app/src/main/java/io/beldex/bchat/onboarding/RecoveryPhraseRestoreActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/RecoveryPhraseRestoreActivity.kt index 30d3f2696..1819b4484 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/RecoveryPhraseRestoreActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/RecoveryPhraseRestoreActivity.kt @@ -50,7 +50,7 @@ class RecoveryPhraseRestoreActivity : BaseActionBarActivity() { restore() } else{ - Toast.makeText(this,"Please enter valid seed",Toast.LENGTH_SHORT).show() + Toast.makeText(this,getString(R.string.please_enter_valid_seed),Toast.LENGTH_SHORT).show() } } @@ -147,14 +147,5 @@ class RecoveryPhraseRestoreActivity : BaseActionBarActivity() { } MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) } - - private fun openURL(url: String) { - try { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - startActivity(intent) - } catch (e: Exception) { - Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show() - } - } // endregion } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/onboarding/RegisterActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/RegisterActivity.kt index f2e7247b2..c022e3145 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/RegisterActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/RegisterActivity.kt @@ -86,7 +86,7 @@ class RegisterActivity : BaseActionBarActivity() { binding.registerButton.setOnClickListener { register() } binding.copyButton.setOnClickListener { copyPublicKey() } val termsExplanation = - SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy") + SpannableStringBuilder(getString(R.string.terms_and_privacy_message)) termsExplanation.setSpan( StyleSpan(Typeface.BOLD), 40, @@ -120,7 +120,7 @@ class RegisterActivity : BaseActionBarActivity() { localPassword = intent.extras?.getString(REQUEST_PASSWORD) displayName =intent.extras?.getString(REQUEST_NAME) val displayedName : String=displayName?.substring(0, 1)?.uppercase(Locale.ROOT) + displayName?.substring(1)?.lowercase(Locale.ROOT) - binding.titleContentTextView.text= "Hey $displayedName, welcome to BChat!" + binding.titleContentTextView.text= resources.getString(R.string.hey_user_welcome_to_bchat).format(displayedName) showDetails() diff --git a/app/src/main/java/io/beldex/bchat/onboarding/SeedActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/SeedActivity.kt index 1fefec97d..40546dcc9 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/SeedActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/SeedActivity.kt @@ -41,7 +41,7 @@ class SeedActivity : BaseActionBarActivity() { binding = ActivitySeedBinding.inflate(layoutInflater) setContentView(binding.root) supportActionBar!!.title = resources.getString(R.string.activity_seed_title) - val seedReminderViewTitle = SpannableString("You're almost finished! 90%") // Intentionally not yet translated + val seedReminderViewTitle = SpannableString(resources.getString(R.string.you_are_almost_finished)) // Intentionally not yet translated seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) with(binding) { seedReminderView.title = seedReminderViewTitle @@ -67,7 +67,7 @@ class SeedActivity : BaseActionBarActivity() { // region Updating private fun revealSeed() { - val seedReminderViewTitle = SpannableString("Account secured! 100%") // Intentionally not yet translated + val seedReminderViewTitle = SpannableString(resources.getString(R.string.account_secured)) // Intentionally not yet translated seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 17, 21, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) with(binding) { seedReminderView.title = seedReminderViewTitle diff --git a/app/src/main/java/io/beldex/bchat/onboarding/SplashScreenActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/SplashScreenActivity.kt index b549f98ca..9331e8b65 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/SplashScreenActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/SplashScreenActivity.kt @@ -2,12 +2,12 @@ package io.beldex.bchat.onboarding import android.content.Intent import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat +import io.beldex.bchat.BaseAppCompatActivity import io.beldex.bchat.onboarding.ui.PinCodeAction import io.beldex.bchat.databinding.ActivitySplashScreenBinding -class SplashScreenActivity : AppCompatActivity() { +class SplashScreenActivity : BaseAppCompatActivity() { private lateinit var binding: ActivitySplashScreenBinding private var nextPage: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/io/beldex/bchat/onboarding/SplashScreenHomeActivtity.kt b/app/src/main/java/io/beldex/bchat/onboarding/SplashScreenHomeActivtity.kt index fde779a30..5cd5d3178 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/SplashScreenHomeActivtity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/SplashScreenHomeActivtity.kt @@ -1,13 +1,13 @@ package io.beldex.bchat.onboarding import android.content.Intent -import androidx.appcompat.app.AppCompatActivity +import io.beldex.bchat.BaseAppCompatActivity import android.os.Bundle import android.os.Handler import android.os.Looper import io.beldex.bchat.databinding.ActivitySplashScreenHomeActivtityBinding -class SplashScreenHomeActivtity : AppCompatActivity() { +class SplashScreenHomeActivtity : BaseAppCompatActivity() { private lateinit var binding: ActivitySplashScreenHomeActivtityBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/io/beldex/bchat/onboarding/ui/OnBoardingActivity.kt b/app/src/main/java/io/beldex/bchat/onboarding/ui/OnBoardingActivity.kt index 45a968bef..e4cbac529 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/ui/OnBoardingActivity.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/ui/OnBoardingActivity.kt @@ -6,7 +6,6 @@ import android.content.res.Configuration import android.os.Build import android.os.Bundle import android.widget.Toast -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.safeDrawing @@ -71,18 +70,19 @@ import com.airbnb.lottie.compose.LottieAnimation import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.animateLottieCompositionAsState import com.airbnb.lottie.compose.rememberLottieComposition +import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseComponentActivity +import io.beldex.bchat.R import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.compose_utils.DialogContainer import io.beldex.bchat.compose_utils.appColors import io.beldex.bchat.util.UiMode import io.beldex.bchat.util.UiModeUtilities -import dagger.hilt.android.AndroidEntryPoint -import io.beldex.bchat.R import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @AndroidEntryPoint -class OnBoardingActivity: ComponentActivity() { +class OnBoardingActivity: BaseComponentActivity() { private var destination = OnBoardingScreens.RestoreSeedScreen.route diff --git a/app/src/main/java/io/beldex/bchat/onboarding/ui/PinCodeScreen.kt b/app/src/main/java/io/beldex/bchat/onboarding/ui/PinCodeScreen.kt index fa2eb54bc..caf357206 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/ui/PinCodeScreen.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/ui/PinCodeScreen.kt @@ -231,7 +231,7 @@ private fun PortraitPinCodeScreen( modifier = Modifier .align(Alignment.CenterHorizontally), shape = RoundedCornerShape(50), - colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.appColors.secondaryButtonColor), + colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.appColors.searchBackground), border = BorderStroke( width = 1.dp, color = if (state.pinLength == 4) MaterialTheme.appColors.tertiaryButtonColor else MaterialTheme.appColors.primaryButtonColor @@ -495,8 +495,7 @@ private fun LandscapePinCodeScreen( Spacer(Modifier.height(12.dp)) - Text( - text = state.stepTitle, + Text(text = state.stepTitle, style = if (isTablet) MaterialTheme.typography.headlineSmall @@ -519,7 +518,7 @@ private fun LandscapePinCodeScreen( modifier = Modifier .align(Alignment.CenterHorizontally), shape = RoundedCornerShape(50), - colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.appColors.secondaryButtonColor), + colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.appColors.searchBackground), border = BorderStroke( width = 1.dp, color = if (state.pinLength == 4) MaterialTheme.appColors.tertiaryButtonColor else MaterialTheme.appColors.primaryButtonColor diff --git a/app/src/main/java/io/beldex/bchat/onboarding/ui/PinCodeViewModel.kt b/app/src/main/java/io/beldex/bchat/onboarding/ui/PinCodeViewModel.kt index b41df1110..9bf866a75 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/ui/PinCodeViewModel.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/ui/PinCodeViewModel.kt @@ -1,12 +1,16 @@ package io.beldex.bchat.onboarding.ui +import android.content.Context +import androidx.annotation.StringRes import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import io.beldex.bchat.util.ResourceProvider -import io.beldex.bchat.util.SharedPreferenceUtil +import com.beldex.libbchat.utilities.TextSecurePreferences +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import io.beldex.bchat.R +import io.beldex.bchat.util.SharedPreferenceUtil import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -14,15 +18,27 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import java.util.Locale import javax.inject.Inject @HiltViewModel class PinCodeViewModel @Inject constructor( - private val resourceProvider: ResourceProvider, + @ApplicationContext private val context: Context, private val sharedPreferenceUtil: SharedPreferenceUtil, savedStateHandle: SavedStateHandle ): ViewModel() { + private val localizedContext: Context by lazy { + val language = TextSecurePreferences.getAppSelectedLanguage(context) + ?: Locale.getDefault().language + DynamicLanguageContextWrapper.updateContext(context, language) + } + + private fun stringRes(@StringRes res: Int): String = localizedContext.getString(res) + + private fun stringRes(@StringRes res: Int, vararg formatArgs: Any): String = + localizedContext.getString(res, *formatArgs) + private val _state = MutableStateFlow(PinCodeState()) val state = _state.asStateFlow() @@ -60,42 +76,42 @@ class PinCodeViewModel @Inject constructor( it.copy( pinLength = savedPinLength, step = PinCodeSteps.EnterPin, - stepTitle = resourceProvider.getString(R.string.enter_your_pin) + stepTitle = stringRes(R.string.enter_your_pin) ) } PinCodeAction.VerifyPinCode.action -> { it.copy( pinLength = savedPinLength, step = PinCodeSteps.VerifyPin, - stepTitle = resourceProvider.getString(R.string.enter_your_4_digit_bchat_pin).format(savedPinLength) + stepTitle = stringRes(R.string.enter_your_4_digit_bchat_pin, savedPinLength) ) } PinCodeAction.ChangePinCode.action -> { it.copy( pinLength = savedPinLength, step = PinCodeSteps.OldPin, - stepTitle = resourceProvider.getString(R.string.enter_old_pin) + stepTitle = stringRes(R.string.enter_old_pin) ) } PinCodeAction.VerifyWalletPin.action -> { it.copy( pinLength = savedPinLength, step = PinCodeSteps.VerifyPin, - stepTitle = resourceProvider.getString(R.string.enter_your_4_digit_wallet_pin).format(savedPinLength) + stepTitle = stringRes(R.string.enter_your_4_digit_wallet_pin, savedPinLength) ) } PinCodeAction.CreateWalletPin.action -> { it.copy( pinLength = savedPinLength, step = PinCodeSteps.EnterPin, - stepTitle = resourceProvider.getString(R.string.enter_your_pin) + stepTitle = stringRes(R.string.enter_your_pin) ) } PinCodeAction.ChangeWalletPin.action -> { it.copy( pinLength = savedPinLength, step = PinCodeSteps.OldPin, - stepTitle = resourceProvider.getString(R.string.enter_old_pin) + stepTitle = stringRes(R.string.enter_old_pin) ) } else -> it @@ -145,7 +161,7 @@ class PinCodeViewModel @Inject constructor( pin = "" ) } - _errorMessage.emit(resourceProvider.getString(R.string.invalid_password)) + _errorMessage.emit(stringRes(R.string.invalid_password)) } } } @@ -164,13 +180,13 @@ class PinCodeViewModel @Inject constructor( ) } viewModelScope.launch { - _errorMessage.emit(resourceProvider.getString(R.string.incorrect_password_entered)) + _errorMessage.emit(stringRes(R.string.incorrect_password_entered)) } } else { _state.update { it.copy( step = PinCodeSteps.EnterPin, - stepTitle = resourceProvider.getString(R.string.enter_new_pin) + stepTitle = stringRes(R.string.enter_new_pin) ) } } @@ -184,13 +200,13 @@ class PinCodeViewModel @Inject constructor( ) } viewModelScope.launch { - _errorMessage.emit(resourceProvider.getString(R.string.old_new_password_same)) + _errorMessage.emit(stringRes(R.string.old_new_password_same)) } } else { _state.update { it.copy( step = PinCodeSteps.ReEnterPin, - stepTitle = resourceProvider.getString(R.string.re_enter_your_pin) + stepTitle = stringRes(R.string.re_enter_your_pin) ) } } @@ -203,7 +219,7 @@ class PinCodeViewModel @Inject constructor( ) } viewModelScope.launch { - _errorMessage.emit(resourceProvider.getString(R.string.password_does_not_match)) + _errorMessage.emit(stringRes(R.string.password_does_not_match)) } } else { sharedPreferenceUtil.setPassword(newPin) @@ -211,9 +227,9 @@ class PinCodeViewModel @Inject constructor( sharedPreferenceUtil.setPinLength(state.value.pinLength) viewModelScope.launch { val message = if (action == PinCodeAction.CreatePinCode.action) { - resourceProvider.getString(R.string.pincode_created) + stringRes(R.string.pincode_created) } else { - resourceProvider.getString(R.string.pincode_changed) + stringRes(R.string.pincode_changed) } _successContent.emit(message) @@ -288,7 +304,7 @@ class PinCodeViewModel @Inject constructor( _state.update { it.copy( step = PinCodeSteps.EnterPin, - stepTitle = resourceProvider.getString(R.string.enter_new_pin) + stepTitle = stringRes(R.string.enter_new_pin) ) } } @@ -301,13 +317,13 @@ class PinCodeViewModel @Inject constructor( ) } viewModelScope.launch { - _errorMessage.emit(resourceProvider.getString(R.string.old_new_pin_same)) + _errorMessage.emit(stringRes(R.string.old_new_pin_same)) } } else { _state.update { it.copy( step = PinCodeSteps.ReEnterPin, - stepTitle = resourceProvider.getString(R.string.re_enter_your_pin) + stepTitle = stringRes(R.string.re_enter_your_pin) ) } } diff --git a/app/src/main/java/io/beldex/bchat/onboarding/ui/RestoreSeedScreen.kt b/app/src/main/java/io/beldex/bchat/onboarding/ui/RestoreSeedScreen.kt index 0a232e7ea..93697c6ec 100644 --- a/app/src/main/java/io/beldex/bchat/onboarding/ui/RestoreSeedScreen.kt +++ b/app/src/main/java/io/beldex/bchat/onboarding/ui/RestoreSeedScreen.kt @@ -38,6 +38,7 @@ import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.foundation.layout.imePadding import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -84,7 +85,7 @@ fun RestoreSeedScreen( onValueChange = { seed = it }, - hintText = "Enter your seed", + hintText = stringResource(R.string.mnemonic_edit_text_hint), maxLines = 6, textStyle = MaterialTheme.typography.titleMedium.copy( color = MaterialTheme.appColors.editTextColor, @@ -111,7 +112,7 @@ fun RestoreSeedScreen( .align(Alignment.End) ) { Text( - text = "Paste Seed", + text = stringResource(R.string.paste_seed), style = MaterialTheme.typography.titleSmall.copy( color = Color.White ), @@ -137,7 +138,7 @@ fun RestoreSeedScreen( .fillMaxWidth() ) { Text( - text = "Continue", + text = stringResource(R.string.continue_2), style = MaterialTheme.typography.titleMedium.copy( color = Color.White ), diff --git a/app/src/main/java/io/beldex/bchat/preferences/AppProtectionPreferenceFragment.kt b/app/src/main/java/io/beldex/bchat/preferences/AppProtectionPreferenceFragment.kt index 91487fa53..438604a92 100644 --- a/app/src/main/java/io/beldex/bchat/preferences/AppProtectionPreferenceFragment.kt +++ b/app/src/main/java/io/beldex/bchat/preferences/AppProtectionPreferenceFragment.kt @@ -11,6 +11,7 @@ import com.beldex.libbchat.utilities.TextSecurePreferences import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.getScreenLockTimeout import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.isPasswordDisabled import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.setScreenLockEnabled +import com.beldex.libbchat.utilities.TextSecurePreferences.Companion.setOnionRoutingEnabled import io.beldex.bchat.ApplicationContext import io.beldex.bchat.BuildConfig import io.beldex.bchat.R @@ -38,6 +39,9 @@ class AppProtectionPreferenceFragment : ListSummaryPreferenceFragment() { findPreference(TextSecurePreferences.LINK_PREVIEWS)!!.onPreferenceChangeListener = LinkPreviewToggleListener() + findPreference(TextSecurePreferences.USE_ONION_ROUTING)!!.onPreferenceChangeListener = + OnionRoutingToggleListener() + //New Line callToggleListener = CallToggleListener(this) { setCall(it) } findPreference(TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED)!!.onPreferenceChangeListener = @@ -57,9 +61,9 @@ class AppProtectionPreferenceFragment : ListSummaryPreferenceFragment() { if (isEnabled && !areNotificationsEnabled(requireActivity())) { // show a dialog saying that calls won't work properly if you don't have notifications on at a system level AlertDialog.Builder(requireActivity()) - .setTitle(R.string.CallNotificationBuilder_system_notification_title) + .setTitle(R.string.activity_settings_notifications_button_title) .setMessage(R.string.CallNotificationBuilder_system_notification_message) - .setPositiveButton(R.string.activity_notification_settings_title) { d, w -> + .setPositiveButton(R.string.activity_settings_notifications_button_title) { d, w -> val settingsIntent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) @@ -203,4 +207,27 @@ class AppProtectionPreferenceFragment : ListSummaryPreferenceFragment() { return true } } + + private inner class OnionRoutingToggleListener : Preference.OnPreferenceChangeListener { + override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + val turningOff = !(newValue as Boolean) + if (!turningOff) { + return true + } + + OnionRoutingConfirmDialogFragment( + onConfirm = { + setOnionRoutingEnabled(requireContext(), false) + @Suppress("UNCHECKED_CAST") + (preference as? SwitchPreferenceCompat)?.isChecked = false + }, + onCancel = { + @Suppress("UNCHECKED_CAST") + (preference as? SwitchPreferenceCompat)?.isChecked = true + } + ).show(childFragmentManager, "OnionRoutingConfirm") + + return false + } + } } diff --git a/app/src/main/java/io/beldex/bchat/preferences/ChatsPreferenceFragment.java b/app/src/main/java/io/beldex/bchat/preferences/ChatsPreferenceFragment.java index 585b20a74..66e9b0bd8 100644 --- a/app/src/main/java/io/beldex/bchat/preferences/ChatsPreferenceFragment.java +++ b/app/src/main/java/io/beldex/bchat/preferences/ChatsPreferenceFragment.java @@ -82,7 +82,7 @@ public boolean onPreferenceClick(Preference preference) { builder.setTitle(R.string.ApplicationPreferencesActivity_delete_all_old_messages_now); builder.setMessage(getResources().getQuantityString(R.plurals.ApplicationPreferencesActivity_this_will_immediately_trim_all_conversations_to_the_d_most_recent_messages, threadLengthLimit, threadLengthLimit)); - builder.setPositiveButton(R.string.ApplicationPreferencesActivity_delete, + builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { diff --git a/app/src/main/java/io/beldex/bchat/preferences/ClearAllDataDialog.kt b/app/src/main/java/io/beldex/bchat/preferences/ClearAllDataDialog.kt index b9f90847b..60216411d 100644 --- a/app/src/main/java/io/beldex/bchat/preferences/ClearAllDataDialog.kt +++ b/app/src/main/java/io/beldex/bchat/preferences/ClearAllDataDialog.kt @@ -46,12 +46,12 @@ class ClearAllDataDialog : BaseDialog() { binding = DialogClearAllDataBinding.inflate(LayoutInflater.from(requireContext())) val device = RadioOption( "deviceOnly", - requireContext().getString(R.string.dialog_clear_all_data_clear_device_only_title), + requireContext().getString(R.string.activity_settings_clear_all_data_button_title), requireContext().getString(R.string.dialog_clear_all_data_clear_device_only_subtitle) ) val network = RadioOption( "deviceAndNetwork", - requireContext().getString(R.string.dialog_clear_all_data_clear_device_and_network_title), + requireContext().getString(R.string.delete_entire_account), requireContext().getString(R.string.dialog_clear_all_data_clear_device_and_network_subtitle) ) var selectedOption = device @@ -104,7 +104,7 @@ class ClearAllDataDialog : BaseDialog() { Steps.NETWORK_PROMPT -> { binding.recyclerView.visibility = View.GONE binding.dialogDescriptionText.visibility = View.VISIBLE - binding.clearAllDataTitle.setText(R.string.dialog_clear_all_data_clear_device_and_network_title) + binding.clearAllDataTitle.setText(R.string.delete_entire_account) binding.dialogDescriptionText.setText(R.string.dialog_clear_all_data_network_explanation) } diff --git a/app/src/main/java/io/beldex/bchat/preferences/NotificationSettingsActivity.kt b/app/src/main/java/io/beldex/bchat/preferences/NotificationSettingsActivity.kt index b3ede884e..af97b48dc 100644 --- a/app/src/main/java/io/beldex/bchat/preferences/NotificationSettingsActivity.kt +++ b/app/src/main/java/io/beldex/bchat/preferences/NotificationSettingsActivity.kt @@ -19,7 +19,7 @@ class NotificationSettingsActivity : PassphraseRequiredActionBarActivity() { setContentView(binding.root) with(binding) { back.setOnClickListener { finish() } - title.text = resources.getString(R.string.activity_notification_settings_title) + title.text = resources.getString(R.string.activity_settings_notifications_button_title) } WindowInsetsUtil.applyTopInset(binding.root) val fragment = NotificationsPreferenceFragment() diff --git a/app/src/main/java/io/beldex/bchat/preferences/OnionRoutingConfirmDialogFragment.kt b/app/src/main/java/io/beldex/bchat/preferences/OnionRoutingConfirmDialogFragment.kt new file mode 100644 index 000000000..02237a715 --- /dev/null +++ b/app/src/main/java/io/beldex/bchat/preferences/OnionRoutingConfirmDialogFragment.kt @@ -0,0 +1,47 @@ +package io.beldex.bchat.preferences + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView +import androidx.fragment.app.DialogFragment +import io.beldex.bchat.R +import io.beldex.bchat.compose_utils.BChatTheme +import io.beldex.bchat.conversation.v2.dialogs.UnblockUserDialog +import io.beldex.bchat.util.UiMode +import io.beldex.bchat.util.UiModeUtilities + +class OnionRoutingConfirmDialogFragment( + private val onConfirm: () -> Unit, + private val onCancel: () -> Unit +) : DialogFragment() { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return ComposeView(requireContext()).apply { + setContent { + BChatTheme( + darkTheme = UiModeUtilities.getUserSelectedUiMode(requireContext()) == UiMode.NIGHT + ) { + UnblockUserDialog( + title = getString(R.string.preferences__onion_routing), + message = getString(R.string.onion_routing_turn_off_confirmation), + positiveButtonTitle = getString(R.string.ok), + onAccept = { + dismiss() + onConfirm() + }, + onCancel = { + dismiss() + onCancel() + } + ) + } + } + } + } +} diff --git a/app/src/main/java/io/beldex/bchat/preferences/SettingsActivity.kt b/app/src/main/java/io/beldex/bchat/preferences/SettingsActivity.kt index 5a9463981..929bb4900 100644 --- a/app/src/main/java/io/beldex/bchat/preferences/SettingsActivity.kt +++ b/app/src/main/java/io/beldex/bchat/preferences/SettingsActivity.kt @@ -53,7 +53,6 @@ import io.beldex.bchat.profiles.ProfileMediaConstraints import io.beldex.bchat.showCustomDialog import io.beldex.bchat.CheckOnline import kotlinx.coroutines.Dispatchers -import java.util.regex.Pattern import com.canhub.cropper.CropImage import com.canhub.cropper.CropImageContract import com.beldex.libsignal.utilities.Log @@ -121,7 +120,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity(), Animation.Animat private lateinit var animation1: Animation private lateinit var animation2: Animation private var isFrontOfCardShowing = true - private val namePattern = Pattern.compile("[A-Za-z0-9\\s]+") private var shareButtonLastClickTime: Long = 0 private fun getDisplayName(): String = @@ -132,7 +130,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity(), Animation.Animat super.onCreate(savedInstanceState, isReady) binding = ActivitySettingsBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("My Account") + setUpActionBarBchatLogo(this.getString(R.string.my_account)) val displayName = getDisplayName() glide = Glide.with(this) @@ -397,7 +395,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity(), Animation.Animat if (displayName.isEmpty()) { Toast.makeText( this, - R.string.activity_settings_display_name_missing_error, + R.string.activity_display_name_display_name_missing_error, Toast.LENGTH_SHORT ).show() return false @@ -405,12 +403,12 @@ class SettingsActivity : PassphraseRequiredActionBarActivity(), Animation.Animat if (displayName.toByteArray().size > ProfileManagerProtocol.Companion.NAME_PADDED_LENGTH) { Toast.makeText( this, - R.string.activity_settings_display_name_too_long_error, + R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT ).show() return false } - if (!displayName.matches(namePattern.toRegex())) { + if (!displayName.matches(unicodeNamePattern.toRegex())) { Toast.makeText( this, R.string.display_name_validation, @@ -537,7 +535,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity(), Animation.Animat val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) startActivity(intent) } catch (e: Exception) { - Toast.makeText(this, "Can't open URL", Toast.LENGTH_LONG).show() + Toast.makeText(this, getString(R.string.cannot_open_url), Toast.LENGTH_LONG).show() } } @@ -570,7 +568,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity(), Animation.Animat val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) startActivity(intent) } catch (e: Exception) { - Toast.makeText(this, "Can't open URL", Toast.LENGTH_LONG).show() + Toast.makeText(this, getString(R.string.cannot_open_url), Toast.LENGTH_LONG).show() } } diff --git a/app/src/main/java/io/beldex/bchat/preferences/ShareLogsDialog.kt b/app/src/main/java/io/beldex/bchat/preferences/ShareLogsDialog.kt index 243f05705..35f583afb 100644 --- a/app/src/main/java/io/beldex/bchat/preferences/ShareLogsDialog.kt +++ b/app/src/main/java/io/beldex/bchat/preferences/ShareLogsDialog.kt @@ -102,7 +102,7 @@ class ShareLogsDialog : BaseDialog() { } dismiss() } catch (e: Exception) { - Toast.makeText(context,"Error saving logs", Toast.LENGTH_LONG).show() + Toast.makeText(context,getString(R.string.error_saving_logs), Toast.LENGTH_LONG).show() dismiss() } } diff --git a/app/src/main/java/io/beldex/bchat/reactions/ReactionRecipientsAdapter.java b/app/src/main/java/io/beldex/bchat/reactions/ReactionRecipientsAdapter.java index 0ff3c1ad6..1ecca3d1d 100644 --- a/app/src/main/java/io/beldex/bchat/reactions/ReactionRecipientsAdapter.java +++ b/app/src/main/java/io/beldex/bchat/reactions/ReactionRecipientsAdapter.java @@ -157,7 +157,7 @@ void bind(EmojiCount emojiData, @NonNull ReactionDetails reaction) { if (reaction.getSender().isLocalNumber()) { - this.recipient.setText(R.string.you); + this.recipient.setText(R.string.MediaPreviewActivity_you); } else { String name = reaction.getSender().getName(); if (name == null){ diff --git a/app/src/main/java/io/beldex/bchat/reactions/ReactionsViewAdapter.java b/app/src/main/java/io/beldex/bchat/reactions/ReactionsViewAdapter.java index ebc9c345f..25dcb792e 100644 --- a/app/src/main/java/io/beldex/bchat/reactions/ReactionsViewAdapter.java +++ b/app/src/main/java/io/beldex/bchat/reactions/ReactionsViewAdapter.java @@ -83,7 +83,7 @@ public void bind(ReactionsViewModel reactionsViewModel, ReactionDetails reaction this.avatar.update(reaction.getSender(), false, false); if (reaction.getSender().isLocalNumber()) { - recipientName.setText(R.string.you); + recipientName.setText(R.string.MediaPreviewActivity_you); this.tapToRemove.setVisibility(View.VISIBLE); this.tapToRemove.setText(R.string.tap_to_remove); } else { diff --git a/app/src/main/java/io/beldex/bchat/reactions/any/ReactWithAnyEmojiRepository.java b/app/src/main/java/io/beldex/bchat/reactions/any/ReactWithAnyEmojiRepository.java index d93e8a844..4790d5950 100644 --- a/app/src/main/java/io/beldex/bchat/reactions/any/ReactWithAnyEmojiRepository.java +++ b/app/src/main/java/io/beldex/bchat/reactions/any/ReactWithAnyEmojiRepository.java @@ -38,7 +38,7 @@ final class ReactWithAnyEmojiRepository { List getEmojiPageModels() { List pages = new LinkedList<>(); - pages.add(new ReactWithAnyEmojiPage(Collections.singletonList(new ReactWithAnyEmojiPageBlock(R.string.emojiCategoryRecentlyUsed, recentEmojiPageModel)))); + pages.add(new ReactWithAnyEmojiPage(Collections.singletonList(new ReactWithAnyEmojiPageBlock(R.string.ReactWithAnyEmojiBottomSheetDialogFragment__recently_used, recentEmojiPageModel)))); pages.addAll(emojiPages); return pages; diff --git a/app/src/main/java/io/beldex/bchat/repository/ConversationRepository.kt b/app/src/main/java/io/beldex/bchat/repository/ConversationRepository.kt index 0591e99d0..71cfb6add 100644 --- a/app/src/main/java/io/beldex/bchat/repository/ConversationRepository.kt +++ b/app/src/main/java/io/beldex/bchat/repository/ConversationRepository.kt @@ -23,6 +23,7 @@ import io.beldex.bchat.database.* import io.beldex.bchat.database.model.MessageRecord import io.beldex.bchat.database.model.ThreadRecord import kotlinx.coroutines.flow.Flow +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import app.cash.copper.Query import app.cash.copper.flow.observeQuery import javax.inject.Inject @@ -100,11 +101,14 @@ class DefaultConversationRepository @Inject constructor( ) : ConversationRepository { override fun isBeldexHostedOpenGroup(threadId: Long): Boolean { - val openGroup = beldexThreadDb.getOpenGroupChat(threadId) + val openGroup = beldexThreadDb.getOpenGroupChat(threadId) ?: return false Log.d("Beldex","open group $openGroup") /*return openGroup?.room == "bchat" || openGroup?.room == "beldex" || openGroup?.room == "crypto" || openGroup?.room == "masternode" || openGroup?.room == "belnet"*/ - return openGroup?.publicKey == OpenGroupAPIV2.defaultServerPublicKey + val serverHost = openGroup.server.toHttpUrlOrNull()?.host + val defaultServerHost = OpenGroupAPIV2.defaultServer.toHttpUrlOrNull()?.host + return serverHost != null && serverHost == defaultServerHost + || openGroup.publicKey == OpenGroupAPIV2.defaultServerPublicKey } override fun getRecipientForThreadId(threadId: Long): Recipient? { diff --git a/app/src/main/java/io/beldex/bchat/seed/RecoveryGetSeedDetailsActivity.kt b/app/src/main/java/io/beldex/bchat/seed/RecoveryGetSeedDetailsActivity.kt index 02474249f..a3b10fa5d 100644 --- a/app/src/main/java/io/beldex/bchat/seed/RecoveryGetSeedDetailsActivity.kt +++ b/app/src/main/java/io/beldex/bchat/seed/RecoveryGetSeedDetailsActivity.kt @@ -38,6 +38,7 @@ import io.beldex.bchat.CheckOnline import io.beldex.bchat.R import io.beldex.bchat.WindowInsetsUtil import io.beldex.bchat.databinding.ActivityRecoveryGetSeedDetailsBinding +import io.beldex.bchat.util.englishNamePattern import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -48,7 +49,6 @@ import java.util.Collections import java.util.Locale import java.util.UUID import java.util.concurrent.Executor -import java.util.regex.Pattern class RecoveryGetSeedDetailsActivity : BaseActionBarActivity() { private lateinit var binding:ActivityRecoveryGetSeedDetailsBinding @@ -68,7 +68,6 @@ class RecoveryGetSeedDetailsActivity : BaseActionBarActivity() { private var restoreFromDateHeight = 0 private val dateFormat = SimpleDateFormat("yyyy-MM", Locale.US) - private val namePattern = Pattern.compile("[A-Za-z0-9\\s]+") private val myFormat = "yyyy-MM-dd" // mention the format you need val sdf = SimpleDateFormat(myFormat, Locale.US) @@ -154,7 +153,7 @@ class RecoveryGetSeedDetailsActivity : BaseActionBarActivity() { return Toast.makeText(this, R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT).show() } - if (!displayName.matches(namePattern.toRegex())) { + if (!displayName.matches(englishNamePattern.toRegex())) { return Toast.makeText( this, R.string.display_name_validation, diff --git a/app/src/main/java/io/beldex/bchat/seed/ShowSeedActivity.kt b/app/src/main/java/io/beldex/bchat/seed/ShowSeedActivity.kt index cd9134197..d864b73b3 100644 --- a/app/src/main/java/io/beldex/bchat/seed/ShowSeedActivity.kt +++ b/app/src/main/java/io/beldex/bchat/seed/ShowSeedActivity.kt @@ -21,7 +21,7 @@ class ShowSeedActivity : BaseActionBarActivity() { super.onCreate(savedInstanceState) binding = ActivityShowSeedBinding.inflate(layoutInflater) setContentView(binding.root) - setUpActionBarBchatLogo("Seed") + setUpActionBarBchatLogo(getString(R.string.seed)) with(binding) { showSeedCopyButton.setOnClickListener { copySeed() diff --git a/app/src/main/java/io/beldex/bchat/service/KeyCachingService.java b/app/src/main/java/io/beldex/bchat/service/KeyCachingService.java index 94b5e1b01..8b85f2780 100644 --- a/app/src/main/java/io/beldex/bchat/service/KeyCachingService.java +++ b/app/src/main/java/io/beldex/bchat/service/KeyCachingService.java @@ -43,6 +43,8 @@ import io.beldex.bchat.DatabaseUpgradeActivity; import io.beldex.bchat.DummyActivity; import io.beldex.bchat.home.HomeActivity; + +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper; import com.beldex.libsignal.utilities.Log; import io.beldex.bchat.notifications.NotificationChannels; import com.beldex.libbchat.utilities.ServiceUtil; @@ -70,6 +72,7 @@ public class KeyCachingService extends Service { public static final String LOCK_TOGGLED_EVENT = "io.beldex.bchat.service.action.LOCK_ENABLED_EVENT"; private static final String PASSPHRASE_EXPIRED_EVENT = "io.beldex.bchat.service.action.PASSPHRASE_EXPIRED_EVENT"; public static final String CLEAR_KEY_ACTION = "io.beldex.bchat.service.action.CLEAR_KEY"; + public static final String LANGUAGE_CHANGED_ACTION = "io.beldex.bchat.service.action.LANGUAGE_CHANGED"; private final IBinder binder = new KeySetBinder(); @@ -154,12 +157,19 @@ public int onStartCommand(Intent intent, int flags, int startId) { case CLEAR_KEY_ACTION: handleClearKey(); break; case PASSPHRASE_EXPIRED_EVENT: handleClearKey(); break; case LOCK_TOGGLED_EVENT: handleLockToggled(); break; + case LANGUAGE_CHANGED_ACTION: foregroundService(); break; } } return START_NOT_STICKY; } + @Override + protected void attachBaseContext(Context newBase) { + String language = TextSecurePreferences.getAppSelectedLanguage(newBase); + super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, language)); + } + @Override public void onCreate() { Log.i(TAG, "onCreate()"); @@ -253,15 +263,18 @@ private void foregroundService() { } Log.i(TAG, "foregrounding KCS"); - NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NotificationChannels.LOCKED_STATUS); + String language = TextSecurePreferences.getAppSelectedLanguage(this); + Context localizedContext = DynamicLanguageContextWrapper.updateContext(this, language); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(localizedContext, NotificationChannels.LOCKED_STATUS); - builder.setContentTitle(getString(R.string.KeyCachingService_passphrase_cached)); - builder.setContentText(getString(R.string.KeyCachingService_signal_passphrase_cached)); + builder.setContentTitle(localizedContext.getString(R.string.KeyCachingService_passphrase_cached)); + builder.setContentText(localizedContext.getString(R.string.KeyCachingService_signal_passphrase_cached)); builder.setSmallIcon(R.drawable.icon_cached); builder.setWhen(0); builder.setPriority(Notification.PRIORITY_MIN); - builder.addAction(R.drawable.ic_menu_lock_dark, getString(R.string.KeyCachingService_lock), buildLockIntent()); + builder.addAction(R.drawable.ic_menu_lock_dark, localizedContext.getString(R.string.KeyCachingService_lock), buildLockIntent()); builder.setContentIntent(buildLaunchIntent()); stopForeground(true); diff --git a/app/src/main/java/io/beldex/bchat/service/WebRtcCallService.kt b/app/src/main/java/io/beldex/bchat/service/WebRtcCallService.kt index b34b9f2ad..d82dd1aa5 100644 --- a/app/src/main/java/io/beldex/bchat/service/WebRtcCallService.kt +++ b/app/src/main/java/io/beldex/bchat/service/WebRtcCallService.kt @@ -56,6 +56,7 @@ import io.beldex.bchat.webrtc.data.State as CallState import android.content.pm.ServiceInfo import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper import io.beldex.bchat.ApplicationContext import io.beldex.bchat.service.WebRtcCallService.Companion.ACTION_ICE_CONNECTED @@ -89,6 +90,7 @@ class WebRtcCallService: LifecycleService(), CallManager.WebRtcListener { const val ACTION_ICE_MESSAGE = "ICE_MESSAGE" const val ACTION_REMOTE_HANGUP = "REMOTE_HANGUP" const val ACTION_ICE_CONNECTED = "ICE_CONNECTED" + const val ACTION_LANGUAGE_CHANGED = "LANGUAGE_CHANGED" const val EXTRA_RECIPIENT_ADDRESS = "RECIPIENT_ID" const val EXTRA_ENABLED = "ENABLED" @@ -199,6 +201,8 @@ class WebRtcCallService: LifecycleService(), CallManager.WebRtcListener { private var isNetworkAvailable = true private var scheduledTimeout: ScheduledFuture<*>? = null private var scheduledReconnect: ScheduledFuture<*>? = null + private var currentNotificationType: Int = -1 + private var currentNotificationRecipient: Recipient? = null private val lockManager by lazy { LockManager(this) } private val serviceExecutor = Executors.newSingleThreadExecutor() @@ -239,6 +243,8 @@ class WebRtcCallService: LifecycleService(), CallManager.WebRtcListener { wantsToAnswer = false currentTimeouts = 0 isNetworkAvailable = true + currentNotificationType = -1 + currentNotificationRecipient = null scheduledTimeout?.cancel(false) scheduledReconnect?.cancel(false) scheduledTimeout = null @@ -306,11 +312,17 @@ class WebRtcCallService: LifecycleService(), CallManager.WebRtcListener { ACTION_CHECK_RECONNECT -> handleCheckReconnect(intent) ACTION_IS_IN_CALL_QUERY -> handleIsInCallQuery(intent) ACTION_UPDATE_AUDIO -> handleUpdateAudio(intent) + ACTION_LANGUAGE_CHANGED -> handleLanguageChanged() } } return START_NOT_STICKY } + override fun attachBaseContext(newBase: Context) { + val language = TextSecurePreferences.getAppSelectedLanguage(newBase) + super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, language)) + } + override fun onCreate() { super.onCreate() callManager.registerListener(this) @@ -714,6 +726,8 @@ class WebRtcCallService: LifecycleService(), CallManager.WebRtcListener { // Over the course of setting up a phone call this method is called multiple times with `types` // of PRE_OFFER -> RING_INCOMING -> ICE_MESSAGE private fun setCallInProgressNotification(type: Int, recipient: Recipient?) { + currentNotificationType = type + currentNotificationRecipient = recipient // Wake the device if needed (applicationContext as ApplicationContext).wakeUpDeviceAndDismissKeyguardIfRequired() // If notifications are enabled we'll try and start a foreground service to show the notification @@ -744,6 +758,12 @@ class WebRtcCallService: LifecycleService(), CallManager.WebRtcListener { } } + private fun handleLanguageChanged() { + if (currentNotificationType != -1 && currentNotificationRecipient != null) { + setCallInProgressNotification(currentNotificationType, currentNotificationRecipient) + } + } + private fun getOptionalRemoteRecipient(intent: Intent): Recipient? = intent.takeIf { it.hasExtra(EXTRA_RECIPIENT_ADDRESS) }?.let(::getRemoteRecipient) diff --git a/app/src/main/java/io/beldex/bchat/textformatter/AppTextFormatter.kt b/app/src/main/java/io/beldex/bchat/textformatter/AppTextFormatter.kt index 0701ac411..39ec25bce 100644 --- a/app/src/main/java/io/beldex/bchat/textformatter/AppTextFormatter.kt +++ b/app/src/main/java/io/beldex/bchat/textformatter/AppTextFormatter.kt @@ -18,10 +18,10 @@ class AppTextFormatter(private val text: String, val context: Context, imageInpu private val pattern = Regex( "(\\*\\*\\*([^*\\r\\n]+?)\\*\\*\\*)|" + // ***text*** "(\\*\\*([^*\\r\\n]+?)\\*\\*)|" + // **text** - "(? 0 ? viewPartMessage.getLayout().getLineWidth(viewPartMessageLineCount - 1) : 0; - widthSize = getPaddingLeft() + getPaddingRight(); + widthSize = getPaddingStart() + getPaddingEnd(); heightSize = getPaddingTop() + getPaddingBottom(); - if (viewPartMessageLineCount > 1 && !(viewPartMessageLastLineWidth + viewPartTimeWidth >= viewPartMessage.getMeasuredWidth())) { - widthSize += viewPartMessageWidth; - heightSize += viewPartMessageHeight; - } else if (viewPartMessageLineCount > 1 && (viewPartMessageLastLineWidth + viewPartTimeWidth >= availableWidth)) { + if (viewPartMessageLineCount > 1 && (viewPartMessageLastLineWidth + viewPartTimeWidth >= viewPartMessage.getMeasuredWidth())) { widthSize += viewPartMessageWidth; heightSize += viewPartMessageHeight + viewPartTimeHeight; } else if (viewPartMessageLineCount == 1 && (viewPartMessageWidth + viewPartTimeWidth >= availableWidth)) { widthSize += viewPartMessage.getMeasuredWidth(); heightSize += viewPartMessageHeight + viewPartTimeHeight; + } else if (viewPartMessageLineCount > 1) { + widthSize += viewPartMessageWidth; + heightSize += viewPartMessageHeight; } else { widthSize += viewPartMessageWidth + viewPartTimeWidth; heightSize += viewPartMessageHeight; @@ -98,15 +98,15 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } viewPartMessage.layout( - getPaddingLeft(), + getPaddingStart(), getPaddingTop(), - viewPartMessage.getWidth() + getPaddingLeft(), + viewPartMessage.getWidth() + getPaddingStart(), viewPartMessage.getHeight() + getPaddingTop()); viewPartTime.layout( - right - left - viewPartTimeWidth - getPaddingRight(), + right - left - viewPartTimeWidth - getPaddingEnd(), bottom - top - getPaddingBottom() - viewPartTimeHeight, - right - left - getPaddingRight(), + right - left - getPaddingEnd(), bottom - top - getPaddingBottom()); } } diff --git a/app/src/main/java/io/beldex/bchat/util/CallNotificationBuilder.kt b/app/src/main/java/io/beldex/bchat/util/CallNotificationBuilder.kt index eb5567294..fdee97105 100644 --- a/app/src/main/java/io/beldex/bchat/util/CallNotificationBuilder.kt +++ b/app/src/main/java/io/beldex/bchat/util/CallNotificationBuilder.kt @@ -12,6 +12,7 @@ import android.graphics.PorterDuff import android.graphics.PorterDuffXfermode import android.graphics.Rect import android.os.Build +import android.util.TypedValue import android.view.View import android.widget.RemoteViews import androidx.annotation.DrawableRes @@ -19,6 +20,10 @@ import androidx.annotation.StringRes import androidx.compose.ui.graphics.Canvas import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.app.Person +import androidx.core.graphics.drawable.IconCompat +import com.beldex.libbchat.utilities.TextSecurePreferences +import com.beldex.libbchat.utilities.dynamiclanguage.DynamicLanguageContextWrapper import com.beldex.libbchat.utilities.recipients.Recipient import io.beldex.bchat.notifications.NotificationChannels import io.beldex.bchat.preferences.PrivacySettingsActivity @@ -55,18 +60,20 @@ class CallNotificationBuilder { @JvmStatic fun getFirstCallNotification(context: Context): Notification { + val language = TextSecurePreferences.getAppSelectedLanguage(context) + val localizedContext = DynamicLanguageContextWrapper.updateContext(context, language) val contentIntent = Intent(context, PrivacySettingsActivity::class.java) val pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) - val text = context.getString(R.string.CallNotificationBuilder_first_call_message) + val text = localizedContext.getString(R.string.CallNotificationBuilder_first_call_message) - val builder = NotificationCompat.Builder(context, NotificationChannels.CALLS) + val builder = NotificationCompat.Builder(localizedContext, NotificationChannels.CALLS) .setSound(null) .setSmallIcon(R.drawable.ic_baseline_call_24) .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_HIGH) - .setContentTitle(context.getString(R.string.CallNotificationBuilder_first_call_title)) + .setContentTitle(localizedContext.getString(R.string.CallNotificationBuilder_first_call_title)) .setContentText(text) .setStyle(NotificationCompat.BigTextStyle().bigText(text)) .setAutoCancel(true) @@ -76,146 +83,186 @@ class CallNotificationBuilder { @JvmStatic fun getCallInProgressNotification(context: Context, type: Int, recipient: Recipient?): Notification { + val language = TextSecurePreferences.getAppSelectedLanguage(context) + val localizedContext = DynamicLanguageContextWrapper.updateContext(context, language) val contentIntent = Intent(context, WebRTCComposeActivity::class.java) .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) - val sizeInPX =context.resources.getDimensionPixelSize(R.dimen.extra_large_profile_picture_size) + val sizeInPX = localizedContext.resources.getDimensionPixelSize(R.dimen.extra_large_profile_picture_size) - val contentView=RemoteViews(context.packageName, R.layout.custom_call_notification) + val contentView = RemoteViews(localizedContext.packageName, R.layout.custom_call_notification) val signalProfilePicture = recipient?.contactPhoto - val bit=AvatarPlaceholderGenerator.generate( - context, - sizeInPX, - recipient?.address.toString(), - recipient?.name.toString() + val bit = AvatarPlaceholderGenerator.generate( + context, + sizeInPX, + recipient?.address.toString(), + recipient?.name.toString() ) - if (signalProfilePicture != null) { - val bitmap=decodeStream(signalProfilePicture.openInputStream(context,true)) - if(bitmap != null) { - val output = Bitmap.createBitmap( - bitmap.width, - bitmap.height, Bitmap.Config.ARGB_8888 - ) - val canvas = android.graphics.Canvas(output) - - val paint = Paint() - val rect = Rect(0, 0, bitmap.width, bitmap.height) - - paint.isAntiAlias = true - canvas.drawARGB(0, 0, 0, 0) - canvas.drawCircle( - (bitmap.width / 2).toFloat(), (bitmap.height / 2).toFloat(), - (bitmap.width / 2).toFloat(), paint - ) - paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC_IN)) - canvas.drawBitmap(bitmap, rect, rect, paint) - contentView.setImageViewBitmap(R.id.image, output) - } else { - contentView.setImageViewResource(R.id.image, R.drawable.defualt_profile_pic) - } + val avatarBitmap = if (signalProfilePicture != null) { + val bitmap = decodeStream(signalProfilePicture.openInputStream(context, true)) + if (bitmap != null) toCircularBitmap(bitmap) else toCircularBitmap(bit.bitmap) } else { - val output = Bitmap.createBitmap( - bit.bitmap.width, - bit.bitmap.height, Bitmap.Config.ARGB_8888 - ) - val canvas = android.graphics.Canvas(output) - - val paint = Paint() - val rect = Rect(0, 0, bit.bitmap.width, bit.bitmap.height) - - paint.isAntiAlias = true - canvas.drawARGB(0, 0, 0, 0) - canvas.drawCircle( - (bit.bitmap.width / 2).toFloat(), (bit.bitmap.height / 2).toFloat(), - (bit.bitmap.width / 2).toFloat(), paint - ) - paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC_IN)) - canvas.drawBitmap(bit.bitmap, rect, rect, paint) - contentView.setImageViewBitmap(R.id.image, output) + toCircularBitmap(bit.bitmap) } + contentView.setImageViewBitmap(R.id.image, avatarBitmap) val pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) val intent = Intent(context, WebRtcCallService::class.java) - .setAction(WebRtcCallService.ACTION_DENY_CALL) + .setAction(WebRtcCallService.ACTION_DENY_CALL) val hangUpIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE) val answerIntent = Intent(context, WebRTCComposeActivity::class.java) - .setAction( if (type == TYPE_INCOMING_PRE_OFFER) WebRTCComposeActivity.ACTION_PRE_OFFER else WebRTCComposeActivity.ACTION_ANSWER) + .setAction(if (type == TYPE_INCOMING_PRE_OFFER) WebRTCComposeActivity.ACTION_PRE_OFFER else WebRTCComposeActivity.ACTION_ANSWER) val answerPendingIntent = PendingIntent.getActivity(context, 0, answerIntent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE) - val builder = NotificationCompat.Builder(context, NotificationChannels.CALLS) - .setFullScreenIntent(getFullScreenPendingIntent( - context - ), true) - .setSound(null) - .setColor(context.getColor(R.color.call_notification_background)) - .setSmallIcon(R.drawable.ic_baseline_call_24) - .setContentIntent(pendingIntent) - .setOngoing(true) - .setCustomContentView(contentView).build() + val hangUpText = localizedContext.getString(R.string.hang_up) + val answerText = localizedContext.getString(R.string.NotificationBarManager__answer_call) + val buttonTextSize = calculateButtonTextSize(localizedContext, hangUpText, answerText) + + contentView.setTextViewText(R.id.hangUpButton, hangUpText) + contentView.setTextViewTextSize(R.id.hangUpButton, TypedValue.COMPLEX_UNIT_SP, buttonTextSize) + contentView.setTextViewText(R.id.answerButton, answerText) + contentView.setTextViewTextSize(R.id.answerButton, TypedValue.COMPLEX_UNIT_SP, buttonTextSize) recipient?.name?.let { name -> contentView.setTextViewText(R.id.title, name) } + val isIncomingCall = type == TYPE_INCOMING_PRE_OFFER || type == TYPE_INCOMING_RINGING + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && isIncomingCall) { + return buildIncomingCallStyleNotification( + context = context, + localizedContext = localizedContext, + recipient = recipient, + avatarBitmap = avatarBitmap, + hangUpIntent = hangUpIntent, + answerIntent = answerPendingIntent + ) + } + + val builder = NotificationCompat.Builder(localizedContext, NotificationChannels.CALLS) + .setFullScreenIntent(getFullScreenPendingIntent(context), true) + .setSound(null) + .setColor(localizedContext.getColor(R.color.call_notification_background)) + .setSmallIcon(R.drawable.ic_baseline_call_24) + .setContentIntent(pendingIntent) + .setOngoing(true) + .setCustomContentView(contentView) + when (type) { TYPE_INCOMING_CONNECTING -> { - contentView.setTextViewText(R.id.text, context.getString(R.string.CallNotificationBuilder_connecting)) + contentView.setTextViewText(R.id.text, localizedContext.getString(R.string.CallNotificationBuilder_connecting)) contentView.setViewVisibility(R.id.hangUpButton, View.GONE) contentView.setViewVisibility(R.id.answerButton, View.GONE) } TYPE_INCOMING_PRE_OFFER, TYPE_INCOMING_RINGING -> { - contentView.setTextViewText(R.id.text, context.getString(R.string.NotificationBarManager__incoming_signal_call)) - contentView.setOnClickPendingIntent(R.id.hangUpButton,hangUpIntent) + contentView.setTextViewText(R.id.text, localizedContext.getString(R.string.NotificationBarManager__incoming_signal_call)) + contentView.setOnClickPendingIntent(R.id.hangUpButton, hangUpIntent) contentView.setOnClickPendingIntent(R.id.answerButton, answerPendingIntent) - //builder.setFullScreenIntent(pendingIntent) - /* builder.addAction(getServiceNotificationAction( - context, - WebRtcCallService.ACTION_DENY_CALL, - R.drawable.ic_close_grey600_32dp, - R.string.NotificationBarManager__deny_call - )) - // if notifications aren't enabled, we will trigger the intent from WebRtcCallService - builder.setFullScreenIntent(getFullScreenPendingIntent( - context - ), true) - builder.addAction(getActivityNotificationAction( - context, - if (type == TYPE_INCOMING_PRE_OFFER) WebRTCComposeActivity.ACTION_PRE_OFFER else WebRTCComposeActivity.ACTION_ANSWER, - R.drawable.ic_phone_grey600_32dp, - R.string.NotificationBarManager__answer_call - ))*/ builder.priority = Notification.PRIORITY_MAX } TYPE_OUTGOING_RINGING -> { val intent = Intent(context, WebRtcCallService::class.java) - .setAction(WebRtcCallService.ACTION_LOCAL_HANGUP) + .setAction(WebRtcCallService.ACTION_LOCAL_HANGUP) val establishCall = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE) - contentView.setTextViewText(R.id.text, context.getString(R.string.NotificationBarManager__establishing_signal_call)) - contentView.setOnClickPendingIntent(R.id.text,establishCall ) + contentView.setTextViewText(R.id.text, localizedContext.getString(R.string.NotificationBarManager__establishing_signal_call)) + contentView.setOnClickPendingIntent(R.id.text, establishCall) contentView.setViewVisibility(R.id.hangUpButton, View.GONE) contentView.setViewVisibility(R.id.answerButton, View.GONE) } TYPE_SCREEN_ON -> { - contentView.setTextViewText(R.id.text, context.getString(R.string.CallNotificationBuilder_screen_on)) + contentView.setTextViewText(R.id.text, localizedContext.getString(R.string.CallNotificationBuilder_screen_on)) contentView.setViewVisibility(R.id.hangUpButton, View.GONE) contentView.setViewVisibility(R.id.answerButton, View.GONE) } else -> { val intent = Intent(context, WebRtcCallService::class.java) - .setAction(WebRtcCallService.ACTION_LOCAL_HANGUP) + .setAction(WebRtcCallService.ACTION_LOCAL_HANGUP) val establishCall = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE) - contentView.setTextViewText(R.id.text, context.getString(R.string.NotificationBarManager_call_in_progress)) - contentView.setOnClickPendingIntent(R.id.text,establishCall) + contentView.setTextViewText(R.id.text, localizedContext.getString(R.string.NotificationBarManager_call_in_progress)) + contentView.setOnClickPendingIntent(R.id.text, establishCall) contentView.setViewVisibility(R.id.hangUpButton, View.GONE) contentView.setViewVisibility(R.id.answerButton, View.GONE) } } - return builder + return builder.build() + } + + private fun toCircularBitmap(source: Bitmap): Bitmap { + val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888) + val canvas = android.graphics.Canvas(output) + + val paint = Paint() + val rect = Rect(0, 0, source.width, source.height) + + paint.isAntiAlias = true + canvas.drawARGB(0, 0, 0, 0) + canvas.drawCircle( + (source.width / 2).toFloat(), (source.height / 2).toFloat(), + (source.width / 2).toFloat(), paint + ) + paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC_IN)) + canvas.drawBitmap(source, rect, rect, paint) + return output + } + + private fun buildIncomingCallStyleNotification( + context: Context, + localizedContext: Context, + recipient: Recipient?, + avatarBitmap: Bitmap, + hangUpIntent: PendingIntent, + answerIntent: PendingIntent + ): Notification { + val contactName = recipient?.name?.takeIf { it.isNotBlank() } + ?: localizedContext.getString(R.string.NotificationChannel_missing_display_name) + + val person = Person.Builder() + .setName(contactName) + .setIcon(IconCompat.createWithBitmap(avatarBitmap)) + .build() + + val contentIntent = Intent(context, WebRTCComposeActivity::class.java) + .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + val pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) + + return NotificationCompat.Builder(localizedContext, NotificationChannels.CALLS) + .setFullScreenIntent(getFullScreenPendingIntent(context), true) + .setSound(null) + .setColor(localizedContext.getColor(R.color.call_notification_background)) + .setColorized(true) + .setSmallIcon(R.drawable.ic_baseline_call_24) + .setContentIntent(pendingIntent) + .setOngoing(true) + .setCategory(NotificationCompat.CATEGORY_CALL) + .setPriority(NotificationCompat.PRIORITY_MAX) + .setStyle( + NotificationCompat.CallStyle.forIncomingCall(person, hangUpIntent, answerIntent) + .setAnswerButtonColorHint(localizedContext.getColor(R.color.button_green)) + .setDeclineButtonColorHint(localizedContext.getColor(R.color.destructive)) + ) + .build() + } + + private fun calculateButtonTextSize(context: Context, hangUpText: String, answerText: String): Float { + val displayMetrics = context.resources.displayMetrics + val buttonWidthPx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80f, displayMetrics) + val paint = android.graphics.Paint() + val maxText = if (hangUpText.length > answerText.length) hangUpText else answerText + val startSize = 11f + val minSize = 8f + var size = startSize + while (size > minSize) { + paint.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, size, displayMetrics) + val textWidth = paint.measureText(maxText) + if (textWidth <= buttonWidthPx) break + size -= 0.5f + } + return maxOf(size, minSize) } private fun getServiceNotificationAction(context: Context, action: String, iconResId: Int, titleResId: Int): NotificationCompat.Action { diff --git a/app/src/main/java/io/beldex/bchat/util/ComposeUtil.kt b/app/src/main/java/io/beldex/bchat/util/ComposeUtil.kt index 45c8d4a2a..70dd33066 100644 --- a/app/src/main/java/io/beldex/bchat/util/ComposeUtil.kt +++ b/app/src/main/java/io/beldex/bchat/util/ComposeUtil.kt @@ -1,6 +1,10 @@ package io.beldex.bchat.util import io.beldex.bchat.conversation.v2.contact_sharing.capitalizeFirstLetter +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import java.util.regex.Pattern fun shortNameAndAddress(name:String, address: String): String { return if(name == address) { @@ -15,6 +19,18 @@ fun shortNameAndAddress(name:String, address: String): String { } fun isValidGroupName(name: String): Boolean { - val groupNameRegex = Regex("^(?=.*[A-Za-z0-9])[A-Za-z0-9_\\-\\s]+$") - return name.matches(groupNameRegex) -} \ No newline at end of file + return name.matches(groupNamePattern.toRegex()) +} + +object AppLanguageEvent { + val deviceLanguage = MutableStateFlow("") + var deviceLanguageState: StateFlow = deviceLanguage.asStateFlow() + + fun update(code: String) { + deviceLanguage.value = code + } +} + +val englishNamePattern: Pattern = Pattern.compile("^(?=.*[A-Za-z0-9])[A-Za-z0-9_-]+(?: [A-Za-z0-9_-]+)*$") +val unicodeNamePattern: Pattern = Pattern.compile("^(?=.*[\\p{L}\\p{N}])[\\p{L}\\p{M}\\p{N}'_-]+(?: [\\p{L}\\p{M}\\p{N}'_-]+)*$") +val groupNamePattern: Pattern = Pattern.compile("^(?=.*[\\p{L}\\p{N}])[\\p{L}\\p{M}\\p{N}_-]+(?: [\\p{L}\\p{M}\\p{N}_-]+)*$") \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/util/DateUtils.java b/app/src/main/java/io/beldex/bchat/util/DateUtils.java index 01b385785..073d04436 100644 --- a/app/src/main/java/io/beldex/bchat/util/DateUtils.java +++ b/app/src/main/java/io/beldex/bchat/util/DateUtils.java @@ -58,9 +58,17 @@ public static String getFormattedDateTime(long time, String template, Locale loc final String localizedPattern = getLocalizedPattern(template, locale); return new SimpleDateFormat(localizedPattern, locale).format(new Date(time)); } + + private static boolean isExplicit24HourFormat(Context c) { + String value = android.provider.Settings.System.getString( + c.getContentResolver(), + android.provider.Settings.System.TIME_12_24 + ); + return "24".equals(value); + } public static String getHourFormat(Context c) { - return (DateFormat.is24HourFormat(c)) ? "HH:mm" : "hh:mm a"; + return isExplicit24HourFormat(c) ? "Hm" : "hm"; } public static String getDisplayFormattedTimeSpanString(final Context c, final Locale locale, final long timestamp) { @@ -69,11 +77,11 @@ public static String getDisplayFormattedTimeSpanString(final Context c, final Lo } else if (isToday(timestamp)) { return getFormattedDateTime(timestamp, getHourFormat(c), locale); } else if (isWithin(timestamp, 6, TimeUnit.DAYS)) { - return getFormattedDateTime(timestamp, "EEE " + getHourFormat(c), locale); + return getFormattedDateTime(timestamp, "E" + getHourFormat(c), locale); } else if (isWithin(timestamp, 365, TimeUnit.DAYS)) { - return getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c), locale); + return getFormattedDateTime(timestamp, "MMMd" + getHourFormat(c), locale); } else { - return getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c) + ", yyyy", locale); + return getFormattedDateTime(timestamp, "MMMd" + getHourFormat(c) + "y", locale); } } @@ -85,25 +93,27 @@ public static String getCoversationDisplayFormattedTimeSpanString(final Context if (isWithin(timestamp, 1, TimeUnit.MINUTES)) { return c.getString(R.string.DateUtils_just_now); } else if (isToday(timestamp)) { - return "Today"; + return c.getString(R.string.DateUtils_today); } else if (isWithin(timestamp, 6, TimeUnit.DAYS)) { return getFormattedDateTime(timestamp, "EEEE", locale); } else if (isWithin(timestamp, 365, TimeUnit.DAYS)) { - return getFormattedDateTime(timestamp, "MMM d ", locale); + return getFormattedDateTime(timestamp, "MMMd", locale); } else { - return getFormattedDateTime(timestamp, "MMM d, yyyy", locale); + return getFormattedDateTime(timestamp, "MMMdy", locale); } } public static SimpleDateFormat getDetailedDateFormatter(Context context, Locale locale) { - String dateFormatPattern; + String skeleton; - if (DateFormat.is24HourFormat(context)) { - dateFormatPattern = getLocalizedPattern("MMM d, yyyy HH:mm:ss zzz", locale); + if (isExplicit24HourFormat(context)) { + skeleton = "MMMdyHmsz"; } else { - dateFormatPattern = getLocalizedPattern("MMM d, yyyy hh:mm:ss a zzz", locale); + skeleton = "MMMdyhmsza"; } + String dateFormatPattern = getLocalizedPattern(skeleton, locale); + return new SimpleDateFormat(dateFormatPattern, locale); } @@ -116,7 +126,7 @@ public static String getRelativeDate(@NonNull Context context, } else if (isYesterday(timestamp)) { return context.getString(R.string.DateUtils_yesterday); } else { - return getFormattedDateTime(timestamp, "EEE, MMM d, yyyy", locale); + return getFormattedDateTime(timestamp, "EMMMdy", locale); } } @@ -166,11 +176,11 @@ public static String getBriefRelativeTimeSpanString(final Context c, final Local int hours = convertDelta(timestamp, TimeUnit.HOURS); return c.getResources().getQuantityString(R.plurals.hours_ago, hours, hours); } else if (isWithin(timestamp, 6, TimeUnit.DAYS)) { - return getFormattedDateTime(timestamp, "EEE", locale); + return getFormattedDateTime(timestamp, "E", locale); } else if (isWithin(timestamp, 365, TimeUnit.DAYS)) { - return getFormattedDateTime(timestamp, "MMM d", locale); + return getFormattedDateTime(timestamp, "MMMd", locale); } else { - return getFormattedDateTime(timestamp, "MMM d, yyyy", locale); + return getFormattedDateTime(timestamp, "MMMdy", locale); } } @@ -181,16 +191,15 @@ public static String getExtendedRelativeTimeSpanString(final Context c, final Lo int mins = (int)TimeUnit.MINUTES.convert(System.currentTimeMillis() - timestamp, TimeUnit.MILLISECONDS); return c.getResources().getString(R.string.DateUtils_minutes_ago, mins); } else { - StringBuilder format = new StringBuilder(); - if (isWithin(timestamp, 6, TimeUnit.DAYS)) format.append("EEE "); - else if (isWithin(timestamp, 365, TimeUnit.DAYS)) format.append("MMM d, "); - else format.append("MMM d, yyyy, "); + StringBuilder skeleton = new StringBuilder(); + if (isWithin(timestamp, 6, TimeUnit.DAYS)) skeleton.append("E"); + else if (isWithin(timestamp, 365, TimeUnit.DAYS)) skeleton.append("MMMd"); + else skeleton.append("MMMdy"); - if (DateFormat.is24HourFormat(c)) format.append("HH:mm"); - else format.append("hh:mm a"); + skeleton.append(getHourFormat(c)); - return getFormattedDateTime(timestamp, format.toString(), locale); + return getFormattedDateTime(timestamp, skeleton.toString(), locale); } } // endregion -} +} \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/util/ExtensionFunctions.kt b/app/src/main/java/io/beldex/bchat/util/ExtensionFunctions.kt index 97d599b7a..ce644ecd1 100644 --- a/app/src/main/java/io/beldex/bchat/util/ExtensionFunctions.kt +++ b/app/src/main/java/io/beldex/bchat/util/ExtensionFunctions.kt @@ -15,6 +15,7 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentManager import dagger.hilt.android.internal.managers.ViewComponentManager +import io.beldex.bchat.R import io.beldex.bchat.database.model.MessageRecord import org.json.JSONObject import java.io.Serializable @@ -59,7 +60,7 @@ fun Context.copyToClipBoard(label: String, content: String) { val clipBoard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText(label, content) clipBoard.setPrimaryClip(clip) - Toast.makeText(this, "Copied to clipboard", Toast.LENGTH_SHORT).show() + Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() } fun Context.toPx(dp: Int): Float = TypedValue.applyDimension( diff --git a/app/src/main/java/io/beldex/bchat/util/QuoteBodyTextViewLayout.java b/app/src/main/java/io/beldex/bchat/util/QuoteBodyTextViewLayout.java index f83b7a3bd..30ac29d69 100644 --- a/app/src/main/java/io/beldex/bchat/util/QuoteBodyTextViewLayout.java +++ b/app/src/main/java/io/beldex/bchat/util/QuoteBodyTextViewLayout.java @@ -54,7 +54,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { return; } - int availableWidth = widthSize - getPaddingLeft() - getPaddingRight(); + int availableWidth = widthSize - getPaddingStart() - getPaddingEnd(); int availableHeight = heightSize - getPaddingTop() - getPaddingBottom(); quoteViewPartMessageLayoutParams = (LayoutParams) quoteViewPartMessage.getLayoutParams(); @@ -68,18 +68,18 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int quoteViewPartMessageLineCount = quoteViewPartMessage.getLineCount(); float quoteViewPartMessageLastLineWidth = quoteViewPartMessageLineCount > 0 ? quoteViewPartMessage.getLayout().getLineWidth(quoteViewPartMessageLineCount - 1) : 0; - widthSize = getPaddingLeft() + getPaddingRight(); + widthSize = getPaddingStart() + getPaddingEnd(); heightSize = getPaddingTop() + getPaddingBottom(); - if (quoteViewPartMessageLineCount > 1 && !(quoteViewPartMessageLastLineWidth + quoteViewPartTimeWidth >= quoteViewPartMessage.getMeasuredWidth())) { - widthSize += availableWidth; - heightSize += quoteViewPartMessageHeight; - } else if (quoteViewPartMessageLineCount > 1 && (quoteViewPartMessageLastLineWidth + quoteViewPartTimeWidth >= availableWidth)) { + if (quoteViewPartMessageLineCount > 1 && (quoteViewPartMessageLastLineWidth + quoteViewPartTimeWidth >= quoteViewPartMessage.getMeasuredWidth())) { widthSize += availableWidth; heightSize += quoteViewPartMessageHeight + quoteViewPartTimeHeight; } else if (quoteViewPartMessageLineCount == 1 && (quoteViewPartMessageWidth + quoteViewPartTimeWidth >= availableWidth)) { widthSize += availableWidth; heightSize += quoteViewPartMessageHeight + quoteViewPartTimeHeight; + } else if (quoteViewPartMessageLineCount > 1) { + widthSize += availableWidth; + heightSize += quoteViewPartMessageHeight; } else { widthSize += availableWidth; heightSize += quoteViewPartMessageHeight; @@ -98,15 +98,15 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } quoteViewPartMessage.layout( - getPaddingLeft(), + getPaddingStart(), getPaddingTop(), - quoteViewPartMessage.getWidth() + getPaddingLeft(), + quoteViewPartMessage.getWidth() + getPaddingStart(), quoteViewPartMessage.getHeight() + getPaddingTop()); quoteViewPartTime.layout( - right - left - quoteViewPartTimeWidth - getPaddingRight(), + right - left - quoteViewPartTimeWidth - getPaddingEnd(), bottom - top - getPaddingBottom() - quoteViewPartTimeHeight, - right - left - getPaddingRight(), + right - left - getPaddingEnd(), bottom - top - getPaddingBottom()); } } diff --git a/app/src/main/java/io/beldex/bchat/util/ResourceProvider.kt b/app/src/main/java/io/beldex/bchat/util/ResourceProvider.kt index 76a9621f1..9ef48e526 100644 --- a/app/src/main/java/io/beldex/bchat/util/ResourceProvider.kt +++ b/app/src/main/java/io/beldex/bchat/util/ResourceProvider.kt @@ -9,7 +9,7 @@ import javax.inject.Singleton @Singleton class ResourceProvider @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private var context: Context ) { fun getString(@StringRes stringResId: Int) : String { @@ -23,4 +23,7 @@ class ResourceProvider @Inject constructor( return context.resources.getStringArray(stringResId) } + fun updateContext(newContext: Context) { + context = newContext + } } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/util/SaveAttachmentTask.kt b/app/src/main/java/io/beldex/bchat/util/SaveAttachmentTask.kt index d963a2f2a..e2af0d161 100644 --- a/app/src/main/java/io/beldex/bchat/util/SaveAttachmentTask.kt +++ b/app/src/main/java/io/beldex/bchat/util/SaveAttachmentTask.kt @@ -166,7 +166,13 @@ class SaveAttachmentTask : ProgressDialogAsyncTask = getFileNameParts(fileName) val base = fileParts[0] val extension = fileParts[1] - val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + val extensionMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + val mimeType = if (!extensionMimeType.isNullOrBlank() && isSameMimeFamily(extensionMimeType, contentType)) { + extensionMimeType + } else { + contentType + } + val contentValues = ContentValues() contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType) @@ -199,6 +205,12 @@ class SaveAttachmentTask : ProgressDialogAsyncTask { val tokens = fileName.split("\\.(?=[^\\.]+$)".toRegex()).toTypedArray() return arrayOf(tokens[0], if (tokens.size > 1) tokens[1] else "") diff --git a/app/src/main/java/io/beldex/bchat/util/StickyHeaderDecoration.java b/app/src/main/java/io/beldex/bchat/util/StickyHeaderDecoration.java index 91ce4a2ae..ca81074b9 100644 --- a/app/src/main/java/io/beldex/bchat/util/StickyHeaderDecoration.java +++ b/app/src/main/java/io/beldex/bchat/util/StickyHeaderDecoration.java @@ -91,7 +91,7 @@ protected ViewHolder getHeader(RecyclerView parent, StickyHeaderAdapter adapter, int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); int childWidth = ViewGroup.getChildMeasureSpec(widthSpec, - parent.getPaddingLeft() + parent.getPaddingRight(), header.getLayoutParams().width); + parent.getPaddingStart() + parent.getPaddingEnd(), header.getLayoutParams().width); int childHeight = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), header.getLayoutParams().height); diff --git a/app/src/main/java/io/beldex/bchat/util/ToastMessageUtil.java b/app/src/main/java/io/beldex/bchat/util/ToastMessageUtil.java new file mode 100644 index 000000000..5f3d65884 --- /dev/null +++ b/app/src/main/java/io/beldex/bchat/util/ToastMessageUtil.java @@ -0,0 +1,31 @@ +package io.beldex.bchat.util; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import io.beldex.bchat.R; + +public final class ToastMessageUtil { + + private ToastMessageUtil() { + } + + public static void showLong(@NonNull Context context, @StringRes int messageResId) { + show(context, context.getString(messageResId), Toast.LENGTH_LONG); + } + + public static void show(@NonNull Context context, @NonNull String message, int duration) { + TextView view = (TextView) LayoutInflater.from(context) + .inflate(R.layout.view_toast_message, null, false); + view.setText(message); + + Toast toast = Toast.makeText(context.getApplicationContext(), "", duration); + toast.setView(view); + toast.show(); + } +} \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/util/daterangepicker/DateRangePicker.java b/app/src/main/java/io/beldex/bchat/util/daterangepicker/DateRangePicker.java index 5b7b00ba6..2e9a11657 100644 --- a/app/src/main/java/io/beldex/bchat/util/daterangepicker/DateRangePicker.java +++ b/app/src/main/java/io/beldex/bchat/util/daterangepicker/DateRangePicker.java @@ -38,10 +38,10 @@ public class DateRangePicker extends Dialog implements OnClickListener, OnTabSel private Calendar startDateCal = Calendar.getInstance(); private Calendar endDateCal = Calendar.getInstance(); private TabLayout tabLayout; - private String startDateTitle = "start date"; - private String endDateTitle = "end date"; - private String startDateError = "Please select start date"; - private String endDateError = "Please select end date"; + private String startDateTitle = context.getString(R.string.str_start_date); + private String endDateTitle = context.getString(R.string.str_end_date); + private String startDateError = context.getString(R.string.str_please_select_start_date); + private String endDateError = context.getString(R.string.str_please_select_end_date); public DateRangePicker(@NonNull Context context, DateRangePicker.OnCalenderClickListener onCalenderClickListener) { super(context); diff --git a/app/src/main/java/io/beldex/bchat/util/slidetoact/SlideToActionIconUtil.kt b/app/src/main/java/io/beldex/bchat/util/slidetoact/SlideToActionIconUtil.kt index d15cd5d32..de1296e1b 100644 --- a/app/src/main/java/io/beldex/bchat/util/slidetoact/SlideToActionIconUtil.kt +++ b/app/src/main/java/io/beldex/bchat/util/slidetoact/SlideToActionIconUtil.kt @@ -5,11 +5,6 @@ import android.annotation.SuppressLint import android.content.Context import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.Drawable -import android.os.Build.VERSION.SDK_INT -import android.os.Build.VERSION_CODES.LOLLIPOP -import android.os.Build.VERSION_CODES.N -import androidx.core.content.ContextCompat -import androidx.core.graphics.drawable.DrawableCompat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat internal object SlideToActIconUtil { @@ -17,29 +12,21 @@ internal object SlideToActIconUtil { @SuppressLint("UseCompatLoadingForDrawables") internal fun loadIconCompat(context: Context, value: Int): Drawable { // Due to bug in the AVD implementation in the support library, we use it only for API < 21 - return if (SDK_INT >= LOLLIPOP) { - context.resources.getDrawable(value, context.theme) - } else { - return AnimatedVectorDrawableCompat.create(context, value) - ?: ContextCompat.getDrawable(context, value)!! - } + return context.resources.getDrawable(value, context.theme) } internal fun tintIconCompat(icon: Drawable, color: Int) { // Tinting the tick with the proper implementation method - when { - SDK_INT >= LOLLIPOP -> icon.setTint(color) - else -> DrawableCompat.setTint(icon, color) - } + icon.setTint(color) } /** * Internal method to start the Icon AVD animation, with the proper library based on API level. */ internal fun startIconAnimation(icon: Drawable) { - when { - SDK_INT >= LOLLIPOP && icon is AnimatedVectorDrawable -> icon.start() - icon is AnimatedVectorDrawableCompat -> icon.start() + when (icon) { + is AnimatedVectorDrawable -> icon.start() + is AnimatedVectorDrawableCompat -> icon.start() else -> { // Do nothing as the icon can't be animated } @@ -50,9 +37,9 @@ internal object SlideToActIconUtil { * Internal method to stop the Icon AVD animation, with the proper library based on API level. */ internal fun stopIconAnimation(icon: Drawable) { - when { - SDK_INT >= LOLLIPOP && icon is AnimatedVectorDrawable -> icon.stop() - icon is AnimatedVectorDrawableCompat -> icon.stop() + when (icon) { + is AnimatedVectorDrawable -> icon.stop() + is AnimatedVectorDrawableCompat -> icon.stop() else -> { // Do nothing as the icon can't be animated } @@ -97,10 +84,7 @@ internal object SlideToActIconUtil { * Logic to decide if we should do a Fade or use the [AnimatedVectorDrawable] animation. */ private fun fallbackToFadeAnimation(icon: Drawable) = when { - // We don't use AVD at all for <= N. - SDK_INT <= N -> true - SDK_INT >= LOLLIPOP && icon !is AnimatedVectorDrawable -> true - SDK_INT < LOLLIPOP && icon !is AnimatedVectorDrawableCompat -> true + icon !is AnimatedVectorDrawable -> true else -> false } } \ No newline at end of file diff --git a/app/src/main/java/io/beldex/bchat/webrtc/WebRTCComposeActivity.kt b/app/src/main/java/io/beldex/bchat/webrtc/WebRTCComposeActivity.kt index 57f5d30d9..ce502afbf 100644 --- a/app/src/main/java/io/beldex/bchat/webrtc/WebRTCComposeActivity.kt +++ b/app/src/main/java/io/beldex/bchat/webrtc/WebRTCComposeActivity.kt @@ -71,7 +71,6 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalView @@ -99,6 +98,7 @@ import com.beldex.libbchat.utilities.TextSecurePreferences import com.beldex.libsignal.utilities.hexEncodedPublicKey import com.google.android.gms.common.util.CollectionUtils.listOf import dagger.hilt.android.AndroidEntryPoint +import io.beldex.bchat.BaseComponentActivity import io.beldex.bchat.R import io.beldex.bchat.compose_utils.BChatTheme import io.beldex.bchat.compose_utils.BChatTypography @@ -121,13 +121,14 @@ import org.webrtc.SurfaceViewRenderer @AndroidEntryPoint -class WebRTCComposeActivity : ComponentActivity() { +class WebRTCComposeActivity : BaseComponentActivity() { private var hangupReceiver: BroadcastReceiver? = null val viewModel:CallViewModel by viewModels() val wantsToAnswer :MutableState = mutableStateOf(false) private val isInPictureInPictureMode :MutableState = mutableStateOf(false) + private val isLandscapeState: MutableState = mutableStateOf(false) private val hexEncodedPublicKey: String get() { @@ -162,6 +163,14 @@ class WebRTCComposeActivity : ComponentActivity() { } } + private fun isScreenLandscape(config: Configuration) = + config.screenWidthDp >= config.screenHeightDp + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + isLandscapeState.value = isScreenLandscape(newConfig) + } + override fun onPictureInPictureModeChanged( isInPictureInPictureMode: Boolean, newConfig: Configuration @@ -176,6 +185,7 @@ class WebRTCComposeActivity : ComponentActivity() { @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") override fun onCreate(savedInstanceState : Bundle?) { super.onCreate(savedInstanceState) + isLandscapeState.value = isScreenLandscape(resources.configuration) if (isSystemPipEnabledAndAvailable()) { pipBuilderParams = PictureInPictureParams.Builder() pipBuilderParams!!.setAspectRatio(Rational(9,16)) @@ -259,7 +269,7 @@ class WebRTCComposeActivity : ComponentActivity() { val callViewModel : CallViewModel=hiltViewModel() val context=LocalContext.current val lifecycleOwner=LocalLifecycleOwner.current - val isLandscape=LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE + val isLandscape=isLandscapeState.value val profilePictureSize=if (isLandscape) 112.dp else 194.dp val profilePictureInnerSize=if (isLandscape) 92.dp else 152.dp val profileSize=132.dp @@ -346,7 +356,7 @@ class WebRTCComposeActivity : ComponentActivity() { mutableStateOf(context.getString(R.string.WebRtcCallActivity_Reconnecting)) } var isStatusText by remember { - mutableStateOf("Voice Call") + mutableStateOf(context.getString(R.string.voice_call)) } var isShowDialingStatus by remember { @@ -543,12 +553,12 @@ class WebRTCComposeActivity : ComponentActivity() { fun getUserDisplayName(publicKey : String) : String { val contact=DatabaseComponent.get(context).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: publicKey } fun getUserDisplayNameOrShortestPublicKey(publicKey : String) : String { val contact=DatabaseComponent.get(context).bchatContactDatabase().getContactWithBchatID(publicKey) - return contact?.displayName(Contact.ContactContext.REGULAR) ?: "${publicKey.take(4)}...${publicKey.takeLast(4)}" + return contact?.displayName(Contact.ContactContext.REGULAR, context) ?: "${publicKey.take(4)}...${publicKey.takeLast(4)}" } fun showLocalUserDetailsInFullScreen() : Boolean { @@ -570,7 +580,7 @@ class WebRTCComposeActivity : ComponentActivity() { isStatusCallAudioText=if (isShowCallVideoStatus) { "" } else { - "Call Muted" + getString(R.string.call_muted) } } else { isShowCallAudioStatus=false @@ -695,9 +705,9 @@ class WebRTCComposeActivity : ComponentActivity() { localVideoView= state.userVideoEnabled || state.remoteVideoEnabled isSwitchCameraFlipEnabled=state.userVideoEnabled isStatusText=if (state.userVideoEnabled) { - "Video Call" + context.getString(R.string.video_call) } else { - "Voice Call" + context.getString(R.string.voice_call) } if (state.showFloatingVideo()) { callViewModel.floatingRenderer?.let { sfView -> @@ -730,9 +740,9 @@ class WebRTCComposeActivity : ComponentActivity() { .fillMaxSize() .paint( if (isDarkTheme) - painterResource(id=R.drawable.call_background) + painterResource(if(isLandscape) R.drawable.land_call_background_dark else R.drawable.call_background) else - painterResource(id=R.drawable.call_background_white), + painterResource(id= if(isLandscape) R.drawable.land_call_background_white else R.drawable.call_background_white), contentScale=ContentScale.FillBounds ) ) { @@ -796,121 +806,125 @@ class WebRTCComposeActivity : ComponentActivity() { if (!remoteVideoView) { if (isLandscape) { - // === LANDSCAPE: unchanged from original === Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .fillMaxSize() .padding(horizontal = 24.dp) ) { - Spacer(modifier = Modifier.weight(0.6f)) - - Text( - text = isStatusText, - style = BChatTypography.titleMedium.copy( - color = MaterialTheme.appColors.textColor, - fontSize = 18.sp, - fontWeight = FontWeight(700) - ), - textAlign = TextAlign.Center - ) + Spacer(modifier = Modifier.height(12.dp)) Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.fillMaxWidth() ) { - Icon( - painter = painterResource(id = R.drawable.ic_lock_call), - contentDescription = lockDescription, - tint = MaterialTheme.appColors.textColor, - modifier = Modifier.padding(horizontal = 5.dp) - ) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) + ) { + Icon( + painterResource(id = R.drawable.ic_back_call), + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.appColors.editTextColor, + modifier = Modifier + .clickable { + (context as ComponentActivity).finish() + } + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = if (isShowIncomingStatus) isStatusInComingText else isStatusText, + style = BChatTypography.titleMedium.copy( + color = MaterialTheme.appColors.textColor, + fontSize = 22.sp, + fontWeight = FontWeight(700) + ) + ) + } + + // Center: Person name Text( - text = stringResource(id = R.string.end_to_end_encrypted), + text = if (showLocalUserDetailsInFullScreen()) localUserName else isPersonNameText, style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.textColor, - fontSize = 12.sp, - fontWeight = FontWeight(400) + fontSize = 24.sp, + fontWeight = FontWeight(700) ), - modifier = Modifier.padding(horizontal = 5.dp) + textAlign = TextAlign.Center, + maxLines = 1, + modifier = Modifier.weight(1f) ) + + // Right: Lock icon + E2E encryption text + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.End, + modifier = Modifier.weight(1f) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_lock_call), + contentDescription = lockDescription, + tint = MaterialTheme.appColors.textColor, + modifier = Modifier.padding(horizontal = 5.dp) + ) + Text( + text = stringResource(id = R.string.end_to_end_encrypted), + style = BChatTypography.titleMedium.copy( + color = MaterialTheme.appColors.textColor, + fontSize = 12.sp, + fontWeight = FontWeight(400) + ) + ) + } } - Spacer(modifier = Modifier.weight(0.6f)) + Spacer(modifier = Modifier.height(12.dp)) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center + // ---- Profile picture centered ---- + Box( + modifier = Modifier + .size(profilePictureSize) + .border( + width = 1.dp, + color = MaterialTheme.appColors.switchTrackColor, + shape = CircleShape + ) + .background( + color = MaterialTheme.appColors.backgroundColor, + shape = CircleShape + ), + contentAlignment = Alignment.Center ) { Box( modifier = Modifier - .size(profilePictureSize) - .border( - width = 1.dp, - color = MaterialTheme.appColors.switchTrackColor, - shape = CircleShape - ) - .background( - color = MaterialTheme.appColors.backgroundColor, - shape = CircleShape - ), + .padding(4.dp) + .size(profilePictureInnerSize), contentAlignment = Alignment.Center ) { - Box( - modifier = Modifier.size(profilePictureInnerSize), - contentAlignment = Alignment.Center - ) { - ProfilePictureComponent( - publicKey = if (showLocalUserDetailsInFullScreen()) localUserPublicKey else recipientPublicKey, - displayName = getUserDisplayName(if (showLocalUserDetailsInFullScreen()) localUserPublicKey else recipientPublicKey), - containerSize = profileSize, - pictureMode = ProfilePictureMode.LargePicture - ) - } - } - Spacer(modifier = Modifier.width(24.dp)) - Column( - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.Center - ) { - Text( - text = if (showLocalUserDetailsInFullScreen()) localUserName else isPersonNameText, - style = BChatTypography.titleMedium.copy( - color = MaterialTheme.appColors.textColor, - fontSize = 20.sp, - fontWeight = FontWeight(700) - ), - textAlign = TextAlign.Start, - maxLines = 1 + ProfilePictureComponent( + publicKey = if (showLocalUserDetailsInFullScreen()) localUserPublicKey else recipientPublicKey, + displayName = getUserDisplayName(if (showLocalUserDetailsInFullScreen()) localUserPublicKey else recipientPublicKey), + containerSize = profileSize, + pictureMode = ProfilePictureMode.LargePicture ) } } - Spacer(modifier = Modifier.weight(0.6f)) + Spacer(modifier = Modifier.weight(1f)) + // ---- Calling text + Animation at bottom ---- Column( horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Bottom + verticalArrangement = Arrangement.Bottom, + modifier = Modifier.padding(bottom = 24.dp) ) { if (isShowDialingStatus) { Text( isStatusDialingText, - Modifier.padding(vertical = 6.dp), + Modifier.padding(vertical = 4.dp), style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.textColor, - fontSize = 16.sp, - fontWeight = FontWeight(400) - ) - ) - } - if (isShowIncomingStatus) { - Text( - isStatusInComingText, - Modifier.padding(vertical = 6.dp), - style = BChatTypography.titleMedium.copy( - color = MaterialTheme.appColors.textColor, - fontSize = 16.sp, + fontSize = 14.sp, fontWeight = FontWeight(400) ) ) @@ -920,26 +934,26 @@ class WebRTCComposeActivity : ComponentActivity() { text = isStatusReConnectingText, style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.textColor, - fontSize = 16.sp, + fontSize = 14.sp, fontWeight = FontWeight(400) ), - modifier = Modifier.padding(vertical = 6.dp) + modifier = Modifier.padding(vertical = 4.dp) ) } if (callLoading) { LottieAnimation( composition, progress, - modifier = Modifier.size(52.dp) + modifier = Modifier.size(48.dp) ) } if (isShowCallAudioStatus) { Text( isStatusCallAudioText, - Modifier.padding(vertical = 6.dp), + Modifier.padding(vertical = 4.dp), style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.textColor, - fontSize = 12.sp, + fontSize = 11.sp, fontWeight = FontWeight(400) ) ) @@ -947,10 +961,10 @@ class WebRTCComposeActivity : ComponentActivity() { if (isShowCallVideoStatus) { Text( isStatusCallVideoText, - Modifier.padding(vertical = 6.dp), + Modifier.padding(vertical = 4.dp), style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.textColor, - fontSize = 12.sp, + fontSize = 11.sp, fontWeight = FontWeight(400) ) ) @@ -958,19 +972,17 @@ class WebRTCComposeActivity : ComponentActivity() { if (isShowCallDurationStatus) { Text( isStatusCallDurationText, - Modifier.padding(vertical = 6.dp), + Modifier.padding(vertical = 4.dp), style = BChatTypography.titleMedium.copy( color = MaterialTheme.appColors.textColor, - fontSize = 16.sp, + fontSize = 14.sp, fontWeight = FontWeight(400) ) ) } } - Spacer(modifier = Modifier.height(48.dp)) - - Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.height(8.dp)) } } else { // === PORTRAIT: top group + bottom group === @@ -1082,17 +1094,6 @@ class WebRTCComposeActivity : ComponentActivity() { ) ) } - if (isShowIncomingStatus) { - Text( - isStatusInComingText, - Modifier.padding(vertical = 6.dp), - style = BChatTypography.titleMedium.copy( - color = MaterialTheme.appColors.textColor, - fontSize = 18.sp, - fontWeight = FontWeight(400) - ) - ) - } if (isShowReConnecting) { Text( text = isStatusReConnectingText, @@ -1158,8 +1159,7 @@ class WebRTCComposeActivity : ComponentActivity() { ) { Surface( color = Color.Black, - shape = RoundedCornerShape(12.dp), - modifier = Modifier.aspectRatio(remoteVideoAspect) + modifier = Modifier.fillMaxSize() ) { isRemoteSurfaceView?.let { VideoCallSurfaceView( @@ -1167,17 +1167,75 @@ class WebRTCComposeActivity : ComponentActivity() { ) } } + + // Top bar overlay on remote video + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .align(Alignment.TopCenter) + .padding(top = 12.dp, start = 16.dp, end = 16.dp) + ) { + Icon( + painterResource(id = R.drawable.ic_back_call), + contentDescription = stringResource(R.string.back), + tint = Color.White, + modifier = Modifier + .clickable { + (context as ComponentActivity).finish() + } + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = isStatusText, + style = BChatTypography.titleMedium.copy( + color = Color.White, + fontSize = 16.sp, + fontWeight = FontWeight(700) + ) + ) + Spacer(modifier = Modifier.weight(1f)) + Text( + text = if (isSwitchCameraFlipEnabled && videoSwapped) localUserName else isPersonNameText, + style = BChatTypography.titleMedium.copy( + color = Color.White, + fontSize = 16.sp, + fontWeight = FontWeight(700) + ), + textAlign = TextAlign.Center, + maxLines = 1 + ) + Spacer(modifier = Modifier.weight(1f)) + Icon( + painter = painterResource(id = R.drawable.ic_lock_call), + contentDescription = lockDescription, + tint = Color.White, + modifier = Modifier.padding(horizontal = 5.dp) + ) + Text( + text = stringResource(id = R.string.end_to_end_encrypted), + style = BChatTypography.titleMedium.copy( + color = Color.White, + fontSize = 11.sp, + fontWeight = FontWeight(400) + ) + ) + } } } else { - Surface( - color = Color.Black, - shape = RoundedCornerShape(12.dp), + Box( modifier = Modifier.fillMaxSize() ) { - isRemoteSurfaceView?.let { - VideoCallSurfaceView( - surfaceView = it - ) + Surface( + color = Color.Black, + shape = RoundedCornerShape(12.dp), + modifier = Modifier.fillMaxSize() + ) { + isRemoteSurfaceView?.let { + VideoCallSurfaceView( + surfaceView = it + ) + } } } } @@ -1198,18 +1256,6 @@ class WebRTCComposeActivity : ComponentActivity() { ) ) } - if (isShowIncomingStatus) { - Text( - isStatusInComingText, - Modifier - .padding(all=12.dp) - .offset(y=(-60).dp), - style = BChatTypography.titleMedium.copy( - color = MaterialTheme.appColors.textColor, - fontSize = 18.sp, fontWeight = FontWeight(400) - ) - ) - } if (isShowCallAudioStatus) { Text( isStatusCallAudioText, @@ -1235,19 +1281,6 @@ class WebRTCComposeActivity : ComponentActivity() { ) } - Text( - text = if (isSwitchCameraFlipEnabled && videoSwapped) localUserName else isPersonNameText, - style = BChatTypography.titleMedium.copy( - color = MaterialTheme.appColors.textColor, - fontSize = 20.sp, - fontWeight = FontWeight(700) - ), - textAlign = TextAlign.Center, - modifier = Modifier - .padding(top=12.dp, start=12.dp, end=12.dp) - .offset(y=(-50).dp), - ) - if (isShowCallDurationStatus) { Text( isStatusCallDurationText, @@ -1277,22 +1310,24 @@ class WebRTCComposeActivity : ComponentActivity() { } } } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .align(Alignment.TopCenter) - .padding(top=16.dp, start=16.dp) - ) { - Icon( - painterResource(id = R.drawable.ic_back_call), - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.appColors.editTextColor, + if (!isLandscape) { + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .clickable { - (context as ComponentActivity).finish() - } - ) + .fillMaxWidth() + .align(Alignment.TopCenter) + .padding(top=16.dp, start=16.dp) + ) { + Icon( + painterResource(id = R.drawable.ic_back_call), + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.appColors.editTextColor, + modifier = Modifier + .clickable { + (context as ComponentActivity).finish() + } + ) + } } if (localVideoView) { @@ -1386,9 +1421,8 @@ class WebRTCComposeActivity : ComponentActivity() { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceBetween, modifier = Modifier - .padding(16.dp) .align(Alignment.BottomStart) - .offset(x=(16).dp, y=(10).dp) + .padding(start = if(isLandscape) 82.dp else 32.dp, bottom = 10.dp) .background( color=MaterialTheme.appColors.callBottomBackground, shape=RoundedCornerShape(50.dp) @@ -1438,58 +1472,76 @@ class WebRTCComposeActivity : ComponentActivity() { } if (isShowAnswerOption && isShowDeclineOption) { - Row( - horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Bottom, + modifier = Modifier .fillMaxWidth() - .offset(y=(-50).dp) + .offset(y= if(isLandscape)(-10).dp else (-50).dp) ) { - if (isShowAnswerOption) { - Box( - modifier = Modifier - .height(65.dp) - .width(65.dp) - .background( - MaterialTheme.appColors.walletDashboardReceiveButtonBackground, - shape=CircleShape - ) - .clickable { - if (callViewModel.currentCallState == CallViewModel.State.CALL_PRE_INIT) { - wantsToAnswer=true - updateControls() - } - answerCall(context) - }, contentAlignment = Alignment.Center - - ) { - Image( - painter = painterResource(id = R.drawable.ic_incoming_call), - contentDescription = answerCallDescription, - modifier = Modifier.padding(10.dp) + if (isShowIncomingStatus) { + Text( + isStatusInComingText, + Modifier.padding(bottom = 24.dp), + style = BChatTypography.titleMedium.copy( + color = MaterialTheme.appColors.textColor, + fontSize = 16.sp, + fontWeight = FontWeight(400) ) - } + ) } - if (isShowDeclineOption) { - Box( - modifier = Modifier - .height(65.dp) - .width(65.dp) - .background( - MaterialTheme.appColors.errorMessageColor, - shape=CircleShape + Row( + horizontalArrangement = Arrangement.spacedBy(50.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (isShowAnswerOption) { + Box( + modifier = Modifier + .height(65.dp) + .width(65.dp) + .background( + MaterialTheme.appColors.walletDashboardReceiveButtonBackground, + shape=CircleShape + ) + .clickable { + if (callViewModel.currentCallState == CallViewModel.State.CALL_PRE_INIT) { + wantsToAnswer=true + updateControls() + } + answerCall(context) + }, contentAlignment = Alignment.Center + + ) { + Image( + painter = painterResource(id = R.drawable.ic_incoming_call), + contentDescription = answerCallDescription, + modifier = Modifier.padding(10.dp) ) - .clickable { - val declineIntent= - WebRtcCallService.denyCallIntent(context) - context.startService(declineIntent) - }, contentAlignment = Alignment.Center + } + } + if (isShowDeclineOption) { + Box( + modifier = Modifier + .height(65.dp) + .width(65.dp) + .background( + MaterialTheme.appColors.errorMessageColor, + shape=CircleShape + ) + .clickable { + val declineIntent= + WebRtcCallService.denyCallIntent(context) + context.startService(declineIntent) + }, contentAlignment = Alignment.Center - ) { + ) { - Image( - painter = painterResource(id = R.drawable.ic_decline_call), - contentDescription = declineCallDescription, - modifier = Modifier.padding(10.dp) - ) + Image( + painter = painterResource(id = R.drawable.ic_decline_call), + contentDescription = declineCallDescription, + modifier = Modifier.padding(10.dp) + ) + } } } } @@ -1507,7 +1559,7 @@ class WebRTCComposeActivity : ComponentActivity() { modifier = Modifier .wrapContentSize() .paint( - painterResource(id=R.drawable.call_bottom_background_white), + painterResource(id= if (isLandscape) R.drawable.land_call_bottom_background else R.drawable.call_bottom_background_white), contentScale=ContentScale.FillBounds, colorFilter=ColorFilter.tint(MaterialTheme.appColors.callBottomBackground) ), @@ -1517,365 +1569,389 @@ class WebRTCComposeActivity : ComponentActivity() { horizontalArrangement = if (isLandscape) Arrangement.SpaceEvenly else Arrangement.SpaceAround, modifier = Modifier .fillMaxWidth() - .padding(vertical=20.dp) ) { if (isLandscape) { - if (isShowSpeakerOption) { - Box( - modifier = Modifier - .height(42.dp) - .width(42.dp) - .background( - MaterialTheme.appColors.qrCodeBackground, - shape=CircleShape - ) - .clickable { - if (isBluetoothIsConnected) { - expanded=!expanded - } else { - val command= - AudioManagerCommand.SetUserDevice(if (callViewModel.isSpeaker) SignalAudioManager.AudioDevice.EARPIECE else SignalAudioManager.AudioDevice.SPEAKER_PHONE) - WebRtcCallService.sendAudioManagerCommand( - context, - command - ) - } - - - }, contentAlignment = Alignment.Center - - ) { - if (isBluetoothIsSelected && isBluetoothIsConnected) { - - Image( - painter = if (isDarkTheme) { - painterResource(id = R.drawable.ic_bluetooth_call) - } else { - painterResource(id = R.drawable.ic_bluetooth_call) - }, - colorFilter = ColorFilter.tint( - color = MaterialTheme.appColors.primaryButtonColor - ), - contentDescription = speakerDescription, - modifier = Modifier.align(Alignment.Center) - - ) - } else { - Image( - painter = if (isDarkTheme) { - painterResource(id = R.drawable.ic_speaker_call) - } else { - painterResource(id = R.drawable.ic_speaker_call_white) - }, - colorFilter = ColorFilter.tint( - color = (if (isSpeakerIsSelected) MaterialTheme.appColors.primaryButtonColor else MaterialTheme.appColors.iconTint) - ), - contentDescription = speakerDescription, - modifier = Modifier.align(Alignment.Center) - - ) - } - - if (isBluetoothIsConnected) { - Image( - painter = if (isDarkTheme) { - painterResource(id = R.drawable.ic_switch_speaker_call) - } else { - painterResource(id = R.drawable.ic_switch_speaker_call_white) - }, - contentDescription = speakerDescription, - modifier = Modifier - .align(Alignment.BottomEnd) - .offset(x=((0).dp), y=((5).dp)) - ) - } - } - } - if (isShowMuteOption) { - Box( - modifier = Modifier - .height(42.dp) - .width(42.dp) - .background( - MaterialTheme.appColors.qrCodeBackground, - shape=CircleShape - ) - .clickable(isMuteOptionClickable) { enableMuteOption() }, - contentAlignment = Alignment.Center - - ) { - Image( - painter = - if (isMuteOptionIconChange) { - if (isDarkTheme) { - painterResource(id = R.drawable.ic_unmute_call) + // LEFT group: Speaker + Mute (SpaceEvenly) + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) + ) { + if (isShowSpeakerOption) { + Box( + modifier = Modifier + .height(42.dp) + .width(42.dp) + .background( + MaterialTheme.appColors.qrCodeBackground, + shape=CircleShape + ) + .clickable { + if (isBluetoothIsConnected) { + expanded=!expanded } else { - painterResource(id = R.drawable.ic_unmute_call_white) + val command= + AudioManagerCommand.SetUserDevice(if (callViewModel.isSpeaker) SignalAudioManager.AudioDevice.EARPIECE else SignalAudioManager.AudioDevice.SPEAKER_PHONE) + WebRtcCallService.sendAudioManagerCommand( + context, + command + ) } - } else { - if (isDarkTheme) { - painterResource(id = R.drawable.ic_mute_call) + }, contentAlignment = Alignment.Center + ) { + if (isBluetoothIsSelected && isBluetoothIsConnected) { + Image( + painter = painterResource(id = R.drawable.ic_bluetooth_call), + colorFilter = ColorFilter.tint( + color = MaterialTheme.appColors.primaryButtonColor + ), + contentDescription = speakerDescription, + modifier = Modifier.align(Alignment.Center) + ) + } else { + Image( + painter = if (isDarkTheme) { + painterResource(id = R.drawable.ic_speaker_call) } else { - painterResource(id = R.drawable.ic_mute_call_white) - } - }, - contentDescription = muteDescription, - modifier = Modifier.padding(10.dp) - ) + painterResource(id = R.drawable.ic_speaker_call_white) + }, + colorFilter = ColorFilter.tint( + color = (if (isSpeakerIsSelected) MaterialTheme.appColors.primaryButtonColor else MaterialTheme.appColors.iconTint) + ), + contentDescription = speakerDescription, + modifier = Modifier.align(Alignment.Center) + ) + } + if (isBluetoothIsConnected) { + Image( + painter = if (isDarkTheme) { + painterResource(id = R.drawable.ic_switch_speaker_call) + } else { + painterResource(id = R.drawable.ic_switch_speaker_call_white) + }, + contentDescription = speakerDescription, + modifier = Modifier + .align(Alignment.BottomEnd) + .offset(x=0.dp, y=5.dp) + ) + } + } } - } - if (isShowSwitchCameraOption) { - Box( - modifier = Modifier - .height(42.dp) - .width(42.dp) - .background( - MaterialTheme.appColors.qrCodeBackground, - shape=CircleShape + if (isShowMuteOption) { + Box( + modifier = Modifier + .height(42.dp) + .width(42.dp) + .background( + MaterialTheme.appColors.qrCodeBackground, + shape=CircleShape + ) + .clickable(isMuteOptionClickable) { enableMuteOption() }, + contentAlignment = Alignment.Center + ) { + Image( + painter = + if (isMuteOptionIconChange) { + if (isDarkTheme) { + painterResource(id = R.drawable.ic_unmute_call) + } else { + painterResource(id = R.drawable.ic_unmute_call_white) + } + } else { + if (isDarkTheme) { + painterResource(id = R.drawable.ic_mute_call) + } else { + painterResource(id = R.drawable.ic_mute_call_white) + } + }, + contentDescription = muteDescription, + modifier = Modifier.padding(10.dp) ) - .clickable(enabled=isSwitchCameraFlipEnabled) { - switchCamera() - }, contentAlignment = Alignment.Center - - ) { - - Image( - painter = painterResource( - id = if (isDarkTheme && isSwitchCameraFlipEnabled) R.drawable.ic_switch_camera_call - else if (!isDarkTheme && isSwitchCameraFlipEnabled) R.drawable.ic_switch_camera_call_white - else R.drawable.ic_switch_camera_disable_call - ), - contentDescription = switchCamDescription, - modifier = Modifier.padding(10.dp) - ) + } } } - if (isShowVideoOption) { + + // CENTER: End call button + if (!isLandscape && isShowEndCallOption) { Box( modifier = Modifier - .height(42.dp) - .width(42.dp) + .height(65.dp) + .width(65.dp) .background( - MaterialTheme.appColors.qrCodeBackground, + MaterialTheme.appColors.errorMessageColor, shape = CircleShape ) .clickable { - if (isShowCallDurationStatus) { - enableCamera() - } - }, contentAlignment = Alignment.Center - + context.startService( + WebRtcCallService.hangupIntent( + context + ) + ) + }, + contentAlignment = Alignment.Center ) { - Image( - painter = - if (isSelectedVideoOption) { - if (isDarkTheme) { - painterResource(id = R.drawable.ic_video_disabled_call) - } else { - painterResource(id = R.drawable.ic_video_disable_call_white) - } - } else { - if (isDarkTheme) { - painterResource(id = R.drawable.ic_video_call) - } else { - painterResource(id = R.drawable.ic_video_call_white) - } - }, - contentDescription = enableVideoDescription, - modifier = Modifier.padding(10.dp) + painter = painterResource(id = R.drawable.ic_decline_call), + contentDescription = endCallDescription ) } } - } else { - Row( - - modifier = Modifier.wrapContentWidth() - - ) { - if (isShowSpeakerOption) { - Box( - modifier = Modifier - .height(42.dp) - .width(42.dp) - .background( - MaterialTheme.appColors.qrCodeBackground, - shape=CircleShape - ) - .clickable { - if (isBluetoothIsConnected) { - expanded=!expanded - } else { - val command= - AudioManagerCommand.SetUserDevice(if (callViewModel.isSpeaker) SignalAudioManager.AudioDevice.EARPIECE else SignalAudioManager.AudioDevice.SPEAKER_PHONE) - WebRtcCallService.sendAudioManagerCommand( - context, - command - ) - } - - - }, contentAlignment = Alignment.Center - - ) { - if (isBluetoothIsSelected && isBluetoothIsConnected) { - - Image( - painter = if (isDarkTheme) { - painterResource(id = R.drawable.ic_bluetooth_call) - } else { - painterResource(id = R.drawable.ic_bluetooth_call) - }, - colorFilter = ColorFilter.tint( - color = MaterialTheme.appColors.primaryButtonColor - ), - contentDescription = speakerDescription, - modifier = Modifier.align(Alignment.Center) - ) - } else { + // RIGHT group: Switch Camera + Video (SpaceEvenly) + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) + ) { + if (isShowSwitchCameraOption) { + Box( + modifier = Modifier + .height(42.dp) + .width(42.dp) + .background( + MaterialTheme.appColors.qrCodeBackground, + shape=CircleShape + ) + .clickable(enabled=isSwitchCameraFlipEnabled) { + switchCamera() + }, contentAlignment = Alignment.Center + ) { Image( - painter = if (isDarkTheme) { - painterResource(id = R.drawable.ic_speaker_call) - } else { - painterResource(id = R.drawable.ic_speaker_call_white) - }, - colorFilter = ColorFilter.tint( - color = (if (isSpeakerIsSelected) MaterialTheme.appColors.primaryButtonColor else MaterialTheme.appColors.iconTint) + painter = painterResource( + id = if (isDarkTheme && isSwitchCameraFlipEnabled) R.drawable.ic_switch_camera_call + else if (!isDarkTheme && isSwitchCameraFlipEnabled) R.drawable.ic_switch_camera_call_white + else R.drawable.ic_switch_camera_disable_call ), - contentDescription = speakerDescription, - modifier = Modifier.align(Alignment.Center) - + contentDescription = switchCamDescription, + modifier = Modifier.padding(10.dp) ) } - - if (isBluetoothIsConnected) { + } + if (isShowVideoOption) { + Box( + modifier = Modifier + .height(42.dp) + .width(42.dp) + .background( + MaterialTheme.appColors.qrCodeBackground, + shape = CircleShape + ) + .clickable { + if (isShowCallDurationStatus) { + enableCamera() + } + }, contentAlignment = Alignment.Center + ) { Image( - painter = if (isDarkTheme) { - painterResource(id = R.drawable.ic_switch_speaker_call) - } else { - painterResource(id = R.drawable.ic_switch_speaker_call_white) - }, - contentDescription = speakerDescription, - modifier = Modifier - .align(Alignment.BottomEnd) - .offset(x=((0).dp), y=((5).dp)) + painter = + if (isSelectedVideoOption) { + if (isDarkTheme) { + painterResource(id = R.drawable.ic_video_disabled_call) + } else { + painterResource(id = R.drawable.ic_video_disable_call_white) + } + } else { + if (isDarkTheme) { + painterResource(id = R.drawable.ic_video_call) + } else { + painterResource(id = R.drawable.ic_video_call_white) + } + }, + contentDescription = enableVideoDescription, + modifier = Modifier.padding(10.dp) ) } } } + } else { + Row( - Spacer(modifier = Modifier.width(20.dp)) + modifier = Modifier.wrapContentWidth() - if (isShowMuteOption) { + ) { + if (isShowSpeakerOption) { + Box( + modifier = Modifier + .height(42.dp) + .width(42.dp) + .background( + MaterialTheme.appColors.qrCodeBackground, + shape=CircleShape + ) + .clickable { + if (isBluetoothIsConnected) { + expanded=!expanded + } else { + val command= + AudioManagerCommand.SetUserDevice(if (callViewModel.isSpeaker) SignalAudioManager.AudioDevice.EARPIECE else SignalAudioManager.AudioDevice.SPEAKER_PHONE) + WebRtcCallService.sendAudioManagerCommand( + context, + command + ) + } - Box( - modifier = Modifier - .height(42.dp) - .width(42.dp) - .background( - MaterialTheme.appColors.qrCodeBackground, - shape=CircleShape - ) - .clickable(isMuteOptionClickable) { enableMuteOption() }, - contentAlignment = Alignment.Center - ) { - Image( - painter = - if (isMuteOptionIconChange) { - if (isDarkTheme) { - painterResource(id = R.drawable.ic_unmute_call) + }, contentAlignment = Alignment.Center + + ) { + if (isBluetoothIsSelected && isBluetoothIsConnected) { + + Image( + painter = if (isDarkTheme) { + painterResource(id = R.drawable.ic_bluetooth_call) } else { - painterResource(id = R.drawable.ic_unmute_call_white) - } - } else { - if (isDarkTheme) { - painterResource(id = R.drawable.ic_mute_call) + painterResource(id = R.drawable.ic_bluetooth_call) + }, + colorFilter = ColorFilter.tint( + color = MaterialTheme.appColors.primaryButtonColor + ), + contentDescription = speakerDescription, + modifier = Modifier.align(Alignment.Center) + + ) + } else { + Image( + painter = if (isDarkTheme) { + painterResource(id = R.drawable.ic_speaker_call) } else { - painterResource(id = R.drawable.ic_mute_call_white) - } - }, - contentDescription = muteDescription, - modifier = Modifier.padding(10.dp) - ) + painterResource(id = R.drawable.ic_speaker_call_white) + }, + colorFilter = ColorFilter.tint( + color = (if (isSpeakerIsSelected) MaterialTheme.appColors.primaryButtonColor else MaterialTheme.appColors.iconTint) + ), + contentDescription = speakerDescription, + modifier = Modifier.align(Alignment.Center) + + ) + } + + if (isBluetoothIsConnected) { + Image( + painter = if (isDarkTheme) { + painterResource(id = R.drawable.ic_switch_speaker_call) + } else { + painterResource(id = R.drawable.ic_switch_speaker_call_white) + }, + contentDescription = speakerDescription, + modifier = Modifier + .align(Alignment.BottomEnd) + .offset(x=((0).dp), y=((5).dp)) + ) + } + } } - } - } - Row( - //horizontalArrangement=Arrangement.SpaceAround, - modifier = Modifier.wrapContentWidth() - ) { - // Options within the second box - if (isShowSwitchCameraOption) { - Box( - modifier = Modifier - .height(42.dp) - .width(42.dp) - .background( - MaterialTheme.appColors.qrCodeBackground, - shape=CircleShape - ) - .clickable(enabled=isSwitchCameraFlipEnabled) { - switchCamera() - }, contentAlignment = Alignment.Center + Spacer(modifier = Modifier.width(20.dp)) - ) { + if (isShowMuteOption) { - Image( - painter = painterResource( - id = if (isDarkTheme && isSwitchCameraFlipEnabled) R.drawable.ic_switch_camera_call - else if (!isDarkTheme && isSwitchCameraFlipEnabled) R.drawable.ic_switch_camera_call_white - else R.drawable.ic_switch_camera_disable_call - ), - contentDescription = switchCamDescription, - modifier = Modifier.padding(10.dp) - ) + Box( + modifier = Modifier + .height(42.dp) + .width(42.dp) + .background( + MaterialTheme.appColors.qrCodeBackground, + shape=CircleShape + ) + .clickable(isMuteOptionClickable) { enableMuteOption() }, + contentAlignment = Alignment.Center + + ) { + Image( + painter = + if (isMuteOptionIconChange) { + if (isDarkTheme) { + painterResource(id = R.drawable.ic_unmute_call) + } else { + painterResource(id = R.drawable.ic_unmute_call_white) + } + } else { + if (isDarkTheme) { + painterResource(id = R.drawable.ic_mute_call) + } else { + painterResource(id = R.drawable.ic_mute_call_white) + } + }, + contentDescription = muteDescription, + modifier = Modifier.padding(10.dp) + ) + } } } + Row( + //horizontalArrangement=Arrangement.SpaceAround, + modifier = Modifier.wrapContentWidth() + + ) { + // Options within the second box + if (isShowSwitchCameraOption) { + Box( + modifier = Modifier + .height(42.dp) + .width(42.dp) + .background( + MaterialTheme.appColors.qrCodeBackground, + shape=CircleShape + ) + .clickable(enabled=isSwitchCameraFlipEnabled) { + switchCamera() + }, contentAlignment = Alignment.Center - Spacer(modifier = Modifier.width(20.dp)) + ) { - if (isShowVideoOption) { - Box( - modifier = Modifier - .height(42.dp) - .width(42.dp) - .background( - MaterialTheme.appColors.qrCodeBackground, - shape = CircleShape + Image( + painter = painterResource( + id = if (isDarkTheme && isSwitchCameraFlipEnabled) R.drawable.ic_switch_camera_call + else if (!isDarkTheme && isSwitchCameraFlipEnabled) R.drawable.ic_switch_camera_call_white + else R.drawable.ic_switch_camera_disable_call + ), + contentDescription = switchCamDescription, + modifier = Modifier.padding(10.dp) ) - .clickable { - if (isShowCallDurationStatus) { - enableCamera() - } - }, contentAlignment = Alignment.Center + } + } - ) { + Spacer(modifier = Modifier.width(20.dp)) - Image( - painter = - if (isSelectedVideoOption) { - if (isDarkTheme) { - painterResource(id = R.drawable.ic_video_disabled_call) - } else { - painterResource(id = R.drawable.ic_video_disable_call_white) + if (isShowVideoOption) { + Box( + modifier = Modifier + .height(42.dp) + .width(42.dp) + .background( + MaterialTheme.appColors.qrCodeBackground, + shape = CircleShape + ) + .clickable { + if (isShowCallDurationStatus) { + enableCamera() } - } else { - if (isDarkTheme) { - painterResource(id = R.drawable.ic_video_call) + }, contentAlignment = Alignment.Center + + ) { + + Image( + painter = + if (isSelectedVideoOption) { + if (isDarkTheme) { + painterResource(id = R.drawable.ic_video_disabled_call) + } else { + painterResource(id = R.drawable.ic_video_disable_call_white) + } } else { - painterResource(id = R.drawable.ic_video_call_white) - } - }, - contentDescription = enableVideoDescription, - modifier = Modifier.padding(10.dp) - ) + if (isDarkTheme) { + painterResource(id = R.drawable.ic_video_call) + } else { + painterResource(id = R.drawable.ic_video_call_white) + } + }, + contentDescription = enableVideoDescription, + modifier = Modifier.padding(10.dp) + ) + } } } } - } } if (isShowEndCallOption) { @@ -1883,7 +1959,7 @@ class WebRTCComposeActivity : ComponentActivity() { modifier = Modifier .height(65.dp) .width(65.dp) - .offset(y = (-43).dp) + .offset(y = (-33).dp) .background( MaterialTheme.appColors.errorMessageColor, shape = CircleShape diff --git a/app/src/main/res/drawable/call_bottom_background_white.xml b/app/src/main/res/drawable/call_bottom_background_white.xml index 733f9ac7e..ef3671eae 100644 --- a/app/src/main/res/drawable/call_bottom_background_white.xml +++ b/app/src/main/res/drawable/call_bottom_background_white.xml @@ -1,8 +1,8 @@ + android:viewportHeight="70"> diff --git a/app/src/main/res/drawable/ic_back_arrow.xml b/app/src/main/res/drawable/ic_back_arrow.xml index b89590da6..1db96f486 100644 --- a/app/src/main/res/drawable/ic_back_arrow.xml +++ b/app/src/main/res/drawable/ic_back_arrow.xml @@ -1,4 +1,4 @@ - diff --git a/app/src/main/res/drawable/ic_language.xml b/app/src/main/res/drawable/ic_language.xml new file mode 100644 index 000000000..f68e35b05 --- /dev/null +++ b/app/src/main/res/drawable/ic_language.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_no_contact_found.xml b/app/src/main/res/drawable/ic_no_contact_found.xml index 46ae6fe00..86f5deb5f 100644 --- a/app/src/main/res/drawable/ic_no_contact_found.xml +++ b/app/src/main/res/drawable/ic_no_contact_found.xml @@ -1,60 +1,57 @@ - + android:width="107dp" + android:height="103dp" + android:viewportWidth="107" + android:viewportHeight="103"> - + android:width="107dp" + android:height="103dp" + android:viewportWidth="107" + android:viewportHeight="103"> + + diff --git a/app/src/main/res/drawable/land_call_background_dark.xml b/app/src/main/res/drawable/land_call_background_dark.xml new file mode 100644 index 000000000..5b1d6834d --- /dev/null +++ b/app/src/main/res/drawable/land_call_background_dark.xml @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/land_call_background_white.xml b/app/src/main/res/drawable/land_call_background_white.xml new file mode 100644 index 000000000..a2cb0fffa --- /dev/null +++ b/app/src/main/res/drawable/land_call_background_white.xml @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/land_call_bottom_background.xml b/app/src/main/res/drawable/land_call_bottom_background.xml new file mode 100644 index 000000000..b793f3aba --- /dev/null +++ b/app/src/main/res/drawable/land_call_bottom_background.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/toast_message_background.xml b/app/src/main/res/drawable/toast_message_background.xml new file mode 100644 index 000000000..894abe9cb --- /dev/null +++ b/app/src/main/res/drawable/toast_message_background.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_conversation_v2.xml b/app/src/main/res/layout-land/activity_conversation_v2.xml index 82fc2afd3..e555cb427 100644 --- a/app/src/main/res/layout-land/activity_conversation_v2.xml +++ b/app/src/main/res/layout-land/activity_conversation_v2.xml @@ -575,7 +575,11 @@ android:backgroundTint="@color/receive_payment_card_background" android:fontFamily="@font/open_sans_semi_bold" android:gravity="center" - android:padding="16dp" + android:maxLines="1" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:paddingTop="16dp" + android:paddingBottom="16dp" android:text="@string/clear_chat" android:textAllCaps="false" android:textColor="@color/clear_red_color" @@ -594,7 +598,11 @@ android:backgroundTint="@color/bchat_id_text_color" android:fontFamily="@font/open_sans_semi_bold" android:gravity="center" - android:padding="16dp" + android:maxLines="1" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:paddingTop="16dp" + android:paddingBottom="16dp" android:text="@string/ConversationActivity_unblock" android:textAllCaps="false" android:textColor="@color/white" @@ -646,7 +654,7 @@ android:layout_marginTop="@dimen/small_spacing" android:fontFamily="@font/open_sans_bold" android:paddingHorizontal="@dimen/massive_spacing" - android:text="@string/message_request" + android:text="@string/activity_message_requests_title" android:textColor="@color/text" android:textSize="16sp" android:visibility="visible" /> diff --git a/app/src/main/res/layout-land/activity_recovery_phrase_restore.xml b/app/src/main/res/layout-land/activity_recovery_phrase_restore.xml index 9a9a67709..712086b57 100644 --- a/app/src/main/res/layout-land/activity_recovery_phrase_restore.xml +++ b/app/src/main/res/layout-land/activity_recovery_phrase_restore.xml @@ -91,7 +91,7 @@ android:minHeight="48dp" android:paddingStart="24dp" android:paddingEnd="24dp" - android:text="@string/message_requests_clear" + android:text="@string/clear" app:icon="@drawable/ic_clear_seed" app:iconTint="@color/version_color" /> diff --git a/app/src/main/res/layout-land/contact_selection_list_fragment.xml b/app/src/main/res/layout-land/contact_selection_list_fragment.xml index e517d9334..51f80a176 100644 --- a/app/src/main/res/layout-land/contact_selection_list_fragment.xml +++ b/app/src/main/res/layout-land/contact_selection_list_fragment.xml @@ -70,7 +70,7 @@ diff --git a/app/src/main/res/layout-land/share_activity.xml b/app/src/main/res/layout-land/share_activity.xml index 496522d59..c321d0bef 100644 --- a/app/src/main/res/layout-land/share_activity.xml +++ b/app/src/main/res/layout-land/share_activity.xml @@ -27,9 +27,9 @@ android:layout_height="wrap_content" android:layout_marginStart="16dp" android:fontFamily="@font/open_sans_medium" - android:text="@string/forward" + android:text="@string/media_preview__forward_title" android:textSize="18sp" - tools:text="@string/forward" /> + tools:text="@string/media_preview__forward_title" /> diff --git a/app/src/main/res/layout-sw400dp-land/activity_recovery_phrase_restore.xml b/app/src/main/res/layout-sw400dp-land/activity_recovery_phrase_restore.xml index 9a9a67709..712086b57 100644 --- a/app/src/main/res/layout-sw400dp-land/activity_recovery_phrase_restore.xml +++ b/app/src/main/res/layout-sw400dp-land/activity_recovery_phrase_restore.xml @@ -91,7 +91,7 @@ android:minHeight="48dp" android:paddingStart="24dp" android:paddingEnd="24dp" - android:text="@string/message_requests_clear" + android:text="@string/clear" app:icon="@drawable/ic_clear_seed" app:iconTint="@color/version_color" /> diff --git a/app/src/main/res/layout-sw400dp/activity_app_lock.xml b/app/src/main/res/layout-sw400dp/activity_app_lock.xml index be20f9bd4..7392469a9 100644 --- a/app/src/main/res/layout-sw400dp/activity_app_lock.xml +++ b/app/src/main/res/layout-sw400dp/activity_app_lock.xml @@ -19,8 +19,8 @@ @@ -60,10 +60,10 @@ android:id="@+id/enableButton" android:layout_width="match_parent" android:layout_height="@dimen/medium_button_height" - android:layout_marginLeft="@dimen/massive_spacing" - android:layout_marginRight="@dimen/massive_spacing" + android:layout_marginStart="@dimen/massive_spacing" + android:layout_marginEnd="@dimen/massive_spacing" android:layout_marginBottom="30dp" - android:text="@string/enable" /> + android:text="@string/dialog_link_preview_enable_button_title" /> \ No newline at end of file diff --git a/app/src/main/res/layout-sw400dp/activity_create_password.xml b/app/src/main/res/layout-sw400dp/activity_create_password.xml index fd7ce5ea7..ca559c6ab 100644 --- a/app/src/main/res/layout-sw400dp/activity_create_password.xml +++ b/app/src/main/res/layout-sw400dp/activity_create_password.xml @@ -16,16 +16,16 @@ android:layout_width="match_parent" android:layout_height="30dp" /> - + - + @@ -42,7 +46,7 @@ android:layout_width="match_parent" android:layout_height="54dp" android:gravity="center_vertical" - android:hint="@string/activity_display_name_edit_text_hint" + android:hint="@string/enter_name" android:imeOptions="actionDone" android:inputType="text" android:maxLines="1" @@ -55,13 +59,16 @@ + android:textAlignment="textStart" + android:textSize="@dimen/small_font_size" + android:textStyle="normal" /> + @@ -35,9 +35,9 @@ android:id="@+id/fcmOptionView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/very_large_spacing" + android:layout_marginStart="@dimen/very_large_spacing" android:layout_marginTop="@dimen/large_spacing" - android:layout_marginRight="@dimen/very_large_spacing" + android:layout_marginEnd="@dimen/very_large_spacing" android:padding="12dp" android:orientation="vertical" android:background="@drawable/pn_option_background"> @@ -56,7 +56,7 @@ android:layout_marginTop="4dp" android:textSize="@dimen/very_small_font_size" android:textColor="@color/text" - android:text="@string/activity_pn_mode_fast_mode_explanation" /> + android:text="@string/preferences_notifications_strategy_category_fast_mode_summary" /> @@ -103,9 +103,9 @@ android:id="@+id/registerButton" android:layout_width="match_parent" android:layout_height="@dimen/medium_button_height" - android:layout_marginLeft="@dimen/massive_spacing" + android:layout_marginStart="@dimen/massive_spacing" android:layout_marginTop="@dimen/very_large_spacing" - android:layout_marginRight="@dimen/massive_spacing" + android:layout_marginEnd="@dimen/massive_spacing" android:layout_marginBottom="@dimen/onboarding_button_bottom_offset" android:text="@string/continue_2" /> diff --git a/app/src/main/res/layout-sw400dp/activity_recovery_phrase.xml b/app/src/main/res/layout-sw400dp/activity_recovery_phrase.xml index b76e9f2c6..fe79e7597 100644 --- a/app/src/main/res/layout-sw400dp/activity_recovery_phrase.xml +++ b/app/src/main/res/layout-sw400dp/activity_recovery_phrase.xml @@ -23,9 +23,9 @@ android:fontFamily="@font/open_sans_medium" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginStart="@dimen/very_large_spacing" + android:layout_marginEnd="@dimen/very_large_spacing" android:layout_gravity="center_horizontal" - android:layout_marginLeft="@dimen/very_large_spacing" - android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginTop="@dimen/medium_spacing" android:layout_marginBottom="@dimen/large_spacing" android:maxHeight="80dp" @@ -35,8 +35,8 @@ android:fontFamily="@font/open_sans_medium" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/massive_spacing" - android:layout_marginRight="@dimen/massive_spacing" + android:layout_marginStart="@dimen/massive_spacing" + android:layout_marginEnd="@dimen/massive_spacing" android:layout_marginTop="@dimen/medium_spacing" android:layout_marginBottom="@dimen/medium_spacing" android:textSize="18sp" @@ -50,10 +50,10 @@ @@ -70,7 +70,7 @@ android:textAlignment="center" android:textColor="@color/text" android:visibility="visible" - android:hint="Seed" /> + android:hint="@string/seed" /> @@ -123,21 +123,21 @@ android:layout_marginStart="40dp" android:layout_marginTop="30dp" android:drawableStart="@drawable/ic_baseline_content_copy_24" - android:paddingLeft="30dp" - android:paddingRight="30dp" - android:text="Copy" /> + android:paddingStart="30dp" + android:paddingEnd="30dp" + android:text="@string/copy" />