mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-26 23:25:20 +03:00
Merge pull request #1702 from nextcloud/feature/1661/shareContactFromAttachmentDialog
share contact from attachment dialog
This commit is contained in:
commit
20725387d8
7 changed files with 240 additions and 112 deletions
|
@ -33,7 +33,9 @@ import android.content.ClipData
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.AssetFileDescriptor
|
||||
import android.content.res.Resources
|
||||
import android.database.Cursor
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.media.MediaPlayer
|
||||
|
@ -46,6 +48,7 @@ import android.os.Handler
|
|||
import android.os.SystemClock
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.provider.ContactsContract
|
||||
import android.text.Editable
|
||||
import android.text.InputFilter
|
||||
import android.text.TextUtils
|
||||
|
@ -69,6 +72,7 @@ import android.widget.RelativeLayout
|
|||
import android.widget.Toast
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.content.PermissionChecker
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
|
@ -93,6 +97,7 @@ import com.facebook.drawee.backends.pipeline.Fresco
|
|||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
|
||||
import com.facebook.imagepipeline.image.CloseableImage
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.nextcloud.talk.BuildConfig
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.activities.CallActivity
|
||||
import com.nextcloud.talk.activities.MainActivity
|
||||
|
@ -140,6 +145,7 @@ import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
|
|||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ConductorRemapping
|
||||
import com.nextcloud.talk.utils.ConductorRemapping.remapChatController
|
||||
import com.nextcloud.talk.utils.ContactUtils
|
||||
import com.nextcloud.talk.utils.DateUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.KeyboardUtils
|
||||
|
@ -1100,6 +1106,15 @@ class ChatController(args: Bundle) :
|
|||
)
|
||||
}
|
||||
|
||||
private fun requestReadContacts() {
|
||||
requestPermissions(
|
||||
arrayOf(
|
||||
Manifest.permission.READ_CONTACTS
|
||||
),
|
||||
REQUEST_READ_CONTACT_PERMISSION
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkReadOnlyState() {
|
||||
if (currentConversation != null && isAlive()) {
|
||||
if (currentConversation?.shouldShowLobby(conversationUser) ?: false ||
|
||||
|
@ -1186,62 +1201,83 @@ class ChatController(args: Bundle) :
|
|||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||
if (resultCode != RESULT_OK) {
|
||||
Log.e(TAG, "resultCode for received intent was != ok")
|
||||
return
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_CODE_CHOOSE_FILE) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
try {
|
||||
checkNotNull(intent)
|
||||
filesToUpload.clear()
|
||||
intent.clipData?.let {
|
||||
for (index in 0 until it.itemCount) {
|
||||
filesToUpload.add(it.getItemAt(index).uri.toString())
|
||||
}
|
||||
} ?: run {
|
||||
checkNotNull(intent.data)
|
||||
intent.data.let {
|
||||
filesToUpload.add(intent.data.toString())
|
||||
}
|
||||
try {
|
||||
checkNotNull(intent)
|
||||
filesToUpload.clear()
|
||||
intent.clipData?.let {
|
||||
for (index in 0 until it.itemCount) {
|
||||
filesToUpload.add(it.getItemAt(index).uri.toString())
|
||||
}
|
||||
require(filesToUpload.isNotEmpty())
|
||||
|
||||
val filenamesWithLinebreaks = StringBuilder("\n")
|
||||
|
||||
for (file in filesToUpload) {
|
||||
val filename = UriUtils.getFileName(Uri.parse(file), context)
|
||||
filenamesWithLinebreaks.append(filename).append("\n")
|
||||
} ?: run {
|
||||
checkNotNull(intent.data)
|
||||
intent.data.let {
|
||||
filesToUpload.add(intent.data.toString())
|
||||
}
|
||||
|
||||
val confirmationQuestion = when (filesToUpload.size) {
|
||||
1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let {
|
||||
String.format(it, title)
|
||||
}
|
||||
else -> context?.resources?.getString(R.string.nc_upload_confirm_send_multiple)?.let {
|
||||
String.format(it, title)
|
||||
}
|
||||
}
|
||||
|
||||
LovelyStandardDialog(activity)
|
||||
.setPositiveButtonColorRes(R.color.nc_darkGreen)
|
||||
.setTitle(confirmationQuestion)
|
||||
.setMessage(filenamesWithLinebreaks.toString())
|
||||
.setPositiveButton(R.string.nc_yes) { v ->
|
||||
if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) {
|
||||
uploadFiles(filesToUpload, false)
|
||||
} else {
|
||||
UploadAndShareFilesWorker.requestStoragePermission(this)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.nc_no) {}
|
||||
.show()
|
||||
} catch (e: IllegalStateException) {
|
||||
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||
}
|
||||
require(filesToUpload.isNotEmpty())
|
||||
|
||||
val filenamesWithLinebreaks = StringBuilder("\n")
|
||||
|
||||
for (file in filesToUpload) {
|
||||
val filename = UriUtils.getFileName(Uri.parse(file), context)
|
||||
filenamesWithLinebreaks.append(filename).append("\n")
|
||||
}
|
||||
|
||||
val confirmationQuestion = when (filesToUpload.size) {
|
||||
1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let {
|
||||
String.format(it, title)
|
||||
}
|
||||
else -> context?.resources?.getString(R.string.nc_upload_confirm_send_multiple)?.let {
|
||||
String.format(it, title)
|
||||
}
|
||||
}
|
||||
|
||||
LovelyStandardDialog(activity)
|
||||
.setPositiveButtonColorRes(R.color.nc_darkGreen)
|
||||
.setTitle(confirmationQuestion)
|
||||
.setMessage(filenamesWithLinebreaks.toString())
|
||||
.setPositiveButton(R.string.nc_yes) { v ->
|
||||
if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) {
|
||||
uploadFiles(filesToUpload, false)
|
||||
} else {
|
||||
UploadAndShareFilesWorker.requestStoragePermission(this)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.nc_no) {}
|
||||
.show()
|
||||
} catch (e: IllegalStateException) {
|
||||
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
|
||||
}
|
||||
} else if (requestCode == REQUEST_CODE_SELECT_CONTACT) {
|
||||
val contactUri = intent?.data ?: return
|
||||
val cursor: Cursor? = activity?.contentResolver!!.query(contactUri, null, null, null, null)
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
|
||||
val fileName = ContactUtils.getDisplayNameFromDeviceContact(context!!, id) + ".vcf"
|
||||
val file = File(context?.cacheDir, fileName)
|
||||
writeContactToVcfFile(cursor, file)
|
||||
|
||||
val shareUri = FileProvider.getUriForFile(
|
||||
activity!!,
|
||||
BuildConfig.APPLICATION_ID,
|
||||
File(file.absolutePath)
|
||||
)
|
||||
uploadFiles(mutableListOf(shareUri.toString()), false)
|
||||
}
|
||||
cursor?.close()
|
||||
} else if (requestCode == REQUEST_CODE_PICK_CAMERA) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
try {
|
||||
|
@ -1273,6 +1309,21 @@ class ChatController(args: Bundle) :
|
|||
}
|
||||
}
|
||||
|
||||
private fun writeContactToVcfFile(cursor: Cursor, file: File) {
|
||||
val lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY))
|
||||
val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey)
|
||||
|
||||
val fd: AssetFileDescriptor = activity?.contentResolver!!.openAssetFileDescriptor(uri, "r")!!
|
||||
val fis = fd.createInputStream()
|
||||
|
||||
file.createNewFile()
|
||||
fis.use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
|
@ -1295,6 +1346,17 @@ class ChatController(args: Bundle) :
|
|||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
} else if (requestCode == REQUEST_READ_CONTACT_PERMISSION) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
val intent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
|
||||
startActivityForResult(intent, REQUEST_CODE_SELECT_CONTACT)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context!!.getString(R.string.nc_share_contact_permission),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
} else if (requestCode == REQUEST_CAMERA_PERMISSION) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "launch cam activity since permission for cam has been granted")
|
||||
|
@ -1358,6 +1420,10 @@ class ChatController(args: Bundle) :
|
|||
)
|
||||
}
|
||||
|
||||
fun sendChooseContactIntent() {
|
||||
requestReadContacts()
|
||||
}
|
||||
|
||||
fun showBrowserScreen(browserType: BrowserController.BrowserType) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType))
|
||||
|
@ -2621,7 +2687,9 @@ class ChatController(args: Bundle) :
|
|||
private const val MESSAGE_MAX_LENGTH: Int = 1000
|
||||
private const val AGE_THREHOLD_FOR_DELETE_MESSAGE: Int = 21600000 // (6 hours in millis = 6 * 3600 * 1000)
|
||||
private const val REQUEST_CODE_CHOOSE_FILE: Int = 555
|
||||
private const val REQUEST_CODE_SELECT_CONTACT: Int = 666
|
||||
private const val REQUEST_RECORD_AUDIO_PERMISSION = 222
|
||||
private const val REQUEST_READ_CONTACT_PERMISSION = 234
|
||||
private const val REQUEST_CAMERA_PERMISSION = 223
|
||||
private const val REQUEST_CODE_PICK_CAMERA: Int = 333
|
||||
private const val OBJECT_MESSAGE: String = "{object}"
|
||||
|
|
|
@ -48,6 +48,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
|
|||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.models.json.search.ContactsByNumberOverall
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ContactUtils
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
import io.reactivex.Observer
|
||||
|
@ -299,7 +300,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
|
|||
}
|
||||
|
||||
val numbers = getPhoneNumbersFromDeviceContact(id)
|
||||
val displayName = getDisplayNameFromDeviceContact(id)
|
||||
val displayName = ContactUtils.getDisplayNameFromDeviceContact(context, id)
|
||||
|
||||
if (displayName == null) {
|
||||
return
|
||||
|
@ -393,33 +394,6 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
|
|||
}
|
||||
}
|
||||
|
||||
private fun getDisplayNameFromDeviceContact(id: String?): String? {
|
||||
var displayName: String? = null
|
||||
val whereName =
|
||||
ContactsContract.Data.MIMETYPE +
|
||||
" = ? AND " +
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID +
|
||||
" = ?"
|
||||
val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
|
||||
val nameCursor = context.contentResolver.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
null,
|
||||
whereName,
|
||||
whereNameParams,
|
||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
|
||||
)
|
||||
if (nameCursor != null) {
|
||||
while (nameCursor.moveToNext()) {
|
||||
displayName =
|
||||
nameCursor.getString(
|
||||
nameCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)
|
||||
)
|
||||
}
|
||||
nameCursor.close()
|
||||
}
|
||||
return displayName
|
||||
}
|
||||
|
||||
private fun getPhoneNumbersFromDeviceContact(id: String?): MutableList<String> {
|
||||
val numbers = mutableListOf<String>()
|
||||
val phonesNumbersCursor = context.contentResolver.query(
|
||||
|
|
|
@ -79,6 +79,11 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle
|
|||
chatController.showBrowserScreen(BrowserController.BrowserType.DAV_BROWSER)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
dialogAttachmentBinding.menuAttachContact.setOnClickListener {
|
||||
chatController.sendChooseContactIntent()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
|
34
app/src/main/java/com/nextcloud/talk/utils/ContactUtils.kt
Normal file
34
app/src/main/java/com/nextcloud/talk/utils/ContactUtils.kt
Normal file
|
@ -0,0 +1,34 @@
|
|||
package com.nextcloud.talk.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.provider.ContactsContract
|
||||
|
||||
object ContactUtils {
|
||||
|
||||
fun getDisplayNameFromDeviceContact(context: Context, id: String?): String? {
|
||||
var displayName: String? = null
|
||||
val whereName =
|
||||
ContactsContract.Data.MIMETYPE +
|
||||
" = ? AND " +
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID +
|
||||
" = ?"
|
||||
val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
|
||||
val nameCursor = context.contentResolver.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
null,
|
||||
whereName,
|
||||
whereNameParams,
|
||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
|
||||
)
|
||||
if (nameCursor != null) {
|
||||
while (nameCursor.moveToNext()) {
|
||||
displayName =
|
||||
nameCursor.getString(
|
||||
nameCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)
|
||||
)
|
||||
}
|
||||
nameCursor.close()
|
||||
}
|
||||
return displayName
|
||||
}
|
||||
}
|
10
app/src/main/res/drawable/ic_baseline_person_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_person_24.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||
</vector>
|
|
@ -39,6 +39,39 @@
|
|||
android:textColor="@color/medium_emphasis_text"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_contact"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingRight="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/menu_icon_share_contact"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_baseline_person_24"
|
||||
app:tint="@color/colorPrimary" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/shareContactText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:text="@string/nc_share_contact"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_share_location"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -72,39 +105,6 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_file_from_local"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingRight="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/menu_icon_attach_file_from_local"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/upload"
|
||||
app:tint="@color/colorPrimary" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_attach_file_from_local"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:text="@string/nc_upload_local_file"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_picture_from_cam"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -138,6 +138,39 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_file_from_local"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingRight="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/menu_icon_attach_file_from_local"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/upload"
|
||||
app:tint="@color/colorPrimary" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_attach_file_from_local"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:text="@string/nc_upload_local_file"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_attach_file_from_cloud"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -406,6 +406,10 @@
|
|||
<string name="nc_location_current_position_description">Your current location</string>
|
||||
<string name="nc_location_unknown">Position unknown</string>
|
||||
|
||||
<!-- share contact -->
|
||||
<string name="nc_share_contact">Share contact</string>
|
||||
<string name="nc_share_contact_permission">Permission to read contacts is required</string>
|
||||
|
||||
<!-- voice messages -->
|
||||
<string name="nc_voice_message_filename">Talk recording from %1$s (%2$s)</string>
|
||||
<string name="nc_voice_message_hold_to_record_info">Hold to record, release to send.</string>
|
||||
|
|
Loading…
Reference in a new issue