added remove media in dm and compose

This commit is contained in:
Mariotaku Lee 2017-02-15 13:32:45 +08:00
parent 177b682d84
commit 5e5aad22a9
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
11 changed files with 173 additions and 58 deletions

2
.gitignore vendored
View File

@ -26,3 +26,5 @@ Thumbs.db
# Private files # Private files
/signing.properties /signing.properties
/twidere.jks /twidere.jks
/captures

View File

@ -157,7 +157,7 @@ dependencies {
compile 'com.bluelinelabs:logansquare:1.3.7' compile 'com.bluelinelabs:logansquare:1.3.7'
compile 'com.soundcloud.android:android-crop:1.0.1@aar' compile 'com.soundcloud.android:android-crop:1.0.1@aar'
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.2' compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.2'
compile 'com.github.mariotaku:PickNCrop:0.9.17' compile 'com.github.mariotaku:PickNCrop:0.9.19'
compile "com.github.mariotaku.RestFu:library:$mariotaku_restfu_version" compile "com.github.mariotaku.RestFu:library:$mariotaku_restfu_version"
compile "com.github.mariotaku.RestFu:okhttp3:$mariotaku_restfu_version" compile "com.github.mariotaku.RestFu:okhttp3:$mariotaku_restfu_version"
compile 'com.squareup.okhttp3:okhttp:3.5.0' compile 'com.squareup.okhttp3:okhttp:3.5.0'

View File

@ -89,6 +89,7 @@ import org.mariotaku.twidere.preference.ServicePickerPreference
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.service.LengthyOperationsService import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.task.compose.AbsAddMediaTask import org.mariotaku.twidere.task.compose.AbsAddMediaTask
import org.mariotaku.twidere.task.compose.AbsDeleteMediaTask
import org.mariotaku.twidere.text.MarkForDeleteSpan import org.mariotaku.twidere.text.MarkForDeleteSpan
import org.mariotaku.twidere.text.style.EmojiSpan import org.mariotaku.twidere.text.style.EmojiSpan
import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.*
@ -151,7 +152,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
REQUEST_TAKE_PHOTO, REQUEST_PICK_MEDIA -> { REQUEST_TAKE_PHOTO, REQUEST_PICK_MEDIA -> {
if (resultCode == Activity.RESULT_OK && intent != null) { if (resultCode == Activity.RESULT_OK && intent != null) {
val src = MediaPickerActivity.getMediaUris(intent) val src = MediaPickerActivity.getMediaUris(intent)
TaskStarter.execute(AddMediaTask(this, src, true)) TaskStarter.execute(AddMediaTask(this, src, false, false))
} }
} }
REQUEST_EDIT_IMAGE -> { REQUEST_EDIT_IMAGE -> {
@ -339,7 +340,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
IntentUtils.openDrafts(this) IntentUtils.openDrafts(this)
} }
R.id.delete -> { R.id.delete -> {
AsyncTaskUtils.executeTask(DeleteMediaTask(this, media)) TaskStarter.execute(DeleteMediaTask(this, media))
} }
R.id.toggle_sensitive -> { R.id.toggle_sensitive -> {
if (!hasMedia()) return false if (!hasMedia()) return false
@ -807,21 +808,21 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
val stream = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) val stream = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
if (stream != null) { if (stream != null) {
val src = arrayOf(stream) val src = arrayOf(stream)
TaskStarter.execute(AddMediaTask(this, src, false)) TaskStarter.execute(AddMediaTask(this, src, true, false))
} }
} else if (Intent.ACTION_SEND_MULTIPLE == action) { } else if (Intent.ACTION_SEND_MULTIPLE == action) {
shouldSaveAccounts = false shouldSaveAccounts = false
val extraStream = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM) val extraStream = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
if (extraStream != null) { if (extraStream != null) {
val src = extraStream.toTypedArray() val src = extraStream.toTypedArray()
TaskStarter.execute(AddMediaTask(this, src, false)) TaskStarter.execute(AddMediaTask(this, src, true, false))
} }
} else { } else {
shouldSaveAccounts = !hasAccountIds shouldSaveAccounts = !hasAccountIds
val data = intent.data val data = intent.data
if (data != null) { if (data != null) {
val src = arrayOf(data) val src = arrayOf(data)
TaskStarter.execute(AddMediaTask(this, src, false)) TaskStarter.execute(AddMediaTask(this, src, true, false))
} }
} }
val extraSubject = intent.getCharSequenceExtra(Intent.EXTRA_SUBJECT) val extraSubject = intent.getCharSequenceExtra(Intent.EXTRA_SUBJECT)
@ -1507,8 +1508,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
internal class AddMediaTask( internal class AddMediaTask(
activity: ComposeActivity, activity: ComposeActivity,
sources: Array<Uri>, sources: Array<Uri>,
copySrc: Boolean,
deleteSrc: Boolean deleteSrc: Boolean
) : AbsAddMediaTask<ComposeActivity>(activity, sources, deleteSrc) { ) : AbsAddMediaTask<ComposeActivity>(activity, sources, copySrc, deleteSrc) {
init { init {
callback = activity callback = activity
@ -1529,34 +1531,27 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
} }
internal class DeleteMediaTask(activity: ComposeActivity, val media: Array<ParcelableMediaUpdate>) : AsyncTask<Any, Any, Boolean>() { internal class DeleteMediaTask(
activity: ComposeActivity,
val activity: WeakReference<ComposeActivity> val media: Array<ParcelableMediaUpdate>
) : AbsDeleteMediaTask<ComposeActivity>(activity, media.map { Uri.parse(it.uri) }.toTypedArray()) {
init { init {
this.activity = WeakReference(activity) this.callback = activity
} }
override fun doInBackground(vararg params: Any): Boolean { override fun beforeExecute() {
media.forEach { callback?.setProgressVisible(true)
Utils.deleteMedia(activity.get(), Uri.parse(it.uri)) }
override fun afterExecute(callback: ComposeActivity?, result: BooleanArray?) {
if (callback == null || result == null) return
callback.setProgressVisible(false)
callback.removeAllMedia(media.filterIndexed { i, media -> result[i] })
callback.setMenu()
if (result.any { false }) {
Toast.makeText(callback, R.string.message_toast_error_occurred, Toast.LENGTH_SHORT).show()
} }
return true
}
override fun onPostExecute(result: Boolean) {
val activity = activity.get() ?: return
activity.setProgressVisible(false)
activity.removeAllMedia(Arrays.asList(*media))
activity.setMenu()
if (!result) {
Toast.makeText(activity, R.string.message_toast_error_occurred, Toast.LENGTH_SHORT).show()
}
}
override fun onPreExecute() {
val activity = activity.get() ?: return
activity.setProgressVisible(true)
} }
} }

View File

@ -20,6 +20,7 @@
package org.mariotaku.twidere.adapter package org.mariotaku.twidere.adapter
import android.content.Context import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import org.mariotaku.twidere.R import org.mariotaku.twidere.R
@ -84,7 +85,8 @@ class MediaPreviewAdapter(
} }
interface Listener : SimpleItemTouchHelperCallback.OnStartDragListener { interface Listener : SimpleItemTouchHelperCallback.OnStartDragListener {
fun onRemoveClick(position: Int, holder: MediaPreviewViewHolder) fun onRemoveClick(position: Int, holder: MediaPreviewViewHolder) {}
fun onEditClick(position: Int, holder: MediaPreviewViewHolder) fun onEditClick(position: Int, holder: MediaPreviewViewHolder) {}
override fun onStartDrag(viewHolder: RecyclerView.ViewHolder) {}
} }
} }

View File

@ -25,6 +25,7 @@ import android.support.v4.app.Fragment
import android.support.v4.text.BidiFormatter import android.support.v4.text.BidiFormatter
import com.squareup.otto.Bus import com.squareup.otto.Bus
import org.mariotaku.twidere.fragment.iface.IBaseFragment import org.mariotaku.twidere.fragment.iface.IBaseFragment
import org.mariotaku.twidere.model.DefaultFeatures
import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.premium.ExtraFeaturesService import org.mariotaku.twidere.util.premium.ExtraFeaturesService
@ -61,6 +62,8 @@ open class BaseFragment : Fragment(), IBaseFragment<BaseFragment> {
lateinit var extraFeaturesService: ExtraFeaturesService lateinit var extraFeaturesService: ExtraFeaturesService
@Inject @Inject
lateinit var permissionsManager: PermissionsManager lateinit var permissionsManager: PermissionsManager
@Inject
lateinit var defaultFeatures: DefaultFeatures
private val actionHelper = IBaseFragment.ActionHelper(this) private val actionHelper = IBaseFragment.ActionHelper(this)

View File

@ -14,6 +14,7 @@ import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import com.squareup.otto.Subscribe import com.squareup.otto.Subscribe
import kotlinx.android.synthetic.main.fragment_messages_conversation.* import kotlinx.android.synthetic.main.fragment_messages_conversation.*
import org.mariotaku.abstask.library.TaskStarter import org.mariotaku.abstask.library.TaskStarter
@ -31,6 +32,7 @@ import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_CONVERSATION_ID import org.mariotaku.twidere.constant.IntentConstants.EXTRA_CONVERSATION_ID
import org.mariotaku.twidere.constant.newDocumentApiKey import org.mariotaku.twidere.constant.newDocumentApiKey
import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.loader.ObjectCursorLoader import org.mariotaku.twidere.loader.ObjectCursorLoader
import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationType
@ -40,9 +42,11 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Messages
import org.mariotaku.twidere.service.LengthyOperationsService import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.task.GetMessagesTask import org.mariotaku.twidere.task.GetMessagesTask
import org.mariotaku.twidere.task.compose.AbsAddMediaTask import org.mariotaku.twidere.task.compose.AbsAddMediaTask
import org.mariotaku.twidere.task.compose.AbsDeleteMediaTask
import org.mariotaku.twidere.util.DataStoreUtils import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.IntentUtils import org.mariotaku.twidere.util.IntentUtils
import org.mariotaku.twidere.util.PreviewGridItemDecoration import org.mariotaku.twidere.util.PreviewGridItemDecoration
import org.mariotaku.twidere.view.holder.compose.MediaPreviewViewHolder
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
class MessagesConversationFragment : AbsContentListRecyclerViewFragment<MessagesConversationAdapter>(), class MessagesConversationFragment : AbsContentListRecyclerViewFragment<MessagesConversationAdapter>(),
@ -70,6 +74,10 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
val account = this.account ?: run {
activity?.finish()
return
}
adapter.listener = object : MessagesConversationAdapter.Listener { adapter.listener = object : MessagesConversationAdapter.Listener {
override fun onMediaClick(position: Int, media: ParcelableMedia, accountKey: UserKey?) { override fun onMediaClick(position: Int, media: ParcelableMedia, accountKey: UserKey?) {
val message = adapter.getMessage(position) ?: return val message = adapter.getMessage(position) ?: return
@ -80,6 +88,15 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
} }
mediaPreviewAdapter = MediaPreviewAdapter(context) mediaPreviewAdapter = MediaPreviewAdapter(context)
mediaPreviewAdapter.listener = object : MediaPreviewAdapter.Listener {
override fun onRemoveClick(position: Int, holder: MediaPreviewViewHolder) {
val task = DeleteMediaTask(this@MessagesConversationFragment,
arrayOf(mediaPreviewAdapter.getItem(position)))
TaskStarter.execute(task)
}
}
attachedMediaPreview.layoutManager = FixedLinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) attachedMediaPreview.layoutManager = FixedLinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
attachedMediaPreview.adapter = mediaPreviewAdapter attachedMediaPreview.adapter = mediaPreviewAdapter
attachedMediaPreview.addItemDecoration(PreviewGridItemDecoration(resources.getDimensionPixelSize(R.dimen.element_spacing_small))) attachedMediaPreview.addItemDecoration(PreviewGridItemDecoration(resources.getDimensionPixelSize(R.dimen.element_spacing_small)))
@ -95,6 +112,12 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
refreshEnabled = false refreshEnabled = false
adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.NONE adapter.loadMoreSupportedPosition = ILoadMoreSupportAdapter.NONE
if (account.isOfficial(context)) {
addMedia.visibility = View.VISIBLE
} else {
addMedia.visibility = View.GONE
}
updateMediaPreview() updateMediaPreview()
loaderManager.initLoader(0, null, this) loaderManager.initLoader(0, null, this)
@ -116,7 +139,7 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
REQUEST_PICK_MEDIA -> { REQUEST_PICK_MEDIA -> {
if (resultCode == Activity.RESULT_OK && data != null) { if (resultCode == Activity.RESULT_OK && data != null) {
val mediaUris = MediaPickerActivity.getMediaUris(data) val mediaUris = MediaPickerActivity.getMediaUris(data)
TaskStarter.execute(AddMediaTask(this, mediaUris, true)) TaskStarter.execute(AddMediaTask(this, mediaUris))
} }
} }
} }
@ -179,13 +202,25 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
private fun performSendMessage() { private fun performSendMessage() {
val conversation = adapter.conversation ?: return val conversation = adapter.conversation ?: return
val conversationAccount = this.account ?: return
if (editText.empty && adapter.itemCount == 0) { if (editText.empty && adapter.itemCount == 0) {
editText.error = getString(R.string.hint_error_message_no_content) editText.error = getString(R.string.hint_error_message_no_content)
return return
} }
if (conversationAccount.isOfficial(context)) {
if (adapter.itemCount > defaultFeatures.twitterDirectMessageMediaLimit) {
editText.error = getString(R.string.error_message_media_message_too_many)
return
} else {
editText.error = null
}
} else {
editText.error = getString(R.string.error_message_media_message_attachment_not_supported)
return
}
val text = editText.text.toString() val text = editText.text.toString()
val message = ParcelableNewMessage().apply { val message = ParcelableNewMessage().apply {
this.account = this@MessagesConversationFragment.account this.account = conversationAccount
this.media = mediaPreviewAdapter.asList().toTypedArray() this.media = mediaPreviewAdapter.asList().toTypedArray()
this.conversation_id = conversation.id this.conversation_id = conversation.id
this.recipient_ids = conversation.participants?.map { this.recipient_ids = conversation.participants?.map {
@ -218,12 +253,18 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
updateMediaPreview() updateMediaPreview()
} }
private fun removeMedia(media: List<ParcelableMediaUpdate>) {
mediaPreviewAdapter.removeAll(media)
updateMediaPreview()
}
private fun updateMediaPreview() { private fun updateMediaPreview() {
attachedMediaPreview.visibility = if (mediaPreviewAdapter.itemCount > 0) { attachedMediaPreview.visibility = if (mediaPreviewAdapter.itemCount > 0) {
View.VISIBLE View.VISIBLE
} else { } else {
View.GONE View.GONE
} }
editText.error = null
} }
private fun setProgressVisible(visible: Boolean) { private fun setProgressVisible(visible: Boolean) {
@ -232,9 +273,8 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
internal class AddMediaTask( internal class AddMediaTask(
fragment: MessagesConversationFragment, fragment: MessagesConversationFragment,
sources: Array<Uri>, sources: Array<Uri>
deleteSrc: Boolean ) : AbsAddMediaTask<MessagesConversationFragment>(fragment.context, sources) {
) : AbsAddMediaTask<MessagesConversationFragment>(fragment.context, sources, deleteSrc) {
init { init {
callback = fragment callback = fragment
@ -253,6 +293,32 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
} }
internal class DeleteMediaTask(
fragment: MessagesConversationFragment,
val media: Array<ParcelableMediaUpdate>
) : AbsDeleteMediaTask<MessagesConversationFragment>(fragment.context,
media.map { Uri.parse(it.uri) }.toTypedArray()) {
init {
callback = fragment
}
override fun afterExecute(callback: MessagesConversationFragment?, result: BooleanArray?) {
if (callback == null || result == null) return
callback.setProgressVisible(false)
callback.removeMedia(media.filterIndexed { i, media -> result[i] })
if (result.any { false }) {
Toast.makeText(callback.context, R.string.message_toast_error_occurred, Toast.LENGTH_SHORT).show()
}
}
override fun beforeExecute() {
val fragment = callback ?: return
fragment.setProgressVisible(true)
}
}
internal class ConversationLoader( internal class ConversationLoader(
context: Context, context: Context,
val accountKey: UserKey, val accountKey: UserKey,

View File

@ -62,8 +62,8 @@ class GetMessagesTask(
private fun getMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { private fun getMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
when (details.type) { when (details.type) {
AccountType.FANFOU -> { AccountType.FANFOU -> {
// Use fanfou DM api // Use fanfou DM api, disabled since it's conversation api is not suitable for paging
return getFanfouMessages(microBlog, details, param, index) // return getFanfouMessages(microBlog, details, param, index)
} }
AccountType.TWITTER -> { AccountType.TWITTER -> {
// Use official DM api // Use official DM api

View File

@ -35,7 +35,8 @@ import java.lang.ref.WeakReference
open class AbsAddMediaTask<Callback>( open class AbsAddMediaTask<Callback>(
context: Context, context: Context,
val sources: Array<Uri>, val sources: Array<Uri>,
val deleteSrc: Boolean val copySrc: Boolean = false,
val deleteSrc: Boolean = false
) : AbstractTask<Unit, List<ParcelableMediaUpdate>?, Callback>() { ) : AbstractTask<Unit, List<ParcelableMediaUpdate>?, Callback>() {
private val contextRef = WeakReference(context) private val contextRef = WeakReference(context)
@ -59,13 +60,17 @@ open class AbsAddMediaTask<Callback>(
val extension = sourceMimeType?.let { mimeType -> val extension = sourceMimeType?.let { mimeType ->
MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
} ?: "tmp" } ?: "tmp"
val destination = createTempImageUri(context, index, extension) st = resolver.openInputStream(source) ?: throw FileNotFoundException("Unable to open $source")
st = resolver.openInputStream(source) val destination: Uri
os = resolver.openOutputStream(destination) if (copySrc) {
if (st == null || os == null) throw FileNotFoundException() destination = createTempImageUri(context, index, extension)
StreamUtils.copy(st, os, null, null) os = resolver.openOutputStream(destination) ?: throw FileNotFoundException("Unable to open $destination")
if (deleteSrc) { StreamUtils.copy(st, os, null, null)
Utils.deleteMedia(context, source) if (deleteSrc) {
Utils.deleteMedia(context, source)
}
} else {
destination = source
} }
return@mapIndexedNotNull ParcelableMediaUpdate(destination.toString(), mediaType) return@mapIndexedNotNull ParcelableMediaUpdate(destination.toString(), mediaType)
} catch (e: IOException) { } catch (e: IOException) {

View File

@ -0,0 +1,41 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.task.compose
import android.content.Context
import android.net.Uri
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.twidere.util.Utils
import java.lang.ref.WeakReference
open class AbsDeleteMediaTask<Callback>(
context: Context,
val sources: Array<Uri>
) : AbstractTask<Unit, BooleanArray?, Callback>() {
private val contextRef = WeakReference(context)
val context: Context? get() = contextRef.get()
override fun doLongOperation(params: Unit?): BooleanArray? {
val context = contextRef.get() ?: return null
return BooleanArray(sources.size) { Utils.deleteMedia(context, sources[it]) }
}
}

View File

@ -47,11 +47,11 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:background="?android:colorBackground" android:background="?android:colorBackground"
android:clipToPadding="false"
android:gravity="bottom" android:gravity="bottom"
android:orientation="horizontal" android:orientation="horizontal"
android:outlineProvider="bounds" android:outlineProvider="bounds"
android:paddingBottom="@dimen/element_spacing_normal" android:padding="@dimen/element_spacing_normal"
android:paddingTop="@dimen/element_spacing_normal"
tools:ignore="UnusedAttribute"> tools:ignore="UnusedAttribute">
<org.mariotaku.twidere.view.IconActionView <org.mariotaku.twidere.view.IconActionView
@ -60,25 +60,24 @@
android:layout_height="@dimen/element_size_normal" android:layout_height="@dimen/element_size_normal"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_marginEnd="@dimen/element_spacing_normal"
android:layout_marginRight="@dimen/element_spacing_normal"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:clickable="true" android:clickable="true"
android:contentDescription="@string/add_image" android:contentDescription="@string/add_image"
android:scaleType="centerInside" android:scaleType="centerInside"
android:src="@drawable/ic_action_gallery" android:src="@drawable/ic_action_gallery"
android:visibility="visible" android:visibility="gone"
app:iabColor="?android:textColorSecondary" app:iabColor="?android:textColorSecondary"
tools:tint="?android:textColorSecondary"/> tools:tint="?android:textColorSecondary"
tools:visibility="visible"/>
<FrameLayout <FrameLayout
android:id="@+id/sendMessageContainer" android:id="@+id/sendMessageContainer"
android:layout_width="@dimen/element_size_normal" android:layout_width="@dimen/element_size_normal"
android:layout_height="@dimen/element_size_normal" android:layout_height="@dimen/element_size_normal"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true">
android:layout_marginEnd="@dimen/element_spacing_normal"
android:layout_marginLeft="@dimen/element_spacing_minus_normal"
android:layout_marginRight="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_minus_normal">
<org.mariotaku.twidere.view.IconActionView <org.mariotaku.twidere.view.IconActionView
android:id="@+id/sendMessage" android:id="@+id/sendMessage"

View File

@ -425,7 +425,9 @@
<string name="error_message_device_incompatible">This device is not compatible with Twidere, upgrade to latest Android OS is recommended.\nYou can send information below to help me report this issue to device manufacturer.</string> <string name="error_message_device_incompatible">This device is not compatible with Twidere, upgrade to latest Android OS is recommended.\nYou can send information below to help me report this issue to device manufacturer.</string>
<string name="error_message_media_upload_failed">Media upload failed.</string> <string name="error_message_media_upload_failed">Media upload failed.</string>
<string name="error_message_media_uploader_not_found">Media uploader not found, maybe it was uninstalled.</string> <string name="error_message_media_uploader_not_found">Media uploader not found, maybe it was uninstalled.</string>
<string name="error_message_message_too_long">Message too long.</string> <string name="error_message_media_message_attachment_not_supported">Attach media to DM is not supported</string>
<string name="error_message_media_message_too_many">Too many media</string>
<string name="error_message_message_too_long">Message too long</string>
<string name="error_message_no_content">No content</string> <string name="error_message_no_content">No content</string>
<string <string
name="error_message_rate_limit">Twitter\'s rate limit exceeded, please retry <xliff:g id="time">%s</xliff:g> name="error_message_rate_limit">Twitter\'s rate limit exceeded, please retry <xliff:g id="time">%s</xliff:g>