mirror of
https://github.com/SimpleMobileTools/Simple-SMS-Messenger.git
synced 2025-06-05 21:49:22 +02:00
Merge branch 'master' into enter-to-send
This commit is contained in:
@@ -63,10 +63,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:0828fecd09'
|
||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:9162225f33'
|
||||
implementation 'org.greenrobot:eventbus:3.3.1'
|
||||
implementation 'com.github.tibbi:IndicatorFastScroll:4524cd0b61'
|
||||
implementation 'com.github.tibbi:android-smsmms:875a46a9c4'
|
||||
implementation 'com.github.tibbi:android-smsmms:3581774c39'
|
||||
implementation "me.leolin:ShortcutBadger:1.1.22"
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'com.googlecode.ez-vcard:ez-vcard:0.11.3'
|
||||
|
@@ -52,7 +52,8 @@
|
||||
<activity
|
||||
android:name=".activities.ThreadActivity"
|
||||
android:exported="false"
|
||||
android:parentActivityName=".activities.MainActivity" />
|
||||
android:parentActivityName=".activities.MainActivity"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.SearchActivity"
|
||||
@@ -93,17 +94,14 @@
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="text/plain" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
@@ -87,6 +87,7 @@ class MainActivity : SimpleActivity() {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setupToolbar(main_toolbar)
|
||||
|
||||
if (storedTextColor != getProperTextColor()) {
|
||||
(conversations_list.adapter as? ConversationsAdapter)?.updateTextColor(getProperTextColor())
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.net.Uri
|
||||
@@ -28,21 +27,15 @@ import android.view.Gravity
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.LinearLayout.LayoutParams
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import androidx.core.view.*
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||
@@ -54,6 +47,7 @@ import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.commons.models.SimpleContact
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.adapters.AttachmentsAdapter
|
||||
import com.simplemobiletools.smsmessenger.adapters.AutoCompleteTextViewAdapter
|
||||
import com.simplemobiletools.smsmessenger.adapters.ThreadAdapter
|
||||
import com.simplemobiletools.smsmessenger.dialogs.ScheduleMessageDialog
|
||||
@@ -61,8 +55,8 @@ import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import kotlinx.android.synthetic.main.activity_thread.*
|
||||
import kotlinx.android.synthetic.main.item_attachment.view.*
|
||||
import kotlinx.android.synthetic.main.item_selected_contact.view.*
|
||||
import kotlinx.android.synthetic.main.layout_attachment_picker.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
@@ -73,12 +67,6 @@ import java.io.OutputStream
|
||||
|
||||
class ThreadActivity : SimpleActivity() {
|
||||
private val MIN_DATE_TIME_DIFF_SECS = 300
|
||||
private val PICK_ATTACHMENT_INTENT = 1
|
||||
private val PICK_SAVE_FILE_INTENT = 11
|
||||
private val TAKE_PHOTO_INTENT = 42
|
||||
|
||||
private val TYPE_TAKE_PHOTO = 12
|
||||
private val TYPE_CHOOSE_PHOTO = 13
|
||||
|
||||
private val TYPE_EDIT = 14
|
||||
private val TYPE_SEND = 15
|
||||
@@ -94,8 +82,6 @@ class ThreadActivity : SimpleActivity() {
|
||||
private var privateContacts = ArrayList<SimpleContact>()
|
||||
private var messages = ArrayList<Message>()
|
||||
private val availableSIMCards = ArrayList<SIMCard>()
|
||||
private var attachmentSelections = mutableMapOf<String, AttachmentSelection>()
|
||||
private val imageCompressor by lazy { ImageCompressor(this) }
|
||||
private var lastAttachmentUri: String? = null
|
||||
private var capturedImageUri: Uri? = null
|
||||
private var loadingOlderMessages = false
|
||||
@@ -106,6 +92,8 @@ class ThreadActivity : SimpleActivity() {
|
||||
private var scheduledMessage: Message? = null
|
||||
private lateinit var scheduledDateTime: DateTime
|
||||
|
||||
private var isAttachmentPickerVisible = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_thread)
|
||||
@@ -146,6 +134,10 @@ class ThreadActivity : SimpleActivity() {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
setupAttachmentPickerView()
|
||||
setupKeyboardListener()
|
||||
hideAttachmentPicker()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -162,17 +154,25 @@ class ThreadActivity : SimpleActivity() {
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
if (thread_type_message.value != "" && attachmentSelections.isEmpty()) {
|
||||
if (thread_type_message.value != "" && getAttachmentSelections().isEmpty()) {
|
||||
saveSmsDraft(thread_type_message.value, threadId)
|
||||
} else {
|
||||
deleteSmsDraft(threadId)
|
||||
}
|
||||
|
||||
bus?.post(Events.RefreshMessages())
|
||||
|
||||
isActivityVisible = false
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
isAttachmentPickerVisible = false
|
||||
if (attachment_picker_holder.isVisible()) {
|
||||
hideAttachmentPicker()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
bus?.unregister(this)
|
||||
@@ -215,13 +215,16 @@ class ThreadActivity : SimpleActivity() {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData)
|
||||
if (resultCode != Activity.RESULT_OK) return
|
||||
val data = resultData?.data
|
||||
|
||||
if (requestCode == TAKE_PHOTO_INTENT && capturedImageUri != null) {
|
||||
if (requestCode == CAPTURE_PHOTO_INTENT && capturedImageUri != null) {
|
||||
addAttachment(capturedImageUri!!)
|
||||
} else if (requestCode == PICK_ATTACHMENT_INTENT && resultData != null && resultData.data != null) {
|
||||
addAttachment(resultData.data!!)
|
||||
} else if (requestCode == PICK_SAVE_FILE_INTENT && resultData != null && resultData.data != null) {
|
||||
saveAttachment(resultData)
|
||||
} else if (data != null) {
|
||||
when (requestCode) {
|
||||
CAPTURE_VIDEO_INTENT, PICK_DOCUMENT_INTENT, CAPTURE_AUDIO_INTENT, PICK_PHOTO_INTENT, PICK_VIDEO_INTENT -> addAttachment(data)
|
||||
PICK_CONTACT_INTENT -> addContactAttachment(data)
|
||||
PICK_SAVE_FILE_INTENT -> saveAttachment(resultData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,11 +375,13 @@ class ThreadActivity : SimpleActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
confirm_inserted_number?.setOnClickListener {
|
||||
val number = add_contact_or_number.value
|
||||
val phoneNumber = PhoneNumber(number, 0, "", number)
|
||||
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", arrayListOf(phoneNumber), ArrayList(), ArrayList())
|
||||
addSelectedContact(contact)
|
||||
runOnUiThread {
|
||||
confirm_inserted_number?.setOnClickListener {
|
||||
val number = add_contact_or_number.value
|
||||
val phoneNumber = PhoneNumber(number, 0, "", number)
|
||||
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", arrayListOf(phoneNumber), ArrayList(), ArrayList())
|
||||
addSelectedContact(contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,6 +433,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
setTextColor(textColor)
|
||||
compoundDrawables.forEach { it?.applyColorFilter(textColor) }
|
||||
}
|
||||
|
||||
confirm_manage_contacts.applyColorFilter(textColor)
|
||||
thread_add_attachment.applyColorFilter(textColor)
|
||||
|
||||
@@ -441,6 +447,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
thread_send_message.setOnClickListener {
|
||||
sendMessage()
|
||||
}
|
||||
|
||||
thread_send_message.setOnLongClickListener {
|
||||
if (!isScheduledMessage) {
|
||||
launchScheduleSendDialog()
|
||||
@@ -499,7 +506,15 @@ class ThreadActivity : SimpleActivity() {
|
||||
|
||||
thread_type_message.setText(intent.getStringExtra(THREAD_TEXT))
|
||||
thread_add_attachment.setOnClickListener {
|
||||
takeOrPickPhotoVideo()
|
||||
if (attachment_picker_holder.isVisible()) {
|
||||
isAttachmentPickerVisible = false
|
||||
WindowCompat.getInsetsController(window, thread_type_message).show(WindowInsetsCompat.Type.ime())
|
||||
} else {
|
||||
isAttachmentPickerVisible = true
|
||||
showOrHideAttachmentPicker()
|
||||
WindowCompat.getInsetsController(window, thread_type_message).hide(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
window.decorView.requestApplyInsets()
|
||||
}
|
||||
|
||||
if (intent.extras?.containsKey(THREAD_ATTACHMENT_URI) == true) {
|
||||
@@ -796,135 +811,130 @@ class ThreadActivity : SimpleActivity() {
|
||||
return items
|
||||
}
|
||||
|
||||
private fun takeOrPickPhotoVideo() {
|
||||
val items = arrayListOf(
|
||||
RadioItem(TYPE_TAKE_PHOTO, getString(R.string.take_photo)),
|
||||
RadioItem(TYPE_CHOOSE_PHOTO, getString(R.string.choose_photo))
|
||||
)
|
||||
RadioGroupDialog(this, items = items) {
|
||||
val checkedId = it as Int
|
||||
if (checkedId == TYPE_TAKE_PHOTO) {
|
||||
launchTakePhotoIntent()
|
||||
} else if (checkedId == TYPE_CHOOSE_PHOTO) {
|
||||
launchPickPhotoVideoIntent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchTakePhotoIntent() {
|
||||
val imageFile = createImageFile()
|
||||
capturedImageUri = getMyFileUri(imageFile)
|
||||
private fun launchActivityForResult(intent: Intent, requestCode: Int, @StringRes error: Int = R.string.no_app_found) {
|
||||
hideKeyboard()
|
||||
try {
|
||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
|
||||
putExtra(MediaStore.EXTRA_OUTPUT, capturedImageUri)
|
||||
}
|
||||
startActivityForResult(intent, TAKE_PHOTO_INTENT)
|
||||
startActivityForResult(intent, requestCode)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
showErrorToast(getString(R.string.no_app_found))
|
||||
showErrorToast(getString(error))
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchPickPhotoVideoIntent() {
|
||||
hideKeyboard()
|
||||
val mimeTypes = arrayOf("image/*", "video/*")
|
||||
private fun getAttachmentsDir(): File {
|
||||
return File(cacheDir, "attachments").apply {
|
||||
if (!exists()) {
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchCapturePhotoIntent() {
|
||||
val imageFile = File.createTempFile("attachment_", ".jpg", getAttachmentsDir())
|
||||
capturedImageUri = getMyFileUri(imageFile)
|
||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
|
||||
putExtra(MediaStore.EXTRA_OUTPUT, capturedImageUri)
|
||||
}
|
||||
launchActivityForResult(intent, CAPTURE_PHOTO_INTENT)
|
||||
}
|
||||
|
||||
private fun launchCaptureVideoIntent() {
|
||||
val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
|
||||
launchActivityForResult(intent, CAPTURE_VIDEO_INTENT)
|
||||
}
|
||||
|
||||
private fun launchCaptureAudioIntent() {
|
||||
val intent = Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
|
||||
launchActivityForResult(intent, CAPTURE_AUDIO_INTENT)
|
||||
}
|
||||
|
||||
private fun launchGetContentIntent(mimeTypes: Array<String>, requestCode: Int) {
|
||||
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/*"
|
||||
putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
||||
launchActivityForResult(this, requestCode)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
startActivityForResult(this, PICK_ATTACHMENT_INTENT)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
showErrorToast(getString(R.string.no_app_found))
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
private fun launchPickContactIntent() {
|
||||
Intent(Intent.ACTION_PICK).apply {
|
||||
type = ContactsContract.Contacts.CONTENT_TYPE
|
||||
launchActivityForResult(this, PICK_CONTACT_INTENT)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addContactAttachment(contactUri: Uri) {
|
||||
ensureBackgroundThread {
|
||||
val contact = ContactsHelper(this).getContactFromUri(contactUri)
|
||||
if (contact != null) {
|
||||
val outputFile = File(getAttachmentsDir(), "${contact.contactId}.vcf")
|
||||
val outputStream = outputFile.outputStream()
|
||||
|
||||
VcfExporter().exportContacts(
|
||||
activity = this,
|
||||
outputStream = outputStream,
|
||||
contacts = arrayListOf(contact),
|
||||
showExportingToast = false,
|
||||
) {
|
||||
if (it == VcfExporter.ExportResult.EXPORT_OK) {
|
||||
val vCardUri = getMyFileUri(outputFile)
|
||||
runOnUiThread {
|
||||
addAttachment(vCardUri)
|
||||
}
|
||||
} else {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAttachmentsAdapter(): AttachmentsAdapter? {
|
||||
val adapter = thread_attachments_recyclerview.adapter
|
||||
return adapter as? AttachmentsAdapter
|
||||
}
|
||||
|
||||
private fun getAttachmentSelections() = getAttachmentsAdapter()?.attachments ?: emptyList()
|
||||
|
||||
private fun addAttachment(uri: Uri) {
|
||||
val originalUriString = uri.toString()
|
||||
if (attachmentSelections.containsKey(originalUriString)) {
|
||||
val id = uri.toString()
|
||||
if (getAttachmentSelections().any { it.id == id }) {
|
||||
toast(R.string.duplicate_item_warning)
|
||||
return
|
||||
}
|
||||
|
||||
attachmentSelections[originalUriString] = AttachmentSelection(uri, false)
|
||||
val attachmentView = addAttachmentView(originalUriString, uri)
|
||||
val mimeType = contentResolver.getType(uri) ?: return
|
||||
|
||||
if (mimeType.isImageMimeType() && config.mmsFileSizeLimit != FILE_SIZE_NONE) {
|
||||
val selection = attachmentSelections[originalUriString]
|
||||
attachmentSelections[originalUriString] = selection!!.copy(isPending = true)
|
||||
checkSendMessageAvailability()
|
||||
attachmentView.thread_attachment_progress.beVisible()
|
||||
imageCompressor.compressImage(uri, config.mmsFileSizeLimit) { compressedUri ->
|
||||
runOnUiThread {
|
||||
if (compressedUri != null) {
|
||||
attachmentSelections[originalUriString] = AttachmentSelection(compressedUri, false)
|
||||
loadAttachmentPreview(attachmentView, compressedUri)
|
||||
} else {
|
||||
toast(R.string.compress_error)
|
||||
removeAttachment(attachmentView, originalUriString)
|
||||
}
|
||||
var adapter = getAttachmentsAdapter()
|
||||
if (adapter == null) {
|
||||
adapter = AttachmentsAdapter(
|
||||
activity = this,
|
||||
recyclerView = thread_attachments_recyclerview,
|
||||
onAttachmentsRemoved = {
|
||||
thread_attachments_recyclerview.beGone()
|
||||
checkSendMessageAvailability()
|
||||
attachmentView.thread_attachment_progress.beGone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addAttachmentView(originalUri: String, uri: Uri): View {
|
||||
thread_attachments_holder.beVisible()
|
||||
val attachmentView = layoutInflater.inflate(R.layout.item_attachment, null).apply {
|
||||
thread_attachments_wrapper.addView(this)
|
||||
thread_remove_attachment.setOnClickListener {
|
||||
removeAttachment(this, originalUri)
|
||||
}
|
||||
},
|
||||
onReady = { checkSendMessageAvailability() }
|
||||
)
|
||||
thread_attachments_recyclerview.adapter = adapter
|
||||
}
|
||||
|
||||
loadAttachmentPreview(attachmentView, uri)
|
||||
return attachmentView
|
||||
}
|
||||
|
||||
private fun loadAttachmentPreview(attachmentView: View, uri: Uri) {
|
||||
if (isDestroyed || isFinishing) {
|
||||
thread_attachments_recyclerview.beVisible()
|
||||
val mimeType = contentResolver.getType(uri)
|
||||
if (mimeType == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return
|
||||
}
|
||||
|
||||
val roundedCornersRadius = resources.getDimension(R.dimen.medium_margin).toInt()
|
||||
val options = RequestOptions()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.transform(CenterCrop(), RoundedCorners(roundedCornersRadius))
|
||||
|
||||
Glide.with(attachmentView.thread_attachment_preview)
|
||||
.load(uri)
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
.apply(options)
|
||||
.listener(object : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
|
||||
attachmentView.thread_attachment_preview.beGone()
|
||||
attachmentView.thread_remove_attachment.beGone()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(dr: Drawable?, a: Any?, t: Target<Drawable>?, d: DataSource?, i: Boolean): Boolean {
|
||||
attachmentView.thread_attachment_preview.beVisible()
|
||||
attachmentView.thread_remove_attachment.beVisible()
|
||||
checkSendMessageAvailability()
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(attachmentView.thread_attachment_preview)
|
||||
}
|
||||
|
||||
private fun removeAttachment(attachmentView: View, originalUri: String) {
|
||||
thread_attachments_wrapper.removeView(attachmentView)
|
||||
attachmentSelections.remove(originalUri)
|
||||
if (attachmentSelections.isEmpty()) {
|
||||
thread_attachments_holder.beGone()
|
||||
}
|
||||
val attachment = AttachmentSelection(
|
||||
id = id,
|
||||
uri = uri,
|
||||
mimetype = mimeType,
|
||||
filename = getFilenameFromUri(uri),
|
||||
isPending = mimeType.isImageMimeType() && !mimeType.isGifMimeType()
|
||||
)
|
||||
adapter.addAttachment(attachment)
|
||||
checkSendMessageAvailability()
|
||||
}
|
||||
|
||||
@@ -949,7 +959,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
}
|
||||
|
||||
private fun checkSendMessageAvailability() {
|
||||
if (thread_type_message.text!!.isNotEmpty() || (attachmentSelections.isNotEmpty() && !attachmentSelections.values.any { it.isPending })) {
|
||||
if (thread_type_message.text!!.isNotEmpty() || (getAttachmentSelections().isNotEmpty() && !getAttachmentSelections().any { it.isPending })) {
|
||||
thread_send_message.isEnabled = true
|
||||
thread_send_message.isClickable = true
|
||||
thread_send_message.alpha = 0.9f
|
||||
@@ -963,7 +973,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
|
||||
private fun sendMessage() {
|
||||
var text = thread_type_message.value
|
||||
if (text.isEmpty() && attachmentSelections.isEmpty()) {
|
||||
if (text.isEmpty() && getAttachmentSelections().isEmpty()) {
|
||||
showErrorToast(getString(R.string.unknown_error_occurred))
|
||||
return
|
||||
}
|
||||
@@ -1003,13 +1013,16 @@ class ThreadActivity : SimpleActivity() {
|
||||
conversationsDB.insertOrUpdate(conversation.copy(date = nowSeconds))
|
||||
}
|
||||
scheduleMessage(message)
|
||||
}
|
||||
clearCurrentMessage()
|
||||
hideScheduleSendUi()
|
||||
scheduledMessage = null
|
||||
|
||||
if (!refreshedSinceSent) {
|
||||
refreshMessages()
|
||||
runOnUiThread {
|
||||
clearCurrentMessage()
|
||||
hideScheduleSendUi()
|
||||
scheduledMessage = null
|
||||
|
||||
if (!refreshedSinceSent) {
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e.localizedMessage ?: getString(R.string.unknown_error_occurred))
|
||||
@@ -1018,8 +1031,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
|
||||
private fun sendNormalMessage(text: String, subscriptionId: Int) {
|
||||
val addresses = participants.getAddresses()
|
||||
val attachments = attachmentSelections.values
|
||||
.map { it.uri }
|
||||
val attachments = buildMessageAttachments()
|
||||
|
||||
try {
|
||||
refreshedSinceSent = false
|
||||
@@ -1038,9 +1050,8 @@ class ThreadActivity : SimpleActivity() {
|
||||
|
||||
private fun clearCurrentMessage() {
|
||||
thread_type_message.setText("")
|
||||
attachmentSelections.clear()
|
||||
thread_attachments_holder.beGone()
|
||||
thread_attachments_wrapper.removeAllViews()
|
||||
getAttachmentsAdapter()?.clear()
|
||||
checkSendMessageAvailability()
|
||||
}
|
||||
|
||||
// show selected contacts, properly split to new lines when appropriate
|
||||
@@ -1165,14 +1176,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
type = mimeType
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_TITLE, path.split("/").last())
|
||||
|
||||
try {
|
||||
startActivityForResult(this, PICK_SAVE_FILE_INTENT)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
showErrorToast(getString(R.string.system_service_disabled))
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
launchActivityForResult(this, PICK_SAVE_FILE_INTENT, error = R.string.system_service_disabled)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1220,7 +1224,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
private fun isMmsMessage(text: String): Boolean {
|
||||
val isGroupMms = participants.size > 1 && config.sendGroupMessageMMS
|
||||
val isLongMmsMessage = isLongMmsMessage(text) && config.sendLongMessageMMS
|
||||
return attachmentSelections.isNotEmpty() || isGroupMms || isLongMmsMessage
|
||||
return getAttachmentSelections().isNotEmpty() || isGroupMms || isLongMmsMessage
|
||||
}
|
||||
|
||||
private fun updateMessageType() {
|
||||
@@ -1233,15 +1237,6 @@ class ThreadActivity : SimpleActivity() {
|
||||
thread_send_message.setText(stringId)
|
||||
}
|
||||
|
||||
private fun createImageFile(): File {
|
||||
val outputDirectory = File(cacheDir, "captured").apply {
|
||||
if (!exists()) {
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
return File.createTempFile("IMG_", ".jpg", outputDirectory)
|
||||
}
|
||||
|
||||
private fun showScheduledMessageInfo(message: Message) {
|
||||
val items = arrayListOf(
|
||||
RadioItem(TYPE_EDIT, getString(R.string.update_message)),
|
||||
@@ -1298,7 +1293,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
|
||||
private fun setupScheduleSendUi() {
|
||||
val textColor = getProperTextColor()
|
||||
scheduled_message_holder.background.applyColorFilter(getProperBackgroundColor().getContrastColor())
|
||||
scheduled_message_holder.background.applyColorFilter(getProperPrimaryColor().darkenColor())
|
||||
scheduled_message_button.apply {
|
||||
val clockDrawable = ResourcesCompat.getDrawable(resources, R.drawable.ic_clock_vector, theme)?.apply { applyColorFilter(textColor) }
|
||||
setCompoundDrawablesWithIntrinsicBounds(clockDrawable, null, null, null)
|
||||
@@ -1365,7 +1360,7 @@ class ThreadActivity : SimpleActivity() {
|
||||
read = false,
|
||||
threadId = threadId,
|
||||
isMMS = isMmsMessage(text),
|
||||
attachment = buildMessageAttachment(text, messageId),
|
||||
attachment = MessageAttachment(messageId, text, buildMessageAttachments(messageId)),
|
||||
senderName = "",
|
||||
senderPhotoUri = "",
|
||||
subscriptionId = subscriptionId,
|
||||
@@ -1373,11 +1368,138 @@ class ThreadActivity : SimpleActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildMessageAttachment(text: String, messageId: Long): MessageAttachment {
|
||||
val attachments = attachmentSelections.values
|
||||
.map { Attachment(null, messageId, it.uri.toString(), contentResolver.getType(it.uri) ?: "*/*", 0, 0, "") }
|
||||
.toArrayList()
|
||||
private fun buildMessageAttachments(messageId: Long = -1L) = getAttachmentSelections()
|
||||
.map { Attachment(null, messageId, it.uri.toString(), it.mimetype, 0, 0, it.filename) }
|
||||
.toArrayList()
|
||||
|
||||
return MessageAttachment(messageId, text, attachments)
|
||||
private fun setupAttachmentPickerView() {
|
||||
val buttonColors = arrayOf(
|
||||
R.color.md_red_500,
|
||||
R.color.md_brown_500,
|
||||
R.color.md_pink_500,
|
||||
R.color.md_purple_500,
|
||||
R.color.md_teal_500,
|
||||
R.color.md_green_500,
|
||||
R.color.md_indigo_500,
|
||||
R.color.md_blue_500
|
||||
).map { ResourcesCompat.getColor(resources, it, theme) }
|
||||
arrayOf(
|
||||
choose_photo_icon,
|
||||
choose_video_icon,
|
||||
take_photo_icon,
|
||||
record_video_icon,
|
||||
record_audio_icon,
|
||||
pick_file_icon,
|
||||
pick_contact_icon,
|
||||
schedule_message_icon
|
||||
).forEachIndexed { index, icon ->
|
||||
val iconColor = buttonColors[index]
|
||||
icon.background.applyColorFilter(iconColor)
|
||||
icon.applyColorFilter(iconColor.getContrastColor())
|
||||
}
|
||||
|
||||
val textColor = getProperTextColor()
|
||||
arrayOf(
|
||||
choose_photo_text,
|
||||
choose_video_text,
|
||||
take_photo_text,
|
||||
record_video_text,
|
||||
record_audio_text,
|
||||
pick_file_text,
|
||||
pick_contact_text,
|
||||
schedule_message_text
|
||||
).forEach { it.setTextColor(textColor) }
|
||||
|
||||
choose_photo.setOnClickListener {
|
||||
launchGetContentIntent(arrayOf("image/*"), PICK_PHOTO_INTENT)
|
||||
}
|
||||
choose_video.setOnClickListener {
|
||||
launchGetContentIntent(arrayOf("video/*"), PICK_VIDEO_INTENT)
|
||||
}
|
||||
take_photo.setOnClickListener {
|
||||
launchCapturePhotoIntent()
|
||||
}
|
||||
record_video.setOnClickListener {
|
||||
launchCaptureVideoIntent()
|
||||
}
|
||||
record_audio.setOnClickListener {
|
||||
launchCaptureAudioIntent()
|
||||
}
|
||||
pick_file.setOnClickListener {
|
||||
launchGetContentIntent(arrayOf("*/*"), PICK_DOCUMENT_INTENT)
|
||||
}
|
||||
pick_contact.setOnClickListener {
|
||||
launchPickContactIntent()
|
||||
}
|
||||
schedule_message.setOnClickListener {
|
||||
if (isScheduledMessage) {
|
||||
launchScheduleSendDialog(scheduledDateTime)
|
||||
} else {
|
||||
launchScheduleSendDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showAttachmentPicker() {
|
||||
attachment_picker_divider.showWithAnimation()
|
||||
attachment_picker_holder.showWithAnimation()
|
||||
animateAttachmentButton(rotation = -135f)
|
||||
}
|
||||
|
||||
private fun hideAttachmentPicker() {
|
||||
attachment_picker_divider.beGone()
|
||||
attachment_picker_holder.apply {
|
||||
beGone()
|
||||
updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
height = config.keyboardHeight
|
||||
}
|
||||
}
|
||||
animateAttachmentButton(rotation = 0f)
|
||||
}
|
||||
|
||||
private fun animateAttachmentButton(rotation: Float) {
|
||||
thread_add_attachment.animate()
|
||||
.rotation(rotation)
|
||||
.setDuration(500L)
|
||||
.setInterpolator(OvershootInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
private fun setupKeyboardListener() {
|
||||
window.decorView.setOnApplyWindowInsetsListener { view, insets ->
|
||||
showOrHideAttachmentPicker()
|
||||
view.onApplyWindowInsets(insets)
|
||||
}
|
||||
|
||||
val callback = object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
|
||||
override fun onPrepare(animation: WindowInsetsAnimationCompat) {
|
||||
super.onPrepare(animation)
|
||||
showOrHideAttachmentPicker()
|
||||
}
|
||||
|
||||
override fun onProgress(insets: WindowInsetsCompat, runningAnimations: MutableList<WindowInsetsAnimationCompat>) = insets
|
||||
}
|
||||
ViewCompat.setWindowInsetsAnimationCallback(window.decorView, callback)
|
||||
}
|
||||
|
||||
private fun showOrHideAttachmentPicker() {
|
||||
val type = WindowInsetsCompat.Type.ime()
|
||||
val insets = ViewCompat.getRootWindowInsets(window.decorView) ?: return
|
||||
val isKeyboardVisible = insets.isVisible(type)
|
||||
|
||||
if (isKeyboardVisible) {
|
||||
val keyboardHeight = insets.getInsets(type).bottom
|
||||
val bottomBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
|
||||
|
||||
// check keyboard height just to be sure, 150 seems like a good middle ground between ime and navigation bar
|
||||
config.keyboardHeight = if (keyboardHeight > 150) {
|
||||
keyboardHeight - bottomBarHeight
|
||||
} else {
|
||||
getDefaultKeyboardHeight()
|
||||
}
|
||||
hideAttachmentPicker()
|
||||
} else if (isAttachmentPickerVisible) {
|
||||
showAttachmentPicker()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,207 @@
|
||||
package com.simplemobiletools.smsmessenger.adapters
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.VCardViewerActivity
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.AttachmentSelection
|
||||
import kotlinx.android.synthetic.main.item_attachment_media_preview.view.*
|
||||
import kotlinx.android.synthetic.main.item_remove_attachment_button.view.*
|
||||
|
||||
class AttachmentsAdapter(
|
||||
val activity: BaseSimpleActivity,
|
||||
val recyclerView: RecyclerView,
|
||||
val onAttachmentsRemoved: () -> Unit,
|
||||
val onReady: (() -> Unit)
|
||||
) : ListAdapter<AttachmentSelection, AttachmentsAdapter.ViewHolder>(AttachmentDiffCallback()) {
|
||||
|
||||
private val config = activity.config
|
||||
private val resources = activity.resources
|
||||
private val primaryColor = activity.getProperPrimaryColor()
|
||||
private val imageCompressor by lazy { ImageCompressor(activity) }
|
||||
|
||||
val attachments = mutableListOf<AttachmentSelection>()
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return getItem(position).viewType
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val layoutRes = when (viewType) {
|
||||
ATTACHMENT_DOCUMENT -> R.layout.item_attachment_document_preview
|
||||
ATTACHMENT_VCARD -> R.layout.item_attachment_vcard_preview
|
||||
ATTACHMENT_MEDIA -> R.layout.item_attachment_media_preview
|
||||
else -> throw IllegalArgumentException("Unknown view type: $viewType")
|
||||
}
|
||||
|
||||
val view = activity.layoutInflater.inflate(layoutRes, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val attachment = getItem(position)
|
||||
holder.bindView() { view, _ ->
|
||||
when (attachment.viewType) {
|
||||
ATTACHMENT_DOCUMENT -> {
|
||||
view.setupDocumentPreview(
|
||||
uri = attachment.uri,
|
||||
title = attachment.filename,
|
||||
mimeType = attachment.mimetype,
|
||||
attachment = true,
|
||||
onClick = { activity.launchViewIntent(attachment.uri, attachment.mimetype, attachment.filename) },
|
||||
onRemoveButtonClicked = { removeAttachment(attachment) }
|
||||
)
|
||||
}
|
||||
ATTACHMENT_VCARD -> {
|
||||
view.setupVCardPreview(
|
||||
activity = activity,
|
||||
uri = attachment.uri,
|
||||
attachment = true,
|
||||
onClick = {
|
||||
val intent = Intent(activity, VCardViewerActivity::class.java).also {
|
||||
it.putExtra(EXTRA_VCARD_URI, attachment.uri)
|
||||
}
|
||||
activity.startActivity(intent)
|
||||
},
|
||||
onRemoveButtonClicked = { removeAttachment(attachment) }
|
||||
)
|
||||
}
|
||||
ATTACHMENT_MEDIA -> setupMediaPreview(view, attachment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
attachments.clear()
|
||||
submitList(emptyList())
|
||||
recyclerView.onGlobalLayout {
|
||||
onAttachmentsRemoved()
|
||||
}
|
||||
}
|
||||
|
||||
fun addAttachment(attachment: AttachmentSelection) {
|
||||
attachments.removeAll { AttachmentSelection.areItemsTheSame(it, attachment) }
|
||||
attachments.add(attachment)
|
||||
submitList(attachments.toList())
|
||||
}
|
||||
|
||||
private fun removeAttachment(attachment: AttachmentSelection) {
|
||||
attachments.removeAll { AttachmentSelection.areItemsTheSame(it, attachment) }
|
||||
if (attachments.isEmpty()) {
|
||||
clear()
|
||||
} else {
|
||||
submitList(attachments.toList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMediaPreview(view: View, attachment: AttachmentSelection) {
|
||||
view.apply {
|
||||
media_attachment_holder.background.applyColorFilter(primaryColor.darkenColor())
|
||||
media_attachment_holder.setOnClickListener {
|
||||
activity.launchViewIntent(attachment.uri, attachment.mimetype, attachment.filename)
|
||||
}
|
||||
remove_attachment_button.apply {
|
||||
beVisible()
|
||||
background.applyColorFilter(primaryColor)
|
||||
setOnClickListener {
|
||||
removeAttachment(attachment)
|
||||
}
|
||||
}
|
||||
|
||||
val compressImage = attachment.mimetype.isImageMimeType() && !attachment.mimetype.isGifMimeType()
|
||||
if (compressImage && attachment.isPending && config.mmsFileSizeLimit != FILE_SIZE_NONE) {
|
||||
thumbnail.beGone()
|
||||
compression_progress.beVisible()
|
||||
|
||||
imageCompressor.compressImage(attachment.uri, config.mmsFileSizeLimit) { compressedUri ->
|
||||
activity.runOnUiThread {
|
||||
when (compressedUri) {
|
||||
attachment.uri -> {
|
||||
attachments.find { it.uri == attachment.uri }?.isPending = false
|
||||
loadMediaPreview(view, attachment)
|
||||
}
|
||||
null -> {
|
||||
activity.toast(R.string.compress_error)
|
||||
removeAttachment(attachment)
|
||||
}
|
||||
else -> {
|
||||
attachments.remove(attachment)
|
||||
addAttachment(attachment.copy(uri = compressedUri, isPending = false))
|
||||
}
|
||||
}
|
||||
onReady()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loadMediaPreview(view, attachment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadMediaPreview(view: View, attachment: AttachmentSelection) {
|
||||
val roundedCornersRadius = resources.getDimension(R.dimen.activity_margin).toInt()
|
||||
val size = resources.getDimension(R.dimen.attachment_preview_size).toInt()
|
||||
|
||||
val options = RequestOptions()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.transform(CenterCrop(), RoundedCorners(roundedCornersRadius))
|
||||
|
||||
Glide.with(view.thumbnail)
|
||||
.load(attachment.uri)
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
.override(size, size)
|
||||
.apply(options)
|
||||
.listener(object : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
|
||||
removeAttachment(attachment)
|
||||
activity.toast(R.string.unknown_error_occurred)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(dr: Drawable?, a: Any?, t: Target<Drawable>?, d: DataSource?, i: Boolean): Boolean {
|
||||
view.thumbnail.beVisible()
|
||||
view.play_icon.beVisibleIf(attachment.mimetype.isVideoMimeType())
|
||||
view.compression_progress.beGone()
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(view.thumbnail)
|
||||
}
|
||||
|
||||
open inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
fun bindView(callback: (itemView: View, adapterPosition: Int) -> Unit): View {
|
||||
return itemView.apply {
|
||||
callback(this, adapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AttachmentDiffCallback : DiffUtil.ItemCallback<AttachmentSelection>() {
|
||||
override fun areItemsTheSame(oldItem: AttachmentSelection, newItem: AttachmentSelection): Boolean {
|
||||
return AttachmentSelection.areItemsTheSame(oldItem, newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: AttachmentSelection, newItem: AttachmentSelection): Boolean {
|
||||
return AttachmentSelection.areContentsTheSame(oldItem, newItem)
|
||||
}
|
||||
}
|
@@ -1,13 +1,12 @@
|
||||
package com.simplemobiletools.smsmessenger.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.util.Size
|
||||
import android.util.TypedValue
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
@@ -38,20 +37,16 @@ import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.models.*
|
||||
import kotlinx.android.synthetic.main.item_attachment_image.view.*
|
||||
import kotlinx.android.synthetic.main.item_attachment_vcard.view.*
|
||||
import kotlinx.android.synthetic.main.item_received_message.view.*
|
||||
import kotlinx.android.synthetic.main.item_received_message.view.thread_mesage_attachments_holder
|
||||
import kotlinx.android.synthetic.main.item_received_message.view.thread_message_body
|
||||
import kotlinx.android.synthetic.main.item_received_message.view.thread_message_holder
|
||||
import kotlinx.android.synthetic.main.item_received_message.view.thread_message_play_outline
|
||||
import kotlinx.android.synthetic.main.item_received_unknown_attachment.view.*
|
||||
import kotlinx.android.synthetic.main.item_sent_message.view.*
|
||||
import kotlinx.android.synthetic.main.item_sent_unknown_attachment.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_date_time.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_error.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_sending.view.*
|
||||
import kotlinx.android.synthetic.main.item_thread_success.view.*
|
||||
import java.util.*
|
||||
|
||||
class ThreadAdapter(
|
||||
activity: SimpleActivity, var messages: ArrayList<ThreadItem>, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit, val onThreadIdUpdate: (Long) -> Unit
|
||||
@@ -60,6 +55,7 @@ class ThreadAdapter(
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private val hasMultipleSIMCards = (activity.subscriptionManagerCompat().activeSubscriptionInfoList?.size ?: 0) > 1
|
||||
private val maxChatBubbleWidth = activity.usableScreenSize.x * 0.8f
|
||||
|
||||
init {
|
||||
setupDragListener(true)
|
||||
@@ -286,12 +282,10 @@ class ThreadAdapter(
|
||||
if (message.attachment?.attachments?.isNotEmpty() == true) {
|
||||
for (attachment in message.attachment.attachments) {
|
||||
val mimetype = attachment.mimetype
|
||||
if (mimetype.isImageMimeType() || mimetype.startsWith("video/")) {
|
||||
setupImageView(holder, view, message, attachment)
|
||||
} else if (mimetype.isVCardMimeType()) {
|
||||
setupVCardView(holder, view, message, attachment)
|
||||
} else {
|
||||
setupFileView(holder, view, message, attachment)
|
||||
when {
|
||||
mimetype.isImageMimeType() || mimetype.isVideoMimeType() -> setupImageView(holder, view, message, attachment)
|
||||
mimetype.isVCardMimeType() -> setupVCardView(holder, view, message, attachment)
|
||||
else -> setupFileView(holder, view, message, attachment)
|
||||
}
|
||||
|
||||
thread_message_play_outline.beVisibleIf(mimetype.startsWith("video/"))
|
||||
@@ -374,14 +368,20 @@ class ThreadAdapter(
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(dr: Drawable?, a: Any?, t: Target<Drawable>?, d: DataSource?, i: Boolean) =
|
||||
false
|
||||
override fun onResourceReady(dr: Drawable?, a: Any?, t: Target<Drawable>?, d: DataSource?, i: Boolean) = false
|
||||
})
|
||||
|
||||
// limit attachment sizes to avoid causing OOM
|
||||
var wantedAttachmentSize = Size(attachment.width, attachment.height)
|
||||
if (wantedAttachmentSize.width > maxChatBubbleWidth) {
|
||||
val newHeight = wantedAttachmentSize.height / (wantedAttachmentSize.width / maxChatBubbleWidth)
|
||||
wantedAttachmentSize = Size(maxChatBubbleWidth.toInt(), newHeight.toInt())
|
||||
}
|
||||
|
||||
builder = if (isTallImage) {
|
||||
builder.override(attachment.width, attachment.width)
|
||||
builder.override(wantedAttachmentSize.width, wantedAttachmentSize.width)
|
||||
} else {
|
||||
builder.override(attachment.width, attachment.height)
|
||||
builder.override(wantedAttachmentSize.width, wantedAttachmentSize.height)
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -393,7 +393,7 @@ class ThreadAdapter(
|
||||
if (actModeCallback.isSelectable) {
|
||||
holder.viewClicked(message)
|
||||
} else {
|
||||
launchViewIntent(uri, mimetype, attachment.filename)
|
||||
activity.launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
}
|
||||
imageView.setOnLongClickListener {
|
||||
@@ -407,50 +407,23 @@ class ThreadAdapter(
|
||||
val uri = attachment.getUri()
|
||||
parent.apply {
|
||||
val vCardView = layoutInflater.inflate(R.layout.item_attachment_vcard, null).apply {
|
||||
background.applyColorFilter(backgroundColor.getContrastColor())
|
||||
vcard_title.setTextColor(textColor)
|
||||
vcard_subtitle.setTextColor(textColor)
|
||||
view_contact_details.setTextColor(properPrimaryColor)
|
||||
setupVCardPreview(
|
||||
activity = activity,
|
||||
uri = uri,
|
||||
onClick = {
|
||||
if (actModeCallback.isSelectable) {
|
||||
holder.viewClicked(message)
|
||||
} else {
|
||||
val intent = Intent(context, VCardViewerActivity::class.java).also {
|
||||
it.putExtra(EXTRA_VCARD_URI, uri)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
},
|
||||
onLongClick = { holder.viewLongClicked() }
|
||||
)
|
||||
}
|
||||
thread_mesage_attachments_holder.addView(vCardView)
|
||||
|
||||
parseVCardFromUri(context, uri) { vCards ->
|
||||
val title = vCards.firstOrNull()?.parseNameFromVCard()
|
||||
val imageIcon = if (title != null) {
|
||||
SimpleContactsHelper(context).getContactLetterIcon(title)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
activity.runOnUiThread {
|
||||
vCardView.apply {
|
||||
vcard_title.text = title
|
||||
vcard_photo.setImageBitmap(imageIcon)
|
||||
|
||||
if (vCards.size > 1) {
|
||||
vcard_subtitle.beVisible()
|
||||
val quantity = vCards.size - 1
|
||||
vcard_subtitle.text = resources.getQuantityString(R.plurals.and_other_contacts, quantity, quantity)
|
||||
} else {
|
||||
vcard_subtitle.beGone()
|
||||
}
|
||||
|
||||
setOnClickListener {
|
||||
if (actModeCallback.isSelectable) {
|
||||
holder.viewClicked(message)
|
||||
} else {
|
||||
val intent = Intent(context, VCardViewerActivity::class.java).also {
|
||||
it.putExtra(EXTRA_VCARD_URI, uri)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
setOnLongClickListener {
|
||||
holder.viewLongClicked()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,73 +431,22 @@ class ThreadAdapter(
|
||||
val mimetype = attachment.mimetype
|
||||
val uri = attachment.getUri()
|
||||
parent.apply {
|
||||
if (message.isReceivedMessage()) {
|
||||
val attachmentView = layoutInflater.inflate(R.layout.item_received_unknown_attachment, null).apply {
|
||||
thread_received_attachment_label.apply {
|
||||
if (attachment.filename.isNotEmpty()) {
|
||||
thread_received_attachment_label.text = attachment.filename
|
||||
val attachmentView = layoutInflater.inflate(R.layout.item_attachment_document, null).apply {
|
||||
setupDocumentPreview(
|
||||
uri = uri,
|
||||
title = attachment.filename,
|
||||
mimeType = attachment.mimetype,
|
||||
onClick = {
|
||||
if (actModeCallback.isSelectable) {
|
||||
holder.viewClicked(message)
|
||||
} else {
|
||||
activity.launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
setTextColor(textColor)
|
||||
setOnClickListener {
|
||||
if (actModeCallback.isSelectable) {
|
||||
holder.viewClicked(message)
|
||||
} else {
|
||||
launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
}
|
||||
setOnLongClickListener {
|
||||
holder.viewLongClicked()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
thread_mesage_attachments_holder.addView(attachmentView)
|
||||
} else {
|
||||
val background = context.getProperPrimaryColor()
|
||||
val attachmentView = layoutInflater.inflate(R.layout.item_sent_unknown_attachment, null).apply {
|
||||
thread_sent_attachment_label.apply {
|
||||
this.background.applyColorFilter(background)
|
||||
setTextColor(background.getContrastColor())
|
||||
if (attachment.filename.isNotEmpty()) {
|
||||
thread_sent_attachment_label.text = attachment.filename
|
||||
}
|
||||
setOnClickListener {
|
||||
if (actModeCallback.isSelectable) {
|
||||
holder.viewClicked(message)
|
||||
} else {
|
||||
launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
}
|
||||
setOnLongClickListener {
|
||||
holder.viewLongClicked()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
thread_mesage_attachments_holder.addView(attachmentView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchViewIntent(uri: Uri, mimetype: String, filename: String) {
|
||||
Intent().apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
setDataAndType(uri, mimetype.lowercase(Locale.getDefault()))
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
try {
|
||||
activity.hideKeyboard()
|
||||
activity.startActivity(this)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
val newMimetype = filename.getMimeType()
|
||||
if (newMimetype.isNotEmpty() && mimetype != newMimetype) {
|
||||
launchViewIntent(uri, newMimetype, filename)
|
||||
} else {
|
||||
activity.toast(R.string.no_app_found)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
},
|
||||
onLongClick = { holder.viewLongClicked() },
|
||||
)
|
||||
}
|
||||
thread_mesage_attachments_holder.addView(attachmentView)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,10 +4,12 @@ import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import com.simplemobiletools.commons.extensions.getMimeType
|
||||
import com.simplemobiletools.commons.extensions.hideKeyboard
|
||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import java.util.*
|
||||
|
||||
fun Activity.dialNumber(phoneNumber: String, callback: (() -> Unit)? = null) {
|
||||
hideKeyboard()
|
||||
@@ -24,3 +26,25 @@ fun Activity.dialNumber(phoneNumber: String, callback: (() -> Unit)? = null) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.launchViewIntent(uri: Uri, mimetype: String, filename: String) {
|
||||
Intent().apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
setDataAndType(uri, mimetype.lowercase(Locale.getDefault()))
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
try {
|
||||
hideKeyboard()
|
||||
startActivity(this)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
val newMimetype = filename.getMimeType()
|
||||
if (newMimetype.isNotEmpty() && mimetype != newMimetype) {
|
||||
launchViewIntent(uri, newMimetype, filename)
|
||||
} else {
|
||||
toast(R.string.no_app_found)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -35,6 +35,7 @@ import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
|
||||
import com.simplemobiletools.smsmessenger.databases.MessagesDatabase
|
||||
import com.simplemobiletools.smsmessenger.helpers.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.AttachmentUtils.parseAttachmentNames
|
||||
import com.simplemobiletools.smsmessenger.interfaces.AttachmentsDao
|
||||
import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
|
||||
import com.simplemobiletools.smsmessenger.interfaces.MessageAttachmentsDao
|
||||
@@ -307,7 +308,8 @@ fun Context.getMmsAttachment(id: Long, getImageResolutions: Boolean): MessageAtt
|
||||
val selectionArgs = arrayOf(id.toString())
|
||||
val messageAttachment = MessageAttachment(id, "", arrayListOf())
|
||||
|
||||
var attachmentName = ""
|
||||
var attachmentNames: List<String>? = null
|
||||
var attachmentCount = 0
|
||||
queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val partId = cursor.getLongValue(Mms._ID)
|
||||
val mimetype = cursor.getStringValue(Mms.Part.CONTENT_TYPE)
|
||||
@@ -332,14 +334,13 @@ fun Context.getMmsAttachment(id: Long, getImageResolutions: Boolean): MessageAtt
|
||||
val attachment = Attachment(partId, id, fileUri.toString(), mimetype, width, height, "")
|
||||
messageAttachment.attachments.add(attachment)
|
||||
} else if (mimetype != "application/smil") {
|
||||
val attachmentName = attachmentNames?.getOrNull(attachmentCount) ?: ""
|
||||
val attachment = Attachment(partId, id, Uri.withAppendedPath(uri, partId.toString()).toString(), mimetype, 0, 0, attachmentName)
|
||||
messageAttachment.attachments.add(attachment)
|
||||
attachmentCount++
|
||||
} else {
|
||||
val text = cursor.getStringValue(Mms.Part.TEXT)
|
||||
val cutName = text.substringAfter("ref src=\"").substringBefore("\"")
|
||||
if (cutName.isNotEmpty()) {
|
||||
attachmentName = cutName
|
||||
}
|
||||
attachmentNames = parseAttachmentNames(text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1064,3 +1065,5 @@ fun Context.clearExpiredScheduledMessages(threadId: Long, messagesToDelete: List
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getDefaultKeyboardHeight() = resources.getDimensionPixelSize(R.dimen.default_keyboard_height)
|
||||
|
@@ -15,7 +15,35 @@ fun String.isImageMimeType(): Boolean {
|
||||
return lowercase().startsWith("image")
|
||||
}
|
||||
|
||||
fun String.isGifMimeType(): Boolean {
|
||||
return lowercase().endsWith("gif")
|
||||
}
|
||||
|
||||
fun String.isVideoMimeType(): Boolean {
|
||||
return lowercase().startsWith("video")
|
||||
}
|
||||
|
||||
fun String.isVCardMimeType(): Boolean {
|
||||
val lowercase = lowercase()
|
||||
return lowercase.endsWith("x-vcard") || lowercase.endsWith("vcard")
|
||||
}
|
||||
|
||||
fun String.isAudioMimeType(): Boolean {
|
||||
return lowercase().startsWith("audio")
|
||||
}
|
||||
|
||||
fun String.isCalendarMimeType(): Boolean {
|
||||
return lowercase().endsWith("calendar")
|
||||
}
|
||||
|
||||
fun String.isPdfMimeType(): Boolean {
|
||||
return lowercase().endsWith("pdf")
|
||||
}
|
||||
|
||||
fun String.isZipMimeType(): Boolean {
|
||||
return lowercase().endsWith("zip")
|
||||
}
|
||||
|
||||
fun String.isPlainTextMimeType(): Boolean {
|
||||
return lowercase() == "text/plain"
|
||||
}
|
||||
|
@@ -0,0 +1,18 @@
|
||||
package com.simplemobiletools.smsmessenger.extensions
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.view.View
|
||||
import androidx.core.animation.doOnStart
|
||||
import androidx.core.view.isVisible
|
||||
|
||||
fun View.showWithAnimation(duration: Long = 250L) {
|
||||
if (!isVisible) {
|
||||
ObjectAnimator.ofFloat(
|
||||
this, "alpha", 0f, 1f
|
||||
).apply {
|
||||
this.duration = duration
|
||||
doOnStart { visibility = View.VISIBLE }
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,152 @@
|
||||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.app.Activity
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import kotlinx.android.synthetic.main.item_attachment_document.view.*
|
||||
import kotlinx.android.synthetic.main.item_attachment_vcard.view.*
|
||||
import kotlinx.android.synthetic.main.item_attachment_vcard_preview.view.*
|
||||
import kotlinx.android.synthetic.main.item_remove_attachment_button.view.*
|
||||
|
||||
fun View.setupDocumentPreview(
|
||||
uri: Uri,
|
||||
title: String,
|
||||
mimeType: String,
|
||||
attachment: Boolean = false,
|
||||
onClick: (() -> Unit)? = null,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
onRemoveButtonClicked: (() -> Unit)? = null
|
||||
) {
|
||||
if (title.isNotEmpty()) {
|
||||
filename.text = title
|
||||
}
|
||||
|
||||
try {
|
||||
val size = context.getFileSizeFromUri(uri)
|
||||
file_size.beVisible()
|
||||
file_size.text = size.formatSize()
|
||||
} catch (e: Exception) {
|
||||
file_size.beGone()
|
||||
}
|
||||
|
||||
val textColor = context.getProperTextColor()
|
||||
val primaryColor = context.getProperPrimaryColor()
|
||||
|
||||
document_attachment_holder.background.applyColorFilter(textColor)
|
||||
filename.setTextColor(textColor)
|
||||
file_size.setTextColor(textColor)
|
||||
|
||||
icon.setImageResource(getIconResourceForMimeType(mimeType))
|
||||
icon.background.setTint(primaryColor)
|
||||
document_attachment_holder.background.applyColorFilter(primaryColor.darkenColor())
|
||||
|
||||
if (attachment) {
|
||||
remove_attachment_button.apply {
|
||||
beVisible()
|
||||
background.applyColorFilter(primaryColor)
|
||||
if (onRemoveButtonClicked != null) {
|
||||
setOnClickListener {
|
||||
onRemoveButtonClicked.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document_attachment_holder.setOnClickListener {
|
||||
onClick?.invoke()
|
||||
}
|
||||
document_attachment_holder.setOnLongClickListener {
|
||||
onLongClick?.invoke()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun View.setupVCardPreview(
|
||||
activity: Activity,
|
||||
uri: Uri,
|
||||
attachment: Boolean = false,
|
||||
onClick: (() -> Unit)? = null,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
onRemoveButtonClicked: (() -> Unit)? = null,
|
||||
) {
|
||||
val textColor = activity.getProperTextColor()
|
||||
val primaryColor = activity.getProperPrimaryColor()
|
||||
|
||||
vcard_attachment_holder.background.applyColorFilter(primaryColor.darkenColor())
|
||||
vcard_title.setTextColor(textColor)
|
||||
vcard_subtitle.setTextColor(textColor)
|
||||
|
||||
if (attachment) {
|
||||
vcard_progress.beVisible()
|
||||
}
|
||||
arrayOf(vcard_photo, vcard_title, vcard_subtitle, view_contact_details).forEach {
|
||||
it.beGone()
|
||||
}
|
||||
|
||||
parseVCardFromUri(activity, uri) { vCards ->
|
||||
activity.runOnUiThread {
|
||||
if (vCards.isEmpty()) {
|
||||
vcard_title.beVisible()
|
||||
vcard_title.text = context.getString(R.string.unknown_error_occurred)
|
||||
return@runOnUiThread
|
||||
}
|
||||
val title = vCards.firstOrNull()?.parseNameFromVCard()
|
||||
val imageIcon = if (title != null) {
|
||||
SimpleContactsHelper(activity).getContactLetterIcon(title)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
arrayOf(vcard_photo, vcard_title).forEach {
|
||||
it.beVisible()
|
||||
}
|
||||
|
||||
vcard_photo.setImageBitmap(imageIcon)
|
||||
vcard_title.text = title
|
||||
|
||||
if (vCards.size > 1) {
|
||||
vcard_subtitle.beVisible()
|
||||
val quantity = vCards.size - 1
|
||||
vcard_subtitle.text = resources.getQuantityString(R.plurals.and_other_contacts, quantity, quantity)
|
||||
} else {
|
||||
vcard_subtitle.beGone()
|
||||
}
|
||||
|
||||
if (attachment) {
|
||||
vcard_progress.beGone()
|
||||
remove_attachment_button.apply {
|
||||
beVisible()
|
||||
background.applyColorFilter(primaryColor)
|
||||
if (onRemoveButtonClicked != null) {
|
||||
setOnClickListener {
|
||||
onRemoveButtonClicked.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view_contact_details.setTextColor(primaryColor)
|
||||
view_contact_details.beVisible()
|
||||
}
|
||||
|
||||
vcard_attachment_holder.setOnClickListener {
|
||||
onClick?.invoke()
|
||||
}
|
||||
vcard_attachment_holder.setOnLongClickListener {
|
||||
onLongClick?.invoke()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIconResourceForMimeType(mimeType: String) = when {
|
||||
mimeType.isAudioMimeType() -> R.drawable.ic_vector_audio_file
|
||||
mimeType.isCalendarMimeType() -> R.drawable.ic_calendar_month_vector
|
||||
mimeType.isPdfMimeType() -> R.drawable.ic_vector_pdf
|
||||
mimeType.isZipMimeType() -> R.drawable.ic_vector_folder_zip
|
||||
else -> R.drawable.ic_document_vector
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.util.Xml
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
|
||||
object AttachmentUtils {
|
||||
|
||||
fun parseAttachmentNames(text: String): List<String> {
|
||||
val parser = Xml.newPullParser()
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
||||
parser.setInput(text.reader())
|
||||
parser.nextTag()
|
||||
return readSmil(parser)
|
||||
}
|
||||
|
||||
private fun readSmil(parser: XmlPullParser): List<String> {
|
||||
parser.require(XmlPullParser.START_TAG, null, "smil")
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (parser.name == "body") {
|
||||
return readBody(parser)
|
||||
} else {
|
||||
skip(parser)
|
||||
}
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun readBody(parser: XmlPullParser): List<String> {
|
||||
val names = mutableListOf<String>()
|
||||
parser.require(XmlPullParser.START_TAG, null, "body")
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (parser.name == "par") {
|
||||
parser.require(XmlPullParser.START_TAG, null, "par")
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (parser.name == "ref") {
|
||||
val value = parser.getAttributeValue(null, "src")
|
||||
names.add(value)
|
||||
parser.nextTag()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
skip(parser)
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
private fun skip(parser: XmlPullParser) {
|
||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
var depth = 1
|
||||
while (depth != 0) {
|
||||
when (parser.next()) {
|
||||
XmlPullParser.END_TAG -> depth--
|
||||
XmlPullParser.START_TAG -> depth++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@ package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.content.Context
|
||||
import com.simplemobiletools.commons.helpers.BaseConfig
|
||||
import com.simplemobiletools.smsmessenger.extensions.getDefaultKeyboardHeight
|
||||
import com.simplemobiletools.smsmessenger.models.Conversation
|
||||
|
||||
class Config(context: Context) : BaseConfig(context) {
|
||||
@@ -90,4 +91,8 @@ class Config(context: Context) : BaseConfig(context) {
|
||||
var wasDbCleared: Boolean
|
||||
get() = prefs.getBoolean(WAS_DB_CLEARED, false)
|
||||
set(wasDbCleared) = prefs.edit().putBoolean(WAS_DB_CLEARED, wasDbCleared).apply()
|
||||
|
||||
var keyboardHeight: Int
|
||||
get() = prefs.getInt(SOFT_KEYBOARD_HEIGHT, context.getDefaultKeyboardHeight())
|
||||
set(keyboardHeight) = prefs.edit().putInt(SOFT_KEYBOARD_HEIGHT, keyboardHeight).apply()
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ const val IMPORT_MMS = "import_mms"
|
||||
const val WAS_DB_CLEARED = "was_db_cleared_2"
|
||||
const val EXTRA_VCARD_URI = "vcard"
|
||||
const val SCHEDULED_MESSAGE_ID = "scheduled_message_id"
|
||||
const val SOFT_KEYBOARD_HEIGHT = "soft_keyboard_height"
|
||||
|
||||
private const val PATH = "com.simplemobiletools.smsmessenger.action."
|
||||
const val MARK_AS_READ = PATH + "mark_as_read"
|
||||
@@ -46,6 +47,11 @@ const val THREAD_SENT_MESSAGE_ERROR = 4
|
||||
const val THREAD_SENT_MESSAGE_SENT = 5
|
||||
const val THREAD_SENT_MESSAGE_SENDING = 6
|
||||
|
||||
// view types for attachment list
|
||||
const val ATTACHMENT_DOCUMENT = 7
|
||||
const val ATTACHMENT_MEDIA = 8
|
||||
const val ATTACHMENT_VCARD = 9
|
||||
|
||||
// lock screen visibility constants
|
||||
const val LOCK_SCREEN_SENDER_MESSAGE = 1
|
||||
const val LOCK_SCREEN_SENDER = 2
|
||||
@@ -61,6 +67,16 @@ const val FILE_SIZE_2_MB = 2_097_152L
|
||||
|
||||
const val MESSAGES_LIMIT = 50
|
||||
|
||||
// intent launch request codes
|
||||
const val PICK_PHOTO_INTENT = 42
|
||||
const val PICK_VIDEO_INTENT = 49
|
||||
const val PICK_SAVE_FILE_INTENT = 43
|
||||
const val CAPTURE_PHOTO_INTENT = 44
|
||||
const val CAPTURE_VIDEO_INTENT = 45
|
||||
const val CAPTURE_AUDIO_INTENT = 46
|
||||
const val PICK_DOCUMENT_INTENT = 47
|
||||
const val PICK_CONTACT_INTENT = 48
|
||||
|
||||
fun refreshMessages() {
|
||||
EventBus.getDefault().post(Events.RefreshMessages())
|
||||
}
|
||||
|
@@ -0,0 +1,449 @@
|
||||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.CommonDataKinds.*
|
||||
import android.provider.ContactsContract.Data
|
||||
import android.text.TextUtils
|
||||
import android.util.SparseArray
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.models.PhoneNumber
|
||||
import com.simplemobiletools.commons.models.contacts.*
|
||||
import com.simplemobiletools.commons.models.contacts.Email
|
||||
import com.simplemobiletools.commons.models.contacts.Event
|
||||
import com.simplemobiletools.commons.models.contacts.Organization
|
||||
import com.simplemobiletools.commons.overloads.times
|
||||
|
||||
// based on the ContactsHelper from Simple-Contacts
|
||||
class ContactsHelper(val context: Context) {
|
||||
private var displayContactSources = ArrayList<String>()
|
||||
|
||||
fun getContactFromUri(uri: Uri): Contact? {
|
||||
val key = getLookupKeyFromUri(uri) ?: return null
|
||||
return getContactWithLookupKey(key)
|
||||
}
|
||||
|
||||
private fun getLookupKeyFromUri(lookupUri: Uri): String? {
|
||||
val projection = arrayOf(ContactsContract.Contacts.LOOKUP_KEY)
|
||||
val cursor = context.contentResolver.query(lookupUri, projection, null, null, null)
|
||||
cursor?.use {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getStringValue(ContactsContract.Contacts.LOOKUP_KEY)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getContactWithLookupKey(key: String): Contact? {
|
||||
val selection = "(${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?) AND ${Data.LOOKUP_KEY} = ?"
|
||||
val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE, key)
|
||||
return parseContactCursor(selection, selectionArgs)
|
||||
}
|
||||
|
||||
private fun parseContactCursor(selection: String, selectionArgs: Array<String>): Contact? {
|
||||
val storedGroups = getDeviceStoredGroups()
|
||||
val uri = Data.CONTENT_URI
|
||||
val projection = getContactProjection()
|
||||
|
||||
val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
cursor?.use {
|
||||
if (cursor.moveToFirst()) {
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
|
||||
var prefix = ""
|
||||
var firstName = ""
|
||||
var middleName = ""
|
||||
var surname = ""
|
||||
var suffix = ""
|
||||
val mimetype = cursor.getStringValue(Data.MIMETYPE)
|
||||
|
||||
// ignore names at Organization type contacts
|
||||
if (mimetype == StructuredName.CONTENT_ITEM_TYPE) {
|
||||
prefix = cursor.getStringValue(StructuredName.PREFIX) ?: ""
|
||||
firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: ""
|
||||
middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: ""
|
||||
surname = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: ""
|
||||
suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: ""
|
||||
}
|
||||
|
||||
val nickname = getNicknames(id)[id] ?: ""
|
||||
val photoUri = cursor.getStringValue(Phone.PHOTO_URI) ?: ""
|
||||
val number = getPhoneNumbers(id)[id] ?: ArrayList()
|
||||
val emails = getEmails(id)[id] ?: ArrayList()
|
||||
val addresses = getAddresses(id)[id] ?: ArrayList()
|
||||
val events = getEvents(id)[id] ?: ArrayList()
|
||||
val notes = getNotes(id)[id] ?: ""
|
||||
val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: ""
|
||||
val starred = cursor.getIntValue(StructuredName.STARRED)
|
||||
val ringtone = cursor.getStringValue(StructuredName.CUSTOM_RINGTONE)
|
||||
val contactId = cursor.getIntValue(Data.CONTACT_ID)
|
||||
val groups = getContactGroups(storedGroups, contactId)[contactId] ?: ArrayList()
|
||||
val thumbnailUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
|
||||
val organization = getOrganizations(id)[id] ?: Organization("", "")
|
||||
val websites = getWebsites(id)[id] ?: ArrayList()
|
||||
val ims = getIMs(id)[id] ?: ArrayList()
|
||||
return Contact(
|
||||
id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, number, emails, addresses, events,
|
||||
accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims, mimetype, ringtone
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getPhoneNumbers(contactId: Int? = null): SparseArray<ArrayList<PhoneNumber>> {
|
||||
val phoneNumbers = SparseArray<ArrayList<PhoneNumber>>()
|
||||
val uri = Phone.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
Phone.NUMBER,
|
||||
Phone.NORMALIZED_NUMBER,
|
||||
Phone.TYPE,
|
||||
Phone.LABEL,
|
||||
Phone.IS_PRIMARY
|
||||
)
|
||||
|
||||
val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
|
||||
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val number = cursor.getStringValue(Phone.NUMBER) ?: return@queryCursor
|
||||
val normalizedNumber = cursor.getStringValue(Phone.NORMALIZED_NUMBER) ?: number.normalizePhoneNumber()
|
||||
val type = cursor.getIntValue(Phone.TYPE)
|
||||
val label = cursor.getStringValue(Phone.LABEL) ?: ""
|
||||
val isPrimary = cursor.getIntValue(Phone.IS_PRIMARY) != 0
|
||||
|
||||
if (phoneNumbers[id] == null) {
|
||||
phoneNumbers.put(id, ArrayList())
|
||||
}
|
||||
|
||||
val phoneNumber = PhoneNumber(number, type, label, normalizedNumber, isPrimary)
|
||||
phoneNumbers[id].add(phoneNumber)
|
||||
}
|
||||
|
||||
return phoneNumbers
|
||||
}
|
||||
|
||||
private fun getNicknames(contactId: Int? = null): SparseArray<String> {
|
||||
val nicknames = SparseArray<String>()
|
||||
val uri = Data.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
Nickname.NAME
|
||||
)
|
||||
|
||||
val selection = getSourcesSelection(true, contactId != null)
|
||||
val selectionArgs = getSourcesSelectionArgs(Nickname.CONTENT_ITEM_TYPE, contactId)
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val nickname = cursor.getStringValue(Nickname.NAME) ?: return@queryCursor
|
||||
nicknames.put(id, nickname)
|
||||
}
|
||||
|
||||
return nicknames
|
||||
}
|
||||
|
||||
private fun getEmails(contactId: Int? = null): SparseArray<ArrayList<Email>> {
|
||||
val emails = SparseArray<ArrayList<Email>>()
|
||||
val uri = ContactsContract.CommonDataKinds.Email.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.Email.DATA,
|
||||
ContactsContract.CommonDataKinds.Email.TYPE,
|
||||
ContactsContract.CommonDataKinds.Email.LABEL
|
||||
)
|
||||
|
||||
val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
|
||||
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val email = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.DATA) ?: return@queryCursor
|
||||
val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Email.TYPE)
|
||||
val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.LABEL) ?: ""
|
||||
|
||||
if (emails[id] == null) {
|
||||
emails.put(id, ArrayList())
|
||||
}
|
||||
|
||||
emails[id]!!.add(Email(email, type, label))
|
||||
}
|
||||
|
||||
return emails
|
||||
}
|
||||
|
||||
private fun getAddresses(contactId: Int? = null): SparseArray<ArrayList<Address>> {
|
||||
val addresses = SparseArray<ArrayList<Address>>()
|
||||
val uri = StructuredPostal.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
StructuredPostal.FORMATTED_ADDRESS,
|
||||
StructuredPostal.TYPE,
|
||||
StructuredPostal.LABEL
|
||||
)
|
||||
|
||||
val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
|
||||
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val address = cursor.getStringValue(StructuredPostal.FORMATTED_ADDRESS) ?: return@queryCursor
|
||||
val type = cursor.getIntValue(StructuredPostal.TYPE)
|
||||
val label = cursor.getStringValue(StructuredPostal.LABEL) ?: ""
|
||||
|
||||
if (addresses[id] == null) {
|
||||
addresses.put(id, ArrayList())
|
||||
}
|
||||
|
||||
addresses[id]!!.add(Address(address, type, label))
|
||||
}
|
||||
|
||||
return addresses
|
||||
}
|
||||
|
||||
private fun getIMs(contactId: Int? = null): SparseArray<ArrayList<IM>> {
|
||||
val IMs = SparseArray<ArrayList<IM>>()
|
||||
val uri = Data.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
Im.DATA,
|
||||
Im.PROTOCOL,
|
||||
Im.CUSTOM_PROTOCOL
|
||||
)
|
||||
|
||||
val selection = getSourcesSelection(true, contactId != null)
|
||||
val selectionArgs = getSourcesSelectionArgs(Im.CONTENT_ITEM_TYPE, contactId)
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val IM = cursor.getStringValue(Im.DATA) ?: return@queryCursor
|
||||
val type = cursor.getIntValue(Im.PROTOCOL)
|
||||
val label = cursor.getStringValue(Im.CUSTOM_PROTOCOL) ?: ""
|
||||
|
||||
if (IMs[id] == null) {
|
||||
IMs.put(id, ArrayList())
|
||||
}
|
||||
|
||||
IMs[id]!!.add(IM(IM, type, label))
|
||||
}
|
||||
|
||||
return IMs
|
||||
}
|
||||
|
||||
private fun getEvents(contactId: Int? = null): SparseArray<ArrayList<Event>> {
|
||||
val events = SparseArray<ArrayList<Event>>()
|
||||
val uri = Data.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.Event.START_DATE,
|
||||
ContactsContract.CommonDataKinds.Event.TYPE
|
||||
)
|
||||
|
||||
val selection = getSourcesSelection(true, contactId != null)
|
||||
val selectionArgs = getSourcesSelectionArgs(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, contactId)
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val startDate = cursor.getStringValue(ContactsContract.CommonDataKinds.Event.START_DATE) ?: return@queryCursor
|
||||
val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Event.TYPE)
|
||||
|
||||
if (events[id] == null) {
|
||||
events.put(id, ArrayList())
|
||||
}
|
||||
|
||||
events[id]!!.add(Event(startDate, type))
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
private fun getNotes(contactId: Int? = null): SparseArray<String> {
|
||||
val notes = SparseArray<String>()
|
||||
val uri = Data.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
Note.NOTE
|
||||
)
|
||||
|
||||
val selection = getSourcesSelection(true, contactId != null)
|
||||
val selectionArgs = getSourcesSelectionArgs(Note.CONTENT_ITEM_TYPE, contactId)
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val note = cursor.getStringValue(Note.NOTE) ?: return@queryCursor
|
||||
notes.put(id, note)
|
||||
}
|
||||
|
||||
return notes
|
||||
}
|
||||
|
||||
private fun getOrganizations(contactId: Int? = null): SparseArray<Organization> {
|
||||
val organizations = SparseArray<Organization>()
|
||||
val uri = Data.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.Organization.COMPANY,
|
||||
ContactsContract.CommonDataKinds.Organization.TITLE
|
||||
)
|
||||
|
||||
val selection = getSourcesSelection(true, contactId != null)
|
||||
val selectionArgs = getSourcesSelectionArgs(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE, contactId)
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val company = cursor.getStringValue(ContactsContract.CommonDataKinds.Organization.COMPANY) ?: ""
|
||||
val title = cursor.getStringValue(ContactsContract.CommonDataKinds.Organization.TITLE) ?: ""
|
||||
if (company.isEmpty() && title.isEmpty()) {
|
||||
return@queryCursor
|
||||
}
|
||||
|
||||
val organization = Organization(company, title)
|
||||
organizations.put(id, organization)
|
||||
}
|
||||
|
||||
return organizations
|
||||
}
|
||||
|
||||
private fun getWebsites(contactId: Int? = null): SparseArray<ArrayList<String>> {
|
||||
val websites = SparseArray<ArrayList<String>>()
|
||||
val uri = Data.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.RAW_CONTACT_ID,
|
||||
Website.URL
|
||||
)
|
||||
|
||||
val selection = getSourcesSelection(true, contactId != null)
|
||||
val selectionArgs = getSourcesSelectionArgs(Website.CONTENT_ITEM_TYPE, contactId)
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||
val url = cursor.getStringValue(Website.URL) ?: return@queryCursor
|
||||
|
||||
if (websites[id] == null) {
|
||||
websites.put(id, ArrayList())
|
||||
}
|
||||
|
||||
websites[id]!!.add(url)
|
||||
}
|
||||
|
||||
return websites
|
||||
}
|
||||
|
||||
private fun getDeviceStoredGroups(): java.util.ArrayList<Group> {
|
||||
val groups = java.util.ArrayList<Group>()
|
||||
|
||||
val uri = ContactsContract.Groups.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
ContactsContract.Groups._ID,
|
||||
ContactsContract.Groups.TITLE,
|
||||
ContactsContract.Groups.SYSTEM_ID
|
||||
)
|
||||
|
||||
val selection = "${ContactsContract.Groups.AUTO_ADD} = ? AND ${ContactsContract.Groups.FAVORITES} = ?"
|
||||
val selectionArgs = arrayOf("0", "0")
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getLongValue(ContactsContract.Groups._ID)
|
||||
val title = cursor.getStringValue(ContactsContract.Groups.TITLE) ?: return@queryCursor
|
||||
|
||||
val systemId = cursor.getStringValue(ContactsContract.Groups.SYSTEM_ID)
|
||||
if (groups.map { it.title }.contains(title) && systemId != null) {
|
||||
return@queryCursor
|
||||
}
|
||||
|
||||
groups.add(Group(id, title))
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
private fun getContactGroups(storedGroups: ArrayList<Group>, contactId: Int? = null): SparseArray<ArrayList<Group>> {
|
||||
val groups = SparseArray<ArrayList<Group>>()
|
||||
|
||||
val uri = Data.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
Data.CONTACT_ID,
|
||||
Data.DATA1
|
||||
)
|
||||
|
||||
val selection = getSourcesSelection(true, contactId != null, false)
|
||||
val selectionArgs = getSourcesSelectionArgs(GroupMembership.CONTENT_ITEM_TYPE, contactId)
|
||||
|
||||
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||
val id = cursor.getIntValue(Data.CONTACT_ID)
|
||||
val newRowId = cursor.getLongValue(Data.DATA1)
|
||||
|
||||
val groupTitle = storedGroups.firstOrNull { it.id == newRowId }?.title ?: return@queryCursor
|
||||
val group = Group(newRowId, groupTitle)
|
||||
if (groups[id] == null) {
|
||||
groups.put(id, ArrayList())
|
||||
}
|
||||
groups[id]!!.add(group)
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
private fun getQuestionMarks() = ("?," * displayContactSources.filter { it.isNotEmpty() }.size).trimEnd(',')
|
||||
|
||||
private fun getSourcesSelection(addMimeType: Boolean = false, addContactId: Boolean = false, useRawContactId: Boolean = true): String {
|
||||
val strings = ArrayList<String>()
|
||||
if (addMimeType) {
|
||||
strings.add("${Data.MIMETYPE} = ?")
|
||||
}
|
||||
|
||||
if (addContactId) {
|
||||
strings.add("${if (useRawContactId) Data.RAW_CONTACT_ID else Data.CONTACT_ID} = ?")
|
||||
} else {
|
||||
// sometimes local device storage has null account_name, handle it properly
|
||||
val accountNameString = StringBuilder()
|
||||
if (displayContactSources.contains("")) {
|
||||
accountNameString.append("(")
|
||||
}
|
||||
accountNameString.append("${ContactsContract.RawContacts.ACCOUNT_NAME} IN (${getQuestionMarks()})")
|
||||
if (displayContactSources.contains("")) {
|
||||
accountNameString.append(" OR ${ContactsContract.RawContacts.ACCOUNT_NAME} IS NULL)")
|
||||
}
|
||||
strings.add(accountNameString.toString())
|
||||
}
|
||||
|
||||
return TextUtils.join(" AND ", strings)
|
||||
}
|
||||
|
||||
private fun getSourcesSelectionArgs(mimetype: String? = null, contactId: Int? = null): Array<String> {
|
||||
val args = ArrayList<String>()
|
||||
|
||||
if (mimetype != null) {
|
||||
args.add(mimetype)
|
||||
}
|
||||
|
||||
if (contactId != null) {
|
||||
args.add(contactId.toString())
|
||||
} else {
|
||||
args.addAll(displayContactSources.filter { it.isNotEmpty() })
|
||||
}
|
||||
|
||||
return args.toTypedArray()
|
||||
}
|
||||
|
||||
private fun getContactProjection() = arrayOf(
|
||||
Data.MIMETYPE,
|
||||
Data.CONTACT_ID,
|
||||
Data.RAW_CONTACT_ID,
|
||||
StructuredName.PREFIX,
|
||||
StructuredName.GIVEN_NAME,
|
||||
StructuredName.MIDDLE_NAME,
|
||||
StructuredName.FAMILY_NAME,
|
||||
StructuredName.SUFFIX,
|
||||
StructuredName.PHOTO_URI,
|
||||
StructuredName.PHOTO_THUMBNAIL_URI,
|
||||
StructuredName.STARRED,
|
||||
StructuredName.CUSTOM_RINGTONE,
|
||||
ContactsContract.RawContacts.ACCOUNT_NAME,
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE
|
||||
)
|
||||
|
||||
}
|
@@ -4,7 +4,6 @@ import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.core.app.AlarmManagerCompat
|
||||
@@ -15,6 +14,8 @@ import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import com.simplemobiletools.commons.helpers.isMarshmallowPlus
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import com.simplemobiletools.smsmessenger.extensions.isPlainTextMimeType
|
||||
import com.simplemobiletools.smsmessenger.models.Attachment
|
||||
import com.simplemobiletools.smsmessenger.models.Message
|
||||
import com.simplemobiletools.smsmessenger.receivers.ScheduledMessageReceiver
|
||||
import com.simplemobiletools.smsmessenger.receivers.SmsStatusDeliveredReceiver
|
||||
@@ -34,7 +35,7 @@ fun Context.getSendMessageSettings(): Settings {
|
||||
return settings
|
||||
}
|
||||
|
||||
fun Context.sendMessage(text: String, addresses: List<String>, subscriptionId: Int?, attachments: List<Uri>) {
|
||||
fun Context.sendMessage(text: String, addresses: List<String>, subscriptionId: Int?, attachments: List<Attachment>) {
|
||||
val settings = getSendMessageSettings()
|
||||
if (subscriptionId != null) {
|
||||
settings.subscriptionId = subscriptionId
|
||||
@@ -44,11 +45,19 @@ fun Context.sendMessage(text: String, addresses: List<String>, subscriptionId: I
|
||||
val message = com.klinker.android.send_message.Message(text, addresses.toTypedArray())
|
||||
|
||||
if (attachments.isNotEmpty()) {
|
||||
for (uri in attachments) {
|
||||
for (attachment in attachments) {
|
||||
try {
|
||||
val byteArray = contentResolver.openInputStream(uri)?.readBytes() ?: continue
|
||||
val mimeType = contentResolver.getType(uri) ?: continue
|
||||
message.addMedia(byteArray, mimeType)
|
||||
val uri = attachment.getUri()
|
||||
contentResolver.openInputStream(uri)?.use {
|
||||
val bytes = it.readBytes()
|
||||
val mimeType = if (attachment.mimetype.isPlainTextMimeType()) {
|
||||
"application/txt"
|
||||
} else {
|
||||
attachment.mimetype
|
||||
}
|
||||
val name = attachment.filename
|
||||
message.addMedia(bytes, mimeType, name, name)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
} catch (e: Error) {
|
||||
|
@@ -8,7 +8,12 @@ import ezvcard.VCard
|
||||
|
||||
fun parseVCardFromUri(context: Context, uri: Uri, callback: (vCards: List<VCard>) -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
val inputStream = try {
|
||||
context.contentResolver.openInputStream(uri)
|
||||
} catch (e: Exception) {
|
||||
callback(emptyList())
|
||||
return@ensureBackgroundThread
|
||||
}
|
||||
val vCards = Ezvcard.parse(inputStream).all()
|
||||
callback(vCards)
|
||||
}
|
||||
|
@@ -1,8 +1,36 @@
|
||||
package com.simplemobiletools.smsmessenger.models
|
||||
|
||||
import android.net.Uri
|
||||
import com.simplemobiletools.smsmessenger.extensions.isImageMimeType
|
||||
import com.simplemobiletools.smsmessenger.extensions.isVCardMimeType
|
||||
import com.simplemobiletools.smsmessenger.extensions.isVideoMimeType
|
||||
import com.simplemobiletools.smsmessenger.helpers.ATTACHMENT_DOCUMENT
|
||||
import com.simplemobiletools.smsmessenger.helpers.ATTACHMENT_MEDIA
|
||||
import com.simplemobiletools.smsmessenger.helpers.ATTACHMENT_VCARD
|
||||
|
||||
data class AttachmentSelection(
|
||||
val id: String,
|
||||
val uri: Uri,
|
||||
val isPending: Boolean,
|
||||
)
|
||||
val mimetype: String,
|
||||
val filename: String,
|
||||
var isPending: Boolean,
|
||||
val viewType: Int = getViewTypeForMimeType(mimetype)
|
||||
) {
|
||||
companion object {
|
||||
fun getViewTypeForMimeType(mimetype: String): Int {
|
||||
return when {
|
||||
mimetype.isImageMimeType() || mimetype.isVideoMimeType() -> ATTACHMENT_MEDIA
|
||||
mimetype.isVCardMimeType() -> ATTACHMENT_VCARD
|
||||
else -> ATTACHMENT_DOCUMENT
|
||||
}
|
||||
}
|
||||
|
||||
fun areItemsTheSame(first: AttachmentSelection, second: AttachmentSelection): Boolean {
|
||||
return first.id == second.id
|
||||
}
|
||||
|
||||
fun areContentsTheSame(first: AttachmentSelection, second: AttachmentSelection): Boolean {
|
||||
return first.uri == second.uri && first.mimetype == second.mimetype && first.filename == second.filename
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import com.simplemobiletools.commons.extensions.normalizePhoneNumber
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.extensions.*
|
||||
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
|
||||
|
||||
// more info at https://github.com/klinker41/android-smsmms
|
||||
class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() {
|
||||
@@ -42,6 +43,7 @@ class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() {
|
||||
ensureBackgroundThread {
|
||||
context.conversationsDB.insertOrUpdate(conversation)
|
||||
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ class ScheduledMessageReceiver : BroadcastReceiver() {
|
||||
}
|
||||
|
||||
val addresses = message.participants.getAddresses()
|
||||
val attachments = message.attachment?.attachments?.mapNotNull { it.getUri() } ?: emptyList()
|
||||
val attachments = message.attachment?.attachments ?: emptyList()
|
||||
|
||||
try {
|
||||
context.sendMessage(message.body, addresses, message.subscriptionId, attachments)
|
||||
|
3
app/src/main/res/drawable/ic_document_vector.xml
Normal file
3
app/src/main/res/drawable/ic_document_vector.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M14 2H6C4.9 2 4.01 2.9 4.01 4L4 20c0 1.1 0.89 2 1.99 2H18c1.1 0 2-0.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/>
|
||||
</vector>
|
3
app/src/main/res/drawable/ic_image_vector.xml
Normal file
3
app/src/main/res/drawable/ic_image_vector.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M21 19V5c0-1.1-0.9-2-2-2H5C3.9 3 3 3.9 3 5v14c0 1.1 0.9 2 2 2h14c1.1 0 2-0.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
||||
</vector>
|
3
app/src/main/res/drawable/ic_music_vector.xml
Normal file
3
app/src/main/res/drawable/ic_music_vector.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12 3v10.55C11.41 13.21 10.73 13 10 13c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
|
||||
</vector>
|
3
app/src/main/res/drawable/ic_vector_audio_file.xml
Normal file
3
app/src/main/res/drawable/ic_vector_audio_file.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M14 2H6C4.9 2 4.01 2.9 4.01 4L4 20c0 1.1 0.89 2 1.99 2H18c1.1 0 2-0.9 2-2V8l-6-6zm2 11h-3v3.75c0 1.24-1.01 2.25-2.25 2.25S8.5 17.99 8.5 16.75s1.01-2.25 2.25-2.25c0.46 0 0.89 0.14 1.25 0.38V11h4v2zm-3-4V3.5L18.5 9H13z"/>
|
||||
</vector>
|
3
app/src/main/res/drawable/ic_vector_folder_zip.xml
Normal file
3
app/src/main/res/drawable/ic_vector_folder_zip.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20 6h-8l-2-2H4C2.9 4 2.01 4.9 2.01 6L2 18c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9 2-2V8c0-1.1-0.9-2-2-2zm-2 6h-2v2h2v2h-2v2h-2v-2h2v-2h-2v-2h2v-2h-2V8h2v2h2v2z"/>
|
||||
</vector>
|
3
app/src/main/res/drawable/ic_vector_pdf.xml
Normal file
3
app/src/main/res/drawable/ic_vector_pdf.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="1024" android:viewportHeight="1024">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M597.3 384H832L597.3 149.3V384M298.7 85.3H640l256 256v512a85.3 85.3 0 0 1-85.3 85.3h-512a85.3 85.3 0 0 1-85.3-85.3V170.7a85.3 85.3 0 0 1 85.3-85.3M509 530.8c17.5 38.4 39.7 70 65.3 91.7l17.5 13.7c-37.1 6.8-88.3 18.8-142.5 39.7l-4.7 1.7 21.3-44.4c19.2-37.1 33.3-70.8 43.1-102.4m276.5 162.6c7.7-7.7 11.5-17.5 11.9-28.2 1.3-8.5-0.9-16.6-5.1-23.5-12.4-20.1-44.4-29.4-97.3-29.4l-55 3-37.1-24.7c-26.9-22.2-51.2-61-68.3-109.2l1.7-6c14.1-56.7 27.3-125.4-0.9-153.6a36.4 36.4 0 0 0-26-10.2h-10.2c-15.8 0-29.9 16.6-33.7 32.9-15.8 56.7-6.4 87.9 9.4 139.5v0.4c-10.7 37.5-24.3 81.1-46.1 125l-41 76.8-38 20.9c-51.2 32-75.5 67.8-80.2 90.5-1.7 8.1-0.9 15.4 2.1 23l1.3 2.1 20.5 13.2 18.8 4.7c34.6 0 73.8-40.5 126.7-131l7.7-3c43.9-14.1 98.6-23.9 171.9-32 43.9 21.8 95.6 31.6 128 31.6 18.8 0 31.6-4.7 38.8-12.8m-17.5-30.3l3.8 4.7c-0.4 4.3-1.7 4.7-3.8 5.5h-1.7l-8.1 0.9c-19.6 0-49.9-8.1-81.1-21.8 3.8-4.3 5.5-4.3 9.8-4.3 59.7 0 76.8 10.7 81.1 14.9m-391.2 62.3c-27.7 50.8-52.9 78.9-72.1 85.3 2.1-16.2 21.3-44.4 51.6-72.1l20.5-13.2m128.9-294.8c-9.8-38.4-10.2-69.5-3-87.5l3-5.1 6.4 2.1c7.3 10.2 8.1 23.9 3.8 46.9l-1.3 6.8-6.8 35-2.1 1.7z"/>
|
||||
</vector>
|
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>
|
||||
</vector>
|
3
app/src/main/res/drawable/ic_video_camera_vector.xml
Normal file
3
app/src/main/res/drawable/ic_video_camera_vector.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18 10.48V6c0-1.1-0.9-2-2-2H4C2.9 4 2 4.9 2 6v12c0 1.1 0.9 2 2 2h12c1.1 0 2-0.9 2-2v-4.48l4 3.98v-11l-4 3.98zM5 16l2.38-3.17L9 15l2.62-3.5L15 16H5z"/>
|
||||
</vector>
|
3
app/src/main/res/drawable/ic_videocam_vector.xml
Normal file
3
app/src/main/res/drawable/ic_videocam_vector.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17 10.5V7c0-0.55-0.45-1-1-1H4C3.45 6 3 6.45 3 7v10c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1v-3.5l4 4v-11l-4 4z"/>
|
||||
</vector>
|
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<size
|
||||
android:width="0dp"
|
||||
android:height="@dimen/tiny_margin" />
|
||||
</shape>
|
@@ -22,10 +22,10 @@
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<RelativeLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/thread_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/tiny_margin"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
@@ -106,9 +107,9 @@
|
||||
<com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
|
||||
android:id="@+id/thread_messages_fastscroller"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/message_divider"
|
||||
android:layout_below="@+id/thread_add_contacts"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/message_divider"
|
||||
app:layout_constraintTop_toBottomOf="@id/thread_add_contacts"
|
||||
app:supportSwipeToRefresh="true">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyRecyclerView
|
||||
@@ -130,43 +131,47 @@
|
||||
android:id="@+id/message_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:layout_above="@+id/scheduled_message_holder"
|
||||
android:background="@color/divider_grey"
|
||||
android:importantForAccessibility="no" />
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/scheduled_message_holder"
|
||||
app:layout_constraintTop_toBottomOf="@id/thread_messages_fastscroller"
|
||||
tools:layout_height="1dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thread_add_attachment"
|
||||
android:layout_width="@dimen/normal_icon_size"
|
||||
android:layout_height="@dimen/normal_icon_size"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="@dimen/small_margin"
|
||||
android:layout_marginEnd="@dimen/small_margin"
|
||||
android:layout_marginStart="@dimen/small_margin"
|
||||
android:alpha="0.9"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/attachment"
|
||||
android:padding="@dimen/normal_margin"
|
||||
android:src="@drawable/ic_plus_vector" />
|
||||
android:src="@drawable/ic_plus_vector"
|
||||
app:layout_constraintBottom_toTopOf="@id/attachment_picker_divider"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<RelativeLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/scheduled_message_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/thread_attachments_holder"
|
||||
android:layout_alignStart="@id/thread_type_message"
|
||||
android:layout_marginStart="@dimen/normal_margin"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:layout_marginEnd="@dimen/medium_margin"
|
||||
android:layout_marginBottom="@dimen/small_margin"
|
||||
android:background="@drawable/section_holder_stroke"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/thread_attachments_recyclerview"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/message_divider"
|
||||
app:layout_goneMarginBottom="@dimen/medium_margin"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/scheduled_message_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_gravity="start"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:drawableStart="@drawable/ic_clock_vector"
|
||||
@@ -184,57 +189,55 @@
|
||||
android:id="@+id/discard_scheduled_message"
|
||||
android:layout_width="@dimen/normal_icon_size"
|
||||
android:layout_height="@dimen/normal_icon_size"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_gravity="end"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/cancel_schedule_send"
|
||||
android:padding="@dimen/normal_margin"
|
||||
android:src="@drawable/ic_cross_vector" />
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/thread_attachments_holder"
|
||||
android:layout_width="wrap_content"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/thread_attachments_recyclerview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/thread_type_message"
|
||||
android:layout_alignStart="@+id/thread_type_message"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:layout_marginTop="@dimen/tiny_margin"
|
||||
android:layout_marginBottom="@dimen/small_margin"
|
||||
android:overScrollMode="never"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/normal_margin"
|
||||
android:paddingEnd="@dimen/normal_margin"
|
||||
android:scrollbars="none"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/thread_attachments_wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/attachment_preview_size"
|
||||
android:divider="@drawable/linear_layout_horizontal_divider"
|
||||
android:orientation="horizontal"
|
||||
android:paddingEnd="@dimen/normal_margin"
|
||||
android:showDividers="middle" />
|
||||
</HorizontalScrollView>
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toTopOf="@id/thread_type_message"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/scheduled_message_holder"
|
||||
app:layout_goneMarginTop="@dimen/medium_margin"
|
||||
tools:itemCount="2"
|
||||
tools:listitem="@layout/item_attachment_document_preview"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyEditText
|
||||
android:id="@+id/thread_type_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginStart="@dimen/small_margin"
|
||||
android:layout_marginEnd="@dimen/small_margin"
|
||||
android:layout_toStartOf="@+id/thread_select_sim_icon"
|
||||
android:layout_toEndOf="@+id/thread_add_attachment"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center_vertical"
|
||||
android:hint="@string/type_a_message"
|
||||
android:inputType="textCapSentences|textMultiLine"
|
||||
android:minHeight="@dimen/normal_icon_size" />
|
||||
android:minHeight="@dimen/normal_icon_size"
|
||||
app:layout_constraintBottom_toTopOf="@id/attachment_picker_divider"
|
||||
app:layout_constraintEnd_toStartOf="@id/thread_select_sim_icon"
|
||||
app:layout_constraintStart_toEndOf="@+id/thread_add_attachment" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thread_select_sim_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="@dimen/small_margin"
|
||||
android:layout_toStartOf="@+id/thread_character_counter"
|
||||
android:alpha="0.9"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:paddingStart="@dimen/medium_margin"
|
||||
@@ -242,43 +245,46 @@
|
||||
android:paddingEnd="@dimen/medium_margin"
|
||||
android:paddingBottom="@dimen/normal_margin"
|
||||
android:src="@drawable/ic_sim_vector"
|
||||
android:visibility="gone" />
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/attachment_picker_divider"
|
||||
app:layout_constraintEnd_toStartOf="@id/thread_character_counter"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thread_select_sim_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignStart="@+id/thread_select_sim_icon"
|
||||
android:layout_alignTop="@+id/thread_select_sim_icon"
|
||||
android:layout_alignEnd="@+id/thread_select_sim_icon"
|
||||
android:layout_alignBottom="@+id/thread_select_sim_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/normal_text_size"
|
||||
android:visibility="gone"
|
||||
tools:text="1" />
|
||||
app:layout_constraintBottom_toBottomOf="@id/thread_select_sim_icon"
|
||||
app:layout_constraintEnd_toEndOf="@id/thread_select_sim_icon"
|
||||
app:layout_constraintStart_toStartOf="@id/thread_select_sim_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/thread_select_sim_icon"
|
||||
tools:text="1"
|
||||
tools:textColor="@color/dark_grey"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/thread_character_counter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignTop="@+id/thread_send_message"
|
||||
android:layout_alignBottom="@+id/thread_send_message"
|
||||
android:layout_toStartOf="@+id/thread_send_message"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingStart="@dimen/small_margin"
|
||||
android:paddingEnd="@dimen/small_margin"
|
||||
android:text="0"
|
||||
android:textSize="@dimen/normal_text_size"
|
||||
android:visibility="gone"
|
||||
tools:ignore="HardcodedText" />
|
||||
app:layout_constraintBottom_toTopOf="@id/attachment_picker_divider"
|
||||
app:layout_constraintEnd_toStartOf="@+id/thread_send_message"
|
||||
app:layout_constraintTop_toTopOf="@+id/thread_send_message"
|
||||
tools:ignore="HardcodedText"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyButton
|
||||
android:id="@+id/thread_send_message"
|
||||
android:layout_width="@dimen/normal_icon_size"
|
||||
android:layout_height="@dimen/normal_icon_size"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="@dimen/small_margin"
|
||||
android:layout_marginEnd="@dimen/small_margin"
|
||||
android:alpha="0.4"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
@@ -287,7 +293,37 @@
|
||||
android:drawableTop="@drawable/ic_send_vector"
|
||||
android:paddingVertical="@dimen/small_margin"
|
||||
android:text="@string/sms"
|
||||
android:textSize="@dimen/smaller_text_size" />
|
||||
android:textSize="@dimen/smaller_text_size"
|
||||
app:layout_constraintBottom_toTopOf="@id/attachment_picker_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
<View
|
||||
android:id="@+id/attachment_picker_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:background="@color/divider_grey"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toTopOf="@id/attachment_picker_holder" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/attachment_picker_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/small_margin"
|
||||
android:overScrollMode="always"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<include
|
||||
layout="@layout/layout_attachment_picker"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/conversation_holder"
|
||||
android:layout_width="@dimen/attachment_preview_size"
|
||||
android:layout_height="@dimen/attachment_preview_size">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thread_attachment_preview"
|
||||
android:layout_width="@dimen/attachment_preview_size"
|
||||
android:layout_height="@dimen/attachment_preview_size"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/thread_attachment_progress"
|
||||
android:layout_width="@dimen/remove_attachment_size"
|
||||
android:layout_height="@dimen/remove_attachment_size"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thread_remove_attachment"
|
||||
android:layout_width="@dimen/remove_attachment_size"
|
||||
android:layout_height="@dimen/remove_attachment_size"
|
||||
android:layout_alignTop="@+id/thread_attachment_preview"
|
||||
android:layout_alignEnd="@+id/thread_attachment_preview"
|
||||
android:padding="@dimen/tiny_margin"
|
||||
android:src="@drawable/ic_cross_vector"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</RelativeLayout>
|
53
app/src/main/res/layout/item_attachment_document.xml
Normal file
53
app/src/main/res/layout/item_attachment_document.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/document_attachment_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/section_holder_stroke"
|
||||
android:foreground="@drawable/ripple_all_corners"
|
||||
android:minHeight="@dimen/attachment_preview_min_height"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/normal_margin"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_min="@dimen/attachment_preview_min_height"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:background="@drawable/circle_background"
|
||||
android:padding="@dimen/medium_margin"
|
||||
app:srcCompat="@drawable/ic_document_vector" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/medium_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/filename"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:text="@string/attachment"
|
||||
android:textSize="@dimen/normal_text_size"
|
||||
android:textStyle="bold"
|
||||
tools:text="Event_16_02_2022.ics" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/file_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/normal_text_size"
|
||||
tools:text="2.18 KB" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
30
app/src/main/res/layout/item_attachment_document_preview.xml
Normal file
30
app/src/main/res/layout/item_attachment_document_preview.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/thread_attachment_wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include
|
||||
layout="@layout/item_attachment_document"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_min="@dimen/attachment_preview_min_height"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1"
|
||||
app:layout_constraintWidth_max="@dimen/attachment_preview_max_width" />
|
||||
|
||||
<include
|
||||
layout="@layout/item_remove_attachment_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@id/document_attachment_holder"
|
||||
app:layout_constraintEnd_toEndOf="@id/document_attachment_holder"
|
||||
app:layout_constraintStart_toEndOf="@id/document_attachment_holder"
|
||||
app:layout_constraintTop_toTopOf="@id/document_attachment_holder" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
55
app/src/main/res/layout/item_attachment_media_preview.xml
Normal file
55
app/src/main/res/layout/item_attachment_media_preview.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/thread_attachment_wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/media_attachment_holder"
|
||||
android:layout_width="@dimen/attachment_preview_size"
|
||||
android:layout_height="@dimen/attachment_preview_size"
|
||||
android:background="@drawable/section_holder_stroke"
|
||||
android:foreground="@drawable/ripple_all_corners"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="1dp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/play_icon"
|
||||
android:layout_width="@dimen/remove_attachment_size"
|
||||
android:layout_height="@dimen/remove_attachment_size"
|
||||
android:layout_gravity="center"
|
||||
android:alpha="0.8"
|
||||
android:src="@drawable/ic_vector_play_circle_outline"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/compression_progress"
|
||||
android:layout_width="@dimen/remove_attachment_size"
|
||||
android:layout_height="@dimen/remove_attachment_size"
|
||||
android:layout_gravity="center" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/item_remove_attachment_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@id/media_attachment_holder"
|
||||
app:layout_constraintEnd_toEndOf="@id/media_attachment_holder"
|
||||
app:layout_constraintStart_toEndOf="@id/media_attachment_holder"
|
||||
app:layout_constraintTop_toTopOf="@id/media_attachment_holder" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -1,57 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/vcard_attachment_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/section_holder_stroke"
|
||||
android:orientation="vertical"
|
||||
android:foreground="@drawable/ripple_all_corners"
|
||||
android:minHeight="@dimen/attachment_preview_min_height"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/normal_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/vcard_photo"
|
||||
android:layout_width="@dimen/normal_icon_size"
|
||||
android:layout_height="@dimen/normal_icon_size"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@color/md_red" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/vcard_title"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/normal_margin"
|
||||
android:ellipsize="end"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintStart_toEndOf="@id/vcard_photo"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Bob" />
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/medium_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/vcard_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/normal_margin"
|
||||
android:textSize="@dimen/normal_text_size"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/view_contact_details"
|
||||
app:layout_constraintStart_toEndOf="@id/vcard_photo"
|
||||
app:layout_constraintTop_toBottomOf="@id/vcard_title"
|
||||
tools:text="and 6 others"
|
||||
tools:visibility="visible" />
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/vcard_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textSize="@dimen/normal_text_size"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="Elon Reeve Musk"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/view_contact_details"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/normal_margin"
|
||||
android:layout_marginTop="@dimen/small_margin"
|
||||
android:text="@string/view_contact_details"
|
||||
android:textColor="@color/color_primary"
|
||||
android:textSize="@dimen/smaller_text_size"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/vcard_photo"
|
||||
app:layout_constraintTop_toBottomOf="@id/vcard_subtitle" />
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/vcard_subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/smaller_text_size"
|
||||
android:visibility="gone"
|
||||
tools:text="and 6 others"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/view_contact_details"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/small_margin"
|
||||
android:text="@string/view_contact_details"
|
||||
android:textColor="@color/color_primary"
|
||||
android:textSize="@dimen/smaller_text_size"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
39
app/src/main/res/layout/item_attachment_vcard_preview.xml
Normal file
39
app/src/main/res/layout/item_attachment_vcard_preview.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/thread_attachment_wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include
|
||||
layout="@layout/item_attachment_vcard"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_min="@dimen/attachment_preview_min_height"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1"
|
||||
app:layout_constraintWidth_max="@dimen/attachment_preview_max_width" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/vcard_progress"
|
||||
android:layout_width="@dimen/remove_attachment_size"
|
||||
android:layout_height="@dimen/remove_attachment_size"
|
||||
app:layout_constraintBottom_toBottomOf="@id/vcard_attachment_holder"
|
||||
app:layout_constraintEnd_toEndOf="@id/vcard_attachment_holder"
|
||||
app:layout_constraintStart_toStartOf="@id/vcard_attachment_holder"
|
||||
app:layout_constraintTop_toTopOf="@id/vcard_attachment_holder" />
|
||||
|
||||
<include
|
||||
layout="@layout/item_remove_attachment_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@id/vcard_attachment_holder"
|
||||
app:layout_constraintEnd_toEndOf="@id/vcard_attachment_holder"
|
||||
app:layout_constraintStart_toEndOf="@id/vcard_attachment_holder"
|
||||
app:layout_constraintTop_toTopOf="@id/vcard_attachment_holder" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -34,7 +34,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@+id/thread_message_sender_photo"
|
||||
android:orientation="vertical" />
|
||||
android:divider="@drawable/linear_layout_vertical_divider"
|
||||
android:orientation="vertical"
|
||||
android:showDividers="middle" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thread_message_play_outline"
|
||||
|
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/thread_received_attachment_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thread_received_attachment_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:background="@drawable/item_received_background"
|
||||
android:drawableStart="@drawable/ic_attach_file_vector"
|
||||
android:drawablePadding="@dimen/small_margin"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="@dimen/small_margin"
|
||||
android:paddingTop="@dimen/normal_margin"
|
||||
android:paddingEnd="@dimen/normal_margin"
|
||||
android:paddingBottom="@dimen/normal_margin"
|
||||
android:text="@string/attachment"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
|
||||
</RelativeLayout>
|
22
app/src/main/res/layout/item_remove_attachment_button.xml
Normal file
22
app/src/main/res/layout/item_remove_attachment_button.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/remove_attachment_button"
|
||||
android:layout_width="@dimen/remove_attachment_size"
|
||||
android:layout_height="@dimen/remove_attachment_size"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:layout_marginEnd="@dimen/medium_margin"
|
||||
android:background="@drawable/button_background_rounded"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/tiny_margin"
|
||||
android:src="@drawable/ic_cross_vector"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@@ -23,7 +23,9 @@
|
||||
android:id="@+id/thread_mesage_attachments_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
android:divider="@drawable/linear_layout_vertical_divider"
|
||||
android:orientation="vertical"
|
||||
android:showDividers="middle" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thread_message_play_outline"
|
||||
|
275
app/src/main/res/layout/layout_attachment_picker.xml
Normal file
275
app/src/main/res/layout/layout_attachment_picker.xml
Normal file
@@ -0,0 +1,275 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="@dimen/normal_margin">
|
||||
|
||||
<androidx.constraintlayout.helper.widget.Flow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="choose_photo,choose_video,take_photo,record_video,record_audio,pick_file,pick_contact,schedule_message"
|
||||
app:flow_verticalGap="@dimen/small_margin"
|
||||
app:flow_wrapMode="aligned"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/choose_photo"
|
||||
android:layout_width="@dimen/attachment_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:minHeight="@dimen/attachment_button_height"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/medium_margin"
|
||||
android:paddingVertical="@dimen/medium_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/choose_photo_icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_image_vector" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/choose_photo_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:text="@string/choose_photo"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/default_text_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/choose_video"
|
||||
android:layout_width="@dimen/attachment_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:minHeight="@dimen/attachment_button_height"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/medium_margin"
|
||||
android:paddingVertical="@dimen/medium_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/choose_video_icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_video_camera_vector" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/choose_video_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:text="@string/choose_video"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/default_text_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/take_photo"
|
||||
android:layout_width="@dimen/attachment_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:minHeight="@dimen/attachment_button_height"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/medium_margin"
|
||||
android:paddingVertical="@dimen/medium_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/take_photo_icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_camera_vector" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/take_photo_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:text="@string/take_photo"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/default_text_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/record_video"
|
||||
android:layout_width="@dimen/attachment_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:minHeight="@dimen/attachment_button_height"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/medium_margin"
|
||||
android:paddingVertical="@dimen/medium_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/record_video_icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_videocam_vector" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/record_video_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:text="@string/record_video"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/default_text_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/record_audio"
|
||||
android:layout_width="@dimen/attachment_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:minHeight="@dimen/attachment_button_height"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/medium_margin"
|
||||
android:paddingVertical="@dimen/medium_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/record_audio_icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_microphone_vector" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/record_audio_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:text="@string/record_audio"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/default_text_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/pick_file"
|
||||
android:layout_width="@dimen/attachment_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:minHeight="@dimen/attachment_button_height"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/medium_margin"
|
||||
android:paddingVertical="@dimen/medium_margin"
|
||||
android:textColor="@color/default_text_color">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/pick_file_icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_document_vector" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/pick_file_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:text="@string/choose_file"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/default_text_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/pick_contact"
|
||||
android:layout_width="@dimen/attachment_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:minHeight="@dimen/attachment_button_height"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/medium_margin"
|
||||
android:paddingVertical="@dimen/medium_margin"
|
||||
android:textColor="@color/default_text_color">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/pick_contact_icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_person_vector" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/pick_contact_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:text="@string/choose_contact"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/default_text_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/schedule_message"
|
||||
android:layout_width="@dimen/attachment_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:minHeight="@dimen/attachment_button_height"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/medium_margin"
|
||||
android:paddingVertical="@dimen/medium_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/schedule_message_icon"
|
||||
android:layout_width="@dimen/medium_icon_size"
|
||||
android:layout_height="@dimen/medium_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:padding="@dimen/medium_margin"
|
||||
android:src="@drawable/ic_clock_vector" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/schedule_message_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:text="@string/schedule_message"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/default_text_color"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">ازالة التثبيت</string>
|
||||
<string name="forward_message">اعادة ارسال</string>
|
||||
<string name="compress_error">غير قادر على ضغط الصورة إلى الحجم المحدد</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="zero">و %d أخرى</item>
|
||||
<item quantity="one">و %d أخرى</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Адмацаваць</string>
|
||||
<string name="forward_message">Пераслаць</string>
|
||||
<string name="compress_error">Немагчыма сціснуць выяву да выбранага памеру</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="few">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Откачване</string>
|
||||
<string name="forward_message">Препращане</string>
|
||||
<string name="compress_error">Невъзможно е да се компресира изображението до избрания размер</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">No fixis</string>
|
||||
<string name="forward_message">Reenvia</string>
|
||||
<string name="compress_error">No s\'ha pogut comprimir la imatge a la mida seleccionada</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">i %d altra</item>
|
||||
<item quantity="other">i %d altres</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Odepnout</string>
|
||||
<string name="forward_message">Přeposlat</string>
|
||||
<string name="compress_error">Nepodařilo se komprimovat obrázek na požadovanou velikost</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">a %d další</item>
|
||||
<item quantity="few">a %d další</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Frigør</string>
|
||||
<string name="forward_message">Fremad</string>
|
||||
<string name="compress_error">Billedet kan ikke komprimeres til den valgte størrelse</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Losheften</string>
|
||||
<string name="forward_message">Weiterleiten</string>
|
||||
<string name="compress_error">Bild kann nicht auf ausgewählte Größe komprimiert werden</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">und %d andere</item>
|
||||
<item quantity="other">und %d anderen</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Ξεκαρφίτσωμα</string>
|
||||
<string name="forward_message">Προώθηση</string>
|
||||
<string name="compress_error">Αδυναμία συμπίεσης εικόνας στο επιλεγμένο μέγεθος</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">και άλλος %d</item>
|
||||
<item quantity="other">και άλλοι %d</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Depingli</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Desanclar</string>
|
||||
<string name="forward_message">Reenviar</string>
|
||||
<string name="compress_error">Incapaz de comprimir la imagen al tamaño seleccionado</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">y %d otro</item>
|
||||
<item quantity="many">y %d otros</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Eemalda kinnitus</string>
|
||||
<string name="forward_message">Edasta</string>
|
||||
<string name="compress_error">Pildi muutmine valitud suurusesse ei õnnestu</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">ja %d muud</item>
|
||||
<item quantity="other">ja %d teised</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Poista</string>
|
||||
<string name="forward_message">Välitä</string>
|
||||
<string name="compress_error">Kuvan pakkaaminen valittuun kokoon ei onnistu</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">ja %d muu</item>
|
||||
<item quantity="other">ja %d muuta</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Désépingler</string>
|
||||
<string name="forward_message">Transférer</string>
|
||||
<string name="compress_error">Impossible de compresser l\'image à la taille sélectionnée</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">et %d autre</item>
|
||||
<item quantity="many">et %d autres</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Desenganche</string>
|
||||
<string name="forward_message">Adiante</string>
|
||||
<string name="compress_error">Non se puido comprimir a imaxe ao tamaño seleccionado</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">E %d outro</item>
|
||||
<item quantity="other">E %d outros</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Otkvači</string>
|
||||
<string name="forward_message">Proslijedi</string>
|
||||
<string name="compress_error">Isključi za komprimiranje slike na odabranu veličinu</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">i još %d druga</item>
|
||||
<item quantity="few">i još %d druge</item>
|
||||
@@ -31,14 +33,14 @@
|
||||
<string name="add_contact_or_number">Dodaj kontakt ili broj …</string>
|
||||
<string name="suggestions">Prijedlozi</string>
|
||||
<!-- Schedule send -->
|
||||
<string name="scheduled_message">Scheduled message</string>
|
||||
<string name="schedule_message">Schedule message</string>
|
||||
<string name="schedule_send">Schedule send</string>
|
||||
<string name="cancel_schedule_send">Cancel schedule send</string>
|
||||
<string name="must_pick_time_in_the_future">You must pick a time in the future</string>
|
||||
<string name="schedule_send_warning">Keep the phone on and make sure there is nothing killing the app while in background.</string>
|
||||
<string name="update_message">Update message</string>
|
||||
<string name="send_now">Send now</string>
|
||||
<string name="scheduled_message">Zakazana poruka</string>
|
||||
<string name="schedule_message">Raspored poruka</string>
|
||||
<string name="schedule_send">Raspored slanja</string>
|
||||
<string name="cancel_schedule_send">Otkaži slanje rasporeda</string>
|
||||
<string name="must_pick_time_in_the_future">Morate odabrati vrijeme u budućnosti</string>
|
||||
<string name="schedule_send_warning">Držite telefon uključen i provjerite da ništa ne uništava aplikaciju dok je u pozadini.</string>
|
||||
<string name="update_message">Ažuriraj poruku</string>
|
||||
<string name="send_now">Pošalji sada</string>
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Primljene SMS poruke</string>
|
||||
<string name="new_message">Nova poruka</string>
|
||||
@@ -85,8 +87,8 @@
|
||||
<string name="faq_1_text">Nažalost, potreban je za slanje MMS privitaka. Nemogućnost slanja MMS-a bila bi zaista veliki nedostatak u usporedbi s drugim aplikacijama, pa smo odlučili krenuti ovim putem. No, kao i obično, nema reklama, praćenja ili analiziranja podataka, internet se koristi samo za slanje MMS-a.</string>
|
||||
<string name="faq_2_title">Druga strana ne prima moj MMS. Mogu li išta učiniti po tom pitanju\?</string>
|
||||
<string name="faq_2_text">Veličinu MMS-a ograničavaju operateri. Možeš pokušati smanjiti ograničenje u postavkama aplikacije.</string>
|
||||
<string name="faq_3_title">Does the app support scheduled messages?</string>
|
||||
<string name="faq_3_text">Yes, you can schedule messages to be sent in the future by long pressing the Send button and picking the desired date and time.</string>
|
||||
<string name="faq_3_title">Podržava li aplikacija zakazane poruke\?</string>
|
||||
<string name="faq_3_text">Da, možete zakazati slanje poruka u budućnosti tako da dugo pritisnete gumb Pošalji i odaberete željeni datum i vrijeme.</string>
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Kitűzés megszüntetése</string>
|
||||
<string name="forward_message">Továbbítás</string>
|
||||
<string name="compress_error">Nem lehet tömöríteni a képet a kiválasztott méretre</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">és még %d fő</item>
|
||||
<item quantity="other">és még %d fő</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="other">and %d others</item>
|
||||
</plurals>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Rimuovi</string>
|
||||
<string name="forward_message">Inoltra</string>
|
||||
<string name="compress_error">Impossibile comprimere l\'immagine alla dimensione selezionata</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">e %d altro</item>
|
||||
<item quantity="many">e %d altri</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">בטל הצמדה</string>
|
||||
<string name="forward_message">התקדם</string>
|
||||
<string name="compress_error">לא ניתן לדחוס תמונה לגודל שנבחר</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="two">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">固定を外す</string>
|
||||
<string name="forward_message">転送</string>
|
||||
<string name="compress_error">選択したサイズに画像を圧縮できません</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="other">and %d others</item>
|
||||
</plurals>
|
||||
@@ -80,7 +82,7 @@
|
||||
<string name="faq_2_title">メッセージ相手が MMS を受信できません。何かできますか?</string>
|
||||
<string name="faq_2_text">MMS のサイズは通信キャリアによって制限されています。アプリの設定で制限を小さくしてみてください。</string>
|
||||
<string name="faq_3_title">アプリは予約メッセージをサポートしていますか?</string>
|
||||
<string name="faq_3_text">はい、送信ボタンを長押しして、希望の日付と日時を選択することで、今後送信したいメッセージを予約できます。</string>
|
||||
<string name="faq_3_text">はい、送信ボタンを長押しして、希望の日付と時間を選択することで、今後送信したいメッセージを予約できます。</string>
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Atjunkite</string>
|
||||
<string name="forward_message">Pirmyn</string>
|
||||
<string name="compress_error">Nepavyksta suspausti vaizdo iki pasirinkto dydžio</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="zero">and %d other</item>
|
||||
<item quantity="one">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Løsne</string>
|
||||
<string name="forward_message">Videresend</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Losmaken</string>
|
||||
<string name="forward_message">Doorsturen</string>
|
||||
<string name="compress_error">Kon de afbeelding niet comprimeren naar de gekozen grootte</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">en %d andere</item>
|
||||
<item quantity="other">en %d anderen</item>
|
||||
|
@@ -16,11 +16,13 @@
|
||||
<string name="no_reply_support">بھیجݨ والا جواب نہیں لیندے اے</string>
|
||||
<string name="draft">کھرڑا</string>
|
||||
<string name="sending">بھیجیا جا رہا اے…</string>
|
||||
<string name="pin_conversation">Pin to the top</string>
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="pin_conversation">سکھر تے لگو</string>
|
||||
<string name="unpin_conversation">لگݨ اُلٹاؤ</string>
|
||||
<string name="forward_message">اگے</string>
|
||||
<string name="compress_error">تصویر نوں چݨے ہوۓ اکار وچ سنکُچت کر نہیں سکدی</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">تے %d ہور</item>
|
||||
<item quantity="other">تے %d ہور</item>
|
||||
@@ -31,21 +33,21 @@
|
||||
<string name="suggestions">سُجھے</string>
|
||||
<!-- Schedule send -->
|
||||
<string name="scheduled_message">نیت سنہے</string>
|
||||
<string name="schedule_message">Schedule message</string>
|
||||
<string name="schedule_send">Schedule send</string>
|
||||
<string name="cancel_schedule_send">Cancel schedule send</string>
|
||||
<string name="must_pick_time_in_the_future">You must pick a time in the future</string>
|
||||
<string name="schedule_message">تالکا وچ سنیہا پایو</string>
|
||||
<string name="schedule_send">بھیجݨ دی سمان سارݨی بݨاؤ</string>
|
||||
<string name="cancel_schedule_send">سماں دا بھیجݨ رد کرو</string>
|
||||
<string name="must_pick_time_in_the_future">بھوِکھ وچ سماں چݨیو</string>
|
||||
<string name="schedule_send_warning">Keep the phone on and make sure there is nothing killing the app while in background.</string>
|
||||
<string name="update_message">Update message</string>
|
||||
<string name="send_now">Send now</string>
|
||||
<string name="update_message">سنیہا بدلو</string>
|
||||
<string name="send_now">ہݨے بھیجو</string>
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Received SMS</string>
|
||||
<string name="new_message">New message</string>
|
||||
<string name="mark_as_read">Mark as Read</string>
|
||||
<string name="mark_as_unread">Mark as Unread</string>
|
||||
<string name="me">Me</string>
|
||||
<string name="channel_received_sms">سنیہا لیا گیا</string>
|
||||
<string name="new_message">نواں سنیہا</string>
|
||||
<string name="mark_as_read">پرھے وجوں چنھت کرو</string>
|
||||
<string name="mark_as_unread">نا پڑھے وجوں چنھت کرو</string>
|
||||
<string name="me">میں</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Are you sure you want to delete all messages of this conversation\?</string>
|
||||
<string name="delete_whole_conversation_confirmation">تسیں پکے اے، سارے سنیہے مٹاؤ؟</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d conversation</item>
|
||||
@@ -53,35 +55,35 @@
|
||||
</plurals>
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d message</item>
|
||||
<item quantity="other">%d messages</item>
|
||||
<item quantity="one">%d سنیہا</item>
|
||||
<item quantity="other">%d سنیہے</item>
|
||||
</plurals>
|
||||
<!-- Settings -->
|
||||
<string name="lock_screen_visibility">Lock screen notification visibility</string>
|
||||
<string name="sender_and_message">Sender and message</string>
|
||||
<string name="sender_only">Sender only</string>
|
||||
<string name="sender_and_message">بھیجݨ والا تے سنیہا</string>
|
||||
<string name="sender_only">صرف بھیجݨ والا</string>
|
||||
<string name="enable_delivery_reports">Enable delivery reports</string>
|
||||
<string name="use_simple_characters">Remove accents and diacritics at sending messages</string>
|
||||
<string name="use_simple_characters">سنیہے وچ سارے عراب ہٹاؤ</string>
|
||||
<string name="send_on_enter">Send message on pressing Enter</string>
|
||||
<string name="mms_file_size_limit">Resize sent MMS images</string>
|
||||
<string name="mms_file_size_limit_none">No limit</string>
|
||||
<string name="mms_file_size_limit_none">کوئی حد نہیں</string>
|
||||
<string name="outgoing_messages">Outgoing messages</string>
|
||||
<string name="group_message_mms">Send group messages as MMS</string>
|
||||
<string name="send_long_message_mms">Send long messages as MMS</string>
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Messages</string>
|
||||
<string name="export_messages">Export messages</string>
|
||||
<string name="export_sms">Export SMS</string>
|
||||
<string name="export_mms">Export MMS</string>
|
||||
<string name="import_messages">Import messages</string>
|
||||
<string name="import_sms">Import SMS</string>
|
||||
<string name="import_mms">Import MMS</string>
|
||||
<string name="no_option_selected">You have to select at least one item</string>
|
||||
<string name="messages">سنیہے</string>
|
||||
<string name="export_messages">سنیہے ایکسپورٹ کرو</string>
|
||||
<string name="export_sms">سنیہے ایکسپورٹ کرو</string>
|
||||
<string name="export_mms">میڈیا سنیہا ایکسپورٹ کرو</string>
|
||||
<string name="import_messages">سنیہے ایمپورٹ کرو</string>
|
||||
<string name="import_sms">سنیہے ایمپورٹ کرو</string>
|
||||
<string name="import_mms">میڈیا دیاں سنیہے ایمپورٹ کرو</string>
|
||||
<string name="no_option_selected">کجھ چیز چݨیو</string>
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Why does the app require access to the internet\?</string>
|
||||
<string name="faq_1_text">Sadly it is needed for sending MMS attachments. Not being able to send MMS would be a really huge disadvantage compared to other apps, so we decided to go this way. However, as usually, there are no ads, tracking or analytics whatsoever, the internet is used only for sending MMS.</string>
|
||||
<string name="faq_2_title">The other end is not receiving my MMS, is there anything I can do about it\?</string>
|
||||
<string name="faq_2_text">MMS size is limited by carriers, you can try setting a smaller limit in the app settings.</string>
|
||||
<string name="faq_3_title">Does the app support scheduled messages\?</string>
|
||||
<string name="faq_3_title">ایس اَیپ وچ سنیہاں دا تالکا کیہ اے؟</string>
|
||||
<string name="faq_3_text">Yes, you can schedule messages to be sent in the future by long pressing the Send button and picking the desired date and time.</string>
|
||||
</resources>
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Odepnij</string>
|
||||
<string name="forward_message">Przekaż dalej</string>
|
||||
<string name="compress_error">Nie udało się skompresować obrazu do wybranego rozmiaru</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Powielony element nie został uwzględniony</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">i %d inny</item>
|
||||
<item quantity="few">i %d inne</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Desfixar</string>
|
||||
<string name="forward_message">Encaminhar</string>
|
||||
<string name="compress_error">Não pôde comprimir imagem ao tamanho selecionado</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="many">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Desafixar</string>
|
||||
<string name="forward_message">Reencaminhar</string>
|
||||
<string name="compress_error">Incapaz de comprimir imagem no tamanho selecionado</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="many">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Elimină fixarea</string>
|
||||
<string name="forward_message">Redirecţionare</string>
|
||||
<string name="compress_error">Nu se poate comprima imaginea la dimensiunea selectată</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">și %d alt</item>
|
||||
<item quantity="few">și %d altele</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Открепить</string>
|
||||
<string name="forward_message">Переслать</string>
|
||||
<string name="compress_error">Невозможно сжать изображение до выбранного размера</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">и ещё %d</item>
|
||||
<item quantity="few">и ещё %d</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Odopnúť</string>
|
||||
<string name="forward_message">Preposlať</string>
|
||||
<string name="compress_error">Nepodarilo sa zmenšiť obrázok na požadovanú veľkosť</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicitná položka nebola pridaná</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">a %d ďalší</item>
|
||||
<item quantity="few">a %d ďalší</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Odpni</string>
|
||||
<string name="forward_message">Posreduj</string>
|
||||
<string name="compress_error">Slike ni mogoče stisniti na izbrano velikost</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">in %d drug</item>
|
||||
<item quantity="two">in %d druga</item>
|
||||
|
91
app/src/main/res/values-sr/strings.xml
Normal file
91
app/src/main/res/values-sr/strings.xml
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Једноставан СМС Мессенгер</string>
|
||||
<string name="app_launcher_name">СМС Мессенгер</string>
|
||||
<string name="type_a_message">Унесите поруку…</string>
|
||||
<string name="message_not_sent_short">Порука није послата</string>
|
||||
<string name="message_not_sent_touch_retry">Није послата. Додирните да бисте покушали поново.</string>
|
||||
<string name="message_sending_error">Ваша порука за \'%s\' није послато</string>
|
||||
<string name="add_person">Додај особу</string>
|
||||
<string name="attachment">Прилог</string>
|
||||
<string name="no_conversations_found">Није пронађена ниједна сачувана преписка</string>
|
||||
<string name="start_conversation">Започните разговор</string>
|
||||
<string name="reply">Одговори</string>
|
||||
<string name="show_character_counter">Прикажи бројач знакова при писању порука</string>
|
||||
<string name="loading_messages">Учитавање порука…</string>
|
||||
<string name="no_reply_support">Пошиљалац не подржава одговоре</string>
|
||||
<string name="draft">Нацрт</string>
|
||||
<string name="sending">Шаље се…</string>
|
||||
<string name="pin_conversation">Закачите на врх</string>
|
||||
<string name="unpin_conversation">Oткопчати</string>
|
||||
<string name="forward_message">Напред</string>
|
||||
<string name="compress_error">Није могуће компримовати слику на изабрану величину</string>
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">и %d друго</item>
|
||||
<item quantity="few">и %d других</item>
|
||||
<item quantity="other">и %d други</item>
|
||||
</plurals>
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">Нови разговор</string>
|
||||
<string name="add_contact_or_number">Додајте контакт или број…</string>
|
||||
<string name="suggestions">Предлози</string>
|
||||
<!-- Schedule send -->
|
||||
<string name="scheduled_message">Заказана порука</string>
|
||||
<string name="schedule_message">Закажите поруку</string>
|
||||
<string name="schedule_send">Закажите слање</string>
|
||||
<string name="cancel_schedule_send">Откажи слање распореда</string>
|
||||
<string name="must_pick_time_in_the_future">Морате изабрати време у будућности</string>
|
||||
<string name="schedule_send_warning">Држите телефон укључен и уверите се да ништа не убија апликацију док је у позадини.</string>
|
||||
<string name="update_message">Ажурирајте поруку</string>
|
||||
<string name="send_now">Пошаљи одмах</string>
|
||||
<!-- Notifications -->
|
||||
<string name="channel_received_sms">Примите СМС</string>
|
||||
<string name="new_message">Нова порука</string>
|
||||
<string name="mark_as_read">Означи као прочитано</string>
|
||||
<string name="mark_as_unread">Означи као непрочитанo</string>
|
||||
<string name="me">Ja</string>
|
||||
<!-- Confirmation dialog -->
|
||||
<string name="delete_whole_conversation_confirmation">Да ли сте сигурни да желите да избришете све поруке ове конверзације\?</string>
|
||||
<!-- Are you sure you want to delete 5 conversations? -->
|
||||
<plurals name="delete_conversations">
|
||||
<item quantity="one">%d разговор</item>
|
||||
<item quantity="few">%d разговорa</item>
|
||||
<item quantity="other">%d разговори</item>
|
||||
</plurals>
|
||||
<!-- Are you sure you want to delete 5 messages? -->
|
||||
<plurals name="delete_messages">
|
||||
<item quantity="one">%d порука</item>
|
||||
<item quantity="few">%d порука</item>
|
||||
<item quantity="other">%d порукe</item>
|
||||
</plurals>
|
||||
<!-- Settings -->
|
||||
<string name="lock_screen_visibility">Видљивост обавештења на закључаном екрану</string>
|
||||
<string name="sender_and_message">Пошиљалац и порука</string>
|
||||
<string name="sender_only">Само пошиљалац</string>
|
||||
<string name="enable_delivery_reports">Омогућите извештаје о испоруци</string>
|
||||
<string name="use_simple_characters">Уклоните акценте и дијакритичке знакове при слању порука</string>
|
||||
<string name="mms_file_size_limit">Промените величину посланих ММС слика</string>
|
||||
<string name="mms_file_size_limit_none">Без лимита</string>
|
||||
<string name="outgoing_messages">Одлазне поруке</string>
|
||||
<string name="group_message_mms">Пошаљите групне поруке као ММС</string>
|
||||
<string name="send_long_message_mms">Шаљите дугачке поруке као ММС</string>
|
||||
<!-- Export / Import -->
|
||||
<string name="messages">Поруке</string>
|
||||
<string name="export_messages">Извезите поруке</string>
|
||||
<string name="export_sms">Извези СМС</string>
|
||||
<string name="export_mms">Извези ММС</string>
|
||||
<string name="import_messages">Увезите поруке</string>
|
||||
<string name="import_sms">Увезите СМС</string>
|
||||
<string name="import_mms">Увези ММС</string>
|
||||
<string name="no_option_selected">Морате да изаберете најмање једну ставку</string>
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">Зашто је апликацији потребан приступ интернету\?</string>
|
||||
<string name="faq_1_text">Нажалост, то је потребно за слање ММС прилога. Немогућност слања ММС-а била би заиста велика мана у поређењу са другим апликацијама, па смо одлучили да идемо овим путем. Међутим, као и обично, нема реклама, праћења или аналитике, интернет се користи само за слање ММС-а.</string>
|
||||
<string name="faq_2_title">Други крај не прима мој ММС, да ли могу нешто да урадим поводом тога\?</string>
|
||||
<string name="faq_2_text">Величина ММС-а је ограничена оператерима, можете покушати да подесите мање ограничење у подешавањима апликације.</string>
|
||||
<string name="faq_3_title">Да ли апликација подржава заказане поруке\?</string>
|
||||
<string name="faq_3_text">Да, можете заказати слање порука у будућности дугим притиском на дугме Пошаљи и одабиром жељеног датума и времена.</string>
|
||||
</resources>
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Lossa</string>
|
||||
<string name="forward_message">Vidarebefordra</string>
|
||||
<string name="compress_error">Det gick inte att komprimera bilden till den valda storleken</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">och %d annan</item>
|
||||
<item quantity="other">och %d andra</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">பின் நீக்கு</string>
|
||||
<string name="forward_message">முன்னோக்கி</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="other">and %d others</item>
|
||||
</plurals>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Sabitlemeyi kaldır</string>
|
||||
<string name="forward_message">İlet</string>
|
||||
<string name="compress_error">Resim seçilen boyuta sıkıştırılamıyor</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">ve %d diğeri</item>
|
||||
<item quantity="other">ve %d diğeri</item>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Відкріпити</string>
|
||||
<string name="forward_message">Переслати</string>
|
||||
<string name="compress_error">Не вдається стиснути зображення до вибраного розміру</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="few">and %d others</item>
|
||||
|
@@ -20,9 +20,11 @@
|
||||
<string name="unpin_conversation">取消固定</string>
|
||||
<string name="forward_message">转发</string>
|
||||
<string name="compress_error">无法将图像压缩到选定的大小</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">未包含重复项</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="other">及 %d 个其他人</item>
|
||||
<item quantity="other">及其他 %d 个联系人</item>
|
||||
</plurals>
|
||||
<!-- New conversation -->
|
||||
<string name="new_conversation">新的对话</string>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">取消釘選</string>
|
||||
<string name="forward_message">轉傳</string>
|
||||
<string name="compress_error">無法將圖片壓縮至指定大小</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="other">and %d others</item>
|
||||
</plurals>
|
||||
|
@@ -3,9 +3,15 @@
|
||||
<dimen name="notification_large_icon_size">72dp</dimen>
|
||||
<dimen name="bigger_avatar_size">64dp</dimen>
|
||||
<dimen name="play_outline_size">36dp</dimen>
|
||||
<dimen name="attachment_preview_size">60dp</dimen>
|
||||
<dimen name="attachment_preview_size">64dp</dimen>
|
||||
<dimen name="attachment_preview_min_height">@dimen/attachment_preview_size</dimen>
|
||||
<dimen name="attachment_preview_max_width">156dp</dimen>
|
||||
<dimen name="remove_attachment_size">24dp</dimen>
|
||||
<dimen name="pin_icon_size">15dp</dimen>
|
||||
<dimen name="vcard_property_start_margin">64dp</dimen>
|
||||
<dimen name="small_icon_size">20dp</dimen>
|
||||
<dimen name="medium_icon_size">36dp</dimen>
|
||||
<dimen name="attachment_button_height">96dp</dimen>
|
||||
<dimen name="attachment_button_width">90dp</dimen>
|
||||
<dimen name="default_keyboard_height">250dp</dimen>
|
||||
</resources>
|
||||
|
@@ -20,7 +20,9 @@
|
||||
<string name="unpin_conversation">Unpin</string>
|
||||
<string name="forward_message">Forward</string>
|
||||
<string name="compress_error">Unable to compress image to selected size</string>
|
||||
<!-- vCard-->
|
||||
<!-- Attachments -->
|
||||
<string name="duplicate_item_warning">Duplicate item was not included</string>
|
||||
<!-- vCard -->
|
||||
<plurals name="and_other_contacts">
|
||||
<item quantity="one">and %d other</item>
|
||||
<item quantity="other">and %d others</item>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path name="compressed_files" path="compressed/"/>
|
||||
<cache-path name="captured_files" path="captured/"/>
|
||||
<cache-path name="attachment_files" path="attachments/"/>
|
||||
</paths>
|
||||
|
@@ -1 +1 @@
|
||||
SMS- und MMS-Nachrichten schnell versenden. Saubere schöne Schnittstelle
|
||||
SMS- und MMS-Nachrichten schnell versenden, schöne Benutzeroberfläche
|
||||
|
1
fastlane/metadata/android/de-DE/video.txt
Normal file
1
fastlane/metadata/android/de-DE/video.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
@@ -1 +1 @@
|
||||
Sencilla Mensajería de SMS
|
||||
Mensajería Sencilla de SMS
|
||||
|
27
fastlane/metadata/android/hr/full_description.txt
Normal file
27
fastlane/metadata/android/hr/full_description.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
Sjajan način da ostanete u kontaktu sa rodbinom, slanjem poruka. Aplikacija ispravno obrađuje i grupne poruke, kao i blokiranje brojeva s Androida 7+. Ostanite u kontaktu sa svojim kontaktima pomoću aplikacije za razmjenu poruka na telefonu. Nikada nije bilo lakše dijeliti fotografije, slati emojije ili samo pozdraviti. Postoji toliko toga što možete učiniti sa porukama, poput isključivanja zvuka razgovora ili dodjele posebnih tonova poruka za određene kontakte. S aplikacijom za poruke možete uživati u svakodnevnoj razmjeni poruka na zabavniji način.
|
||||
|
||||
Nudi mnoge formate datuma koje možete izabrati kako biste se osjećali ugodno dok ga koristite. Također možete mijenjati format vremena između 12 i 24 sata. Ova vam aplikacija također daje fleksibilnost sigurnosnog kopiranja SMS-a. Ne morate spremati poruke na bilo koji vanjski uređaj ili koristiti bilo koji drugi hardver za njihovo spremanje. Ova značajka sigurnosne kopije SMS-a pomoći će vam da spremite tekstualne poruke i mms podatke bez interne pohrane.
|
||||
|
||||
Ova aplikacija za razmjenu poruka ima stvarno malu veličinu aplikacije u usporedbi s konkurencijom, što ju čini vrlo brzom za preuzimanje. Tehnika sigurnosne kopije sms-a korisna je kada morate promijeniti svoj uređaj ili vam ga ukradu. Na ovaj način možete lako dohvatiti tekstualnu poruku i iz grupne i iz privatne razmjene poruka pomoću sigurnosne kopije sms-a u ovoj aplikaciji za razmjenu.
|
||||
|
||||
Značajka blokiranja pomaže u sprječavanju neželjenih poruka, možete blokirati i poruke kontakata. Blokirani brojevi mogu se izvoziti i uvoziti za kopiranje. Svi razgovori mogu se jednostavno izvesti u datoteku za sigurnosno kopiranje ili migraciju između uređaja.
|
||||
|
||||
Možete prilagoditi koji će dio poruke biti vidljiv na zaslonu. Možete odabrati želite li da se prikazuje samo pošiljatelj, poruka ili ništa za privatnost.
|
||||
|
||||
Aplikacija za razmjenu poruka korisnicima također pruža mogućnost brzog i učinkovitog pretraživanja poruka. Prošli su dani kada se morate pomicati kroz sve privatne i grupne razgovore kako biste došli do željene poruke. Pretražite i dobijte što želite s ovom aplikacijom za razmjenu poruka.
|
||||
|
||||
Dolazi s materijalnim dizajnom i tamnom temom prema zadanim postavkama, pruža izvrsno korisničko iskustvo za jednostavno korištenje. Nedostatak pristupa internetu daje vam više privatnosti, sigurnosti i stabilnosti od aplikacija.
|
||||
|
||||
Ne sadrži oglase niti dopuštenja. Potpuno je otvorenog koda, pruža boje.
|
||||
|
||||
Ovdje pogledajte kompletan paket jednostavnih alata:
|
||||
https://www.simplemobiletools.com
|
||||
|
||||
Facebook:
|
||||
https://www.facebook.com/simplemobiletools
|
||||
|
||||
Reddit:
|
||||
https://www.reddit.com/r/SimpleMobileTools
|
||||
|
||||
Telegram:
|
||||
https://t.me/SimpleMobileTools
|
1
fastlane/metadata/android/hr/short_description.txt
Normal file
1
fastlane/metadata/android/hr/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Aplikacija za razmjenu poruka za brzo slanje poruka, lijepo korisničko sučelje
|
1
fastlane/metadata/android/hr/title.txt
Normal file
1
fastlane/metadata/android/hr/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
Jednostavan SMS slatelj
|
1
fastlane/metadata/android/pa-PK/short_description.txt
Normal file
1
fastlane/metadata/android/pa-PK/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
آنڈروئیڈ لئی سنیہے دی ایپلیکیشن، جالدی بھیجدے، اِنٹرفیس چنگی اے
|
1
fastlane/metadata/android/pa-PK/title.txt
Normal file
1
fastlane/metadata/android/pa-PK/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
سادے سنہے
|
@@ -1,21 +1,2 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
android.useAndroidX=true
|
||||
|
Reference in New Issue
Block a user