Merge pull request #471 from Naveen3Singh/feature_attachments
Handle sending and receiving all file types
This commit is contained in:
commit
07114e9810
|
@ -63,10 +63,10 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:0828fecd09'
|
||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:d5ae570e2a'
|
||||
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
|
||||
|
@ -27,21 +26,15 @@ import android.util.TypedValue
|
|||
import android.view.Gravity
|
||||
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
|
||||
|
@ -53,6 +46,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
|
||||
|
@ -60,8 +54,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
|
||||
|
@ -72,12 +66,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
|
||||
|
@ -93,8 +81,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
|
||||
|
@ -105,6 +91,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)
|
||||
|
@ -145,6 +133,10 @@ class ThreadActivity : SimpleActivity() {
|
|||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
setupAttachmentPickerView()
|
||||
setupKeyboardListener()
|
||||
hideAttachmentPicker()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -161,17 +153,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)
|
||||
|
@ -214,13 +214,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,6 +374,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
confirm_inserted_number?.setOnClickListener {
|
||||
val number = add_contact_or_number.value
|
||||
val phoneNumber = PhoneNumber(number, 0, "", number)
|
||||
|
@ -378,6 +382,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
addSelectedContact(contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleItemClick(any: Any) {
|
||||
when {
|
||||
|
@ -427,6 +432,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
setTextColor(textColor)
|
||||
compoundDrawables.forEach { it?.applyColorFilter(textColor) }
|
||||
}
|
||||
|
||||
confirm_manage_contacts.applyColorFilter(textColor)
|
||||
thread_add_attachment.applyColorFilter(textColor)
|
||||
|
||||
|
@ -440,6 +446,7 @@ class ThreadActivity : SimpleActivity() {
|
|||
thread_send_message.setOnClickListener {
|
||||
sendMessage()
|
||||
}
|
||||
|
||||
thread_send_message.setOnLongClickListener {
|
||||
if (!isScheduledMessage) {
|
||||
launchScheduleSendDialog()
|
||||
|
@ -478,7 +485,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) {
|
||||
|
@ -775,135 +790,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)
|
||||
var adapter = getAttachmentsAdapter()
|
||||
if (adapter == null) {
|
||||
adapter = AttachmentsAdapter(
|
||||
activity = this,
|
||||
recyclerView = thread_attachments_recyclerview,
|
||||
onAttachmentsRemoved = {
|
||||
thread_attachments_recyclerview.beGone()
|
||||
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)
|
||||
}
|
||||
checkSendMessageAvailability()
|
||||
attachmentView.thread_attachment_progress.beGone()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onReady = { checkSendMessageAvailability() }
|
||||
)
|
||||
thread_attachments_recyclerview.adapter = adapter
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -928,7 +938,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
|
||||
|
@ -942,7 +952,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
|
||||
}
|
||||
|
@ -982,7 +992,8 @@ class ThreadActivity : SimpleActivity() {
|
|||
conversationsDB.insertOrUpdate(conversation.copy(date = nowSeconds))
|
||||
}
|
||||
scheduleMessage(message)
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
clearCurrentMessage()
|
||||
hideScheduleSendUi()
|
||||
scheduledMessage = null
|
||||
|
@ -990,6 +1001,8 @@ class ThreadActivity : SimpleActivity() {
|
|||
if (!refreshedSinceSent) {
|
||||
refreshMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e.localizedMessage ?: getString(R.string.unknown_error_occurred))
|
||||
}
|
||||
|
@ -997,8 +1010,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
|
||||
|
@ -1017,9 +1029,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
|
||||
|
@ -1144,14 +1155,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1199,7 +1203,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() {
|
||||
|
@ -1212,15 +1216,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)),
|
||||
|
@ -1277,7 +1272,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)
|
||||
|
@ -1344,7 +1339,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,
|
||||
|
@ -1352,11 +1347,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, "") }
|
||||
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,11 @@
|
|||
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
|
||||
|
@ -39,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
|
||||
|
@ -288,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/"))
|
||||
|
@ -401,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 {
|
||||
|
@ -415,34 +407,10 @@ 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)
|
||||
}
|
||||
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 {
|
||||
setupVCardPreview(
|
||||
activity = activity,
|
||||
uri = uri,
|
||||
onClick = {
|
||||
if (actModeCallback.isSelectable) {
|
||||
holder.viewClicked(message)
|
||||
} else {
|
||||
|
@ -451,14 +419,11 @@ class ThreadAdapter(
|
|||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
},
|
||||
onLongClick = { holder.viewLongClicked() }
|
||||
)
|
||||
}
|
||||
setOnLongClickListener {
|
||||
holder.viewLongClicked()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
thread_mesage_attachments_holder.addView(vCardView)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,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
|
||||
}
|
||||
setTextColor(textColor)
|
||||
setOnClickListener {
|
||||
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 {
|
||||
launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
}
|
||||
setOnLongClickListener {
|
||||
holder.viewLongClicked()
|
||||
true
|
||||
}
|
||||
activity.launchViewIntent(uri, mimetype, attachment.filename)
|
||||
}
|
||||
},
|
||||
onLongClick = { holder.viewLongClicked() },
|
||||
)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
@ -86,4 +87,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()
|
||||
}
|
||||
|
|
|
@ -30,6 +30,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"
|
||||
|
@ -45,6 +46,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
|
||||
|
@ -60,6 +66,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)
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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: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:clipToPadding="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/normal_margin"
|
||||
android:paddingEnd="@dimen/normal_margin"
|
||||
android:showDividers="middle" />
|
||||
</HorizontalScrollView>
|
||||
android:scrollbars="none"
|
||||
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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/medium_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/vcard_title"
|
||||
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: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/vcard_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/normal_margin"
|
||||
android:textSize="@dimen/normal_text_size"
|
||||
android:textSize="@dimen/smaller_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/view_contact_details"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
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" />
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
|
|
|
@ -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>
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
<string name="unpin_conversation">Unpin</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">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">Duplicate item was not included</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">Duplicate item was not included</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>
|
||||
|
|
|
@ -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,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">及 %d 个其他人</item>
|
||||
</plurals>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue