rewrite update status logic

close #506
close #539
This commit is contained in:
Mariotaku Lee 2017-02-08 17:36:47 +08:00
parent 8f7fd2b330
commit 632db4ad0c
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
16 changed files with 297 additions and 153 deletions

View File

@ -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<I extends IInterface> implements IInterface {
private final Context mContext;
@ -40,27 +44,10 @@ public abstract class AbsServiceInterface<I extends IInterface> 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<I extends IInterface> 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<Boolean> futureTask = new FutureTask<>(new Callable<Boolean>() {
@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 {

View File

@ -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;
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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) {}
}

View File

@ -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<List<ParcelableActivity>> {
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<Parcelable>()

View File

@ -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<ParcelableStatusesAdapter>(),
abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<ParcelableStatusesAdapter>(),
LoaderCallbacks<List<ParcelableStatus>?>, 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<UserKey>(IntentConstants.EXTRA_ACCOUNT_KEY)
val extras = data.getBundleExtra(IntentConstants.EXTRA_EXTRAS)
val status = extras.getParcelable<ParcelableStatus>(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<UserKey>(IntentConstants.EXTRA_ACCOUNT_KEY)
val extras = data.getBundleExtra(IntentConstants.EXTRA_EXTRAS)
val status = extras.getParcelable<ParcelableStatus>(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
}
}
}

View File

@ -97,7 +97,7 @@ class ExtensionsListFragment : AbsContentListViewFragment<ExtensionsAdapter>(),
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)

View File

@ -62,10 +62,16 @@ class ItemsListFragment : AbsContentListRecyclerViewFragment<VariousItemsAdapter
override fun onItemActionClick(holder: RecyclerView.ViewHolder, id: Int, position: Int) {
val status = dummyItemAdapter.getStatus(position) ?: return
AbsStatusesFragment.handleStatusActionClick(context, fragmentManager,
AbsStatusesFragment.handleActionClick(context, fragmentManager,
twitterWrapper, holder as StatusViewHolder, status, id)
}
override fun onItemActionLongClick(holder: RecyclerView.ViewHolder, id: Int, position: Int): Boolean {
val status = dummyItemAdapter.getStatus(position) ?: return false
return AbsStatusesFragment.handleActionLongClick(this@ItemsListFragment, status,
adapter.getItemId(position), id)
}
override fun onItemMenuClick(holder: RecyclerView.ViewHolder, menuView: View, position: Int) {
if (activity == null) return
val view = layoutManager.findViewByPosition(position) ?: return
@ -101,6 +107,15 @@ class ItemsListFragment : AbsContentListRecyclerViewFragment<VariousItemsAdapter
return adapter
}
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)
}
}
}
override fun onCreateLoader(id: Int, args: Bundle?): Loader<List<*>?> {
return ItemsLoader(context, arguments)
}

View File

@ -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<ParcelableStatus>(EXTRA_STATUS)
}
private val status: ParcelableStatus
get() = arguments.getParcelable<ParcelableStatus>(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
}

View File

@ -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<SingleResponse<Parcelable
IntentUtils.openStatus(activity, accountKey, status.id)
}
}
AbsStatusesFragment.REQUEST_FAVORITE_SELECT_ACCOUNT,
AbsStatusesFragment.REQUEST_RETWEET_SELECT_ACCOUNT -> {
AbsStatusesFragment.handleActionActivityResult(this, requestCode, resultCode, data)
}
}
}
@ -310,11 +311,17 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
}
override fun onItemActionClick(holder: ViewHolder, id: Int, position: Int) {
val status = adapter.getStatus(position)
AbsStatusesFragment.handleStatusActionClick(context, fragmentManager, twitterWrapper,
val status = adapter.getStatus(position) ?: return
AbsStatusesFragment.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 AbsStatusesFragment.handleActionLongClick(this, status, adapter.getItemId(position), id)
}
override fun onStatusClick(holder: IStatusViewHolder, position: Int) {
val status = adapter.getStatus(position) ?: return
IntentUtils.openStatus(activity, status)
@ -339,15 +346,14 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
override fun onUserProfileClick(holder: IStatusViewHolder, position: Int) {
val status = adapter.getStatus(position)!!
IntentUtils.openUserProfile(activity, status.account_key, status.user_key,
status.user_screen_name, preferences.getBoolean(KEY_NEW_DOCUMENT_API), Referral.TIMELINE_STATUS,
status.user_screen_name, preferences[newDocumentApiKey], Referral.TIMELINE_STATUS,
null)
}
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey?, id: Long) {
val status = adapter.status
if (status == null || media == null) return
IntentUtils.openMediaDirectly(activity, accountKey, status, media, preferences.getBoolean(KEY_NEW_DOCUMENT_API),
null)
val status = adapter.status ?: return
IntentUtils.openMediaDirectly(activity, accountKey, status, media,
preferences[newDocumentApiKey], null)
// BEGIN HotMobi
val event = MediaEvent.create(activity, status, media, TimelineType.OTHER,
adapter.mediaPreviewEnabled)

View File

@ -105,7 +105,7 @@ class ExtensionsListLoader(
val label: String
val description: String
val pname: String
val settings: String
val settings: String?
val icon: Drawable
init {

View File

@ -1,11 +1,14 @@
package org.mariotaku.twidere.model.analyzer
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.draftActionTypeString
import org.mariotaku.twidere.extension.model.parcelableMediaTypeString
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
import org.mariotaku.twidere.util.Analyzer
import java.io.IOException
/**
* Created by mariotaku on 2016/12/28.
@ -17,7 +20,8 @@ data class UpdateStatus(
@ParcelableMedia.Type val mediaType: Int,
val hasLocation: Boolean,
val preciseLocation: Boolean,
val success: Boolean
val success: Boolean,
val exception: Exception?
) : Analyzer.Event {
private val locationType: String get() = if (!hasLocation) {
@ -28,6 +32,36 @@ data class UpdateStatus(
"place"
}
private val errorReason: String? get() {
val ex = exception ?: return null
when (ex) {
is UpdateStatusTask.ShortenerNotFoundException,
is UpdateStatusTask.UploaderNotFoundException ->
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())
}
}

View File

@ -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<ParcelableDirectMessage>(e)
return SingleResponse(e)
} catch (e: MicroBlogException) {
return SingleResponse.getInstance<ParcelableDirectMessage>(e)
return SingleResponse(e)
}
}

View File

@ -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 {

View File

@ -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
}