diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/AbsServiceInterface.java b/twidere/src/main/java/org/mariotaku/twidere/util/AbsServiceInterface.java index 90411f110..9c655074e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/AbsServiceInterface.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/AbsServiceInterface.java @@ -31,6 +31,10 @@ import android.support.annotation.Nullable; import org.mariotaku.twidere.constant.IntentConstants; import org.mariotaku.twidere.util.ServiceUtils.ServiceToken; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + public abstract class AbsServiceInterface implements IInterface { private final Context mContext; @@ -40,27 +44,10 @@ public abstract class AbsServiceInterface implements IInte private I mIInterface; private ServiceToken mToken; - private boolean mDisconnected; - - private final ServiceConnection mConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(final ComponentName service, final IBinder obj) { - mIInterface = AbsServiceInterface.this.onServiceConnected(service, obj); - mDisconnected = false; - } - - @Override - public void onServiceDisconnected(final ComponentName service) { - mIInterface = null; - mDisconnected = true; - } - }; protected abstract I onServiceConnected(ComponentName service, IBinder obj); protected AbsServiceInterface(final Context context, final String componentName, @Nullable final Bundle metaData) { - mDisconnected = true; mContext = context; mShortenerName = componentName; mMetaData = metaData; @@ -86,19 +73,38 @@ public abstract class AbsServiceInterface implements IInte final Intent intent = new Intent(IntentConstants.INTENT_ACTION_EXTENSION_SHORTEN_STATUS); final ComponentName component = ComponentName.unflattenFromString(mShortenerName); intent.setComponent(component); - mDisconnected = true; - mToken = ServiceUtils.bindToService(mContext, intent, mConnection); - if (mToken == null) return false; - mDisconnected = false; - while (mIInterface == null && !mDisconnected) { - try { - Thread.sleep(50L); - } catch (InterruptedException e) { - // Ignore - return false; + final FutureTask futureTask = new FutureTask<>(new Callable() { + @Override + public Boolean call() throws Exception { + return mIInterface != null; } + }); + mToken = ServiceUtils.bindToService(mContext, intent, new ServiceConnection() { + @Override + public void onServiceConnected(final ComponentName name, final IBinder obj) { + mIInterface = AbsServiceInterface.this.onServiceConnected(name, obj); + if (!futureTask.isDone() && !futureTask.isCancelled()) { + futureTask.run(); + } + } + + @Override + public void onServiceDisconnected(final ComponentName name) { + mIInterface = null; + if (!futureTask.isDone() && !futureTask.isCancelled()) { + futureTask.run(); + } + } + }); + if (mToken == null) return false; + + try { + return futureTask.get(); + } catch (InterruptedException e) { + return false; + } catch (ExecutionException e) { + return false; } - return true; } public final void checkService(CheckServiceAction action) throws CheckServiceException { diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/ServiceUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/ServiceUtils.java index da2e03e52..8007375f1 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/ServiceUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/ServiceUtils.java @@ -49,7 +49,7 @@ public final class ServiceUtils { return new ServiceToken(cw); } } - Log.e(LOGTAG, "Failed to bind to service"); + DebugLog.w(LOGTAG, "Failed to bind to service", null); return null; } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/AccountSelectorActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/AccountSelectorActivity.kt index 461e67bf3..21e1c71dc 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/AccountSelectorActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/AccountSelectorActivity.kt @@ -129,6 +129,7 @@ class AccountSelectorActivity : BaseActivity(), OnItemClickListener { } val data = Intent() data.putExtra(EXTRA_IDS, checkedIds) + data.putExtra(EXTRA_EXTRAS, intent.getBundleExtra(EXTRA_EXTRAS)) setResult(Activity.RESULT_OK, data) finish() } @@ -143,6 +144,7 @@ class AccountSelectorActivity : BaseActivity(), OnItemClickListener { val data = Intent() data.putExtra(EXTRA_ID, account.key.id) data.putExtra(EXTRA_ACCOUNT_KEY, account.key) + data.putExtra(EXTRA_EXTRAS, intent.getBundleExtra(EXTRA_EXTRAS)) val startIntent = startIntent if (startIntent != null) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.kt index 204d1c22d..57372dd9a 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.kt @@ -356,6 +356,8 @@ class ParcelableActivitiesAdapter( fun onStatusActionClick(holder: IStatusViewHolder, id: Int, position: Int) + fun onStatusActionLongClick(holder: IStatusViewHolder, id: Int, position: Int): Boolean + fun onStatusMenuClick(holder: IStatusViewHolder, menuView: View, position: Int) fun onMediaClick(holder: IStatusViewHolder, view: View, media: ParcelableMedia, position: Int) @@ -398,8 +400,13 @@ class ParcelableActivitiesAdapter( } override fun onItemActionClick(holder: RecyclerView.ViewHolder, id: Int, position: Int) { - val adapter = adapterRef.get() ?: return - adapter.activityAdapterListener?.onStatusActionClick(holder as IStatusViewHolder, id, position) + val listener = adapterRef.get()?.activityAdapterListener ?: return + listener.onStatusActionClick(holder as IStatusViewHolder, id, position) + } + + override fun onItemActionLongClick(holder: RecyclerView.ViewHolder, id: Int, position: Int): Boolean { + val listener = adapterRef.get()?.activityAdapterListener ?: return false + return listener.onStatusActionLongClick(holder as IStatusViewHolder, id, position) } override fun onStatusLongClick(holder: IStatusViewHolder, position: Int): Boolean { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/iface/ContentCardClickListener.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/iface/ContentCardClickListener.kt index fa142141f..3f35c8104 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/iface/ContentCardClickListener.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/iface/ContentCardClickListener.kt @@ -28,5 +28,7 @@ import android.view.View interface ContentCardClickListener { fun onItemActionClick(holder: ViewHolder, id: Int, position: Int) {} + fun onItemActionLongClick(holder: ViewHolder, id: Int, position: Int): Boolean = false + fun onItemMenuClick(holder: ViewHolder, menuView: View, position: Int) {} } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt index c22e4109a..e2bff6974 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt @@ -20,6 +20,7 @@ package org.mariotaku.twidere.fragment import android.accounts.AccountManager +import android.app.Activity import android.content.Context import android.content.Intent import android.graphics.Rect @@ -49,8 +50,12 @@ import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter.Companion.ITEM_ import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter import org.mariotaku.twidere.annotation.ReadPositionTag -import org.mariotaku.twidere.constant.* +import org.mariotaku.twidere.constant.IntentConstants.* import org.mariotaku.twidere.constant.KeyboardShortcutConstants.* +import org.mariotaku.twidere.constant.displaySensitiveContentsKey +import org.mariotaku.twidere.constant.newDocumentApiKey +import org.mariotaku.twidere.constant.readFromBottomKey +import org.mariotaku.twidere.constant.rememberPositionKey import org.mariotaku.twidere.extension.model.getAccountType import org.mariotaku.twidere.fragment.AbsStatusesFragment.DefaultOnLikedListener import org.mariotaku.twidere.loader.iface.IExtendedLoader @@ -101,11 +106,20 @@ abstract class AbsActivitiesFragment protected constructor() : pauseOnScrollListener = PauseRecyclerViewOnScrollListener(adapter.mediaLoader.imageLoader, false, true) val loaderArgs = Bundle(arguments) - loaderArgs.putBoolean(IntentConstants.EXTRA_FROM_USER, true) + loaderArgs.putBoolean(EXTRA_FROM_USER, true) loaderManager.initLoader(0, loaderArgs, this) showProgress() } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + when (requestCode) { + AbsStatusesFragment.REQUEST_FAVORITE_SELECT_ACCOUNT, + AbsStatusesFragment.REQUEST_RETWEET_SELECT_ACCOUNT -> { + AbsStatusesFragment.handleActionActivityResult(this, requestCode, resultCode, data) + } + } + } + abstract fun getActivities(param: RefreshTaskParam): Boolean override fun handleKeyboardShortcutSingle(handler: KeyboardShortcutsHandler, keyCode: Int, event: KeyEvent, metaState: Int): Boolean { @@ -133,8 +147,8 @@ abstract class AbsActivitiesFragment protected constructor() : if (action == null) return false when (action) { ACTION_STATUS_REPLY -> { - val intent = Intent(IntentConstants.INTENT_ACTION_REPLY) - intent.putExtra(IntentConstants.EXTRA_STATUS, status) + val intent = Intent(INTENT_ACTION_REPLY) + intent.putExtra(EXTRA_STATUS, status) startActivity(intent) return true } @@ -185,8 +199,8 @@ abstract class AbsActivitiesFragment protected constructor() : } override fun onCreateLoader(id: Int, args: Bundle): Loader> { - val fromUser = args.getBoolean(IntentConstants.EXTRA_FROM_USER) - args.remove(IntentConstants.EXTRA_FROM_USER) + val fromUser = args.getBoolean(EXTRA_FROM_USER) + args.remove(EXTRA_FROM_USER) return onCreateActivitiesLoader(activity, args, fromUser) } @@ -317,9 +331,9 @@ abstract class AbsActivitiesFragment protected constructor() : val activity = activity when (id) { R.id.reply -> { - val intent = Intent(IntentConstants.INTENT_ACTION_REPLY) + val intent = Intent(INTENT_ACTION_REPLY) intent.`package` = activity.packageName - intent.putExtra(IntentConstants.EXTRA_STATUS, status) + intent.putExtra(EXTRA_STATUS, status) activity.startActivity(intent) } R.id.retweet -> { @@ -335,6 +349,11 @@ abstract class AbsActivitiesFragment protected constructor() : } } + override fun onStatusActionLongClick(holder: IStatusViewHolder, id: Int, position: Int): Boolean { + val status = getActivityStatus(position) ?: return false + return AbsStatusesFragment.handleActionLongClick(this, status, adapter.getItemId(position), id) + } + override fun onActivityClick(holder: ActivityTitleSummaryViewHolder, position: Int) { val activity = adapter.getActivity(position) ?: return val list = ArrayList() diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsStatusesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsStatusesFragment.kt index 05b6e7b99..1abd9f573 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsStatusesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsStatusesFragment.kt @@ -20,10 +20,12 @@ package org.mariotaku.twidere.fragment import android.accounts.AccountManager +import android.app.Activity import android.content.Context import android.content.Intent import android.graphics.Rect import android.os.Bundle +import android.support.v4.app.Fragment import android.support.v4.app.FragmentManager import android.support.v4.app.LoaderManager.LoaderCallbacks import android.support.v4.content.Loader @@ -36,22 +38,18 @@ import edu.tsinghua.hotmobi.HotMobiLogger import edu.tsinghua.hotmobi.model.MediaEvent import kotlinx.android.synthetic.main.fragment_content_recyclerview.* import org.mariotaku.kpreferences.get -import org.mariotaku.ktextension.coerceInOr -import org.mariotaku.ktextension.isNullOrEmpty -import org.mariotaku.ktextension.rangeOfSize +import org.mariotaku.ktextension.* import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants +import org.mariotaku.twidere.activity.AccountSelectorActivity import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter import org.mariotaku.twidere.annotation.ReadPositionTag import org.mariotaku.twidere.annotation.Referral +import org.mariotaku.twidere.constant.* import org.mariotaku.twidere.constant.IntentConstants.* import org.mariotaku.twidere.constant.KeyboardShortcutConstants.* -import org.mariotaku.twidere.constant.displaySensitiveContentsKey -import org.mariotaku.twidere.constant.newDocumentApiKey -import org.mariotaku.twidere.constant.readFromBottomKey -import org.mariotaku.twidere.constant.rememberPositionKey import org.mariotaku.twidere.extension.model.getAccountType import org.mariotaku.twidere.graphic.like.LikeAnimationDrawable import org.mariotaku.twidere.loader.iface.IExtendedLoader @@ -70,8 +68,7 @@ import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder /** * Created by mariotaku on 14/11/5. */ -abstract class AbsStatusesFragment protected constructor() : - AbsContentListRecyclerViewFragment(), +abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment(), LoaderCallbacks?>, IStatusViewHolder.StatusClickListener, KeyboardShortcutCallback { @@ -163,6 +160,14 @@ abstract class AbsStatusesFragment protected constructor() : super.onDestroy() } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + when (requestCode) { + REQUEST_FAVORITE_SELECT_ACCOUNT, REQUEST_RETWEET_SELECT_ACCOUNT -> { + handleActionActivityResult(this, requestCode, resultCode, data) + } + } + } + abstract fun getStatuses(param: RefreshTaskParam): Boolean override fun handleKeyboardShortcutSingle(handler: KeyboardShortcutsHandler, keyCode: Int, event: KeyEvent, metaState: Int): Boolean { @@ -370,7 +375,12 @@ abstract class AbsStatusesFragment protected constructor() : override fun onItemActionClick(holder: RecyclerView.ViewHolder, id: Int, position: Int) { val status = adapter.getStatus(position) ?: return - handleStatusActionClick(context, fragmentManager, twitterWrapper, holder as StatusViewHolder, status, id) + handleActionClick(context, fragmentManager, twitterWrapper, holder as StatusViewHolder, status, id) + } + + override fun onItemActionLongClick(holder: RecyclerView.ViewHolder, id: Int, position: Int): Boolean { + val status = adapter.getStatus(position) ?: return false + return handleActionLongClick(this, status, adapter.getItemId(position), id) } override fun createItemDecoration(context: Context, recyclerView: RecyclerView, layoutManager: LinearLayoutManager): RecyclerView.ItemDecoration? { @@ -508,12 +518,13 @@ abstract class AbsStatusesFragment protected constructor() : class DefaultOnLikedListener( private val twitter: AsyncTwitterWrapper, - private val status: ParcelableStatus + private val status: ParcelableStatus, + private val accountKey: UserKey? = null ) : LikeAnimationDrawable.OnLikedListener { override fun onLiked(): Boolean { if (status.is_favorite) return false - twitter.createFavoriteAsync(status.account_key, status) + twitter.createFavoriteAsync(accountKey ?: status.account_key, status) return true } } @@ -533,11 +544,11 @@ abstract class AbsStatusesFragment protected constructor() : companion object { - fun handleStatusActionClick(context: Context, fm: FragmentManager, - twitter: AsyncTwitterWrapper?, holder: StatusViewHolder, - status: ParcelableStatus?, id: Int) { + const val REQUEST_FAVORITE_SELECT_ACCOUNT = 101 + const val REQUEST_RETWEET_SELECT_ACCOUNT = 102 - if (status == null) return + fun handleActionClick(context: Context, fm: FragmentManager, twitter: AsyncTwitterWrapper?, + holder: StatusViewHolder, status: ParcelableStatus, id: Int) { when (id) { R.id.reply -> { val intent = Intent(INTENT_ACTION_REPLY) @@ -553,11 +564,58 @@ abstract class AbsStatusesFragment protected constructor() : if (status.is_favorite) { twitter.destroyFavoriteAsync(status.account_key, status.id) } else { - holder.playLikeAnimation(DefaultOnLikedListener(twitter, - status)) + holder.playLikeAnimation(DefaultOnLikedListener(twitter, status)) } } } } + + fun handleActionLongClick(fragment: Fragment, status: ParcelableStatus, itemId: Long, id: Int): Boolean { + when (id) { + R.id.favorite -> { + val intent = selectAccountIntent(fragment.context, status, itemId) + fragment.startActivityForResult(intent, REQUEST_FAVORITE_SELECT_ACCOUNT) + return true + } + R.id.retweet -> { + val intent = selectAccountIntent(fragment.context, status, itemId) + fragment.startActivityForResult(intent, REQUEST_RETWEET_SELECT_ACCOUNT) + return true + } + } + return false + } + + fun handleActionActivityResult(fragment: BaseFragment, requestCode: Int, resultCode: Int, data: Intent?) { + when (requestCode) { + AbsStatusesFragment.REQUEST_FAVORITE_SELECT_ACCOUNT -> { + if (resultCode != Activity.RESULT_OK || data == null) return + val accountKey = data.getParcelableExtra(IntentConstants.EXTRA_ACCOUNT_KEY) + val extras = data.getBundleExtra(IntentConstants.EXTRA_EXTRAS) + val status = extras.getParcelable(IntentConstants.EXTRA_STATUS) + fragment.twitterWrapper.createFavoriteAsync(accountKey, status) + } + AbsStatusesFragment.REQUEST_RETWEET_SELECT_ACCOUNT -> { + if (resultCode != Activity.RESULT_OK || data == null) return + val accountKey = data.getParcelableExtra(IntentConstants.EXTRA_ACCOUNT_KEY) + val extras = data.getBundleExtra(IntentConstants.EXTRA_EXTRAS) + val status = extras.getParcelable(IntentConstants.EXTRA_STATUS) + RetweetQuoteDialogFragment.show(fragment.childFragmentManager, status, accountKey) + } + } + } + + fun selectAccountIntent(context: Context, status: ParcelableStatus, itemId: Long): Intent { + val intent = Intent(context, AccountSelectorActivity::class.java) + intent.putExtra(EXTRA_SELECT_ONLY_ITEM_AUTOMATICALLY, true) + intent.putExtra(EXTRA_ACCOUNT_HOST, status.account_key.host) + intent.putExtra(EXTRA_SINGLE_SELECTION, true) + intent.putExtra(EXTRA_EXTRAS, Bundle { + this[EXTRA_STATUS] = status + this[EXTRA_ID] = itemId + }) + return intent + } + } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ExtensionsListFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ExtensionsListFragment.kt index 489658ea7..182b6ae45 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ExtensionsListFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ExtensionsListFragment.kt @@ -97,7 +97,7 @@ class ExtensionsListFragment : AbsContentListViewFragment(), inflater.inflate(R.menu.action_extension, menu) val adapterMenuInfo = menuInfo as AdapterContextMenuInfo val extensionInfo = adapter.getItem(adapterMenuInfo.position) - if (extensionInfo.pname != null && extensionInfo.settings != null) { + if (extensionInfo.settings != null) { val intent = Intent(IntentConstants.INTENT_ACTION_EXTENSION_SETTINGS) intent.setClassName(extensionInfo.pname, extensionInfo.settings) menu.setItemAvailability(R.id.settings, packageManager!!.queryIntentActivities(intent, 0).size == 1) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ItemsListFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ItemsListFragment.kt index 3c4c0142b..39c60a0d2 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ItemsListFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/ItemsListFragment.kt @@ -62,10 +62,16 @@ class ItemsListFragment : AbsContentListRecyclerViewFragment { + AbsStatusesFragment.handleActionActivityResult(this, requestCode, resultCode, data) + } + } + } + override fun onCreateLoader(id: Int, args: Bundle?): Loader?> { return ItemsLoader(context, arguments) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/RetweetQuoteDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/RetweetQuoteDialogFragment.kt index 099f38de4..4a1e41493 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/RetweetQuoteDialogFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/RetweetQuoteDialogFragment.kt @@ -35,6 +35,8 @@ import android.view.Gravity import android.view.View import android.widget.EditText import com.twitter.Validator +import org.mariotaku.ktextension.Bundle +import org.mariotaku.ktextension.set import org.mariotaku.ktextension.setItemAvailability import org.mariotaku.twidere.R import org.mariotaku.twidere.adapter.DummyItemAdapter @@ -42,10 +44,7 @@ import org.mariotaku.twidere.annotation.AccountType import org.mariotaku.twidere.constant.IntentConstants.* import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_QUICK_SEND import org.mariotaku.twidere.extension.applyTheme -import org.mariotaku.twidere.model.AccountDetails -import org.mariotaku.twidere.model.Draft -import org.mariotaku.twidere.model.ParcelableStatus -import org.mariotaku.twidere.model.ParcelableStatusUpdate +import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.service.LengthyOperationsService import org.mariotaku.twidere.util.Analyzer @@ -62,9 +61,9 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = AlertDialog.Builder(context) - val context = builder.context - val status = status!! - val details = AccountUtils.getAccountDetails(AccountManager.get(context), status.account_key, true)!! + val status = this.status + val accountKey = this.accountKey + val details = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true)!! builder.setView(R.layout.dialog_status_quote_retweet) builder.setTitle(R.string.retweet_quote_confirm_title) @@ -104,7 +103,7 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() { val useQuote = useQuote(!status.user_is_protected, details) commentContainer.visibility = if (useQuote) View.VISIBLE else View.GONE - editComment.accountKey = (status.account_key) + editComment.accountKey = details.key val sendByEnter = preferences.getBoolean(KEY_QUICK_SEND) val enterHandler = EditTextEnterHandler.attach(editComment, object : EditTextEnterHandler.EnterListener { @@ -155,7 +154,7 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() { if (editComment.length() > 0) { dismissDialog = retweetOrQuote(details, status, SHOW_PROTECTED_CONFIRM) } else if (isMyRetweet(status)) { - twitterWrapper.cancelRetweetAsync(status.account_key, status.id, status.my_retweet_id) + twitterWrapper.cancelRetweetAsync(details.key, status.id, status.my_retweet_id) dismissDialog = true } else if (useQuote(!status.user_is_protected, details)) { dismissDialog = retweetOrQuote(details, status, SHOW_PROTECTED_CONFIRM) @@ -192,12 +191,11 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() { textCountView.textCount = validator.getTweetLength(s.toString()) } - private val status: ParcelableStatus? - get() { - val args = arguments - if (!args.containsKey(EXTRA_STATUS)) return null - return args.getParcelable(EXTRA_STATUS) - } + private val status: ParcelableStatus + get() = arguments.getParcelable(EXTRA_STATUS) + + private val accountKey: UserKey + get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY) ?: status.account_key @CheckResult private fun retweetOrQuote(account: AccountDetails, status: ParcelableStatus, @@ -252,7 +250,7 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() { update.is_possibly_sensitive = status.is_possibly_sensitive LengthyOperationsService.updateStatusesAsync(context, Draft.Action.QUOTE, update) } else { - twitter.retweetStatusAsync(status.account_key, status) + twitter.retweetStatusAsync(account.key, status) } return true } @@ -314,11 +312,12 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() { val FRAGMENT_TAG = "retweet_quote" private val SHOW_PROTECTED_CONFIRM = java.lang.Boolean.parseBoolean("false") - fun show(fm: FragmentManager, status: ParcelableStatus): RetweetQuoteDialogFragment { - val args = Bundle() - args.putParcelable(EXTRA_STATUS, status) + fun show(fm: FragmentManager, status: ParcelableStatus, accountKey: UserKey? = null): RetweetQuoteDialogFragment { val f = RetweetQuoteDialogFragment() - f.arguments = args + f.arguments = Bundle { + this[EXTRA_STATUS] = status + this[EXTRA_ACCOUNT_KEY] = accountKey + } f.show(fm, FRAGMENT_TAG) return f } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt index 58cbb7566..373f899f3 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt @@ -33,7 +33,6 @@ import android.os.Bundle import android.support.annotation.UiThread import android.support.v4.app.LoaderManager.LoaderCallbacks import android.support.v4.app.hasRunningLoadersSafe -import android.support.v4.content.AsyncTaskLoader import android.support.v4.content.ContextCompat import android.support.v4.content.FixedAsyncTaskLoader import android.support.v4.content.Loader @@ -98,8 +97,6 @@ import org.mariotaku.twidere.loader.ConversationLoader import org.mariotaku.twidere.loader.ParcelableStatusLoader import org.mariotaku.twidere.menu.FavoriteItemProvider import org.mariotaku.twidere.model.* -import org.mariotaku.twidere.model.ParcelableStatusValuesCreator -import org.mariotaku.twidere.model.ParcelableActivityCursorIndices import org.mariotaku.twidere.model.analyzer.Share import org.mariotaku.twidere.model.analyzer.StatusView import org.mariotaku.twidere.model.message.FavoriteTaskEvent @@ -259,6 +256,10 @@ class StatusFragment : BaseFragment(), LoaderCallbacks { + AbsStatusesFragment.handleActionActivityResult(this, requestCode, resultCode, data) + } } } @@ -310,11 +311,17 @@ class StatusFragment : BaseFragment(), LoaderCallbacks + return "extension not found" + else -> { + val cause = ex.cause + when (cause) { + is UpdateStatusTask.ExtensionVersionMismatchException -> + return "extension version mismatch" + is IOException -> + return "io exception" + is MicroBlogException -> { + if (cause.isCausedByNetworkIssue) { + return "network error" + } + return "request error" + } + } + when (ex) { + is UpdateStatusTask.ShortenException, + is UpdateStatusTask.UploadException -> + return "extension error" + } + return "internal error" + } + } + } + override val name: String get() = "Tweet" @@ -36,6 +70,7 @@ data class UpdateStatus( action("Media Type", parcelableMediaTypeString(mediaType)) action("Location Type", locationType) action("Success", success.toString()) + } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/service/LengthyOperationsService.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/service/LengthyOperationsService.kt index c91a8ee57..65eb5c6dd 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/service/LengthyOperationsService.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/service/LengthyOperationsService.kt @@ -378,11 +378,11 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") { Utils.setLastSeen(this, UserKey(recipientId, accountKey.host), System.currentTimeMillis()) - return SingleResponse.getInstance(directMessage) + return SingleResponse(directMessage) } catch (e: IOException) { - return SingleResponse.getInstance(e) + return SingleResponse(e) } catch (e: MicroBlogException) { - return SingleResponse.getInstance(e) + return SingleResponse(e) } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt index bd09811dd..542d218c3 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt @@ -94,7 +94,7 @@ class UpdateStatusTask( val hasLocation = statusUpdate.location != null val preciseLocation = statusUpdate.display_coordinates Analyzer.log(UpdateStatus(result.accountTypes.firstOrNull(), actionType, mediaType, - hasLocation, preciseLocation, result.succeed)) + hasLocation, preciseLocation, result.succeed, result.exceptions.firstOrNull() ?: result.exception)) } @Throws(UpdateStatusException::class) @@ -110,11 +110,7 @@ class UpdateStatusTask( uploadMedia(uploader, update, pendingUpdate) shortenStatus(shortener, update, pendingUpdate) - try { - result = requestUpdateStatus(update, pendingUpdate, draftId) - } catch (e: IOException) { - return UpdateStatusResult(UpdateStatusException(e), draftId) - } + result = requestUpdateStatus(update, pendingUpdate, draftId) mediaUploadCallback(uploader, pendingUpdate, result) statusShortenCallback(shortener, pendingUpdate, result) @@ -240,10 +236,12 @@ class UpdateStatusTask( } } - @Throws(IOException::class) - private fun requestUpdateStatus(statusUpdate: ParcelableStatusUpdate, - pendingUpdate: PendingStatusUpdate, - draftId: Long): UpdateStatusResult { + @Throws(UpdateStatusException::class) + private fun requestUpdateStatus( + statusUpdate: ParcelableStatusUpdate, + pendingUpdate: PendingStatusUpdate, + draftId: Long + ): UpdateStatusResult { stateCallback.onUpdatingStatus() @@ -253,55 +251,54 @@ class UpdateStatusTask( val account = statusUpdate.accounts[i] result.accountTypes[i] = account.type val microBlog = MicroBlogAPIFactory.getInstance(context, account.key) - var mediaBody: MediaStreamBody? = null try { - when (account.type) { + val requestResult = when (account.type) { AccountType.FANFOU -> { // Call uploadPhoto if media present - if (!ArrayUtils.isEmpty(statusUpdate.media)) { + if (statusUpdate.media.isNotNullOrEmpty()) { // Fanfou only allow one photo - if (statusUpdate.media.size > 1) { - result.exceptions[i] = MicroBlogException( - context.getString(R.string.error_too_many_photos_fanfou)) - } else { - val sizeLimit = account.size_limit - val firstMedia = statusUpdate.media.first() - mediaBody = getBodyFromMedia(context, mediaLoader, Uri.parse(firstMedia.uri), - sizeLimit, firstMedia.type, false, ContentLengthInputStream.ReadListener { length, position -> - stateCallback.onUploadingProgressChanged(-1, position, length) - }) - val photoUpdate = PhotoStatusUpdate(mediaBody.body, - pendingUpdate.overrideTexts[i]) - val requestResult = microBlog.uploadPhoto(photoUpdate) - - result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult, - account.key, false) - } + fanfouUpdateStatusWithPhoto(microBlog, statusUpdate, pendingUpdate, + pendingUpdate.overrideTexts[i], account.size_limit, i) } else { - val requestResult = twitterUpdateStatus(microBlog, statusUpdate, - pendingUpdate, pendingUpdate.overrideTexts[i], i) - - result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult, - account.key, false) + twitterUpdateStatus(microBlog, statusUpdate, pendingUpdate, + pendingUpdate.overrideTexts[i], i) } } else -> { - val requestResult = twitterUpdateStatus(microBlog, statusUpdate, - pendingUpdate, pendingUpdate.overrideTexts[i], i) - - result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult, - account.key, false) + twitterUpdateStatus(microBlog, statusUpdate, pendingUpdate, + pendingUpdate.overrideTexts[i], i) } } + result.statuses[i] = ParcelableStatusUtils.fromStatus(requestResult, + account.key, false) } catch (e: MicroBlogException) { result.exceptions[i] = e - } finally { - Utils.closeSilently(mediaBody) } } return result } + @Throws(MicroBlogException::class, UploadException::class) + private fun fanfouUpdateStatusWithPhoto(microBlog: MicroBlog, statusUpdate: ParcelableStatusUpdate, + pendingUpdate: PendingStatusUpdate, overrideText: String, + sizeLimit: SizeLimit, updateIndex: Int): Status { + if (statusUpdate.media.size > 1) { + throw MicroBlogException(context.getString(R.string.error_too_many_photos_fanfou)) + } + val media = statusUpdate.media.first() + try { + return getBodyFromMedia(context, mediaLoader, Uri.parse(media.uri), sizeLimit, media.type, + false, ContentLengthInputStream.ReadListener { length, position -> + stateCallback.onUploadingProgressChanged(-1, position, length) + }).use { mediaBody -> + val photoUpdate = PhotoStatusUpdate(mediaBody.body, pendingUpdate.overrideTexts[updateIndex]) + return@use microBlog.uploadPhoto(photoUpdate) + } + } catch (e: IOException) { + throw UploadException(e) + } + } + /** * Calling Twitter's upload method. This method sets multiple owner for bandwidth saving */ @@ -439,7 +436,7 @@ class UpdateStatusTask( } } catch (e: AbsServiceInterface.CheckServiceException) { if (e is ExtensionVersionMismatchException) { - throw UploadException(context.getString(R.string.uploader_version_incompatible)) + throw UploadException(context.getString(R.string.uploader_version_incompatible), e) } throw UploadException(e) } @@ -622,25 +619,16 @@ class UpdateStatusTask( open class UpdateStatusException : Exception { - constructor() : super() + protected constructor() : super() - constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) + protected constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) - constructor(throwable: Throwable) : super(throwable) + protected constructor(throwable: Throwable) : super(throwable) - constructor(message: String) : super(message) + protected constructor(message: String) : super(message) } - class UploaderNotFoundException : UpdateStatusException { - - constructor() : super() - - constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) - - constructor(throwable: Throwable) : super(throwable) - - constructor(message: String) : super(message) - } + class UploaderNotFoundException(message: String) : UpdateStatusException(message) class UploadException : UpdateStatusException { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/StatusViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/StatusViewHolder.kt index f2f9a6faa..4e3506ccd 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/StatusViewHolder.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/StatusViewHolder.kt @@ -11,7 +11,6 @@ import android.view.View.OnLongClickListener import android.widget.ImageView import kotlinx.android.synthetic.main.list_item_status.view.* import org.mariotaku.ktextension.applyFontFamily -import org.mariotaku.twidere.Constants import org.mariotaku.twidere.Constants.* import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.USER_TYPE_FANFOU_COM @@ -40,7 +39,7 @@ import java.lang.ref.WeakReference * * Created by mariotaku on 14/11/19. */ -class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) : ViewHolder(itemView), Constants, IStatusViewHolder { +class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) : ViewHolder(itemView), IStatusViewHolder { override val profileImageView: ProfileImageView by lazy { itemView.profileImage } override val profileTypeView: ImageView by lazy { itemView.profileType } @@ -463,6 +462,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) replyButton.setOnClickListener(eventListener) retweetButton.setOnClickListener(eventListener) favoriteButton.setOnClickListener(eventListener) + retweetButton.setOnLongClickListener(eventListener) + favoriteButton.setOnLongClickListener(eventListener) mediaLabel.setOnClickListener(eventListener) @@ -648,6 +649,12 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View) } return listener.onStatusLongClick(holder, position) } + holder.favoriteButton -> { + return listener.onItemActionLongClick(holder, R.id.favorite, position) + } + holder.retweetButton -> { + return listener.onItemActionLongClick(holder, R.id.retweet, position) + } } return false }