diff --git a/.gitignore b/.gitignore index b59a81640..186157d27 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,6 @@ Thumbs.db # Private files /signing.properties -/twidere.jks \ No newline at end of file +/twidere.jks + +/captures \ No newline at end of file diff --git a/twidere/build.gradle b/twidere/build.gradle index d977ab5dd..0b6e94d11 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -157,7 +157,7 @@ dependencies { compile 'com.bluelinelabs:logansquare:1.3.7' compile 'com.soundcloud.android:android-crop:1.0.1@aar' 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:okhttp3:$mariotaku_restfu_version" compile 'com.squareup.okhttp3:okhttp:3.5.0' diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt index 866142f6c..6b8b29644 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt @@ -89,6 +89,7 @@ import org.mariotaku.twidere.preference.ServicePickerPreference import org.mariotaku.twidere.provider.TwidereDataStore.Drafts import org.mariotaku.twidere.service.LengthyOperationsService 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.style.EmojiSpan import org.mariotaku.twidere.util.* @@ -151,7 +152,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener REQUEST_TAKE_PHOTO, REQUEST_PICK_MEDIA -> { if (resultCode == Activity.RESULT_OK && intent != null) { val src = MediaPickerActivity.getMediaUris(intent) - TaskStarter.execute(AddMediaTask(this, src, true)) + TaskStarter.execute(AddMediaTask(this, src, false, false)) } } REQUEST_EDIT_IMAGE -> { @@ -339,7 +340,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener IntentUtils.openDrafts(this) } R.id.delete -> { - AsyncTaskUtils.executeTask(DeleteMediaTask(this, media)) + TaskStarter.execute(DeleteMediaTask(this, media)) } R.id.toggle_sensitive -> { if (!hasMedia()) return false @@ -807,21 +808,21 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener val stream = intent.getParcelableExtra(Intent.EXTRA_STREAM) if (stream != null) { val src = arrayOf(stream) - TaskStarter.execute(AddMediaTask(this, src, false)) + TaskStarter.execute(AddMediaTask(this, src, true, false)) } } else if (Intent.ACTION_SEND_MULTIPLE == action) { shouldSaveAccounts = false val extraStream = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM) if (extraStream != null) { val src = extraStream.toTypedArray() - TaskStarter.execute(AddMediaTask(this, src, false)) + TaskStarter.execute(AddMediaTask(this, src, true, false)) } } else { shouldSaveAccounts = !hasAccountIds val data = intent.data if (data != null) { val src = arrayOf(data) - TaskStarter.execute(AddMediaTask(this, src, false)) + TaskStarter.execute(AddMediaTask(this, src, true, false)) } } val extraSubject = intent.getCharSequenceExtra(Intent.EXTRA_SUBJECT) @@ -1507,8 +1508,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener internal class AddMediaTask( activity: ComposeActivity, sources: Array, + copySrc: Boolean, deleteSrc: Boolean - ) : AbsAddMediaTask(activity, sources, deleteSrc) { + ) : AbsAddMediaTask(activity, sources, copySrc, deleteSrc) { init { callback = activity @@ -1529,34 +1531,27 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener } - internal class DeleteMediaTask(activity: ComposeActivity, val media: Array) : AsyncTask() { - - val activity: WeakReference + internal class DeleteMediaTask( + activity: ComposeActivity, + val media: Array + ) : AbsDeleteMediaTask(activity, media.map { Uri.parse(it.uri) }.toTypedArray()) { init { - this.activity = WeakReference(activity) + this.callback = activity } - override fun doInBackground(vararg params: Any): Boolean { - media.forEach { - Utils.deleteMedia(activity.get(), Uri.parse(it.uri)) + override fun beforeExecute() { + callback?.setProgressVisible(true) + } + + 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) } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MediaPreviewAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MediaPreviewAdapter.kt index db5e90be5..3d1304c4a 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MediaPreviewAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/MediaPreviewAdapter.kt @@ -20,6 +20,7 @@ package org.mariotaku.twidere.adapter import android.content.Context +import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.ViewGroup import org.mariotaku.twidere.R @@ -84,7 +85,8 @@ class MediaPreviewAdapter( } interface Listener : SimpleItemTouchHelperCallback.OnStartDragListener { - fun onRemoveClick(position: Int, holder: MediaPreviewViewHolder) - fun onEditClick(position: Int, holder: MediaPreviewViewHolder) + fun onRemoveClick(position: Int, holder: MediaPreviewViewHolder) {} + fun onEditClick(position: Int, holder: MediaPreviewViewHolder) {} + override fun onStartDrag(viewHolder: RecyclerView.ViewHolder) {} } } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/BaseFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/BaseFragment.kt index f18dcd1e0..083321814 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/BaseFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/BaseFragment.kt @@ -25,6 +25,7 @@ import android.support.v4.app.Fragment import android.support.v4.text.BidiFormatter import com.squareup.otto.Bus import org.mariotaku.twidere.fragment.iface.IBaseFragment +import org.mariotaku.twidere.model.DefaultFeatures import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.premium.ExtraFeaturesService @@ -61,6 +62,8 @@ open class BaseFragment : Fragment(), IBaseFragment { lateinit var extraFeaturesService: ExtraFeaturesService @Inject lateinit var permissionsManager: PermissionsManager + @Inject + lateinit var defaultFeatures: DefaultFeatures private val actionHelper = IBaseFragment.ActionHelper(this) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/MessagesConversationFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/MessagesConversationFragment.kt index 55d500e53..9dc5429ce 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/MessagesConversationFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/MessagesConversationFragment.kt @@ -14,6 +14,7 @@ import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import com.squareup.otto.Subscribe import kotlinx.android.synthetic.main.fragment_messages_conversation.* 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_CONVERSATION_ID import org.mariotaku.twidere.constant.newDocumentApiKey +import org.mariotaku.twidere.extension.model.isOfficial import org.mariotaku.twidere.loader.ObjectCursorLoader import org.mariotaku.twidere.model.* 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.task.GetMessagesTask 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.IntentUtils import org.mariotaku.twidere.util.PreviewGridItemDecoration +import org.mariotaku.twidere.view.holder.compose.MediaPreviewViewHolder import java.util.concurrent.atomic.AtomicReference class MessagesConversationFragment : AbsContentListRecyclerViewFragment(), @@ -70,6 +74,10 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment { if (resultCode == Activity.RESULT_OK && data != null) { val mediaUris = MediaPickerActivity.getMediaUris(data) - TaskStarter.execute(AddMediaTask(this, mediaUris, true)) + TaskStarter.execute(AddMediaTask(this, mediaUris)) } } } @@ -179,13 +202,25 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment 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 message = ParcelableNewMessage().apply { - this.account = this@MessagesConversationFragment.account + this.account = conversationAccount this.media = mediaPreviewAdapter.asList().toTypedArray() this.conversation_id = conversation.id this.recipient_ids = conversation.participants?.map { @@ -218,12 +253,18 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment) { + mediaPreviewAdapter.removeAll(media) + updateMediaPreview() + } + private fun updateMediaPreview() { attachedMediaPreview.visibility = if (mediaPreviewAdapter.itemCount > 0) { View.VISIBLE } else { View.GONE } + editText.error = null } private fun setProgressVisible(visible: Boolean) { @@ -232,9 +273,8 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment, - deleteSrc: Boolean - ) : AbsAddMediaTask(fragment.context, sources, deleteSrc) { + sources: Array + ) : AbsAddMediaTask(fragment.context, sources) { init { callback = fragment @@ -253,6 +293,32 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment + ) : AbsDeleteMediaTask(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( context: Context, val accountKey: UserKey, diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/GetMessagesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/GetMessagesTask.kt index 75d2b128b..ac3ae3e2f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/GetMessagesTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/GetMessagesTask.kt @@ -62,8 +62,8 @@ class GetMessagesTask( private fun getMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData { when (details.type) { AccountType.FANFOU -> { - // Use fanfou DM api - return getFanfouMessages(microBlog, details, param, index) + // Use fanfou DM api, disabled since it's conversation api is not suitable for paging + // return getFanfouMessages(microBlog, details, param, index) } AccountType.TWITTER -> { // Use official DM api diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsAddMediaTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsAddMediaTask.kt index 32220cdc2..205188133 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsAddMediaTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsAddMediaTask.kt @@ -35,7 +35,8 @@ import java.lang.ref.WeakReference open class AbsAddMediaTask( context: Context, val sources: Array, - val deleteSrc: Boolean + val copySrc: Boolean = false, + val deleteSrc: Boolean = false ) : AbstractTask?, Callback>() { private val contextRef = WeakReference(context) @@ -59,13 +60,17 @@ open class AbsAddMediaTask( val extension = sourceMimeType?.let { mimeType -> MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) } ?: "tmp" - val destination = createTempImageUri(context, index, extension) - st = resolver.openInputStream(source) - os = resolver.openOutputStream(destination) - if (st == null || os == null) throw FileNotFoundException() - StreamUtils.copy(st, os, null, null) - if (deleteSrc) { - Utils.deleteMedia(context, source) + st = resolver.openInputStream(source) ?: throw FileNotFoundException("Unable to open $source") + val destination: Uri + if (copySrc) { + destination = createTempImageUri(context, index, extension) + os = resolver.openOutputStream(destination) ?: throw FileNotFoundException("Unable to open $destination") + StreamUtils.copy(st, os, null, null) + if (deleteSrc) { + Utils.deleteMedia(context, source) + } + } else { + destination = source } return@mapIndexedNotNull ParcelableMediaUpdate(destination.toString(), mediaType) } catch (e: IOException) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsDeleteMediaTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsDeleteMediaTask.kt new file mode 100644 index 000000000..4344c7501 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsDeleteMediaTask.kt @@ -0,0 +1,41 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * 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 . + */ + +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( + context: Context, + val sources: Array +) : AbstractTask() { + + 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]) } + } + +} \ No newline at end of file diff --git a/twidere/src/main/res/layout/fragment_messages_conversation.xml b/twidere/src/main/res/layout/fragment_messages_conversation.xml index 9dead9271..bb9143a6e 100644 --- a/twidere/src/main/res/layout/fragment_messages_conversation.xml +++ b/twidere/src/main/res/layout/fragment_messages_conversation.xml @@ -47,11 +47,11 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="?android:colorBackground" + android:clipToPadding="false" android:gravity="bottom" android:orientation="horizontal" android:outlineProvider="bounds" - android:paddingBottom="@dimen/element_spacing_normal" - android:paddingTop="@dimen/element_spacing_normal" + android:padding="@dimen/element_spacing_normal" tools:ignore="UnusedAttribute"> + tools:tint="?android:textColorSecondary" + tools:visibility="visible"/> + android:layout_alignParentRight="true"> 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. Media upload failed. Media uploader not found, maybe it was uninstalled. - Message too long. + Attach media to DM is not supported + Too many media + Message too long No content Twitter\'s rate limit exceeded, please retry %s